From b5555628a22bfccc4cd56a96146e79153e16ec6d Mon Sep 17 00:00:00 2001 From: Nazarov Konstantin Date: Fri, 16 Sep 2022 18:05:29 +0300 Subject: [PATCH] implement escape and unescape builtins Change-Id: I9c75ddd6614c2304e4b45203ef70961ca3228961 Signed-off-by: Nazarov Konstantin --- runtime/builtins.cpp | 2 + runtime/builtins/builtins_global.cpp | 168 +++++++- runtime/builtins/builtins_global.h | 48 +++ runtime/runtime_call_id.h | 2 + tests/runtime/CMakeLists.txt | 24 ++ .../runtime/builtins/builtins_global_test.cpp | 402 ++++++++++++++++++ 6 files changed, 638 insertions(+), 8 deletions(-) create mode 100644 tests/runtime/builtins/builtins_global_test.cpp diff --git a/runtime/builtins.cpp b/runtime/builtins.cpp index 4aba4b8ee..bda67a855 100644 --- a/runtime/builtins.cpp +++ b/runtime/builtins.cpp @@ -375,6 +375,8 @@ void Builtins::InitializeGlobalObject(const JSHandle &env, const JSHa SetFunction(env, globalObject, "eval", Global::NotSupportEval, FunctionLength::ONE); SetFunction(env, globalObject, "isFinite", Global::IsFinite, FunctionLength::ONE); SetFunction(env, globalObject, "isNaN", Global::IsNaN, FunctionLength::ONE); + SetFunction(env, globalObject, "unescape", Global::Unescape, FunctionLength::ONE); + SetFunction(env, globalObject, "escape", Global::Escape, FunctionLength::ONE); SetFunction(env, globalObject, "decodeURI", Global::DecodeURI, FunctionLength::ONE); SetFunction(env, globalObject, "encodeURI", Global::EncodeURI, FunctionLength::ONE); SetFunction(env, globalObject, "decodeURIComponent", Global::DecodeURIComponent, FunctionLength::ONE); diff --git a/runtime/builtins/builtins_global.cpp b/runtime/builtins/builtins_global.cpp index 399853f7a..d0a0e9038 100644 --- a/runtime/builtins/builtins_global.cpp +++ b/runtime/builtins/builtins_global.cpp @@ -82,10 +82,7 @@ JSTaggedValue BuiltinsGlobal::IsNaN(EcmaRuntimeCallInfo *msg) bool BuiltinsGlobal::IsUnescapedURI(uint16_t ch) { - if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) { - return true; - } - return IsInMarkURISet(ch); + return IsAlNum(ch) || IsInMarkURISet(ch); } bool BuiltinsGlobal::IsInUnescapedURISet(uint16_t ch) @@ -107,15 +104,13 @@ bool BuiltinsGlobal::IsInReservedURISet(uint16_t ch) bool BuiltinsGlobal::IsReservedURI(uint16_t ch) { std::u16string str(u";/?:@&=+$,"); - std::u16string::size_type index = str.find(ch); - return (index != std::u16string::npos); + return (str.find(ch) != std::u16string::npos); } bool BuiltinsGlobal::IsInMarkURISet(uint16_t ch) { std::u16string str(u"-_.!~*'()"); - std::u16string::size_type index = str.find(ch); - return (index != std::u16string::npos); + return (str.find(ch) != std::u16string::npos); } bool BuiltinsGlobal::IsHexDigits(uint16_t ch) @@ -123,6 +118,163 @@ bool BuiltinsGlobal::IsHexDigits(uint16_t ch) return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f'); } +JSTaggedValue BuiltinsGlobal::Unescape(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + constexpr uint16_t PERCENT_SIGN = 0x0025; + constexpr uint16_t LATIN_SMALL_LETTER_U = 0x0075; + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, Unescape); + [[maybe_unused]] EcmaHandleScope handle_scope(thread); + // 1. Set string to ? ToString(string). + [[maybe_unused]] JSHandle sstring = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + // 2. ReturnIfAbrupt(uriString). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 2. Let length be the length of string. + const int32_t length = sstring->GetLength(); + // 3. Let R be the empty String. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string RR; + // 4. Let k be 0. + // 5. Repeat, while k != length + for (int32_t k = 0; k != length; ++k) { + // a. Let char be the code unit (represented as a 16-bit unsigned integer) at index k within string. + uint16_t cc = sstring->At(k); + // b. If c is the code unit 0x0025 (PERCENT_SIGN) then + if (cc != PERCENT_SIGN) { + // c. Set R to the string-concatenation of R and c. + RR.append(StringHelper::Utf16ToU16String(&cc, 1)); + continue; + } + // i. Let hexEscape be the empty String. + std::u16string hex_escape; + // ii. Let skip be 0. + auto skip = 0; + // iii. If k ≤ length - 6 and the code unit at index k + 1 within string is the code unit 0x0075 + // (LATIN_SMALL_LETTER_U), then + bool is_valid_hex_sequence = false; + int res_char = 0; + if (k <= length - 6 && sstring->At(k + 1) == LATIN_SMALL_LETTER_U) { + // 1. Set hexEscape to the substring of string from k + 2 to k + 6 + hex_escape = StringHelper::StringToU16string(StringHelper::SubString(sstring, k + 2, 4)); + // 2. Set skip to 5 + skip = 5; + // check if hex sequence is valid + int digit_1 = IsHex(hex_escape[0]); + int digit_2 = IsHex(hex_escape[1]); + int digit_3 = IsHex(hex_escape[2]); + int digit_4 = IsHex(hex_escape[3]); + if (digit_1 != -1 && digit_2 != -1 && digit_3 != -1 && digit_4 != -1) { + // NOLINTNEXTLINE(hicpp-signed-bitwise,readability-magic-numbers) + res_char = (digit_1 << 12) + (digit_2 << 8) + (digit_3 << 4) + digit_4; + is_valid_hex_sequence = true; + } + } + // iv. Else if k ≤ length - 3, then + else if (k <= length - 3) { + // 1. Set hexEscape to the substring of string from k + 1 to k + 3. + hex_escape = StringHelper::StringToU16string(StringHelper::SubString(sstring, k + 1, 2)); + // 2. Set skip to 2. + skip = 2; + // check if hex sequence is valid + int digit_1 = IsHex(hex_escape[0]); + int digit_2 = IsHex(hex_escape[1]); + + if (digit_1 != -1 && digit_2 != -1) { + // NOLINTNEXTLINE(hicpp-signed-bitwise,readability-magic-numbers) + res_char = (digit_1 << 4) + digit_2; + is_valid_hex_sequence = true; + } + } + // v. If hexEscape can be interpreted as an expansion of HexDigits[~Sep], then + if (is_valid_hex_sequence) { + // 1. Let hexIntegerLiteral be the string-concatenation of "0x" and hexEscape. + // 2. Let n be ! ToNumber(hexIntegerLiteral). + // 3. Set c to the code unit whose value is ℝ(n). + cc = res_char; + // 4. Set k to k + skip + k += skip; + } + // c. Set R to the string-concatenation of R and c. + RR.append(StringHelper::Utf16ToU16String(&cc, 1)); + } + // 6. Return R. + auto *rr_data = reinterpret_cast(RR.data()); + int32_t rr_size = RR.size(); + return factory->NewFromUtf16Literal(rr_data, rr_size).GetTaggedValue(); +} + +JSTaggedValue BuiltinsGlobal::Escape(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + constexpr uint16_t MAX_DOUBLE_DIGIT = 256; + static const std::u16string hex_str = u"0123456789ABCDEF"; + JSThread *thread = msg->GetThread(); + BUILTINS_API_TRACE(thread, Global, Escape); + [[maybe_unused]] EcmaHandleScope handle_scope(thread); + // 1. Set string to ? ToString(string). + [[maybe_unused]] JSHandle sstring = JSTaggedValue::ToString(thread, GetCallArg(msg, 0)); + // ReturnIfAbrupt(uriString). + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + // 2. Let length be the length of string. + const int32_t length = sstring->GetLength(); + // 3. Let R be the empty String. + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + std::u16string RR; + // 4. Let k be 0. + // 5. Repeat, while k < length + for (int32_t k = 0; k < length; ++k) { + // a. Let char be the code unit (represented as a 16-bit unsigned integer) at index k within string. + uint16_t cc = sstring->At(k); + std::u16string SS; + // b. If char is one of the code units in + // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./", then + if (IsNotEscaped(cc)) { + // i. Let S be the String value containing the single code unit char. + SS = StringHelper::Utf16ToU16String(&cc, 1); + } + // c. Else if char ≥ 256, then + else if (cc >= MAX_DOUBLE_DIGIT) { + // i. Let n be the numeric value of char. + // ii. Let S be the string-concatenation of: + // * "%u" + SS = u"%u"; + // * the String representation of n, formatted as a four-digit uppercase hexadecimal number, padded to + // the left with zeroes if necessary + constexpr uint16_t N_DIGITS = 4; + for (uint16_t j = 0; j < N_DIGITS; j++) { + // NOLINTNEXTLINE(hicpp-signed-bitwise,readability-magic-numbers) + uint16_t hex_c = hex_str[cc >> ((N_DIGITS - 1 - j) * 4U) & BIT_MASK]; + SS.append(StringHelper::Utf16ToU16String(&hex_c, 1)); + } + } + // d. Else + else { + // i. Assert: char < 256 + ASSERT(cc < MAX_DOUBLE_DIGIT); + // ii. Let n be the numeric value of char + // iii. Let S be the string-concatenation of: + // * "%" + SS = u"%"; + // * the String representation of n, formatted as a two-digit uppercase hexadecimal number, padded to + // the left with a zero if necessary + constexpr uint16_t N_DIGITS = 2; + for (uint16_t j = 0; j < N_DIGITS; j++) { + // NOLINTNEXTLINE(hicpp-signed-bitwise,readability-magic-numbers) + uint16_t hex_c = hex_str[cc >> ((N_DIGITS - 1 - j) * 4U) & BIT_MASK]; + SS.append(StringHelper::Utf16ToU16String(&hex_c, 1)); + } + } + // e. Set R to the string-concatenation of R and S. + RR.append(SS); + // f. Set k to k + 1 + } + // 6. Return R. + auto *rr_data = reinterpret_cast(RR.data()); + int32_t rr_size = RR.size(); + return factory->NewFromUtf16Literal(rr_data, rr_size).GetTaggedValue(); +} + // 18.2.6 JSTaggedValue BuiltinsGlobal::DecodeURI(EcmaRuntimeCallInfo *msg) { diff --git a/runtime/builtins/builtins_global.h b/runtime/builtins/builtins_global.h index 61d75fa92..d269f644a 100644 --- a/runtime/builtins/builtins_global.h +++ b/runtime/builtins/builtins_global.h @@ -35,6 +35,9 @@ public: static JSTaggedValue IsFinite(EcmaRuntimeCallInfo *msg); // 18.2.3 static JSTaggedValue IsNaN(EcmaRuntimeCallInfo *msg); + // escape / unescape + static JSTaggedValue Escape(EcmaRuntimeCallInfo *msg); + static JSTaggedValue Unescape(EcmaRuntimeCallInfo *msg); // 18.2.6 static JSTaggedValue DecodeURI(EcmaRuntimeCallInfo *msg); static JSTaggedValue EncodeURI(EcmaRuntimeCallInfo *msg); @@ -55,12 +58,57 @@ private: static void PrintValue(int64_t value, int64_t tag); static JSTaggedValue Encode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet); static JSTaggedValue Decode(JSThread *thread, const JSHandle &str, judgURIFunc IsInURISet); + + static inline bool IsAlNum(uint16_t ch) + { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'); + } + static inline bool IsInNonEscapedSymbols(uint16_t ch) + { + switch (ch) { + case '@': + case '*': + case '_': + case '+': + case '-': + case '.': + case '/': + return true; + default: + return false; + } + } + static inline bool IsNotEscaped(uint16_t ch) + { + return IsAlNum(ch) || IsInNonEscapedSymbols(ch); + } static bool IsUnescapedURI(uint16_t ch); static bool IsInUnescapedURISet(uint16_t ch); static bool IsInReservedURISet(uint16_t ch); static bool IsReservedURI(uint16_t ch); static bool IsInMarkURISet(uint16_t ch); static bool IsHexDigits(uint16_t ch); + static inline int IsHex(uint16_t cc) + { + constexpr uint16_t HEX_MIN_LETTER_VALUE = 10; + constexpr uint16_t HEX_MAX_LETTER_VALUE = 15; + if (cc > 'f') { + return -1; + } + cc -= '0'; + if (cc < HEX_MIN_LETTER_VALUE) { + return cc; + } + cc -= ('A' - '0'); + if (cc <= HEX_MAX_LETTER_VALUE - HEX_MIN_LETTER_VALUE) { + return cc + HEX_MIN_LETTER_VALUE; + } + cc -= ('a' - 'A'); + if (cc <= HEX_MAX_LETTER_VALUE - HEX_MIN_LETTER_VALUE) { + return cc + HEX_MIN_LETTER_VALUE; + } + return -1; + } static uint8_t GetValueFromTwoHex(uint16_t front, uint16_t behind); }; } // namespace panda::ecmascript::builtins diff --git a/runtime/runtime_call_id.h b/runtime/runtime_call_id.h index 414f9e906..9fd144983 100644 --- a/runtime/runtime_call_id.h +++ b/runtime/runtime_call_id.h @@ -317,6 +317,8 @@ namespace panda::ecmascript { V(AsyncFromSyncIteratorPrototype, Throw) \ V(Global, IsFinite) \ V(Global, IsNaN) \ + V(Global, Escape) \ + V(Global, Unescape) \ V(Global, PrintEntryPoint) \ V(Global, GcEntryPoint) \ V(Global, NewobjDynrange) \ diff --git a/tests/runtime/CMakeLists.txt b/tests/runtime/CMakeLists.txt index 57b48d40f..7b85cf8da 100644 --- a/tests/runtime/CMakeLists.txt +++ b/tests/runtime/CMakeLists.txt @@ -172,6 +172,11 @@ set(ECMASCRIPT_BUILTINS_TYPEDARRAY_TESTS_SOURCES common/test_helper.cpp ) +set(ECMASCRIPT_BUILTINS_GLOBAL_TESTS_SOURCES + builtins/builtins_global_test.cpp + common/test_helper.cpp +) + #0 panda_add_gtest( NO_CORES @@ -634,3 +639,22 @@ if(TARGET arkruntime4ecmascript_tests2) ) target_compile_options(arkruntime4ecmascript_tests2 PUBLIC "-Wno-ignored-attributes") endif() +#22 +panda_add_gtest( + NO_CORES +NAME arkruntime4ecmascript_builtins_global_tests + SOURCES + ${ECMASCRIPT_BUILTINS_GLOBAL_TESTS_SOURCES} + LIBRARIES + arkruntime arkassembler + INCLUDE_DIRS + ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/tests/runtime/common + SANITIZERS + ${PANDA_SANITIZERS_LIST} +) +if(TARGET arkruntime4ecmascript_builtins_global_tests) + target_include_directories(arkruntime4ecmascript_builtins_global_tests + PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} + ) + target_compile_options(arkruntime4ecmascript_builtins_global_tests PUBLIC "-Wno-ignored-attributes") +endif() \ No newline at end of file diff --git a/tests/runtime/builtins/builtins_global_test.cpp b/tests/runtime/builtins/builtins_global_test.cpp new file mode 100644 index 000000000..9daeb3d4d --- /dev/null +++ b/tests/runtime/builtins/builtins_global_test.cpp @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2021-2022 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 "plugins/ecmascript/runtime/base/builtins_base.h" +#include "plugins/ecmascript/runtime/builtins/builtins_global.h" +#include "plugins/ecmascript/runtime/ecma_runtime_call_info.h" +#include "plugins/ecmascript/runtime/ecma_string.h" +#include "plugins/ecmascript/runtime/ecma_vm.h" +#include "plugins/ecmascript/runtime/global_env.h" + +#include "plugins/ecmascript/runtime/object_factory.h" +#include "plugins/ecmascript/tests/runtime/common/test_helper.h" + +// NOLINTNEXTLINE(google-build-using-namespace) +using namespace panda::ecmascript; +// NOLINTNEXTLINE(google-build-using-namespace) +using namespace panda::ecmascript::builtins; +// NOLINTNEXTLINE(google-build-using-namespace) +using namespace panda::ecmascript::base; + +namespace panda::test { +class BuiltinsGlobalTest : public testing::Test { +public: + static void SetUpTestCase() + { + TestHelper::CreateEcmaVMWithScope(instance_, thread_, scope_); + } + + static void TearDownTestCase() + { + TestHelper::DestroyEcmaVMWithScope(instance_, scope_); + } + + static void TestEscape(const uint16_t *src, const uint32_t len, const char *dst) + { + ASSERT_NE(thread_, nullptr); + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle str = factory->NewFromUtf16Literal(src, len); + JSHandle expected = factory->NewFromString(dst); + + auto ecma_runtime_call_info = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecma_runtime_call_info->SetFunction(JSTaggedValue::Undefined()); + ecma_runtime_call_info->SetThis(JSTaggedValue::Undefined()); + ecma_runtime_call_info->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecma_runtime_call_info.get()); + JSTaggedValue result = BuiltinsGlobal::Escape(ecma_runtime_call_info.get()); + ASSERT_TRUE(result.IsString()); + + ASSERT_EQ(expected->Compare(reinterpret_cast(result.GetRawData())), 0); + } + + static void TestUnescape(const char *src, const char *res) + { + ASSERT_NE(thread_, nullptr); + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle str = factory->NewFromString(src); + JSHandle expected = factory->NewFromString(res); + + auto ecma_runtime_call_info = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecma_runtime_call_info->SetFunction(JSTaggedValue::Undefined()); + ecma_runtime_call_info->SetThis(JSTaggedValue::Undefined()); + ecma_runtime_call_info->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecma_runtime_call_info.get()); + JSTaggedValue result = BuiltinsGlobal::Unescape(ecma_runtime_call_info.get()); + ASSERT_TRUE(result.IsString()); + + ASSERT_EQ(expected->Compare(reinterpret_cast(result.GetRawData())), 0); + } + + static PandaVM *instance_; + static EcmaHandleScope *scope_; + static JSThread *thread_; +}; +PandaVM *BuiltinsGlobalTest::instance_ = nullptr; +EcmaHandleScope *BuiltinsGlobalTest::scope_ = nullptr; +JSThread *BuiltinsGlobalTest::thread_ = nullptr; + +TEST_F(BuiltinsGlobalTest, Escape) +{ + std::u16string str = u"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./"; + TestEscape(reinterpret_cast(str.data()), str.size(), StringHelper::U16stringToString(str).c_str()); +} + +TEST_F(BuiltinsGlobalTest, Escape1) +{ + std::u16string str = u" / "; + TestEscape(reinterpret_cast(str.data()), str.size(), "%20/%20"); +} + +TEST_F(BuiltinsGlobalTest, Escape2) +{ + std::u16string str = u"\n"; + TestEscape(reinterpret_cast(str.data()), str.size(), "%0A"); +} + +TEST_F(BuiltinsGlobalTest, Escape3) +{ + // NOLINTNEXTLINE(readability-magic-numbers) + constexpr std::array c = {0x123}; + TestEscape(c.data(), c.size(), "%u0123"); +} + +TEST_F(BuiltinsGlobalTest, Escape4) +{ + // NOLINTNEXTLINE(readability-magic-numbers) + constexpr std::array c = {0xabcd}; + TestEscape(c.data(), c.size(), "%uABCD"); +} + +TEST_F(BuiltinsGlobalTest, Escape5) +{ + constexpr std::array c = {0x41, 0x20, 0x42, 0x1234, 0, 0x20, 0x43}; + TestEscape(c.data(), c.size(), "A%20B%u1234%00%20C"); +} + +TEST_F(BuiltinsGlobalTest, Escape1000) +{ + ASSERT_NE(thread_, nullptr); + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + + static const std::u16string unescaped = u"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./"; + + // NOLINTNEXTLINE(readability-magic-numbers) + for (uint16_t i = 0; i < 1000; ++i) { + JSHandle str = factory->NewFromUtf16Literal(&i, 1); + + auto ecma_runtime_call_info = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecma_runtime_call_info->SetFunction(JSTaggedValue::Undefined()); + ecma_runtime_call_info->SetThis(JSTaggedValue::Undefined()); + ecma_runtime_call_info->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecma_runtime_call_info.get()); + JSTaggedValue result = BuiltinsGlobal::Escape(ecma_runtime_call_info.get()); + ASSERT_TRUE(result.IsString()); + + bool is_equal = (str->Compare(reinterpret_cast(result.GetRawData())) == 0); + + if (unescaped.find(i) == std::string::npos) { + ASSERT_FALSE(is_equal); + } else { + ASSERT_TRUE(is_equal); + } + } +} + +TEST_F(BuiltinsGlobalTest, Unescape1000) +{ + ASSERT_NE(thread_, nullptr); + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + + // NOLINTNEXTLINE(readability-magic-numbers) + for (uint16_t i = 0; i < 1000; i += 10) { + // NOLINTNEXTLINE(readability-magic-numbers) + std::array c = {static_cast(i), static_cast(i + 1), + // NOLINTNEXTLINE(readability-magic-numbers) + static_cast(i + 2), static_cast(i + 3), + // NOLINTNEXTLINE(readability-magic-numbers) + static_cast(i + 4), static_cast(i + 5), + // NOLINTNEXTLINE(readability-magic-numbers) + static_cast(i + 6), static_cast(i + 7), + // NOLINTNEXTLINE(readability-magic-numbers) + static_cast(i + 8), static_cast(i + 9)}; + JSHandle str = factory->NewFromUtf16Literal(c.data(), c.size()); + + auto ecma_runtime_call_info = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecma_runtime_call_info->SetFunction(JSTaggedValue::Undefined()); + ecma_runtime_call_info->SetThis(JSTaggedValue::Undefined()); + ecma_runtime_call_info->SetCallArg(0, str.GetTaggedValue()); + + [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread_, ecma_runtime_call_info.get()); + JSTaggedValue result_escape = BuiltinsGlobal::Escape(ecma_runtime_call_info.get()); + + auto ecma_runtime_call_info2 = TestHelper::CreateEcmaRuntimeCallInfo(thread_, JSTaggedValue::Undefined(), 6); + ecma_runtime_call_info2->SetFunction(JSTaggedValue::Undefined()); + ecma_runtime_call_info2->SetThis(JSTaggedValue::Undefined()); + ecma_runtime_call_info2->SetCallArg(0, result_escape); + + [[maybe_unused]] auto prev2 = TestHelper::SetupFrame(thread_, ecma_runtime_call_info2.get()); + JSTaggedValue result = BuiltinsGlobal::Unescape(ecma_runtime_call_info2.get()); + + ASSERT_TRUE(result.IsString()); + ASSERT_EQ(str->Compare(reinterpret_cast(result.GetRawData())), 0); + } +} + +TEST_F(BuiltinsGlobalTest, Unescape1) +{ + TestUnescape("%41%4A%4a", "AJJ"); +} + +TEST_F(BuiltinsGlobalTest, Unescape2) +{ + TestUnescape("%U1234", "%U1234"); +} + +TEST_F(BuiltinsGlobalTest, Unescape3) +{ + TestUnescape("%%20", "% "); +} + +TEST_F(BuiltinsGlobalTest, Unescape4) +{ + TestUnescape("%%%20", "%% "); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape1) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape2) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%4"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape3) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape4) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u4"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape5) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u44"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape6) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u444"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape7) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%4z"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape8) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%uzzzz"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape9) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u4zzz"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape10) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u44zz"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape11) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u444z"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape12) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%4+"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape13) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u++++"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape14) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u4+++"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape15) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u44++"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape16) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "%u444+"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape17) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%4+"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape18) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%u++++"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape19) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%u4+++"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape20) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%u44++"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape21) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%u444+"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape22) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%4+bar"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape23) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%u++++bar"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape24) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%u4+++bar"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape25) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%u44++bar"; + TestUnescape(s, s); +} + +TEST_F(BuiltinsGlobalTest, MalformedUnescape26) +{ + // NOLINTNEXTLINE(modernize-avoid-c-arrays) + const char s[] = "foo%u444+bar"; + TestUnescape(s, s); +} + +} // namespace panda::test -- Gitee