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