• 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/global_env.h"
19 #include "ecmascript/js_primitive_ref.h"
20 #include "ecmascript/symbol_table.h"
21 
22 namespace panda::ecmascript::builtins {
23 // prototype
24 // 19.4.3.1
25 // constructor
SymbolConstructor(EcmaRuntimeCallInfo * argv)26 JSTaggedValue BuiltinsSymbol::SymbolConstructor(EcmaRuntimeCallInfo *argv)
27 {
28     ASSERT(argv);
29     BUILTINS_API_TRACE(argv->GetThread(), Symbol, Constructor);
30     JSThread *thread = argv->GetThread();
31     [[maybe_unused]] EcmaHandleScope handleScope(thread);
32     // 1.If NewTarget is not undefined, throw a TypeError exception.
33     JSHandle<JSTaggedValue> key = GetCallArg(argv, 0);
34     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
35     if (!newTarget->IsUndefined()) {
36         THROW_TYPE_ERROR_AND_RETURN(thread, "SymbolConstructor: NewTarget is not undefined",
37                                     JSTaggedValue::Exception());
38     }
39     // 2.If description is undefined, let descString be undefined.
40     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
41     if (key->IsUndefined()) {
42         JSHandle<JSSymbol> jsSymbol = factory->NewJSSymbol();
43         return jsSymbol.GetTaggedValue();
44     }
45     // 3.Else, let descString be ToString(description).
46     JSHandle<JSTaggedValue> descHandle = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, key));
47     // 4.ReturnIfAbrupt(descString).
48     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
49     // 5.Return a new unique Symbol value whose [[Description]] value is descString.
50     JSHandle<JSSymbol> jsSymbol = factory->NewPublicSymbol(descHandle);
51     return jsSymbol.GetTaggedValue();
52 }
53 
54 // 19.4.3.2 Symbol.prototype.toString()
ToString(EcmaRuntimeCallInfo * argv)55 JSTaggedValue BuiltinsSymbol::ToString(EcmaRuntimeCallInfo *argv)
56 {
57     ASSERT(argv);
58     BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToString);
59     JSThread *thread = argv->GetThread();
60     [[maybe_unused]] EcmaHandleScope handleScope(thread);
61     // Let s be the this value.
62     JSHandle<JSTaggedValue> valueHandle = GetThis(argv);
63     // 1.If value is a Symbol, return value.
64     if (valueHandle->IsSymbol()) {
65         // Return SymbolDescriptiveString(sym).
66         return SymbolDescriptiveString(thread, valueHandle.GetTaggedValue());
67     }
68 
69     // 2.If value is an Object and value has a [[SymbolData]] internal slot, then
70     if (valueHandle->IsJSPrimitiveRef()) {
71         // Let sym be the value of s's [[SymbolData]] internal slot.
72         JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue();
73         if (primitive.IsSymbol()) {
74             return SymbolDescriptiveString(thread, primitive);
75         }
76     }
77 
78     // 3.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
79     THROW_TYPE_ERROR_AND_RETURN(thread, "ToString: no [[SymbolData]]", JSTaggedValue::Exception());
80 }
81 
SymbolDescriptiveString(JSThread * thread,JSTaggedValue sym)82 JSTaggedValue BuiltinsSymbol::SymbolDescriptiveString(JSThread *thread, JSTaggedValue sym)
83 {
84     BUILTINS_API_TRACE(thread, Symbol, SymbolDescriptiveString);
85     // Assert: Type(sym) is Symbol.
86     ASSERT(sym.IsSymbol());
87     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
88 
89     // Let desc be sym’s [[Description]] value.
90     auto symbolObject = reinterpret_cast<JSSymbol *>(sym.GetTaggedObject());
91     JSHandle<JSTaggedValue> descHandle(thread, symbolObject->GetDescription());
92 
93     // If desc is undefined, let desc be the empty string.
94 
95     if (descHandle->IsUndefined()) {
96         JSHandle<EcmaString> leftHandle(factory->NewFromASCII("Symbol("));
97         JSHandle<EcmaString> rightHandle(factory->NewFromASCII(")"));
98         JSHandle<EcmaString> str = factory->ConcatFromString(leftHandle, rightHandle);
99         return str.GetTaggedValue();
100     }
101     // Assert: Type(desc) is String.
102     ASSERT(descHandle->IsString());
103     // Return the result of concatenating the strings "Symbol(", desc, and ")".
104     JSHandle<EcmaString> leftHandle(factory->NewFromASCII("Symbol("));
105     JSHandle<EcmaString> rightHandle(factory->NewFromASCII(")"));
106     JSHandle<EcmaString> stringLeft =
107         factory->ConcatFromString(leftHandle, JSTaggedValue::ToString(thread, descHandle));
108     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
109     JSHandle<EcmaString> str = factory->ConcatFromString(stringLeft, rightHandle);
110     return str.GetTaggedValue();
111 }
112 
113 // 19.4.3.3
ValueOf(EcmaRuntimeCallInfo * argv)114 JSTaggedValue BuiltinsSymbol::ValueOf(EcmaRuntimeCallInfo *argv)
115 {
116     ASSERT(argv);
117     BUILTINS_API_TRACE(argv->GetThread(), Symbol, ValueOf);
118     JSThread *thread = argv->GetThread();
119     [[maybe_unused]] EcmaHandleScope handleScope(thread);
120     // Let s be the this value.
121     JSHandle<JSTaggedValue> valueHandle = GetThis(argv);
122     // 1.If value is a Symbol, return value.
123     if (valueHandle->IsSymbol()) {
124         return valueHandle.GetTaggedValue();
125     }
126 
127     // 2.If value is an Object and value has a [[SymbolData]] internal slot, then
128     if (valueHandle->IsJSPrimitiveRef()) {
129         // Let sym be the value of s's [[SymbolData]] internal slot.
130         JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue();
131         if (primitive.IsSymbol()) {
132             return primitive;
133         }
134     }
135 
136     // 3.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
137     THROW_TYPE_ERROR_AND_RETURN(thread, "ValueOf: no [[SymbolData]]", JSTaggedValue::Exception());
138 }
139 
140 // 19.4.2.1 Symbol.for (key)
For(EcmaRuntimeCallInfo * argv)141 JSTaggedValue BuiltinsSymbol::For(EcmaRuntimeCallInfo *argv)
142 {
143     ASSERT(argv);
144     BUILTINS_API_TRACE(argv->GetThread(), Symbol, For);
145     JSThread *thread = argv->GetThread();
146     [[maybe_unused]] EcmaHandleScope handleScope(thread);
147     // 1.Let stringKey be ToString(key).
148     JSHandle<JSTaggedValue> key = BuiltinsSymbol::GetCallArg(argv, 0);
149     JSHandle<JSTaggedValue> stringHandle = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, key));
150     // 2.ReturnIfAbrupt
151     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
152 
153     // 3.For each element e of the GlobalSymbolRegistry List,
154     // If SameValue(e.[[key]], stringKey) is true, return e.[[symbol]].
155     // 4.Assert: GlobalSymbolRegistry does not currently contain an entry for stringKey.
156     // 5.Let newSymbol be a new unique Symbol value whose [[Description]] value is stringKey.
157     // 6.Append the record { [[key]]: stringKey, [[symbol]]: newSymbol } to the GlobalSymbolRegistry List.
158     // 7.Return newSymbol.
159     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
160     JSHandle<JSSymbol> symbol = factory->NewSymbolWithTable(stringHandle);
161     return symbol.GetTaggedValue();
162 }
163 
164 // 19.4.2.5 Symbol.keyFor (sym)
KeyFor(EcmaRuntimeCallInfo * argv)165 JSTaggedValue BuiltinsSymbol::KeyFor(EcmaRuntimeCallInfo *argv)
166 {
167     ASSERT(argv);
168     BUILTINS_API_TRACE(argv->GetThread(), Symbol, KeyFor);
169     JSThread *thread = argv->GetThread();
170     [[maybe_unused]] EcmaHandleScope handleScope(thread);
171     // 1.If Type(sym) is not Symbol, throw a TypeError exception.
172     JSHandle<JSTaggedValue> sym = BuiltinsSymbol::GetCallArg(argv, 0);
173     if (!sym->IsSymbol()) {
174         // return typeError
175         THROW_TYPE_ERROR_AND_RETURN(thread, "KeyFor: sym is not Symbol", JSTaggedValue::Exception());
176     }
177     // 2.For each element e of the GlobalSymbolRegistry List,
178     // If SameValue(e.[[symbol]], sym) is true, return e.[[key]].
179     // 3.Assert: GlobalSymbolRegistry does not currently contain an entry for sym.
180     // 4.Return undefined.
181     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
182     auto *table = env->GetRegisterSymbols().GetObject<SymbolTable>();
183     JSTaggedValue key = table->FindSymbol(sym.GetTaggedValue());
184     if (key.IsUndefined()) {
185         return JSTaggedValue::Undefined();
186     }
187     return JSTaggedValue::ToString(thread, JSHandle<JSTaggedValue>(thread, key)).GetTaggedValue();
188 }
189 
190 // 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint )
ToPrimitive(EcmaRuntimeCallInfo * argv)191 JSTaggedValue BuiltinsSymbol::ToPrimitive(EcmaRuntimeCallInfo *argv)
192 {
193     ASSERT(argv);
194     BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToPrimitive);
195     JSThread *thread = argv->GetThread();
196     [[maybe_unused]] EcmaHandleScope handleScope(thread);
197     // Let s be the this value.
198     JSHandle<JSTaggedValue> sym = GetThis(argv);
199     // 1.If value is a Symbol, return value.
200     if (sym->IsSymbol()) {
201         return sym.GetTaggedValue();
202     }
203 
204     // 2.If value is an Object and value has a [[SymbolData]] internal slot, then
205     if (sym->IsJSPrimitiveRef()) {
206         // Let sym be the value of s's [[SymbolData]] internal slot.
207         JSTaggedValue primitive = JSPrimitiveRef::Cast(sym->GetTaggedObject())->GetValue();
208         if (primitive.IsSymbol()) {
209             return primitive;
210         }
211     }
212 
213     // 3.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
214     THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: s is not Object", JSTaggedValue::Exception());
215 }
DescriptionGetter(EcmaRuntimeCallInfo * argv)216 JSTaggedValue BuiltinsSymbol::DescriptionGetter(EcmaRuntimeCallInfo *argv)
217 {
218     ASSERT(argv);
219     BUILTINS_API_TRACE(argv->GetThread(), Symbol, DescriptionGetter);
220     // 1.Let s be the this value.
221     JSThread *thread = argv->GetThread();
222     [[maybe_unused]] EcmaHandleScope handleScope(thread);
223     JSHandle<JSTaggedValue> s = GetThis(argv);
224     // 2.Let sym be ? thisSymbolValue(s).
225     // 3.Return sym.[[Description]].
226     return ThisSymbolValue(thread, s);
227 }
228 
ThisSymbolValue(JSThread * thread,const JSHandle<JSTaggedValue> & value)229 JSTaggedValue BuiltinsSymbol::ThisSymbolValue(JSThread *thread, const JSHandle<JSTaggedValue> &value)
230 {
231     BUILTINS_API_TRACE(thread, Symbol, ThisSymbolValue);
232     if (value->IsSymbol()) {
233         JSTaggedValue desValue = JSSymbol::Cast(value->GetTaggedObject())->GetDescription();
234         return desValue;
235     }
236 
237     // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception.
238     if (value->IsJSPrimitiveRef()) {
239         JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue();
240         if (primitive.IsSymbol()) {
241             // Return the value of s's [[SymbolData]] internal slot.
242             JSTaggedValue primitiveDesValue = JSSymbol::Cast(primitive.GetTaggedObject())->GetDescription();
243             return primitiveDesValue;
244         }
245     }
246     THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to Symbol", JSTaggedValue::Exception());
247 }
248 }  // namespace panda::ecmascript::builtins
249