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