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 
21 namespace panda::ecmascript {
Get(JSHandle<JSObject> receiver, uint32_t idx)22 JSTaggedValue 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 
Get(JSObject *receiver, uint32_t idx)38 JSTaggedValue 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 
FastGet(JSHandle<TaggedArray> elements, uint32_t idx, ElementsKind kind)54 JSTaggedValue 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 
IsDictionaryMode(JSHandle<JSObject> receiver)63 bool ElementAccessor::IsDictionaryMode(JSHandle<JSObject> receiver)
64 {
65     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
66     return elements->GetClass()->IsDictionary();
67 }
68 
IsDictionaryMode(JSObject *receiver)69 bool ElementAccessor::IsDictionaryMode(JSObject *receiver)
70 {
71     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
72     return elements->GetClass()->IsDictionary();
73 }
74 
GetElementsLength(JSHandle<JSObject> receiver)75 uint32_t ElementAccessor::GetElementsLength(JSHandle<JSObject> receiver)
76 {
77     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
78     return elements->GetLength();
79 }
80 
GetElementsLength(JSObject *receiver)81 uint32_t ElementAccessor::GetElementsLength(JSObject *receiver)
82 {
83     TaggedArray *elements = TaggedArray::Cast(receiver->GetElements());
84     return elements->GetLength();
85 }
86 
GetTaggedValueWithElementsKind(JSTaggedType rawValue, ElementsKind kind)87 JSTaggedValue 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 
ConvertTaggedValueWithElementsKind(JSTaggedValue rawValue, ElementsKind kind)118 JSTaggedType 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 
CopyJSArrayObject(const JSThread *thread, JSHandle<JSObject>srcObj, JSHandle<JSObject>dstObj, uint32_t effectiveLength)154 void 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 
CopyJSArrayToTaggedArray(const JSThread *thread, JSHandle<JSObject>srcObj, JSHandle<TaggedArray>dstElements, uint32_t effectiveLength)165 void 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