• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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/js_typed_array.h"
17 
18 #include "ecmascript/accessor_data.h"
19 #include "ecmascript/base/typed_array_helper-inl.h"
20 #include "ecmascript/builtins/builtins_arraybuffer.h"
21 
22 namespace panda::ecmascript {
23 using TypedArrayHelper = base::TypedArrayHelper;
24 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
25 
ToPropKey(JSThread * thread,const JSHandle<JSTaggedValue> & key)26 JSHandle<JSTaggedValue> JSTypedArray::ToPropKey(JSThread *thread, const JSHandle<JSTaggedValue> &key)
27 {
28     if (key->IsSymbol()) {
29         return key;
30     }
31     return JSHandle<JSTaggedValue>(JSTaggedValue::ToString(thread, key));
32 }
33 // 9.4.5.1 [[GetOwnProperty]] ( P )
GetOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray,const JSHandle<JSTaggedValue> & key,PropertyDescriptor & desc)34 bool JSTypedArray::GetOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray,
35                                   const JSHandle<JSTaggedValue> &key, PropertyDescriptor &desc)
36 {
37     // 1. Assert : IsPropertyKey(P) is true.
38     ASSERT(JSTaggedValue::IsPropertyKey(key));
39     // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
40     // 3. If Type(P) is String, then
41     //   a. Let numericIndex be CanonicalNumericIndexString(P).
42     //   b. Assert: numericIndex is not an abrupt completion.
43     //   c. If numericIndex is not undefined, then
44     //     i. Let value be IntegerIndexedElementGet (O, numericIndex).
45     //     ii. ReturnIfAbrupt(value).
46     //     iii. If value is undefined, return undefined.
47     //     iv. Return a PropertyDescriptor{ [[Value]]: value, [[Enumerable]]: true, [[Writable]]: true,
48     //         [[Configurable]]: false }.
49     if (key->IsString() || key->IsNumber()) {
50         JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key);
51         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
52         if (!numericIndex.IsUndefined()) {
53             JSHandle<JSTaggedValue> value =
54                 JSTypedArray::IntegerIndexedElementGet(thread, typedarray, numericIndex).GetValue();
55             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
56             if (value->IsUndefined()) {
57                 return false;
58             }
59             desc.SetValue(value);
60             desc.SetEnumerable(true);
61             desc.SetWritable(true);
62             desc.SetConfigurable(true);
63             return true;
64         }
65     }
66     // 4. Return OrdinaryGetOwnProperty(O, P).
67     return JSObject::OrdinaryGetOwnProperty(thread, JSHandle<JSObject>(typedarray), key, desc);
68 }
69 
70 // 9.4.5.2 [[HasProperty]] ( P )
HasProperty(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray,const JSHandle<JSTaggedValue> & key)71 bool JSTypedArray::HasProperty(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray,
72                                const JSHandle<JSTaggedValue> &key)
73 {
74     // 1. Assert: IsPropertyKey(P) is true.
75     ASSERT(JSTaggedValue::IsPropertyKey(key));
76     // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
77     // 3. If Type(P) is String, then
78     //   a. Let numericIndex be CanonicalNumericIndexString(P).
79     //   b. Assert: numericIndex is not an abrupt completion.
80     //   c. If numericIndex is not undefined, then
81     //     i. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot.
82     //     ii. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
83     //     iii. If IsInteger(numericIndex) is false, return false
84     //     iv. If numericIndex = −0, return false.
85     //     v. If numericIndex < 0, return false.
86     //     vi. If numericIndex ≥ the value of O’s [[ArrayLength]] internal slot, return false.
87     //     vii. Return true.
88     JSHandle<JSTypedArray> typedarrayObj(typedarray);
89     if (key->IsString() || key->IsNumber()) {
90         JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key);
91         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
92         if (!numericIndex.IsUndefined()) {
93             JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray();
94             if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) {
95                 THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false);
96             }
97             if (!numericIndex.IsInteger()) {
98                 return false;
99             }
100             JSHandle<JSTaggedValue> numericIndexHandle(thread, numericIndex);
101             JSTaggedNumber numericIndexNumber = JSTaggedValue::ToNumber(thread, numericIndexHandle);
102             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
103             double tNegZero = -0.0;
104             auto eZero = JSTaggedNumber(tNegZero);
105             JSHandle<JSTaggedValue> zero(thread, JSTaggedValue(0));
106             if (JSTaggedNumber::SameValue(numericIndexNumber, eZero)) {
107                 return false;
108             }
109 
110             if (JSTaggedValue::Less(thread, numericIndexHandle, zero)) {
111                 return false;
112             }
113             uint32_t arrLen = typedarrayObj->GetArrayLength();
114             JSHandle<JSTaggedValue> arrLenHandle(thread, JSTaggedValue(arrLen));
115             return JSTaggedValue::Less(thread, numericIndexHandle, arrLenHandle);
116         }
117     }
118     // 4. Return OrdinaryHasProperty(O, P).
119     PropertyDescriptor desc(thread);
120     if (JSObject::OrdinaryGetOwnProperty(thread, JSHandle<JSObject>::Cast(typedarrayObj), key, desc)) {
121         return true;
122     }
123     JSTaggedValue parent = JSTaggedValue::GetPrototype(thread, JSHandle<JSTaggedValue>::Cast(typedarrayObj));
124     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
125     if (!parent.IsNull()) {
126         return JSTaggedValue::HasProperty(thread, JSHandle<JSTaggedValue>(thread, parent), key);
127     }
128     return false;
129 }
130 
131 // 9.4.5.3 [[DefineOwnProperty]] ( P, Desc )
DefineOwnProperty(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray,const JSHandle<JSTaggedValue> & key,const PropertyDescriptor & desc)132 bool JSTypedArray::DefineOwnProperty(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray,
133                                      const JSHandle<JSTaggedValue> &key, const PropertyDescriptor &desc)
134 {
135     // 1. Assert: IsPropertyKey(P) is true.
136     ASSERT(JSTaggedValue::IsPropertyKey(key));
137     // 2. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
138     // 3. If Type(P) is String, then
139     //   a. Let numericIndex be CanonicalNumericIndexString (P).
140     //   b. Assert: numericIndex is not an abrupt completion.
141     //   c. If numericIndex is not undefined, then
142     JSHandle<JSTypedArray> typedarrayObj(typedarray);
143     if (key->IsString() || key->IsNumber()) {
144         JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key);
145         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
146         if (!numericIndex.IsUndefined()) {
147             // i. If IsInteger(numericIndex) is false, return false
148             // ii. Let intIndex be numericIndex.
149             // iii. If intIndex = −0, return false.
150             // iv. If intIndex < 0, return false.
151             // v. Let length be the value of O’s [[ArrayLength]] internal slot.
152             // vi. If intIndex ≥ length, return false.
153             // vii. If IsAccessorDescriptor(Desc) is true, return false.
154             // viii. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is true, return false.
155             // ix. If Desc has an [[Enumerable]] field and if Desc.[[Enumerable]] is false, return false.
156             // x. If Desc has a [[Writable]] field and if Desc.[[Writable]] is false, return false.
157             // xi. If Desc has a [[Value]] field, then
158             //   1. Let value be Desc.[[Value]].
159             //   2. Return IntegerIndexedElementSet (O, intIndex, value).
160             // xii. Return true.
161             if (!numericIndex.IsInteger()) {
162                 return false;
163             }
164             JSHandle<JSTaggedValue> numericIndexHandle(thread, numericIndex);
165             JSTaggedNumber numericIndexNumber = JSTaggedValue::ToNumber(thread, numericIndexHandle);
166             RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
167             double tNegZero = -0.0;
168             auto eZero = JSTaggedNumber(tNegZero);
169             JSHandle<JSTaggedValue> zero(thread, JSTaggedValue(0));
170             if (JSTaggedNumber::SameValue(numericIndexNumber, eZero)) {
171                 return false;
172             }
173             if (JSTaggedValue::Less(thread, numericIndexHandle, zero)) {
174                 return false;
175             }
176             uint32_t arrLen = typedarrayObj->GetArrayLength();
177             JSHandle<JSTaggedValue> arrLenHandle(thread, JSTaggedValue(arrLen));
178             if (!JSTaggedValue::Less(thread, numericIndexHandle, arrLenHandle)) {
179                 return false;
180             }
181             if (desc.IsAccessorDescriptor()) {
182                 return false;
183             }
184             if (desc.HasConfigurable() && !desc.IsConfigurable()) {
185                 return false;
186             }
187             if (desc.HasEnumerable() && !desc.IsEnumerable()) {
188                 return false;
189             }
190             if (desc.HasWritable() && !desc.IsWritable()) {
191                 return false;
192             }
193             if (desc.HasValue()) {
194                 JSHandle<JSTaggedValue> value = desc.GetValue();
195                 return (JSTypedArray::IntegerIndexedElementSet(thread, typedarray, numericIndex, value));
196             }
197             return true;
198         }
199     }
200     // 4. Return OrdinaryDefineOwnProperty(O, P, Desc).
201     bool result = JSObject::OrdinaryDefineOwnProperty(thread, JSHandle<JSObject>::Cast(typedarrayObj), key, desc);
202     if (result) {
203         JSTaggedValue constructorKey = thread->GlobalConstants()->GetConstructorString();
204         if (key.GetTaggedValue() == constructorKey) {
205             typedarrayObj->GetJSHClass()->SetHasConstructor(true);
206             return true;
207         }
208     }
209     return result;
210 }
211 
212 // 9.4.5.4 [[Get]] ( P, Receiver )
GetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & receiver)213 OperationResult JSTypedArray::GetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray,
214                                           const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &receiver)
215 {
216     // 1. Assert : IsPropertyKey(P) is true.
217     ASSERT(JSTaggedValue::IsPropertyKey(key));
218     // 2. If Type(P) is String and if SameValue(O, Receiver) is true, then
219     if ((key->IsString() || key->IsNumber()) && JSTaggedValue::SameValue(typedarray, receiver)) {
220         //   a. Let numericIndex be CanonicalNumericIndexString (P).
221         //   b. Assert: numericIndex is not an abrupt completion.
222         //   c. If numericIndex is not undefined, then
223         //     i. Return IntegerIndexedElementGet (O, numericIndex).
224         JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key);
225         RETURN_VALUE_IF_ABRUPT_COMPLETION(
226             thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
227         if (!numericIndex.IsUndefined()) {
228             return JSTypedArray::IntegerIndexedElementGet(thread, typedarray, numericIndex);
229         }
230     }
231 
232     // 3. Return the result of calling the default ordinary object [[Get]] internal method (9.1.8) on O
233     //   passing P and Receiver as arguments.
234     return JSObject::GetProperty(thread, typedarray, key, receiver);
235 }
236 
237 // 9.4.5.5 [[Set]] ( P, V, Receiver )
SetProperty(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray,const JSHandle<JSTaggedValue> & key,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & receiver,bool mayThrow)238 bool JSTypedArray::SetProperty(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray,
239                                const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
240                                const JSHandle<JSTaggedValue> &receiver, bool mayThrow)
241 {
242     // 1. Assert : IsPropertyKey(P) is true.
243     ASSERT(JSTaggedValue::IsPropertyKey(key));
244     // 2. If Type(P) is String and if SameValue(O, Receiver) is true, then
245     if ((key->IsString() || key->IsNumber()) && JSTaggedValue::SameValue(typedarray, receiver)) {
246         //   a. Let numericIndex be CanonicalNumericIndexString (P).
247         //   b. Assert: numericIndex is not an abrupt completion.
248         //   c. If numericIndex is not undefined, then
249         //     i. Return IntegerIndexedElementSet (O, numericIndex, V).
250         JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key);
251         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
252         if (!numericIndex.IsUndefined()) {
253             return JSTypedArray::IntegerIndexedElementSet(thread, typedarray, numericIndex, value);
254         }
255     }
256     // 3. Return the result of calling the default ordinary object [[Set]] internal method (9.1.8) on O passing
257     // P, V, and Receiver as arguments.
258     return JSObject::SetProperty(thread, typedarray, key, value, receiver, mayThrow);
259 }
260 
261 // s12 10.4.5.6 [[Delete]] ( P )
DeleteProperty(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray,const JSHandle<JSTaggedValue> & key)262 bool JSTypedArray::DeleteProperty(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray,
263                                   const JSHandle<JSTaggedValue> &key)
264 {
265     // 1. Assert: IsPropertyKey(P) is true.
266     // 2. Assert: O is an Integer-Indexed exotic object.
267     ASSERT(JSTaggedValue::IsPropertyKey(key));
268     // 3. If Type(P) is String, then
269         // a. Let numericIndex be CanonicalNumericIndexString(P).
270         // b. If numericIndex is not undefined, then
271            // i. If IsValidIntegerIndex(O, numericIndex) is false, return true; else return false.
272     if (key->IsString() || key->IsNumber()) {
273         JSTaggedValue numericIndex = JSTaggedValue::CanonicalNumericIndexString(thread, key);
274         if (!numericIndex.IsUndefined()) {
275             if (!IsValidIntegerIndex(typedarray, numericIndex)) {
276                 return true;
277             }
278             return false;
279         }
280     }
281     // 4. Return ? OrdinaryDelete(O, P).
282     return JSObject::DeleteProperty(thread, JSHandle<JSObject>(typedarray), key);
283 }
284 
285 // 9.4.5.6 [[OwnPropertyKeys]] ( )
OwnPropertyKeys(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray)286 JSHandle<TaggedArray> JSTypedArray::OwnPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray)
287 {
288     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
289     // 1. Let keys be a new empty List.
290     // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and
291     // [[TypedArrayName]] internal slots.
292     // 3. Let len be the value of O’s [[ArrayLength]] internal slot.
293     JSHandle<JSTypedArray> arrayObj(typedarray);
294     JSHandle<TaggedArray> objKeys = JSObject::GetOwnPropertyKeys(thread, JSHandle<JSObject>::Cast(arrayObj));
295     uint32_t objKeysLen = objKeys->GetLength();
296     uint32_t bufferKeysLen = arrayObj->GetArrayLength();
297     uint32_t length = objKeysLen + bufferKeysLen;
298     JSHandle<TaggedArray> nameList = factory->NewTaggedArray(length);
299 
300     // 4. For each integer i starting with 0 such that i < len, in ascending order,
301     //   a. Add ToString(i) as the last element of keys.
302     uint32_t copyLength = 0;
303     JSMutableHandle<JSTaggedValue> tKey(thread, JSTaggedValue::Undefined());
304     for (uint32_t k = 0; k < bufferKeysLen; k++) {
305         tKey.Update(JSTaggedValue(k));
306         JSHandle<JSTaggedValue> sKey(JSTaggedValue::ToString(thread, tKey));
307         RETURN_HANDLE_IF_ABRUPT_COMPLETION(TaggedArray, thread);
308         nameList->Set(thread, copyLength, sKey.GetTaggedValue());
309         copyLength++;
310     }
311 
312     // 5. For each own property key P of O such that Type(P) is String and P is not an integer index, in
313     // property creation order
314     //   a. Add P as the last element of keys.
315     for (uint32_t i = 0; i < objKeysLen; i++) {
316         JSTaggedValue key = objKeys->Get(i);
317         if (JSTaggedValue(key).IsString()) {
318             nameList->Set(thread, copyLength, key);
319             copyLength++;
320         }
321     }
322 
323     // 6. For each own property key P of O such that Type(P) is Symbol, in property creation order
324     //   a. Add P as the last element of keys.
325     for (uint32_t i = 0; i < objKeysLen; i++) {
326         JSTaggedValue key = objKeys->Get(i);
327         if (JSTaggedValue(key).IsSymbol()) {
328             nameList->Set(thread, copyLength, key);
329             copyLength++;
330         }
331     }
332 
333     // 7. Return keys.
334     return factory->CopyArray(nameList, length, copyLength);
335 }
336 
OwnEnumPropertyKeys(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray)337 JSHandle<TaggedArray> JSTypedArray::OwnEnumPropertyKeys(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray)
338 {
339     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
340     // 1. Let keys be a new empty List.
341     // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and
342     // [[TypedArrayName]] internal slots.
343     // 3. Let len be the value of O’s [[ArrayLength]] internal slot.
344     JSHandle<JSTypedArray> arrayObj(typedarray);
345     JSHandle<TaggedArray> objKeys = JSObject::GetOwnEnumPropertyKeys(thread, JSHandle<JSObject>::Cast(arrayObj));
346     uint32_t objKeysLen = objKeys->GetLength();
347     uint32_t bufferKeysLen = arrayObj->GetArrayLength();
348     uint32_t length = objKeysLen + bufferKeysLen;
349     JSHandle<TaggedArray> nameList = factory->NewTaggedArray(length);
350 
351     // 4. For each integer i starting with 0 such that i < len, in ascending order,
352     //   a. Add ToString(i) as the last element of keys.
353     uint32_t copyLength = 0;
354     for (uint32_t k = 0; k < bufferKeysLen; k++) {
355         auto key = base::NumberHelper::IntToEcmaString(thread, k);
356         nameList->Set(thread, copyLength, key);
357         copyLength++;
358     }
359 
360     // 5. For each own property key P of O such that Type(P) is String and P is not an integer index, in
361     // property creation order
362     //   a. Add P as the last element of keys.
363     for (uint32_t i = 0; i < objKeysLen; i++) {
364         JSTaggedValue key = objKeys->Get(i);
365         nameList->Set(thread, copyLength, key);
366         copyLength++;
367     }
368 
369     // 7. Return keys.
370     return factory->CopyArray(nameList, length, copyLength);
371 }
372 
373 // 9.4.5.7 IntegerIndexedObjectCreate (prototype, internalSlotsList)
374 
375 // 9.4.5.8 IntegerIndexedElementGet ( O, index )
IntegerIndexedElementGet(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray,JSTaggedValue index)376 OperationResult JSTypedArray::IntegerIndexedElementGet(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray,
377                                                        JSTaggedValue index)
378 {
379     // 1. Assert: Type(index) is Number.
380     ASSERT(index.IsNumber());
381     // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and
382     // [[TypedArrayName]] internal slots.
383     ASSERT(typedarray->IsTypedArray());
384     // 3. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot.
385     JSHandle<JSTypedArray> typedarrayObj(typedarray);
386     JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray();
387     // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
388     if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) {
389         THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer",
390                                     OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
391     }
392     // 5. If IsInteger(index) is false, return undefined
393     if (!index.IsInteger()) {
394         return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true));
395     }
396 
397     // 6. If index = −0, return undefined.
398     // 7. Let length be the value of O’s [[ArrayLength]] internal slot.
399     // 8. If index < 0 or index ≥ length, return undefined.
400     JSHandle<JSTaggedValue> indexHandle(thread, index);
401     JSTaggedNumber indexNumber = JSTaggedValue::ToNumber(thread, indexHandle);
402     RETURN_VALUE_IF_ABRUPT_COMPLETION(
403         thread, OperationResult(thread, JSTaggedValue::Exception(), PropertyMetaData(false)));
404     double tNegZero = -0.0;
405     auto eZero = JSTaggedNumber(tNegZero);
406     JSHandle<JSTaggedValue> zero(thread, JSTaggedValue(0));
407     if (JSTaggedNumber::SameValue(indexNumber, eZero)) {
408         return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true));
409     }
410     uint32_t arrLen = typedarrayObj->GetArrayLength();
411     JSHandle<JSTaggedValue> arrLenHandle(thread, JSTaggedValue(arrLen));
412     if (JSTaggedValue::Less(thread, indexHandle, zero) || !JSTaggedValue::Less(thread, indexHandle, arrLenHandle)) {
413         return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true));
414     }
415     // 9. Let offset be the value of O’s [[ByteOffset]] internal slot.
416     uint32_t offset = typedarrayObj->GetByteOffset();
417     // 10. Let arrayTypeName be the String value of O’s [[TypedArrayName]] internal slot.
418     // 11. Let elementSize be the Number value of the Element Size value specified in Table 49 for
419     // arrayTypeName.
420     uint32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj);
421     // 12. Let indexedPosition = (index × elementSize) + offset.
422     uint32_t k = static_cast<uint32_t>(JSTaggedValue::ToInteger(thread, indexHandle).ToInt32());
423     uint32_t byteIndex = k * elementSize + offset;
424     // 13. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName.
425     DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj);
426     // 14. Return GetValueFromBuffer(buffer, indexedPosition, elementType).
427     JSTaggedValue result = BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer, byteIndex, elementType, true);
428     return OperationResult(thread, result, PropertyMetaData(true));
429 }
430 
431 // s12 10.4.5.9 IsValidIntegerIndex ( O, index )
IsValidIntegerIndex(const JSHandle<JSTaggedValue> & typedArray,JSTaggedValue index)432 bool JSTypedArray::IsValidIntegerIndex(const JSHandle<JSTaggedValue> &typedArray, JSTaggedValue index)
433 {
434     // 1. Assert: O is an Integer-Indexed exotic object.
435     // 2. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, return false.
436     JSHandle<JSTypedArray> typedarrayObj(typedArray);
437     JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray();
438     if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) {
439         return false;
440     }
441     // 3. If ! IsIntegralNumber(index) is false, return false.
442     if (!index.IsInteger()) {
443         return false;
444     }
445     // 4. If index is -0��, return false.
446     double val = index.GetNumber();
447     if (val == 0 && std::signbit(val)) {
448         return false;
449     }
450 
451     uint32_t arrLen = typedarrayObj->GetArrayLength();
452     // 5. If ℝ(index) < 0 or ℝ(index) ≥ O.[[ArrayLength]], return false.
453     if (val < 0 || val >= arrLen) {
454         return false;
455     }
456     // 6. Return true.
457     return true;
458 }
459 
GetTypeFromName(JSThread * thread,const JSHandle<JSTaggedValue> & typeName)460 DataViewType JSTypedArray::GetTypeFromName(JSThread *thread, const JSHandle<JSTaggedValue> &typeName)
461 {
462     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
463     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat32ArrayString())) {
464         return DataViewType::FLOAT32;
465     }
466     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt8ArrayString())) {
467         return DataViewType::INT8;
468     }
469     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ArrayString())) {
470         return DataViewType::UINT8;
471     }
472     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint8ClampedArrayString())) {
473         return DataViewType::UINT8_CLAMPED;
474     }
475     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt16ArrayString())) {
476         return DataViewType::INT16;
477     }
478     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint16ArrayString())) {
479         return DataViewType::UINT16;
480     }
481     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledInt32ArrayString())) {
482         return DataViewType::INT32;
483     }
484     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledUint32ArrayString())) {
485         return DataViewType::UINT32;
486     }
487     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledFloat64ArrayString())) {
488         return DataViewType::FLOAT64;
489     }
490     if (JSTaggedValue::SameValue(typeName, globalConst->GetHandledBigInt64ArrayString())) {
491         return DataViewType::BIGINT64;
492     }
493     return DataViewType::BIGUINT64;
494 }
495 
496 // static
FastCopyElementToArray(JSThread * thread,const JSHandle<JSTaggedValue> & typedArray,JSHandle<TaggedArray> & array)497 bool JSTypedArray::FastCopyElementToArray(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray,
498                                           JSHandle<TaggedArray> &array)
499 {
500     // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and
501     // [[TypedArrayName]] internal slots.
502     ASSERT(typedArray->IsTypedArray());
503     // 3. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot.
504     JSHandle<JSTypedArray> typedarrayObj(typedArray);
505     JSHandle<JSTaggedValue> bufferHandle = JSHandle<JSTaggedValue>(thread,
506                                                                    typedarrayObj->GetViewedArrayBufferOrByteArray());
507     // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
508     if (BuiltinsArrayBuffer::IsDetachedBuffer(bufferHandle.GetTaggedValue())) {
509         THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", false);
510     }
511 
512     // 7. Let length be the value of O’s [[ArrayLength]] internal slot.
513     // 8. If index < 0 or index ≥ length, return undefined.
514     uint32_t arrLen = typedarrayObj->GetArrayLength();
515 
516     // 9. Let offset be the value of O’s [[ByteOffset]] internal slot.
517     uint32_t offset = typedarrayObj->GetByteOffset();
518     // 11. Let elementSize be the Number value of the Element Size value specified in Table 49 for arrayTypeName.
519     uint32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj);
520     // 13. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName.
521     DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj);
522     for (uint32_t index = 0; index < arrLen; index++) {
523         // 12. Let indexedPosition = (index × elementSize) + offset.
524         uint32_t byteIndex = index * elementSize + offset;
525         // 14. Return GetValueFromBuffer(buffer, indexedPosition, elementType).
526         JSTaggedValue result = BuiltinsArrayBuffer::GetValueFromBuffer(thread, bufferHandle.GetTaggedValue(),
527                                                                        byteIndex, elementType, true);
528         array->Set(thread, index, result);
529     }
530     return true;
531 }
532 
533 // static
FastElementGet(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray,uint32_t index)534 OperationResult JSTypedArray::FastElementGet(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray,
535                                              uint32_t index)
536 {
537     // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and
538     // [[TypedArrayName]] internal slots.
539     ASSERT(typedarray->IsTypedArray());
540     // 3. Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot.
541     JSHandle<JSTypedArray> typedarrayObj(typedarray);
542     JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray();
543     // 10.4.5.15 TypedArrayGetElement ( O, index )
544     //  1. If IsValidIntegerIndex(O, index) is false, return undefined.
545     if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) {
546         return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true));
547     }
548 
549     // 7. Let length be the value of O’s [[ArrayLength]] internal slot.
550     // 8. If index < 0 or index ≥ length, return undefined.
551     uint32_t arrLen = typedarrayObj->GetArrayLength();
552     if (index >= arrLen) {
553         return OperationResult(thread, JSTaggedValue::Undefined(), PropertyMetaData(true));
554     }
555     // 9. Let offset be the value of O’s [[ByteOffset]] internal slot.
556     uint32_t offset = typedarrayObj->GetByteOffset();
557     // 11. Let elementSize be the Number value of the Element Size value specified in Table 49 for arrayTypeName.
558     uint32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj);
559     // 12. Let indexedPosition = (index × elementSize) + offset.
560     uint32_t byteIndex = index * elementSize + offset;
561     // 13. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName.
562     DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj);
563     // 14. Return GetValueFromBuffer(buffer, indexedPosition, elementType).
564     JSTaggedValue result = BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer, byteIndex, elementType, true);
565     return OperationResult(thread, result, PropertyMetaData(true));
566 }
567 
568 // 9.4.5.9 IntegerIndexedElementSet ( O, index, value )
IntegerIndexedElementSet(JSThread * thread,const JSHandle<JSTaggedValue> & typedarray,JSTaggedValue index,const JSHandle<JSTaggedValue> & value)569 bool JSTypedArray::IntegerIndexedElementSet(JSThread *thread, const JSHandle<JSTaggedValue> &typedarray,
570                                             JSTaggedValue index, const JSHandle<JSTaggedValue> &value)
571 {
572     // 1. Assert: Type(index) is Number.
573     ASSERT(index.IsNumber());
574     // 2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and
575     // [[TypedArrayName]] internal slots.
576     ASSERT(typedarray->IsTypedArray());
577     // 3. If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).
578     JSHandle<JSTaggedValue> numValueHandle;
579     ContentType contentType = JSHandle<JSTypedArray>::Cast(typedarray)->GetContentType();
580     if (UNLIKELY(contentType == ContentType::BigInt)) {
581         numValueHandle = JSHandle<JSTaggedValue>(thread, JSTaggedValue::ToBigInt(thread, value));
582         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
583     } else {
584         numValueHandle = JSHandle<JSTaggedValue>(thread, JSTaggedValue::ToNumber(thread, value));
585         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
586     }
587 
588     JSHandle<JSTypedArray> typedarrayObj(typedarray);
589     JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray();
590     JSHandle<JSTaggedValue> indexHandle(thread, index);
591     // 5. If ! IsValidIntegerIndex(O, index) is true, then
592     if (IsValidIntegerIndex(typedarray, index)) {
593         // 6. Let offset be the value of O’s [[ByteOffset]] internal slot.
594         uint32_t offset = typedarrayObj->GetByteOffset();
595         // 7. Let arrayTypeName be the String value of O’s [[TypedArrayName]] internal slot.
596         // 8. Let elementSize be the Number value of the Element Size value specified in Table 49 for
597         // arrayTypeName.
598         uint32_t elementSize = TypedArrayHelper::GetElementSize(typedarrayObj);
599         // 9. Let indexedPosition = (index × elementSize) + offset.
600         uint32_t k = JSTaggedValue::ToInteger(thread, indexHandle).ToUint32();
601         uint32_t byteIndex = k * elementSize + offset;
602         // 10. Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName.
603         DataViewType elementType = TypedArrayHelper::GetType(typedarrayObj);
604         // 11. Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue).
605         BuiltinsArrayBuffer::SetValueInBuffer(thread, buffer, byteIndex, elementType, numValueHandle, true);
606         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
607     }
608     return true;
609 }
610 
611 // only use in TypeArray fast set property
NonEcmaObjectToNumber(JSThread * thread,const JSTaggedValue tagged)612 JSTaggedNumber JSTypedArray::NonEcmaObjectToNumber(JSThread *thread, const JSTaggedValue tagged)
613 {
614     ASSERT_PRINT(!tagged.IsECMAObject(), "tagged must not be EcmaObject");
615     if (tagged.IsInt() || tagged.IsDouble()) {
616         return JSTaggedNumber(tagged);
617     }
618     if (tagged.IsString()) {
619         return JSTaggedValue::StringToDouble(tagged);
620     }
621     switch (tagged.GetRawData()) {
622         case JSTaggedValue::VALUE_UNDEFINED:
623         case JSTaggedValue::VALUE_HOLE: {
624             return JSTaggedNumber(base::NAN_VALUE);
625         }
626         case JSTaggedValue::VALUE_TRUE: {
627             return JSTaggedNumber(1);
628         }
629         case JSTaggedValue::VALUE_FALSE:
630         case JSTaggedValue::VALUE_NULL: {
631             return JSTaggedNumber(0);
632         }
633         default: {
634             break;
635         }
636     }
637     if (tagged.IsSymbol()) {
638         THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Symbol value to a number", JSTaggedNumber::Exception());
639     }
640     if (tagged.IsBigInt()) {
641         THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a BigInt value to a number", JSTaggedNumber::Exception());
642     }
643     THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot convert a Unknown value to a number", JSTaggedNumber::Exception());
644 }
645 
FastGetPropertyByIndex(JSThread * thread,const JSTaggedValue typedarray,uint32_t index,JSType jsType)646 JSTaggedValue JSTypedArray::FastGetPropertyByIndex(JSThread *thread, const JSTaggedValue typedarray, uint32_t index,
647                                                    JSType jsType)
648 {
649     // Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and
650     // [[TypedArrayName]] internal slots.
651     ASSERT(typedarray.IsTypedArray());
652     // Let buffer be the value of O’s [[ViewedArrayBuffer]] internal slot.
653     JSTypedArray *typedarrayObj = JSTypedArray::Cast(typedarray.GetTaggedObject());
654     JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray();
655     if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) {
656         return JSTaggedValue::Undefined();
657     }
658 
659     DISALLOW_GARBAGE_COLLECTION;
660     // Let length be the value of O’s [[ArrayLength]] internal slot.
661     // If arrLen < 0 or index ≥ length, return undefined.
662     uint32_t arrLen = typedarrayObj->GetArrayLength();
663     if (index >= arrLen) {
664         return JSTaggedValue::Undefined();
665     }
666     // Let offset be the value of O’s [[ByteOffset]] internal slot.
667     uint32_t offset = typedarrayObj->GetByteOffset();
668     // Let elementSize be the Number value of the Element Size value specified in Table 49 for arrayTypeName.
669     uint32_t elementSize = TypedArrayHelper::GetElementSize(jsType);
670     // Let indexedPosition = (index × elementSize) + offset.
671     uint32_t byteIndex = index * elementSize + offset;
672     // Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName.
673     DataViewType elementType = TypedArrayHelper::GetType(jsType);
674     // Return GetValueFromBuffer(buffer, indexedPosition, elementType).
675     return BuiltinsArrayBuffer::GetValueFromBuffer(thread, buffer, byteIndex, elementType, true);
676 }
677 
FastSetPropertyByIndex(JSThread * thread,const JSTaggedValue typedarray,uint32_t index,JSTaggedValue value,JSType jsType)678 JSTaggedValue JSTypedArray::FastSetPropertyByIndex(JSThread *thread, const JSTaggedValue typedarray, uint32_t index,
679                                                    JSTaggedValue value, JSType jsType)
680 {
681     // Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and
682     // [[TypedArrayName]] internal slots.
683     ASSERT(typedarray.IsTypedArray());
684     // If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).
685     JSTypedArray *typedarrayObj = JSTypedArray::Cast(typedarray.GetTaggedObject());
686     if (UNLIKELY(typedarrayObj->GetContentType() == ContentType::BigInt || value.IsECMAObject())) {
687         return JSTaggedValue::Hole();
688     }
689     JSTaggedNumber numValue = JSTypedArray::NonEcmaObjectToNumber(thread, value);
690     // ReturnIfAbrupt(numValue).
691     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception());
692 
693     DISALLOW_GARBAGE_COLLECTION;
694     JSTaggedValue buffer = typedarrayObj->GetViewedArrayBufferOrByteArray();
695 
696     // If ℝ(index) < 0 or ℝ(index) ≥ O.[[ArrayLength]], return false.
697     uint32_t arrLen = typedarrayObj->GetArrayLength();
698     if (index >= arrLen) {
699         return JSTaggedValue::Undefined();
700     }
701     // Let offset be the value of O’s [[ByteOffset]] internal slot.
702     uint32_t offset = typedarrayObj->GetByteOffset();
703     // Let arrayTypeName be the String value of O’s [[TypedArrayName]]
704     // Let elementSize be the Number value of the Element Size value specified in Table 49 for
705     // arrayTypeName.
706     uint32_t elementSize = TypedArrayHelper::GetElementSize(jsType);
707     // Let indexedPosition = (index × elementSize) + offset.
708     uint32_t byteIndex = index * elementSize + offset;
709     // Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName.
710     DataViewType elementType = TypedArrayHelper::GetType(jsType);
711     // Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue).
712     return BuiltinsArrayBuffer::FastSetValueInBuffer(thread,
713         buffer, byteIndex, elementType, numValue.GetNumber(), true);
714 }
715 
GetOffHeapBuffer(JSThread * thread,JSHandle<JSTypedArray> & typedArray)716 JSTaggedValue JSTypedArray::GetOffHeapBuffer(JSThread *thread, JSHandle<JSTypedArray> &typedArray)
717 {
718     JSTaggedValue arrBuf = typedArray->GetViewedArrayBufferOrByteArray();
719     if (arrBuf.IsArrayBuffer() || arrBuf.IsSharedArrayBuffer()) {
720         return arrBuf;
721     }
722 
723     ByteArray *byteArray = ByteArray::Cast(arrBuf.GetTaggedObject());
724     int32_t length = static_cast<int32_t>(byteArray->GetArrayLength() * byteArray->GetByteLength());
725     JSHandle<JSArrayBuffer> arrayBuffer = thread->GetEcmaVM()->GetFactory()->NewJSArrayBuffer(length);
726 
727     if (length > 0) {
728         void *fromBuf = reinterpret_cast<void *>(ToUintPtr(
729             ByteArray::Cast(typedArray->GetViewedArrayBufferOrByteArray().GetTaggedObject())->GetData()));
730         JSTaggedValue data = arrayBuffer->GetArrayBufferData();
731         void *toBuf = reinterpret_cast<void *>(
732             ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer()));
733         JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, 0, length);
734     }
735     typedArray->SetViewedArrayBufferOrByteArray(thread, arrayBuffer.GetTaggedValue());
736     JSHandle<JSTaggedValue> typeName(thread, typedArray->GetTypedArrayName());
737     DataViewType arrayType = JSTypedArray::GetTypeFromName(thread, typeName);
738     JSHandle<JSHClass> notOnHeapHclass = TypedArrayHelper::GetNotOnHeapHclassFromType(
739             thread, typedArray, arrayType);
740     TaggedObject::Cast(*typedArray)->SynchronizedSetClass(thread, *notOnHeapHclass); // onHeap->notOnHeap
741 
742     return arrayBuffer.GetTaggedValue();
743 }
744 
FastTypedArrayFill(JSThread * thread,const JSHandle<JSTaggedValue> & typedArray,const JSHandle<JSTaggedValue> & value,uint32_t start,uint32_t end)745 bool JSTypedArray::FastTypedArrayFill(JSThread *thread, const JSHandle<JSTaggedValue> &typedArray,
746                                       const JSHandle<JSTaggedValue> &value, uint32_t start, uint32_t end)
747 {
748     // Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and
749     // [[TypedArrayName]] internal slots.
750     ASSERT(typedArray->IsTypedArray());
751     // If O.[[ContentType]] is BigInt, let numValue be ? ToBigInt(value).
752     JSHandle<JSTypedArray> typedArrayObj = JSHandle<JSTypedArray>::Cast(typedArray);
753     if (UNLIKELY(typedArrayObj->GetContentType() == ContentType::BigInt || value->IsECMAObject())) {
754         return false;
755     }
756     JSTaggedNumber numValue = JSTypedArray::NonEcmaObjectToNumber(thread, value.GetTaggedValue());
757     // ReturnIfAbrupt(numValue).
758     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, true);
759     JSTaggedValue buffer = typedArrayObj->GetViewedArrayBufferOrByteArray();
760     // If ℝ(index) < 0 or ℝ(index) ≥ O.[[ArrayLength]], return false.
761     uint32_t arrLen = typedArrayObj->GetArrayLength();
762     // Let offset be the value of O’s [[ByteOffset]] internal slot.
763     uint32_t offset = typedArrayObj->GetByteOffset();
764     // Let arrayTypeName be the String value of O’s [[TypedArrayName]]
765     // Let elementSize be the Number value of the Element Size value specified in Table 49 for arrayTypeName.
766     JSType jsType = typedArrayObj->GetClass()->GetObjectType();
767     uint32_t elementSize = TypedArrayHelper::GetElementSize(jsType);
768     // Let elementType be the String value of the Element Type value in Table 49 for arrayTypeName.
769     DataViewType elementType = TypedArrayHelper::GetType(jsType);
770     uint64_t byteBeginOffset = start * elementSize + offset;
771     uint64_t byteEndOffset = std::min(end, arrLen) * elementSize + offset;
772     if (byteBeginOffset <= byteEndOffset) {
773         BuiltinsArrayBuffer::TryFastSetValueInBuffer(thread, buffer,
774             byteBeginOffset, byteEndOffset, elementType, numValue.GetNumber(), true);
775     }
776     return true;
777 }
778 }  // namespace panda::ecmascript
779