1/*
2 * Copyright (c) 2021-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#include "ecmascript/builtins/builtins_string_iterator.h"
17#include "ecmascript/ecma_string_table.h"
18#include "ecmascript/js_iterator.h"
19#include "ecmascript/js_string_iterator.h"
20
21namespace panda::ecmascript::builtins {
22JSTaggedValue BuiltinsStringIterator::Next(EcmaRuntimeCallInfo *argv)
23{
24    ASSERT(argv);
25    BUILTINS_API_TRACE(argv->GetThread(), StringIterator, Next);
26    JSThread *thread = argv->GetThread();
27    [[maybe_unused]] EcmaHandleScope handleScope(thread);
28    // 1. Let O be the this value.
29    JSHandle<JSTaggedValue> thisValue = GetThis(argv);
30    return NextInternal(thread, thisValue);
31}
32
33JSTaggedValue BuiltinsStringIterator::NextInternal(JSThread *thread, JSHandle<JSTaggedValue> thisValue)
34{
35    // 2. If Type(O) is not Object, throw a TypeError exception.
36    // 3. If O does not have all of the internal slots of an String Iterator Instance (21.1.5.3),
37    // throw a TypeError exception.
38    if (!thisValue->IsStringIterator()) {
39        THROW_TYPE_ERROR_AND_RETURN(thread, "is not StringIterator", JSTaggedValue::Exception());
40    }
41    // 4. Let s be the value of the [[IteratedString]] internal slot of O.
42    JSHandle<JSTaggedValue> string(thread, thisValue.GetObject<JSStringIterator>()->GetIteratedString());
43    // 5. If s is undefined, return CreateIterResultObject(undefined, true).
44    if (string->IsUndefined()) {
45        return JSIterator::CreateIterResultObject(thread, string, true).GetTaggedValue();
46    }
47    // 6. Let position be the value of the [[StringIteratorNextIndex]] internal slot of O.
48    uint32_t position = thisValue.GetObject<JSStringIterator>()->GetStringIteratorNextIndex();
49
50    // 7. Let len be the number of elements in s.
51    uint32_t len = EcmaStringAccessor(string.GetObject<EcmaString>()).GetLength();
52    // If position ≥ len, then
53    // a. Set the value of the [[IteratedString]] internal slot of O to
54    // b. Return CreateIterResultObject(undefined, true).
55    if (position >= len) {
56        thisValue.GetObject<JSStringIterator>()->SetIteratedString(thread, JSTaggedValue::Undefined());
57        JSHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
58        return JSIterator::CreateIterResultObject(thread, result, true).GetTaggedValue();
59    }
60
61    // 9. Let first be the code unit value at index position in s.
62    uint16_t first = EcmaStringAccessor(string.GetObject<EcmaString>()).Get<false>(position);
63    JSMutableHandle<JSTaggedValue> result(thread, JSTaggedValue::Undefined());
64    uint32_t resultSize = 1;
65    // 10. If first < 0xD800 or first > 0xDBFF or position+1 = len, let resultString be the string consisting of the
66    // single code unit first.
67    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
68    if (position + 1 == len || first < base::utf_helper::DECODE_LEAD_LOW ||
69        first > base::utf_helper::DECODE_LEAD_HIGH) {
70        if (EcmaStringAccessor::CanBeCompressed(&first, 1)) {
71            JSHandle<SingleCharTable> singleCharTable(thread, thread->GetSingleCharTable());
72            result.Update(singleCharTable->GetStringFromSingleCharTable(first));
73        } else {
74            std::vector<uint16_t> resultString {first, 0x0};
75            result.Update(factory->NewFromUtf16(resultString.data(), 1).GetTaggedValue());
76        }
77    } else {
78        // 11. Else,
79        // a. Let second be the code unit value at index position+1 in the String S.
80        // b. If second < 0xDC00 or second > 0xDFFF, let resultString be the string consisting of the single code unit
81        // first.
82        // c. Else, let resultString be the string consisting of the code unit first followed by the code unit second.
83        uint16_t second = EcmaStringAccessor(string.GetObject<EcmaString>()).Get<false>(position + 1);
84        if (second < base::utf_helper::DECODE_TRAIL_LOW || second > base::utf_helper::DECODE_TRAIL_HIGH) {
85            std::vector<uint16_t> resultString {first, 0x0};
86            result.Update(factory->NewFromUtf16NotCompress(resultString.data(), 1).GetTaggedValue());
87        } else {
88            std::vector<uint16_t> resultString {first, second, 0x0};
89            result.Update(
90                factory->NewFromUtf16NotCompress(resultString.data(), 2).GetTaggedValue());  // 2: two bytes
91            resultSize = 2;  // 2: 2 means that two bytes represent a character string
92        }
93    }
94    // 12. Let resultSize be the number of code units in resultString.
95    // 13. Set the value of the [[StringIteratorNextIndex]] internal slot of O to position+ resultSize.
96    thisValue.GetObject<JSStringIterator>()->SetStringIteratorNextIndex(position + resultSize);
97
98    // 14. Return CreateIterResultObject(resultString, false).
99    return JSIterator::CreateIterResultObject(thread, result, false).GetTaggedValue();
100}
101}  // namespace panda::ecmascript::builtins
102