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/ecma_vm.h"
21 #include "ecmascript/global_env.h"
22 #include "ecmascript/js_function.h"
23 #include "ecmascript/js_handle.h"
24 #include "ecmascript/js_hclass.h"
25 #include "ecmascript/js_object-inl.h"
26 #include "ecmascript/js_primitive_ref.h"
27 #include "ecmascript/js_tagged_value-inl.h"
28 #include "ecmascript/js_thread.h"
29 #include "ecmascript/object_factory.h"
30 #include "ecmascript/symbol_table.h"
31 #include "ecmascript/tests/test_helper.h"
32
33 using namespace panda::ecmascript;
34 using namespace panda::ecmascript::builtins;
35
36 namespace panda::test {
37 using Symbol = ecmascript::builtins::BuiltinsSymbol;
38 using BuiltinsBase = panda::ecmascript::base::BuiltinsBase;
39
40 class BuiltinsSymbolTest : public BaseTestWithScope<false> {
41 };
42
43 enum class AlgorithmType {
44 TO_STRING,
45 VALUE_OF,
46 KEY_FOR,
47 BUILTIN_VALUE_OF,
48 BUILTIN_FOR,
49 BUILTIN_KEY_FOR,
50 BUILTIN_TO_PRIMITIVE,
51 };
52
SymbolAlgorithm(JSThread *thread, JSTaggedValue thisArg, std::vector<JSTaggedValue>& args, uint32_t argLen = 8, AlgorithmType type = AlgorithmType::TO_STRING)53 JSTaggedValue SymbolAlgorithm(JSThread *thread, JSTaggedValue thisArg, std::vector<JSTaggedValue>& args,
54 uint32_t argLen = 8, AlgorithmType type = AlgorithmType::TO_STRING)
55 {
56 auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), argLen);
57 ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined());
58 ecmaRuntimeCallInfos->SetThis(thisArg);
59 for (size_t i = 0; i < args.size(); i++) {
60 ecmaRuntimeCallInfos->SetCallArg(i, args[i]);
61 }
62 auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos);
63 JSTaggedValue result;
64 switch (type) {
65 case AlgorithmType::TO_STRING:
66 result = Symbol::ToString(ecmaRuntimeCallInfos);
67 break;
68 case AlgorithmType::VALUE_OF:
69 result = Symbol::ValueOf(ecmaRuntimeCallInfos);
70 break;
71 case AlgorithmType::KEY_FOR:
72 result = Symbol::KeyFor(ecmaRuntimeCallInfos);
73 break;
74 case AlgorithmType::BUILTIN_VALUE_OF:
75 result = BuiltinsSymbol::ValueOf(ecmaRuntimeCallInfos);
76 break;
77 case AlgorithmType::BUILTIN_FOR:
78 result = BuiltinsSymbol::For(ecmaRuntimeCallInfos);
79 break;
80 case AlgorithmType::BUILTIN_KEY_FOR:
81 result = BuiltinsSymbol::KeyFor(ecmaRuntimeCallInfos);
82 break;
83 case AlgorithmType::BUILTIN_TO_PRIMITIVE:
84 result = BuiltinsSymbol::ToPrimitive(ecmaRuntimeCallInfos);
85 break;
86 default:
87 break;
88 }
89 TestHelper::TearDownFrame(thread, prev);
90 return result;
91 }
92
93 // new Symbol.toString()
HWTEST_F_L0(BuiltinsSymbolTest, SymbolNoParameterToString)94 HWTEST_F_L0(BuiltinsSymbolTest, SymbolNoParameterToString)
95 {
96 auto ecmaVM = thread->GetEcmaVM();
97
98 JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewJSSymbol();
99
100 std::vector<JSTaggedValue> args{};
101 auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::TO_STRING);
102 JSHandle<EcmaString> resultHandle(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
103 ASSERT_TRUE(result.IsString());
104
105 auto symbolValue = ecmaVM->GetFactory()->NewFromASCII("Symbol()");
106 ASSERT_EQ(EcmaStringAccessor::Compare(ecmaVM, symbolValue, resultHandle), 0);
107
108 // Undefined not Object
109 result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 4, AlgorithmType::TO_STRING);
110
111 EXPECT_TRUE(thread->HasPendingException());
112 EXPECT_EQ(result, JSTaggedValue::Exception());
113 thread->ClearException();
114
115 // No Symbol data
116 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
117 JSHandle<TaggedArray> array(factory->NewTaggedArray(3));
118 result = SymbolAlgorithm(thread, array.GetTaggedValue(), args, 4, AlgorithmType::TO_STRING);
119 EXPECT_TRUE(thread->HasPendingException());
120 EXPECT_EQ(result, JSTaggedValue::Exception());
121 thread->ClearException();
122 }
123
124 // new Symbol("aaa").toString()
HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterToString)125 HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterToString)
126 {
127 auto ecmaVM = thread->GetEcmaVM();
128
129 JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("aaa");
130 std::vector<JSTaggedValue> args{};
131 auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::TO_STRING);
132 JSHandle<EcmaString> resultHandle(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
133 ASSERT_TRUE(result.IsString());
134
135 auto symbolValue = ecmaVM->GetFactory()->NewFromASCII("Symbol(aaa)");
136 ASSERT_EQ(EcmaStringAccessor::Compare(ecmaVM, symbolValue, resultHandle), 0);
137 }
138
139 // new Symbol().valueOf()
HWTEST_F_L0(BuiltinsSymbolTest, SymbolNoParameterValueOf)140 HWTEST_F_L0(BuiltinsSymbolTest, SymbolNoParameterValueOf)
141 {
142 auto ecmaVM = thread->GetEcmaVM();
143 JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
144
145 JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewJSSymbol();
146
147 std::vector<JSTaggedValue> args{};
148 auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_VALUE_OF);
149
150 EXPECT_TRUE(result.IsSymbol());
151 ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
152
153 JSHandle<JSFunction> symbolObject(env->GetSymbolFunction());
154 JSHandle<JSTaggedValue> symbolValue(symbol);
155 JSHandle<JSPrimitiveRef> symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue);
156
157 auto otherResult = SymbolAlgorithm(thread, symbolRef.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_VALUE_OF);
158 EXPECT_TRUE(otherResult.IsSymbol());
159 ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
160
161 // Undefined not Object
162 result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 4, AlgorithmType::VALUE_OF);
163 EXPECT_TRUE(thread->HasPendingException());
164 EXPECT_EQ(result, JSTaggedValue::Exception());
165 thread->ClearException();
166
167 // No Symbol data
168 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
169 JSHandle<TaggedArray> array(factory->NewTaggedArray(3));
170 result = SymbolAlgorithm(thread, array.GetTaggedValue(), args, 4, AlgorithmType::VALUE_OF);
171 EXPECT_TRUE(thread->HasPendingException());
172 EXPECT_EQ(result, JSTaggedValue::Exception());
173 thread->ClearException();
174 }
175
176 // new Symbol("bbb").valueOf()
HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterValueOf)177 HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterValueOf)
178 {
179 auto ecmaVM = thread->GetEcmaVM();
180 JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
181
182 JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("bbb");
183
184 std::vector<JSTaggedValue> args{};
185 auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_VALUE_OF);
186
187 EXPECT_TRUE(result.IsSymbol());
188 ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
189
190 JSHandle<JSFunction> symbolObject(env->GetSymbolFunction());
191 JSHandle<JSTaggedValue> symbolValue(symbol);
192 JSHandle<JSPrimitiveRef> symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue);
193
194 auto otherResult = SymbolAlgorithm(thread, symbolRef.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_VALUE_OF);
195 EXPECT_TRUE(otherResult.IsSymbol());
196 ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
197 }
198
199 // new Symbol().for
HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterFor)200 HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterFor)
201 {
202 auto ecmaVM = thread->GetEcmaVM();
203 JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
204
205 JSHandle<SymbolTable> tableHandle(env->GetRegisterSymbols());
206
207 JSHandle<EcmaString> string = ecmaVM->GetFactory()->NewFromASCII("ccc");
208 ASSERT_EQ(EcmaStringAccessor(string).GetLength(), 3U);
209 JSHandle<JSTaggedValue> string_handle(string);
210 ASSERT_EQ(tableHandle->ContainsKey(string_handle.GetTaggedValue()), false);
211
212 JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewSymbolWithTableWithChar("ccc");
213
214 std::vector<JSTaggedValue> args{string.GetTaggedValue()};
215 auto result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::BUILTIN_FOR);
216 ASSERT_EQ(tableHandle->ContainsKey(string_handle.GetTaggedValue()), true);
217
218 JSTaggedValue target(*symbol);
219 ASSERT_EQ(result.GetRawData() == target.GetRawData(), true);
220 }
221
222 // Symbol.keyFor (sym)
HWTEST_F_L0(BuiltinsSymbolTest, SymbolKeyFor)223 HWTEST_F_L0(BuiltinsSymbolTest, SymbolKeyFor)
224 {
225 auto ecmaVM = thread->GetEcmaVM();
226 JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
227
228 JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("bbb");
229
230 std::vector<JSTaggedValue> args{symbol.GetTaggedValue()};
231 auto result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::BUILTIN_KEY_FOR);
232 ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED);
233
234 JSHandle<EcmaString> string = ecmaVM->GetFactory()->NewFromASCII("ccc");
235 ASSERT_EQ(EcmaStringAccessor(string).GetLength(), 3U);
236
237 args[0] = string.GetTaggedValue();
238 SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::BUILTIN_FOR);
239
240 JSHandle<JSSymbol> otherSymbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("ccc");
241 args[0] = otherSymbol.GetTaggedValue();
242 auto otherResult = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::BUILTIN_KEY_FOR);
243 ASSERT_TRUE(otherResult.IsString());
244 JSHandle<SymbolTable> tableHandle(env->GetRegisterSymbols());
245 JSTaggedValue stringValue(*string);
246 ASSERT_EQ(tableHandle->ContainsKey(stringValue), true);
247
248 // not symbol
249 args[0] = JSTaggedValue::Undefined();
250 result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::KEY_FOR);
251 EXPECT_TRUE(thread->HasPendingException());
252 EXPECT_EQ(result, JSTaggedValue::Exception());
253 thread->ClearException();
254 }
255
256 // Symbol.ToPrimitive()
HWTEST_F_L0(BuiltinsSymbolTest, SymbolToPrimitive)257 HWTEST_F_L0(BuiltinsSymbolTest, SymbolToPrimitive)
258 {
259 auto ecmaVM = thread->GetEcmaVM();
260 JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
261
262 JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewJSSymbol();
263
264 std::vector<JSTaggedValue> args{};
265 auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_TO_PRIMITIVE);
266
267 EXPECT_TRUE(result.IsSymbol());
268 ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
269
270 JSHandle<JSFunction> symbolObject(env->GetSymbolFunction());
271 JSHandle<JSTaggedValue> symbolValue(symbol);
272 JSHandle<JSPrimitiveRef> symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue);
273
274 auto otherResult =
275 SymbolAlgorithm(thread, symbolRef.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_TO_PRIMITIVE);
276 EXPECT_TRUE(otherResult.IsSymbol());
277 ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
278 }
279
280 // constructor
HWTEST_F_L0(BuiltinsSymbolTest, SymbolConstructor)281 HWTEST_F_L0(BuiltinsSymbolTest, SymbolConstructor)
282 {
283 auto ecmaVM = thread->GetEcmaVM();
284
285 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
286 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
287 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
288 ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined());
289
290 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
291 JSTaggedValue result = BuiltinsSymbol::SymbolConstructor(ecmaRuntimeCallInfo);
292 TestHelper::TearDownFrame(thread, prev);
293 EXPECT_TRUE(result.IsSymbol());
294 JSSymbol *sym = reinterpret_cast<JSSymbol *>(result.GetRawData());
295 ASSERT_EQ(sym->GetDescription().IsUndefined(), true);
296
297 JSHandle<EcmaString> string = ecmaVM->GetFactory()->NewFromASCII("ddd");
298
299 auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
300 otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
301 otherEcmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
302 otherEcmaRuntimeCallInfo->SetCallArg(0, string.GetTaggedValue());
303
304 prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo);
305 JSTaggedValue result1 = BuiltinsSymbol::SymbolConstructor(otherEcmaRuntimeCallInfo);
306 TestHelper::TearDownFrame(thread, prev);
307 JSHandle<EcmaString> resultString = JSTaggedValue::ToString(
308 thread, JSHandle<JSTaggedValue>(thread, reinterpret_cast<JSSymbol *>(result1.GetRawData())->GetDescription()));
309 ASSERT_EQ(EcmaStringAccessor::Compare(ecmaVM, resultString, string), 0);
310 }
311
HWTEST_F_L0(BuiltinsSymbolTest, SymbolGetter)312 HWTEST_F_L0(BuiltinsSymbolTest, SymbolGetter)
313 {
314 auto ecmaVM = thread->GetEcmaVM();
315 JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
316
317 JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("");
318 JSHandle<EcmaString> string = ecmaVM->GetFactory()->NewFromASCII("");
319
320 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
321 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
322 ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue());
323
324 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
325 JSTaggedValue result = BuiltinsSymbol::DescriptionGetter(ecmaRuntimeCallInfo);
326 TestHelper::TearDownFrame(thread, prev);
327 ASSERT_TRUE(result.IsString());
328 EcmaString *resString = reinterpret_cast<EcmaString *>(result.GetRawData());
329 ASSERT_EQ(EcmaStringAccessor(resString).GetLength(), 0U);
330 ASSERT_EQ(EcmaStringAccessor::StringsAreEqual(resString, *string), true);
331
332 // value is not symbol
333 JSHandle<JSFunction> symbolObject(env->GetSymbolFunction());
334 JSHandle<JSTaggedValue> symbolValue(symbol);
335 JSHandle<JSPrimitiveRef> symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue);
336 ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
337 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
338 ecmaRuntimeCallInfo->SetThis(symbolRef.GetTaggedValue());
339
340 prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
341 result = BuiltinsSymbol::DescriptionGetter(ecmaRuntimeCallInfo);
342 TestHelper::TearDownFrame(thread, prev);
343 ASSERT_TRUE(result.IsString());
344 resString = reinterpret_cast<EcmaString *>(result.GetRawData());
345 ASSERT_EQ(EcmaStringAccessor(resString).GetLength(), 0U);
346 ASSERT_EQ(EcmaStringAccessor::StringsAreEqual(resString, *string), true);
347
348 // Undefined
349 ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
350 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
351 ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
352
353 prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
354 result = BuiltinsSymbol::DescriptionGetter(ecmaRuntimeCallInfo);
355 TestHelper::TearDownFrame(thread, prev);
356 EXPECT_TRUE(thread->HasPendingException());
357 EXPECT_EQ(result, JSTaggedValue::Exception());
358 thread->ClearException();
359 }
360 } // namespace panda::test
361