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
33using namespace panda::ecmascript;
34using namespace panda::ecmascript::builtins;
35
36namespace panda::test {
37using Symbol = ecmascript::builtins::BuiltinsSymbol;
38using BuiltinsBase = panda::ecmascript::base::BuiltinsBase;
39
40class BuiltinsSymbolTest : public BaseTestWithScope<false> {
41};
42
43enum 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
53JSTaggedValue 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()
94HWTEST_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()
125HWTEST_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()
140HWTEST_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()
177HWTEST_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
200HWTEST_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)
223HWTEST_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()
257HWTEST_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
281HWTEST_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
312HWTEST_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