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