1/*
2 * Copyright (c) 2023 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/element_accessor.h"
17
18#include "ecmascript/js_tagged_value-inl.h"
19#include "ecmascript/tagged_array-inl.h"
20
21namespace panda::ecmascript {
22JSTaggedValue ElementAccessor::Get(JSHandle<JSObject> receiver, uint32_t idx)
23{
24    TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
25    ASSERT(idx < elements->GetLength());
26    ElementsKind kind = receiver->GetClass()->GetElementsKind();
27    if (!elements->GetClass()->IsMutantTaggedArray()) {
28        kind = ElementsKind::GENERIC;
29    }
30    // Note: Here we can't statically decide the element type is a primitive or heap object, especially for
31    //       dynamically-typed languages like JavaScript. So we simply skip the read-barrier.
32    size_t offset = JSTaggedValue::TaggedTypeSize() * idx;
33    // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
34    JSTaggedType rawValue = Barriers::GetValue<JSTaggedType>(elements->GetData(), offset);
35    return GetTaggedValueWithElementsKind(rawValue, kind);
36}
37
38JSTaggedValue ElementAccessor::Get(JSObject *receiver, uint32_t idx)
39{
40    TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
41    ASSERT(idx < elements->GetLength());
42    ElementsKind kind = receiver->GetClass()->GetElementsKind();
43    if (!elements->GetClass()->IsMutantTaggedArray()) {
44        kind = ElementsKind::GENERIC;
45    }
46    // Note: Here we can't statically decide the element type is a primitive or heap object, especially for
47    //       dynamically-typed languages like JavaScript. So we simply skip the read-barrier.
48    size_t offset = JSTaggedValue::TaggedTypeSize() * idx;
49    // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
50    JSTaggedType rawValue = Barriers::GetValue<JSTaggedType>(elements->GetData(), offset);
51    return GetTaggedValueWithElementsKind(rawValue, kind);
52}
53
54JSTaggedValue ElementAccessor::FastGet(JSHandle<TaggedArray> elements, uint32_t idx, ElementsKind kind)
55{
56    ASSERT(idx < elements->GetLength());
57    size_t offset = JSTaggedValue::TaggedTypeSize() * idx;
58    // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
59    JSTaggedType rawValue = Barriers::GetValue<JSTaggedType>(elements->GetData(), offset);
60    return GetTaggedValueWithElementsKind(rawValue, kind);
61}
62
63bool ElementAccessor::IsDictionaryMode(JSHandle<JSObject> receiver)
64{
65    TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
66    return elements->GetClass()->IsDictionary();
67}
68
69bool ElementAccessor::IsDictionaryMode(JSObject *receiver)
70{
71    TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
72    return elements->GetClass()->IsDictionary();
73}
74
75uint32_t ElementAccessor::GetElementsLength(JSHandle<JSObject> receiver)
76{
77    TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
78    return elements->GetLength();
79}
80
81uint32_t ElementAccessor::GetElementsLength(JSObject *receiver)
82{
83    TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
84    return elements->GetLength();
85}
86
87JSTaggedValue ElementAccessor::GetTaggedValueWithElementsKind(JSTaggedType rawValue, ElementsKind kind)
88{
89    JSTaggedValue convertedValue = JSTaggedValue::Hole();
90    if (rawValue == base::SPECIAL_HOLE) {
91        return convertedValue;
92    }
93    switch (kind) {
94        case ElementsKind::INT:
95        case ElementsKind::HOLE_INT:
96            convertedValue = JSTaggedValue(static_cast<int>(rawValue));
97            break;
98        case ElementsKind::NUMBER:
99        case ElementsKind::HOLE_NUMBER:
100            convertedValue = JSTaggedValue(base::bit_cast<double>(rawValue));
101            break;
102        case ElementsKind::HOLE:
103        case ElementsKind::NONE:
104        case ElementsKind::TAGGED:
105        case ElementsKind::STRING:
106        case ElementsKind::HOLE_TAGGED:
107        case ElementsKind::HOLE_STRING:
108            convertedValue = JSTaggedValue(rawValue);
109            break;
110        default:
111            LOG_ECMA(FATAL) << "Trying to Get TaggedValue With Unknown ElementsKind";
112            UNREACHABLE();
113            break;
114    }
115    return convertedValue;
116}
117
118JSTaggedType ElementAccessor::ConvertTaggedValueWithElementsKind(JSTaggedValue rawValue, ElementsKind kind)
119{
120    JSTaggedType convertedValue = base::SPECIAL_HOLE;
121    if (rawValue.IsHole() && Elements::IsInNumbers(kind)) {
122        return convertedValue;
123    }
124    switch (kind) {
125        case ElementsKind::INT:
126        case ElementsKind::HOLE_INT:
127            convertedValue = static_cast<JSTaggedType>(rawValue.GetInt());
128            break;
129        case ElementsKind::NUMBER:
130        case ElementsKind::HOLE_NUMBER:
131            if (rawValue.IsInt()) {
132                int intValue = rawValue.GetInt();
133                convertedValue = base::bit_cast<JSTaggedType>(static_cast<double>(intValue));
134            } else {
135                convertedValue = base::bit_cast<JSTaggedType>(rawValue.GetDouble());
136            }
137            break;
138        case ElementsKind::HOLE:
139        case ElementsKind::NONE:
140        case ElementsKind::TAGGED:
141        case ElementsKind::STRING:
142        case ElementsKind::HOLE_TAGGED:
143        case ElementsKind::HOLE_STRING:
144            convertedValue = rawValue.GetRawData();
145            break;
146        default:
147            LOG_ECMA(FATAL) << "Trying to Convert TaggedValue With Unknown ElementsKind";
148            UNREACHABLE();
149            break;
150    }
151    return convertedValue;
152}
153
154void ElementAccessor::CopyJSArrayObject(const JSThread *thread, JSHandle<JSObject>srcObj, JSHandle<JSObject>dstObj,
155                                        uint32_t effectiveLength)
156{
157    ASSERT(effectiveLength <= GetElementsLength(srcObj));
158    ASSERT(effectiveLength <= GetElementsLength(dstObj));
159    for (uint32_t i = 0; i < effectiveLength; i++) {
160        JSHandle<JSTaggedValue> value(thread, Get(srcObj, i));
161        Set(thread, dstObj, i, value, true);
162    }
163}
164
165void ElementAccessor::CopyJSArrayToTaggedArray(const JSThread *thread, JSHandle<JSObject>srcObj,
166                                               JSHandle<TaggedArray>dstElements, uint32_t effectiveLength)
167{
168    ASSERT(effectiveLength <= GetElementsLength(srcObj));
169    ASSERT(effectiveLength <= dstElements->GetLength());
170    for (uint32_t i = 0; i < effectiveLength; i++) {
171        JSHandle<JSTaggedValue> value(thread, Get(srcObj, i));
172        dstElements->Set(thread, i, value);
173    }
174}
175}  // namespace panda::ecmascript
176