* Copyright (c) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/builtins/builtins_symbol.h"
#include "ecmascript/ecma_runtime_call_info.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_primitive_ref.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/symbol_table.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda::ecmascript;
using namespace panda::ecmascript::builtins;
namespace panda::test {
using Symbol = ecmascript::builtins::BuiltinsSymbol;
using BuiltinsBase = panda::ecmascript::base::BuiltinsBase;
class BuiltinsSymbolTest : public BaseTestWithScope<false> {
};
enum class AlgorithmType {
TO_STRING,
VALUE_OF,
KEY_FOR,
BUILTIN_VALUE_OF,
BUILTIN_FOR,
BUILTIN_KEY_FOR,
BUILTIN_TO_PRIMITIVE,
};
JSTaggedValue SymbolAlgorithm(JSThread *thread, JSTaggedValue thisArg, std::vector<JSTaggedValue>& args,
uint32_t argLen = 8, AlgorithmType type = AlgorithmType::TO_STRING)
{
auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), argLen);
ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfos->SetThis(thisArg);
for (size_t i = 0; i < args.size(); i++) {
ecmaRuntimeCallInfos->SetCallArg(i, args[i]);
}
auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos);
JSTaggedValue result;
switch (type) {
case AlgorithmType::TO_STRING:
result = Symbol::ToString(ecmaRuntimeCallInfos);
break;
case AlgorithmType::VALUE_OF:
result = Symbol::ValueOf(ecmaRuntimeCallInfos);
break;
case AlgorithmType::KEY_FOR:
result = Symbol::KeyFor(ecmaRuntimeCallInfos);
break;
case AlgorithmType::BUILTIN_VALUE_OF:
result = BuiltinsSymbol::ValueOf(ecmaRuntimeCallInfos);
break;
case AlgorithmType::BUILTIN_FOR:
result = BuiltinsSymbol::For(ecmaRuntimeCallInfos);
break;
case AlgorithmType::BUILTIN_KEY_FOR:
result = BuiltinsSymbol::KeyFor(ecmaRuntimeCallInfos);
break;
case AlgorithmType::BUILTIN_TO_PRIMITIVE:
result = BuiltinsSymbol::ToPrimitive(ecmaRuntimeCallInfos);
break;
default:
break;
}
TestHelper::TearDownFrame(thread, prev);
return result;
}
HWTEST_F_L0(BuiltinsSymbolTest, SymbolNoParameterToString)
{
auto ecmaVM = thread->GetEcmaVM();
JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewJSSymbol();
std::vector<JSTaggedValue> args{};
auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::TO_STRING);
JSHandle<EcmaString> resultHandle(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
ASSERT_TRUE(result.IsString());
auto symbolValue = ecmaVM->GetFactory()->NewFromASCII("Symbol()");
ASSERT_EQ(EcmaStringAccessor::Compare(ecmaVM, symbolValue, resultHandle), 0);
result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 4, AlgorithmType::TO_STRING);
EXPECT_TRUE(thread->HasPendingException());
EXPECT_EQ(result, JSTaggedValue::Exception());
thread->ClearException();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> array(factory->NewTaggedArray(3));
result = SymbolAlgorithm(thread, array.GetTaggedValue(), args, 4, AlgorithmType::TO_STRING);
EXPECT_TRUE(thread->HasPendingException());
EXPECT_EQ(result, JSTaggedValue::Exception());
thread->ClearException();
}
HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterToString)
{
auto ecmaVM = thread->GetEcmaVM();
JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("aaa");
std::vector<JSTaggedValue> args{};
auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::TO_STRING);
JSHandle<EcmaString> resultHandle(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
ASSERT_TRUE(result.IsString());
auto symbolValue = ecmaVM->GetFactory()->NewFromASCII("Symbol(aaa)");
ASSERT_EQ(EcmaStringAccessor::Compare(ecmaVM, symbolValue, resultHandle), 0);
}
HWTEST_F_L0(BuiltinsSymbolTest, SymbolNoParameterValueOf)
{
auto ecmaVM = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewJSSymbol();
std::vector<JSTaggedValue> args{};
auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_VALUE_OF);
EXPECT_TRUE(result.IsSymbol());
ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
JSHandle<JSFunction> symbolObject(env->GetSymbolFunction());
JSHandle<JSTaggedValue> symbolValue(symbol);
JSHandle<JSPrimitiveRef> symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue);
auto otherResult = SymbolAlgorithm(thread, symbolRef.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_VALUE_OF);
EXPECT_TRUE(otherResult.IsSymbol());
ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 4, AlgorithmType::VALUE_OF);
EXPECT_TRUE(thread->HasPendingException());
EXPECT_EQ(result, JSTaggedValue::Exception());
thread->ClearException();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> array(factory->NewTaggedArray(3));
result = SymbolAlgorithm(thread, array.GetTaggedValue(), args, 4, AlgorithmType::VALUE_OF);
EXPECT_TRUE(thread->HasPendingException());
EXPECT_EQ(result, JSTaggedValue::Exception());
thread->ClearException();
}
HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterValueOf)
{
auto ecmaVM = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("bbb");
std::vector<JSTaggedValue> args{};
auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_VALUE_OF);
EXPECT_TRUE(result.IsSymbol());
ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
JSHandle<JSFunction> symbolObject(env->GetSymbolFunction());
JSHandle<JSTaggedValue> symbolValue(symbol);
JSHandle<JSPrimitiveRef> symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue);
auto otherResult = SymbolAlgorithm(thread, symbolRef.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_VALUE_OF);
EXPECT_TRUE(otherResult.IsSymbol());
ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
}
HWTEST_F_L0(BuiltinsSymbolTest, SymbolWithParameterFor)
{
auto ecmaVM = thread->GetEcmaVM();
JSHandle<SymbolTable> tableHandle(thread, ecmaVM->GetRegisterSymbols());
JSHandle<EcmaString> string = ecmaVM->GetFactory()->NewFromASCII("ccc");
ASSERT_EQ(EcmaStringAccessor(string).GetLength(), 3U);
JSHandle<JSTaggedValue> string_handle(string);
ASSERT_EQ(tableHandle->ContainsKey(thread, string_handle.GetTaggedValue()), false);
JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewSymbolWithTableWithChar("ccc");
std::vector<JSTaggedValue> args{string.GetTaggedValue()};
auto result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::BUILTIN_FOR);
ASSERT_EQ(tableHandle->ContainsKey(thread, string_handle.GetTaggedValue()), true);
JSTaggedValue target(*symbol);
ASSERT_EQ(result.GetRawData() == target.GetRawData(), true);
}
HWTEST_F_L0(BuiltinsSymbolTest, SymbolKeyFor)
{
auto ecmaVM = thread->GetEcmaVM();
JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("bbb");
std::vector<JSTaggedValue> args{symbol.GetTaggedValue()};
auto result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::BUILTIN_KEY_FOR);
ASSERT_EQ(result.GetRawData(), JSTaggedValue::VALUE_UNDEFINED);
JSHandle<EcmaString> string = ecmaVM->GetFactory()->NewFromASCII("ccc");
ASSERT_EQ(EcmaStringAccessor(string).GetLength(), 3U);
args[0] = string.GetTaggedValue();
SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::BUILTIN_FOR);
JSHandle<JSSymbol> otherSymbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("ccc");
args[0] = otherSymbol.GetTaggedValue();
auto otherResult = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::BUILTIN_KEY_FOR);
ASSERT_TRUE(otherResult.IsString());
JSHandle<SymbolTable> tableHandle(thread, ecmaVM->GetRegisterSymbols());
JSTaggedValue stringValue(*string);
ASSERT_EQ(tableHandle->ContainsKey(thread, stringValue), true);
args[0] = JSTaggedValue::Undefined();
result = SymbolAlgorithm(thread, JSTaggedValue::Undefined(), args, 6, AlgorithmType::KEY_FOR);
EXPECT_TRUE(thread->HasPendingException());
EXPECT_EQ(result, JSTaggedValue::Exception());
thread->ClearException();
}
HWTEST_F_L0(BuiltinsSymbolTest, SymbolToPrimitive)
{
auto ecmaVM = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewJSSymbol();
std::vector<JSTaggedValue> args{};
auto result = SymbolAlgorithm(thread, symbol.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_TO_PRIMITIVE);
EXPECT_TRUE(result.IsSymbol());
ASSERT_EQ(result.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
JSHandle<JSFunction> symbolObject(env->GetSymbolFunction());
JSHandle<JSTaggedValue> symbolValue(symbol);
JSHandle<JSPrimitiveRef> symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue);
auto otherResult =
SymbolAlgorithm(thread, symbolRef.GetTaggedValue(), args, 4, AlgorithmType::BUILTIN_TO_PRIMITIVE);
EXPECT_TRUE(otherResult.IsSymbol());
ASSERT_EQ(otherResult.GetRawData() == (JSTaggedValue(*symbol)).GetRawData(), true);
}
HWTEST_F_L0(BuiltinsSymbolTest, SymbolConstructor)
{
auto ecmaVM = thread->GetEcmaVM();
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
JSTaggedValue result = BuiltinsSymbol::SymbolConstructor(ecmaRuntimeCallInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(result.IsSymbol());
JSSymbol *sym = reinterpret_cast<JSSymbol *>(result.GetRawData());
ASSERT_EQ(sym->GetDescription(thread).IsUndefined(), true);
JSHandle<EcmaString> string = ecmaVM->GetFactory()->NewFromASCII("ddd");
auto otherEcmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
otherEcmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
otherEcmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
otherEcmaRuntimeCallInfo->SetCallArg(0, string.GetTaggedValue());
prev = TestHelper::SetupFrame(thread, otherEcmaRuntimeCallInfo);
JSTaggedValue result1 = BuiltinsSymbol::SymbolConstructor(otherEcmaRuntimeCallInfo);
TestHelper::TearDownFrame(thread, prev);
JSHandle<EcmaString> resultString = JSTaggedValue::ToString(
thread,
JSHandle<JSTaggedValue>(thread, reinterpret_cast<JSSymbol *>(result1.GetRawData())->GetDescription(thread)));
ASSERT_EQ(EcmaStringAccessor::Compare(ecmaVM, resultString, string), 0);
}
HWTEST_F_L0(BuiltinsSymbolTest, SymbolGetter)
{
auto ecmaVM = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();
JSHandle<JSSymbol> symbol = ecmaVM->GetFactory()->NewPublicSymbolWithChar("");
JSHandle<EcmaString> string = ecmaVM->GetFactory()->NewFromASCII("");
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(symbol.GetTaggedValue());
[[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
JSTaggedValue result = BuiltinsSymbol::DescriptionGetter(ecmaRuntimeCallInfo);
TestHelper::TearDownFrame(thread, prev);
ASSERT_TRUE(result.IsString());
EcmaString *resString = reinterpret_cast<EcmaString *>(result.GetRawData());
ASSERT_EQ(EcmaStringAccessor(resString).GetLength(), 0U);
ASSERT_EQ(EcmaStringAccessor::StringsAreEqual(thread, resString, *string), true);
JSHandle<JSFunction> symbolObject(env->GetSymbolFunction());
JSHandle<JSTaggedValue> symbolValue(symbol);
JSHandle<JSPrimitiveRef> symbolRef = ecmaVM->GetFactory()->NewJSPrimitiveRef(symbolObject, symbolValue);
ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(symbolRef.GetTaggedValue());
prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
result = BuiltinsSymbol::DescriptionGetter(ecmaRuntimeCallInfo);
TestHelper::TearDownFrame(thread, prev);
ASSERT_TRUE(result.IsString());
resString = reinterpret_cast<EcmaString *>(result.GetRawData());
ASSERT_EQ(EcmaStringAccessor(resString).GetLength(), 0U);
ASSERT_EQ(EcmaStringAccessor::StringsAreEqual(thread, resString, *string), true);
ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
result = BuiltinsSymbol::DescriptionGetter(ecmaRuntimeCallInfo);
TestHelper::TearDownFrame(thread, prev);
EXPECT_TRUE(thread->HasPendingException());
EXPECT_EQ(result, JSTaggedValue::Exception());
thread->ClearException();
}
}