• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/builtins/builtins_symbol.h"
17 
18 #include "ecmascript/ecma_runtime_call_info.h"
19 #include "ecmascript/ecma_string.h"
20 #include "ecmascript/global_env.h"
21 #include "ecmascript/js_primitive_ref.h"
22 #include "ecmascript/js_symbol.h"
23 #include "ecmascript/js_tagged_value.h"
24 #include "ecmascript/object_factory.h"
25 #include "ecmascript/symbol_table.h"
26 
27 namespace panda::ecmascript::builtins {
28 // prototype
29 // 19.4.3.1
30 // constructor
SymbolConstructor(EcmaRuntimeCallInfo * argv)31 JSTaggedValue BuiltinsSymbol::SymbolConstructor(EcmaRuntimeCallInfo *argv)
32 {
33     ASSERT(argv);
34     BUILTINS_API_TRACE(argv->GetThread(), Symbol, Constructor);
35     JSThread *thread = argv->GetThread();
36     [[maybe_unused]] EcmaHandleScope handleScope(thread);
37     // 1.If NewTarget is not undefined, throw a TypeError exception.
38     JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
39     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
40     if (!newTarget->IsUndefined()) {
41         THROW_TYPE_ERROR_AND_RETURN(thread, "SymbolConstructor: NewTarget is not undefined",
42                                     JSTaggedValue::Exception());
43     }
44     // 2.If description is undefined, let descString be undefined.
45     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
46     if (key->IsUndefined()) {
47         JSHandle<JSSymbol> jsSymbol = factory->NewJSSymbol();
48         return jsSymbol.GetTaggedValue();
49     }
50     // 3.Else, let descString be ToString(description).
51     JSHandle<JSTaggedValue> descHandle = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, key));
52     // 4.ReturnIfAbrupt(descString).
53     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
54     // 5.Return a new unique Symbol value whose [[Description]] value is descString.
55     JSHandle<JSSymbol> jsSymbol = factory->NewPublicSymbol(descHandle);
56     return jsSymbol.GetTaggedValue();
57 }
58 
59 // 19.4.3.2 Symbol.prototype.toString()
ToString(EcmaRuntimeCallInfo * argv)60 JSTaggedValue BuiltinsSymbol::ToString(EcmaRuntimeCallInfo *argv)
61 {
62     ASSERT(argv);
63     BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToString);
64     JSThread *thread = argv->GetThread();
65     [[maybe_unused]] EcmaHandleScope handleScope(thread);
66     // 1.Let s be the this value.
67     JSHandle<JSTaggedValue> valueHandle = GetThis(argv);
68     // 2.If Type(s) is Symbol, let sym be s.
69     JSTaggedValue sym = valueHandle.GetTaggedValue();
70     // 3.Else
71     if (!valueHandle->IsSymbol()) {
72         if (valueHandle->IsHeapObject()) {
73             if (!valueHandle->IsJSPrimitiveRef()) {
74                 // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
75                 THROW_TYPE_ERROR_AND_RETURN(thread, "ToString: no [[SymbolData]]", JSTaggedValue::Exception());
76             }
77             // Let sym be the value of s's [[SymbolData]] internal slot.
78             JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue();
79             ASSERT(primitive.IsSymbol());
80             sym = primitive;
81         } else {
82             // If Type(s) is not Object, throw a TypeError exception.
83             THROW_TYPE_ERROR_AND_RETURN(thread, "ToString: s is not Object", JSTaggedValue::Exception());
84         }
85     }
86     // Return SymbolDescriptiveString(sym).
87     return SymbolDescriptiveString(thread, sym);
88 }
89 
SymbolDescriptiveString(JSThread * thread,JSTaggedValue sym)90 JSTaggedValue BuiltinsSymbol::SymbolDescriptiveString(JSThread *thread, JSTaggedValue sym)
91 {
92     BUILTINS_API_TRACE(thread, Symbol, SymbolDescriptiveString);
93     // Assert: Type(sym) is Symbol.
94     ASSERT(sym.IsSymbol());
95     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
96 
97     // Let desc be sym’s [[Description]] value.
98     auto symbolObject = reinterpret_cast<JSSymbol *>(sym.GetTaggedObject());
99     JSHandle<JSTaggedValue> descHandle(thread, symbolObject->GetDescription());
100 
101     // If desc is undefined, let desc be the empty string.
102 
103     if (descHandle->IsUndefined()) {
104         JSHandle<EcmaString> leftHandle(factory->NewFromASCII("Symbol("));
105         JSHandle<EcmaString> rightHandle(factory->NewFromASCII(")"));
106         JSHandle<EcmaString> str = factory->ConcatFromString(leftHandle, rightHandle);
107         return str.GetTaggedValue();
108     }
109     // Assert: Type(desc) is String.
110     ASSERT(descHandle->IsString());
111     // Return the result of concatenating the strings "Symbol(", desc, and ")".
112     JSHandle<EcmaString> leftHandle(factory->NewFromASCII("Symbol("));
113     JSHandle<EcmaString> rightHandle(factory->NewFromASCII(")"));
114     JSHandle<EcmaString> stringLeft =
115         factory->ConcatFromString(leftHandle, JSTaggedValue::ToString(thread, descHandle));
116     JSHandle<EcmaString> str = factory->ConcatFromString(stringLeft, rightHandle);
117     return str.GetTaggedValue();
118 }
119 
120 // 19.4.3.3
ValueOf(EcmaRuntimeCallInfo * argv)121 JSTaggedValue BuiltinsSymbol::ValueOf(EcmaRuntimeCallInfo *argv)
122 {
123     ASSERT(argv);
124     BUILTINS_API_TRACE(argv->GetThread(), Symbol, ValueOf);
125     JSThread *thread = argv->GetThread();
126     [[maybe_unused]] EcmaHandleScope handleScope(thread);
127     // Let s be the this value.
128     JSHandle<JSTaggedValue> valueHandle = GetThis(argv);
129     // If Type(s) is Symbol, return s.
130     if (valueHandle->IsSymbol()) {
131         return valueHandle.GetTaggedValue();
132     }
133     // If Type(s) is not Object, throw a TypeError exception.
134     if (!valueHandle->IsHeapObject()) {
135         // return TypeError
136         THROW_TYPE_ERROR_AND_RETURN(thread, "ValueOf: s is not Object", JSTaggedValue::Exception());
137     }
138     // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
139     if (!valueHandle->IsJSPrimitiveRef()) {
140         // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
141         THROW_TYPE_ERROR_AND_RETURN(thread, "ValueOf: no [[SymbolData]]", JSTaggedValue::Exception());
142     }
143     JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue();
144     ASSERT(primitive.IsSymbol());
145     // Return the value of s's [[SymbolData]] internal slot.
146     return primitive;
147 }
148 
149 // 19.4.2.1 Symbol.for (key)
For(EcmaRuntimeCallInfo * argv)150 JSTaggedValue BuiltinsSymbol::For(EcmaRuntimeCallInfo *argv)
151 {
152     ASSERT(argv);
153     BUILTINS_API_TRACE(argv->GetThread(), Symbol, For);
154     JSThread *thread = argv->GetThread();
155     [[maybe_unused]] EcmaHandleScope handleScope(thread);
156     // 1.Let stringKey be ToString(key).
157     JSHandle<JSTaggedValue> key = BuiltinsSymbol::GetCallArg(argv, 0);
158     JSHandle<JSTaggedValue> stringHandle = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, key));
159     // 2.ReturnIfAbrupt
160     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
161 
162     // 3.For each element e of the GlobalSymbolRegistry List,
163     // If SameValue(e.[[key]], stringKey) is true, return e.[[symbol]].
164     // 4.Assert: GlobalSymbolRegistry does not currently contain an entry for stringKey.
165     // 5.Let newSymbol be a new unique Symbol value whose [[Description]] value is stringKey.
166     // 6.Append the record { [[key]]: stringKey, [[symbol]]: newSymbol } to the GlobalSymbolRegistry List.
167     // 7.Return newSymbol.
168     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
169     JSHandle<JSSymbol> symbol = factory->NewSymbolWithTable(stringHandle);
170     return symbol.GetTaggedValue();
171 }
172 
173 // 19.4.2.5 Symbol.keyFor (sym)
KeyFor(EcmaRuntimeCallInfo * argv)174 JSTaggedValue BuiltinsSymbol::KeyFor(EcmaRuntimeCallInfo *argv)
175 {
176     ASSERT(argv);
177     BUILTINS_API_TRACE(argv->GetThread(), Symbol, KeyFor);
178     JSThread *thread = argv->GetThread();
179     [[maybe_unused]] EcmaHandleScope handleScope(thread);
180     // 1.If Type(sym) is not Symbol, throw a TypeError exception.
181     JSHandle<JSTaggedValue> sym = BuiltinsSymbol::GetCallArg(argv, 0);
182     if (!sym->IsSymbol()) {
183         // return typeError
184         THROW_TYPE_ERROR_AND_RETURN(thread, "KeyFor: sym is not Symbol", JSTaggedValue::Exception());
185     }
186     // 2.For each element e of the GlobalSymbolRegistry List,
187     // If SameValue(e.[[symbol]], sym) is true, return e.[[key]].
188     // 3.Assert: GlobalSymbolRegistry does not currently contain an entry for sym.
189     // 4.Return undefined.
190     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
191     auto *table = env->GetRegisterSymbols().GetObject<SymbolTable>();
192     JSTaggedValue key = table->FindSymbol(sym.GetTaggedValue());
193     if (key.IsUndefined()) {
194         return JSTaggedValue::Undefined();
195     }
196     return JSTaggedValue::ToString(thread, JSHandle<JSTaggedValue>(thread, key)).GetTaggedValue();
197 }
198 
199 // 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint )
ToPrimitive(EcmaRuntimeCallInfo * argv)200 JSTaggedValue BuiltinsSymbol::ToPrimitive(EcmaRuntimeCallInfo *argv)
201 {
202     // The allowed values for hint are "default", "number", and "string".
203     ASSERT(argv);
204     BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToPrimitive);
205     JSThread *thread = argv->GetThread();
206     [[maybe_unused]] EcmaHandleScope handleScope(thread);
207     // 1.Let s be the this value.
208     JSHandle<JSTaggedValue> sym = GetThis(argv);
209     // 2.If Type(s) is Symbol, return s.
210     if (sym->IsSymbol()) {
211         return sym.GetTaggedValue();
212     }
213     // 3.If Type(s) is not Object, throw a TypeError exception.
214     if (!sym->IsHeapObject()) {
215         // return TypeError
216         THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: s is not Object", JSTaggedValue::Exception());
217     }
218     ASSERT(sym->IsHeapObject());
219     // 4.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
220     // 5.Return the value of s's [[SymbolData]] internal slot.
221     if (!sym->IsJSPrimitiveRef()) {
222         // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
223         THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: no [[SymbolData]]", JSTaggedValue::Exception());
224     }
225     // Let sym be the value of s's [[SymbolData]] internal slot.
226     JSTaggedValue primitive = JSPrimitiveRef::Cast(sym->GetTaggedObject())->GetValue();
227     ASSERT(primitive.IsSymbol());
228     return primitive;
229 }
230 
DescriptionGetter(EcmaRuntimeCallInfo * argv)231 JSTaggedValue BuiltinsSymbol::DescriptionGetter(EcmaRuntimeCallInfo *argv)
232 {
233     ASSERT(argv);
234     BUILTINS_API_TRACE(argv->GetThread(), Symbol, DescriptionGetter);
235     // 1.Let s be the this value.
236     JSThread *thread = argv->GetThread();
237     [[maybe_unused]] EcmaHandleScope handleScope(thread);
238     JSHandle<JSTaggedValue> s = GetThis(argv);
239     // 2.Let sym be ? thisSymbolValue(s).
240     // 3.Return sym.[[Description]].
241     return ThisSymbolValue(thread, s);
242 }
243 
ThisSymbolValue(JSThread * thread,const JSHandle<JSTaggedValue> & value)244 JSTaggedValue BuiltinsSymbol::ThisSymbolValue(JSThread *thread, const JSHandle<JSTaggedValue> &value)
245 {
246     BUILTINS_API_TRACE(thread, Symbol, ThisSymbolValue);
247     if (value->IsSymbol()) {
248         JSTaggedValue desValue = JSSymbol::Cast(value->GetTaggedObject())->GetDescription();
249         return desValue;
250     }
251 
252     // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
253     if (value->IsJSPrimitiveRef()) {
254         JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue();
255         if (primitive.IsSymbol()) {
256             // Return the value of s's [[SymbolData]] internal slot.
257             JSTaggedValue primitiveDesValue = JSSymbol::Cast(primitive.GetTaggedObject())->GetDescription();
258             return primitiveDesValue;
259         }
260     }
261     THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to Symbol", JSTaggedValue::Exception());
262 }
263 }  // namespace panda::ecmascript::builtins
264