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