1/*
2 * Copyright (c) 2024 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#ifndef ECMASCRIPT_STUBS_RUNTIME_OPTIMIZED_STUBS_INL_H
17#define ECMASCRIPT_STUBS_RUNTIME_OPTIMIZED_STUBS_INL_H
18
19#include "ecmascript/stubs/runtime_stubs.h"
20#include "ecmascript/ecma_string-inl.h"
21
22namespace panda::ecmascript {
23template <typename T>
24uint16_t RuntimeStubs::GetCodeUnit(Span<T> &sp, int32_t index, int32_t length)
25{
26    if ((index < 0) || (index >= length)) {
27        return 0;
28    }
29    return sp[index];
30}
31
32template <typename T>
33JSTaggedValue RuntimeStubs::DecodePercentEncoding(JSThread *thread, int32_t &n, int32_t &k,
34                                                  const JSHandle<EcmaString> &str, uint8_t &bb,
35                                                  std::vector<uint8_t> &oct, Span<T> &sp, int32_t strLen)
36{
37    CString errorMsg;
38    int32_t j = 1;
39    while (j < n) {
40        k++;
41        uint16_t codeUnit = GetCodeUnit<T>(sp, k, strLen);
42        // b. If the code unit at index k within string is not "%", throw a URIError exception.
43        // c. If the code units at index (k +1) and (k + 2) within string do not represent hexadecimal
44        //    digits, throw a URIError exception.
45        if (!(codeUnit == '%')) {
46            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
47            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
48        }
49
50        uint16_t frontChart = GetCodeUnit<T>(sp, k + 1, strLen);
51        uint16_t behindChart = GetCodeUnit<T>(sp, k + 2, strLen);  // 2: means plus 2
52        if (!(base::utf_helper::IsHexDigits(frontChart) && base::utf_helper::IsHexDigits(behindChart))) {
53            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
54            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
55        }
56        bb = GetValueFromTwoHex(frontChart, behindChart);
57        // e. If the two most significant bits in B are not 10, throw a URIError exception.
58        if (!((bb & base::utf_helper::BIT_MASK_2) == base::utf_helper::BIT_MASK_1)) {
59            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
60            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
61        }
62        k += 2;  // 2: means plus 2
63        oct.push_back(bb);
64        j++;
65    }
66    return JSTaggedValue::True();
67}
68
69JSTaggedValue RuntimeStubs::UTF16EncodeCodePoint(JSThread *thread, const std::vector<uint8_t> &oct,
70                                                 const JSHandle<EcmaString> &str, std::u16string &sStr)
71{
72    if (!base::utf_helper::IsValidUTF8(oct)) {
73        CString errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
74        THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
75    }
76    uint32_t vv = base::StringHelper::Utf8ToU32String(oct);
77    if (vv < base::utf_helper::DECODE_SECOND_FACTOR) {
78        sStr = base::StringHelper::Utf16ToU16String(reinterpret_cast<uint16_t *>(&vv), 1);
79    } else {
80        uint16_t lv = (((vv - base::utf_helper::DECODE_SECOND_FACTOR) & base::utf_helper::BIT16_MASK) +
81            base::utf_helper::DECODE_TRAIL_LOW);
82        // NOLINT
83        uint16_t hv = ((((vv - base::utf_helper::DECODE_SECOND_FACTOR) >> 10U) & base::utf_helper::BIT16_MASK) +
84            base::utf_helper::DECODE_LEAD_LOW);  // 10: means shift left by 10 digits
85        sStr = base::StringHelper::Append(base::StringHelper::Utf16ToU16String(&hv, 1),
86                                          base::StringHelper::Utf16ToU16String(&lv, 1));
87    }
88    return JSTaggedValue::True();
89}
90
91template <typename T>
92JSTaggedValue RuntimeStubs::DecodePercentEncoding(JSThread *thread, const JSHandle<EcmaString> &str, int32_t &k,
93                                                  int32_t strLen, std::u16string &sStr, Span<T> &sp)
94{
95    [[maybe_unused]] uint32_t start = static_cast<uint32_t>(k);
96    CString errorMsg;
97    // ii. If k + 2 is greater than or equal to strLen, throw a URIError exception.
98    // iii. If the code units at index (k+1) and (k + 2) within string do not represent hexadecimal digits,
99    //      throw a URIError exception.
100    if ((k + 2) >= strLen) {  // 2: means plus 2
101        errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
102        THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
103    }
104    uint16_t frontChar = GetCodeUnit<T>(sp, k + 1, strLen);
105    uint16_t behindChar = GetCodeUnit<T>(sp, k + 2, strLen);  // 2: means plus 2
106    if (!(base::utf_helper::IsHexDigits(frontChar) && base::utf_helper::IsHexDigits(behindChar))) {
107        errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
108        THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
109    }
110    uint8_t bb = GetValueFromTwoHex(frontChar, behindChar);
111    k += 2;  // 2: means plus 2
112    if ((bb & base::utf_helper::BIT_MASK_1) == 0) {
113        sStr = base::StringHelper::Utf8ToU16String(&bb, 1);
114    } else {
115        // vii. Else the most significant bit in B is 1,
116        //   1. Let n be the smallest nonnegative integer such that (B << n) & 0x80 is equal to 0.
117        //   3. Let Octets be an array of 8-bit integers of size n.
118        //   4. Put B into Octets at index 0.
119        //   6. Let j be 1.
120        //   7. Repeat, while j < n
121        //     a. Increase k by 1.
122        //     d. Let B be the 8-bit value represented by the two hexadecimal digits at
123        //        index (k + 1) and (k + 2).
124        //     f. Increase k by 2.
125        //     g. Put B into Octets at index j.
126        //     h. Increase j by 1.
127        //   9. If V < 0x10000, then
128        //     a. Let C be the code unit V.
129        //     b. If C is not in reservedSet, then
130        //        i. Let S be the String containing only the code unit C.
131        //     c. Else C is in reservedSet,
132        //        i. Let S be the substring of string from index start to index k inclusive.
133        //   10. Else V ≥ 0x10000,
134        //     a. Let L be (((V – 0x10000) & 0x3FF) + 0xDC00).
135        //     b. Let H be ((((V – 0x10000) >> 10) & 0x3FF) + 0xD800).
136        //     c. Let S be the String containing the two code units H and L.
137        int32_t n = 0;
138        while ((((static_cast<uint32_t>(bb) << static_cast<uint32_t>(n)) & base::utf_helper::BIT_MASK_1) != 0)) {
139            n++;
140            if (n > 4) { // 4 : 4 means less than 4
141                break;
142            }
143        }
144        // 2. If n equals 1 or n is greater than 4, throw a URIError exception.
145        if ((n == 1) || (n > 4)) {
146            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
147            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
148        }
149
150        std::vector<uint8_t> oct = {bb};
151
152        // 5. If k + (3 × (n – 1)) is greater than or equal to strLen, throw a URIError exception.
153        if (k + (3 * (n - 1)) >= strLen) {  // 3: means multiply by 3
154            errorMsg = "DecodeURI: invalid character: " + ConvertToString(str.GetTaggedValue());
155            THROW_URI_ERROR_AND_RETURN(thread, errorMsg.c_str(), JSTaggedValue::Exception());
156        }
157        DecodePercentEncoding<T>(thread, n, k, str, bb, oct, sp, strLen);
158        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
159        UTF16EncodeCodePoint(thread, oct, str, sStr);
160        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
161    }
162    return JSTaggedValue::True();
163}
164
165template <typename T>
166JSTaggedValue RuntimeStubs::RuntimeDecodeURIComponent(JSThread *thread, const JSHandle<EcmaString> &string,
167                                                      const T *data)
168{
169    // 1. Let strLen be the number of code units in string.
170    CString errorMsg;
171    auto stringAcc = EcmaStringAccessor(string);
172    int32_t strLen = static_cast<int32_t>(stringAcc.GetLength());
173    // 2. Let R be the empty String.
174    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
175    std::u16string resStr;
176    std::vector<T> tmpVec;
177    tmpVec.resize(strLen);
178    if (LIKELY(strLen != 0)) {
179        if (memcpy_s(tmpVec.data(), sizeof(T) * strLen, data, sizeof(T) * strLen) != EOK) {
180            LOG_FULL(FATAL) << "memcpy_s failed";
181            UNREACHABLE();
182        }
183    }
184    Span<T> sp(tmpVec.data(), strLen);
185    // 3. Let k be 0.
186    // 4. Repeat
187    int32_t k = 0;
188    while (true) {
189        if (k == strLen) {
190            // a. If k equals strLen, return R.
191            auto *uint16tData = reinterpret_cast<uint16_t *>(resStr.data());
192            uint32_t resSize = resStr.size();
193            return factory->NewFromUtf16Literal(uint16tData, resSize).GetTaggedValue();
194        }
195
196        // b. Let C be the code unit at index k within string.
197        // c. If C is not "%", then
198        //    i. Let S be the String containing only the code unit C.
199        // d. Else C is "%",
200        //   i. Let start be k.
201        //   iv. Let B be the 8-bit value represented by the two hexadecimal digits at index (k + 1) and (k + 2).
202        //   v. Increase k by 2.
203        //   vi. If the most significant bit in B is 0, then
204        //      1. Let C be the code unit with code unit value B.
205        //      2. If C is not in reservedSet, then
206        //         a. Let S be the String containing only the code unit C.
207        //      3. Else C is in reservedSet,
208        //         a. Let S be the substring of string from index start to index k inclusive.
209        uint16_t cc = GetCodeUnit<T>(sp, k, strLen);
210        std::u16string sStr;
211        if (cc != '%') {
212            if (cc == 0 && strLen == 1) {
213                JSHandle<EcmaString> tmpEcmaString = factory->NewFromUtf16Literal(&cc, 1);
214                return tmpEcmaString.GetTaggedValue();
215            }
216            sStr = base::StringHelper::Utf16ToU16String(&cc, 1);
217        } else {
218            DecodePercentEncoding<T>(thread, string, k, strLen, sStr, sp);
219            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
220        }
221        resStr += sStr;
222        k++;
223    }
224}
225
226uint8_t RuntimeStubs::GetValueFromTwoHex(uint8_t front, uint8_t behind)
227{
228    std::string hexString("0123456789ABCDEF");
229    size_t idxf = base::StringHelper::FindFromU8ToUpper(hexString, &front);
230    size_t idxb = base::StringHelper::FindFromU8ToUpper(hexString, &behind);
231
232    uint8_t res = ((idxf << 4U) | idxb) & base::utf_helper::BIT_MASK_FF;  // NOLINT 4: means shift left by 4 digits
233    return res;
234}
235} // namespace panda::ecmascript
236#endif  // ECMASCRIPT_STUBS_RUNTIME_OPTIMIZED_STUBS_INL_H