• 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/base/typed_array_helper.h"
17 
18 #include "ecmascript/base/builtins_base.h"
19 #include "ecmascript/base/error_helper.h"
20 #include "ecmascript/base/error_type.h"
21 #include "ecmascript/base/typed_array_helper-inl.h"
22 #include "ecmascript/builtins/builtins_arraybuffer.h"
23 #include "ecmascript/ecma_macros.h"
24 #include "ecmascript/ecma_vm.h"
25 #include "ecmascript/global_env.h"
26 #include "ecmascript/interpreter/interpreter.h"
27 #include "ecmascript/js_array_iterator.h"
28 #include "ecmascript/js_arraybuffer.h"
29 #include "ecmascript/js_hclass.h"
30 #include "ecmascript/js_object-inl.h"
31 #include "ecmascript/js_tagged_value-inl.h"
32 #include "ecmascript/js_tagged_value.h"
33 #include "ecmascript/js_typed_array.h"
34 #include "ecmascript/object_factory.h"
35 #include "ecmascript/js_stable_array.h"
36 
37 namespace panda::ecmascript::base {
38 using BuiltinsArrayBuffer = builtins::BuiltinsArrayBuffer;
39 
40 // es11 22.2.4 The TypedArray Constructors
TypedArrayConstructor(EcmaRuntimeCallInfo * argv,const JSHandle<JSTaggedValue> & constructorName,const DataViewType arrayType)41 JSTaggedValue TypedArrayHelper::TypedArrayConstructor(EcmaRuntimeCallInfo *argv,
42                                                       const JSHandle<JSTaggedValue> &constructorName,
43                                                       const DataViewType arrayType)
44 {
45     ASSERT(argv);
46     JSThread *thread = argv->GetThread();
47     [[maybe_unused]] EcmaHandleScope handleScope(thread);
48     JSHandle<JSTaggedValue> newTarget = BuiltinsBase::GetNewTarget(argv);
49     // 2. If NewTarget is undefined, throw a TypeError exception.
50     if (newTarget->IsUndefined()) {
51         THROW_TYPE_ERROR_AND_RETURN(thread, "The NewTarget is undefined.", JSTaggedValue::Exception());
52     }
53     // 3. Let constructorName be the String value of the Constructor Name value specified in Table 61 for this
54     // TypedArray constructor.
55     // 4. Let O be ? AllocateTypedArray(constructorName, NewTarget, "%TypedArray.prototype%").
56     JSHandle<JSTaggedValue> firstArg = BuiltinsBase::GetCallArg(argv, 0);
57     if (!firstArg->IsECMAObject()) {
58         // es11 22.2.4.1 TypedArray ( )
59         uint32_t elementLength = 0;
60         // es11 22.2.4.2 TypedArray ( length )
61         if (!firstArg->IsUndefined()) {
62             JSTaggedNumber index = JSTaggedValue::ToIndex(thread, firstArg);
63             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
64             elementLength = static_cast<uint32_t>(index.GetNumber());
65         }
66         JSHandle<JSObject> obj = TypedArrayHelper::AllocateTypedArray(thread, constructorName, newTarget,
67                                                                       elementLength, arrayType);
68         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
69         return obj.GetTaggedValue();
70     }
71 
72     JSHandle<JSObject> obj = TypedArrayHelper::AllocateTypedArray(thread, constructorName, newTarget, arrayType);
73     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
74     if (firstArg->IsTypedArray()) {
75         return TypedArrayHelper::CreateFromTypedArray(argv, obj, arrayType);
76     }
77     if (firstArg->IsArrayBuffer() || firstArg->IsSharedArrayBuffer()) {
78         return TypedArrayHelper::CreateFromArrayBuffer(argv, obj, arrayType);
79     }
80     if (firstArg->IsStableJSArray(thread)) {
81         return TypedArrayHelper::FastCopyElementFromArray(argv, obj, arrayType);
82     }
83     return TypedArrayHelper::CreateFromOrdinaryObject(argv, obj, arrayType);
84 }
85 
FastCopyElementFromArray(EcmaRuntimeCallInfo * argv,const JSHandle<JSObject> & obj,const DataViewType arrayType)86 JSTaggedValue TypedArrayHelper::FastCopyElementFromArray(EcmaRuntimeCallInfo *argv, const JSHandle<JSObject> &obj,
87                                                          const DataViewType arrayType)
88 {
89     ASSERT(argv);
90     JSThread *thread = argv->GetThread();
91     [[maybe_unused]] EcmaHandleScope handleScope(thread);
92     JSHandle<JSTaggedValue> argArray = BuiltinsBase::GetCallArg(argv, 0);
93     uint32_t len = JSHandle<JSArray>::Cast(argArray)->GetArrayLength();
94     JSHandle<TaggedArray> elements(thread, JSHandle<JSArray>::Cast(argArray)->GetElements());
95     // load on demand check
96     if (elements->GetLength() < len) {
97         TypedArrayHelper::CreateFromOrdinaryObject(argv, obj, arrayType);
98     }
99 
100     TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, len, arrayType);
101     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
102     JSHandle<JSTypedArray> targetObj = JSHandle<JSTypedArray>::Cast(obj);
103 
104     JSStableArray::FastCopyFromArrayToTypedArray(thread, targetObj, arrayType, 0, len, elements);
105     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
106     return JSHandle<JSObject>::Cast(targetObj).GetTaggedValue();
107 }
108 
109 // es11 22.2.4.4 TypedArray ( object )
CreateFromOrdinaryObject(EcmaRuntimeCallInfo * argv,const JSHandle<JSObject> & obj,const DataViewType arrayType)110 JSTaggedValue TypedArrayHelper::CreateFromOrdinaryObject(EcmaRuntimeCallInfo *argv, const JSHandle<JSObject> &obj,
111                                                          const DataViewType arrayType)
112 {
113     ASSERT(argv);
114     JSThread *thread = argv->GetThread();
115     [[maybe_unused]] EcmaHandleScope handleScope(thread);
116     EcmaVM *ecmaVm = thread->GetEcmaVM();
117     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
118     JSHandle<JSTaggedValue> objectArg = BuiltinsBase::GetCallArg(argv, 0);
119     JSHandle<JSObject> object(objectArg);
120     // 5. Let usingIterator be ? GetMethod(object, @@iterator).
121     JSHandle<JSTaggedValue> iteratorSymbol = env->GetIteratorSymbol();
122     JSHandle<JSTaggedValue> usingIterator =
123         JSObject::GetMethod(thread, JSHandle<JSTaggedValue>::Cast(object), iteratorSymbol);
124     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
125 
126     // 6. If usingIterator is not undefined, then
127     if (!usingIterator->IsUndefined()) {
128         CVector<JSHandle<JSTaggedValue>> vec;
129         // a. Let values be ? IterableToList(object, usingIterator).
130         // b. Let len be the number of elements in values.
131         // c. Perform ? AllocateTypedArrayBuffer(O, len).
132         JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, objectArg, usingIterator);
133         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
134         JSHandle<JSTaggedValue> next(thread, JSTaggedValue::True());
135         while (!next->IsFalse()) {
136             next = JSIterator::IteratorStep(thread, iterator);
137             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
138             if (!next->IsFalse()) {
139                 JSHandle<JSTaggedValue> nextValue = JSIterator::IteratorValue(thread, next);
140                 vec.push_back(nextValue);
141             }
142         }
143         uint32_t len = static_cast<uint32_t>(vec.size());
144         TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, len, arrayType);
145         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
146         // d. Let k be 0.
147         // e. Repeat, while k < len
148         //   i. Let Pk be ! ToString(k).
149         //   ii. Let kValue be the first element of values and remove that element from values.
150         //   iii. Perform ? Set(O, Pk, kValue, true).
151         //   iv. Set k to k + 1.
152         JSMutableHandle<JSTaggedValue> tKey(thread, JSTaggedValue::Undefined());
153         uint32_t k = 0;
154         while (k < len) {
155             tKey.Update(JSTaggedValue(k));
156             JSHandle<JSTaggedValue> kKey(JSTaggedValue::ToString(thread, tKey));
157             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
158             JSHandle<JSTaggedValue> kValue = vec[k];
159             JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(obj), kKey, kValue, true);
160             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
161             k++;
162         }
163         // f. Assert: values is now an empty List.
164         // g. Return O.
165         return obj.GetTaggedValue();
166     }
167 
168     // 7. NOTE: object is not an Iterable so assume it is already an array-like object.
169     // 8. Let arrayLike be object.
170     // 9. Let len be ? LengthOfArrayLike(arrayLike).
171     JSHandle<JSTaggedValue> lengthKey = thread->GlobalConstants()->GetHandledLengthString();
172     JSTaggedNumber lenTemp =
173         JSTaggedValue::ToLength(thread, JSObject::GetProperty(thread, objectArg, lengthKey).GetValue());
174     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
175     uint64_t rawLen = lenTemp.GetNumber();
176     // 10. Perform ? AllocateTypedArrayBuffer(O, len).
177     TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, rawLen, arrayType);
178     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
179     // 11. Let k be 0.
180     // 12. Repeat, while k < len
181     //   a. Let Pk be ! ToString(k).
182     //   b. Let kValue be ? Get(arrayLike, Pk).
183     //   c. Perform ? Set(O, Pk, kValue, true).
184     //   d. Set k to k + 1.
185     JSMutableHandle<JSTaggedValue> tKey(thread, JSTaggedValue::Undefined());
186     uint32_t len = static_cast<uint32_t>(rawLen);
187     uint32_t k = 0;
188     while (k < len) {
189         tKey.Update(JSTaggedValue(k));
190         JSHandle<JSTaggedValue> kKey(JSTaggedValue::ToString(thread, tKey));
191         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
192         JSHandle<JSTaggedValue> kValue = JSObject::GetProperty(thread, objectArg, kKey).GetValue();
193         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
194         JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>::Cast(obj), kKey, kValue, true);
195         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
196         k++;
197     }
198     // 13. Return O.
199     return obj.GetTaggedValue();
200 }
201 
202 // es11 22.2.4.3 TypedArray ( typedArray )
CreateFromTypedArray(EcmaRuntimeCallInfo * argv,const JSHandle<JSObject> & obj,const DataViewType arrayType)203 JSTaggedValue TypedArrayHelper::CreateFromTypedArray(EcmaRuntimeCallInfo *argv, const JSHandle<JSObject> &obj,
204                                                      const DataViewType arrayType)
205 {
206     ASSERT(argv);
207     JSThread *thread = argv->GetThread();
208     [[maybe_unused]] EcmaHandleScope handleScope(thread);
209     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
210     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
211     // 5. Let srcArray be typedArray.
212     JSHandle<JSTaggedValue> srcArray = BuiltinsBase::GetCallArg(argv, 0);
213     JSHandle<JSTypedArray> srcObj(srcArray);
214     // 6. Let srcData be srcArray.[[ViewedArrayBuffer]].
215     JSHandle<JSTaggedValue> srcData(thread, JSTypedArray::GetOffHeapBuffer(thread, srcObj));
216     // 7. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
217     if (BuiltinsArrayBuffer::IsDetachedBuffer(srcData.GetTaggedValue())) {
218         THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception());
219     }
220     // 8. Let elementType be the Element Type value in Table 61 for constructorName.
221     //     which is arrayType passed in.
222     // 9. Let elementLength be srcArray.[[ArrayLength]].
223     // 10. Let srcName be the String value of srcArray.[[TypedArrayName]].
224     // 11. Let srcType be the Element Type value in Table 61 for srcName.
225     // 12. Let srcElementSize be the Element Size value specified in Table 61 for srcName.
226     uint32_t elementLength = srcObj->GetArrayLength();
227     JSHandle<JSTaggedValue> srcName(thread, srcObj->GetTypedArrayName());
228     DataViewType srcType = JSTypedArray::GetTypeFromName(thread, srcName);
229     uint32_t srcElementSize = TypedArrayHelper::GetSizeFromType(srcType);
230     // 13. Let srcByteOffset be srcArray.[[ByteOffset]].
231     // 14. Let elementSize be the Element Size value specified in Table 61 for constructorName.
232     // 15. Let byteLength be elementSize × elementLength.
233     uint32_t srcByteOffset = srcObj->GetByteOffset();
234     uint32_t elementSize = TypedArrayHelper::GetSizeFromType(arrayType);
235     // If elementLength is a large number, the multiplication of elementSize and elementLength may exceed
236     //     the maximum value of uint32, resulting in data overflow. Therefore, the type of byteLength is uint64_t.
237     uint64_t byteLength = elementSize * static_cast<uint64_t>(elementLength);
238     // 16. If IsSharedArrayBuffer(srcData) is false, then
239     //   a. Let bufferConstructor be ? SpeciesConstructor(srcData, %ArrayBuffer%).
240 
241     JSMutableHandle<JSTaggedValue> data(thread, JSTaggedValue::Undefined());
242     // 18. If elementType is the same as srcType, then
243     //   a. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor).
244     if (arrayType == srcType) {
245         JSTaggedValue tmp =
246             BuiltinsArrayBuffer::CloneArrayBuffer(thread, srcData, srcByteOffset, globalConst->GetHandledUndefined());
247         data.Update(tmp);
248         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
249     } else {
250         // 19. Else,
251         //   a. Let data be ? AllocateArrayBuffer(bufferConstructor, byteLength).
252         JSHandle<JSTaggedValue> bufferConstructor =
253             JSObject::SpeciesConstructor(thread, JSHandle<JSObject>(srcData), env->GetArrayBufferFunction());
254         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
255         JSTaggedValue tmp = BuiltinsArrayBuffer::AllocateArrayBuffer(thread, bufferConstructor, byteLength);
256         data.Update(tmp);
257         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
258         //   b. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
259         if (BuiltinsArrayBuffer::IsDetachedBuffer(srcData.GetTaggedValue())) {
260             THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception());
261         }
262         ContentType objContentType = JSHandle<JSTypedArray>::Cast(obj)->GetContentType();
263         ContentType srcArrayContentType = JSHandle<JSTypedArray>::Cast(srcArray)->GetContentType();
264         if (srcArrayContentType != objContentType) {
265             THROW_TYPE_ERROR_AND_RETURN(thread, "srcArrayContentType is not equal objContentType.",
266                                         JSTaggedValue::Exception());
267         }
268         //   d. Let srcByteIndex be srcByteOffset.
269         //   e. Let targetByteIndex be 0.
270         uint32_t srcByteIndex = srcByteOffset;
271         uint32_t targetByteIndex = 0;
272         //   f. Let count be elementLength.
273         //   g. Repeat, while count > 0
274         JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
275         for (uint32_t count = elementLength; count > 0; count--) {
276             // i. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, Unordered).
277             JSTaggedValue taggedData =
278                 BuiltinsArrayBuffer::GetValueFromBuffer(thread, srcData.GetTaggedValue(), srcByteIndex, srcType, true);
279             value.Update(taggedData);
280             // ii. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, Unordered).
281             BuiltinsArrayBuffer::SetValueInBuffer(thread, data.GetTaggedValue(),
282                                                   targetByteIndex, arrayType, value, true);
283             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
284             // iii. Set srcByteIndex to srcByteIndex + srcElementSize.
285             // iv. Set targetByteIndex to targetByteIndex + elementSize.
286             // v. Set count to count - 1.
287             srcByteIndex = srcByteIndex + srcElementSize;
288             targetByteIndex = targetByteIndex + elementSize;
289         }
290     }
291     // 19. Set O’s [[ViewedArrayBuffer]] internal slot to data.
292     // 20. Set O’s [[ByteLength]] internal slot to byteLength.
293     // 21. Set O’s [[ByteOffset]] internal slot to 0.
294     // 22. Set O’s [[ArrayLength]] internal slot to elementLength.
295     JSTypedArray *jsTypedArray = JSTypedArray::Cast(*obj);
296     jsTypedArray->SetViewedArrayBufferOrByteArray(thread, data);
297     jsTypedArray->SetByteLength(byteLength);
298     jsTypedArray->SetByteOffset(0);
299     jsTypedArray->SetArrayLength(elementLength);
300     jsTypedArray->SetIsOnHeap(false);
301     // 23. Return O.
302     return obj.GetTaggedValue();
303 }
304 
305 // es11 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
CreateFromArrayBuffer(EcmaRuntimeCallInfo * argv,const JSHandle<JSObject> & obj,const DataViewType arrayType)306 JSTaggedValue TypedArrayHelper::CreateFromArrayBuffer(EcmaRuntimeCallInfo *argv, const JSHandle<JSObject> &obj,
307                                                       const DataViewType arrayType)
308 {
309     ASSERT(argv);
310     JSThread *thread = argv->GetThread();
311     [[maybe_unused]] EcmaHandleScope handleScope(thread);
312     // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName.
313     // 6. Let offset be ? ToIndex(byteOffset).
314     uint32_t elementSize = TypedArrayHelper::GetSizeFromType(arrayType);
315     JSHandle<JSTaggedValue> byteOffset = BuiltinsBase::GetCallArg(argv, 1);
316     JSTaggedNumber index = JSTaggedValue::ToIndex(thread, byteOffset);
317     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
318     auto offset = static_cast<uint32_t>(index.GetNumber());
319     // 7. If offset modulo elementSize ≠ 0, throw a RangeError exception.
320     if (offset % elementSize != 0) {
321         THROW_RANGE_ERROR_AND_RETURN(thread, "The offset cannot be an integral multiple of elementSize.",
322                                      JSTaggedValue::Exception());
323     }
324     // 8. If length is not undefined, then
325     //   a. Let newLength be ? ToIndex(length).
326     JSHandle<JSTaggedValue> length = BuiltinsBase::GetCallArg(argv, BuiltinsBase::ArgsPosition::THIRD);
327     int32_t newLength = 0;
328     if (!length->IsUndefined()) {
329         index = JSTaggedValue::ToIndex(thread, length);
330         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
331         newLength = static_cast<int32_t>(index.GetNumber());
332     }
333     // 9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
334     JSHandle<JSTaggedValue> buffer = BuiltinsBase::GetCallArg(argv, 0);
335     if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) {
336         THROW_TYPE_ERROR_AND_RETURN(thread, "The srcData is detached buffer.", JSTaggedValue::Exception());
337     }
338     // 10. Let bufferByteLength be buffer.[[ArrayBufferByteLength]].
339     uint32_t bufferByteLength = JSHandle<JSArrayBuffer>(buffer)->GetArrayBufferByteLength();
340     // 11. If length is undefined, then
341     //   a. If bufferByteLength modulo elementSize ≠ 0, throw a RangeError exception.
342     //   b. Let newByteLength be bufferByteLength - offset.
343     //   c. If newByteLength < 0, throw a RangeError exception.
344     uint32_t newByteLength = 0;
345     if (length->IsUndefined()) {
346         if (bufferByteLength % elementSize != 0) {
347             THROW_RANGE_ERROR_AND_RETURN(thread, "The bufferByteLength cannot be an integral multiple of elementSize.",
348                                          JSTaggedValue::Exception());
349         }
350         if (bufferByteLength < offset) {
351             THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is less than 0.", JSTaggedValue::Exception());
352         }
353         newByteLength = bufferByteLength - offset;
354     } else {
355         // 12. Else,
356         //   a. Let newByteLength be newLength × elementSize.
357         //   b. If offset + newByteLength > bufferByteLength, throw a RangeError exception.
358         ASSERT((static_cast<size_t>(newLength) * static_cast<size_t>(elementSize)) <=
359             static_cast<size_t>(INT32_MAX));
360         newByteLength = static_cast<uint32_t>(newLength) * elementSize;
361         if (offset + newByteLength > bufferByteLength) {
362             THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is out of range.", JSTaggedValue::Exception());
363         }
364     }
365     // 13. Set O.[[ViewedArrayBuffer]] to buffer.
366     // 14. Set O.[[ByteLength]] to newByteLength.
367     // 15. Set O.[[ByteOffset]] to offset.
368     // 16. Set O.[[ArrayLength]] to newByteLength / elementSize.
369     JSTypedArray *jsTypedArray = JSTypedArray::Cast(*obj);
370     jsTypedArray->SetViewedArrayBufferOrByteArray(thread, buffer);
371     jsTypedArray->SetByteLength(newByteLength);
372     jsTypedArray->SetByteOffset(offset);
373     jsTypedArray->SetArrayLength(newByteLength / elementSize);
374     // 17. Return O.
375     return obj.GetTaggedValue();
376 }
377 
378 // es11 22.2.4.2.1 Runtime Semantics: AllocateTypedArray ( constructorName, newTarget, defaultProto )
AllocateTypedArray(JSThread * thread,const JSHandle<JSTaggedValue> & constructorName,const JSHandle<JSTaggedValue> & newTarget,const DataViewType arrayType)379 JSHandle<JSObject> TypedArrayHelper::AllocateTypedArray(JSThread *thread,
380                                                         const JSHandle<JSTaggedValue> &constructorName,
381                                                         const JSHandle<JSTaggedValue> &newTarget,
382                                                         const DataViewType arrayType)
383 {
384     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
385     // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto).
386     // 2. Let obj be ! IntegerIndexedObjectCreate(proto).
387     JSHandle<JSFunction> typedArrayFunc = TypedArrayHelper::GetConstructorFromType(thread, arrayType);
388     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget);
389     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
390     // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined.
391     // 4. Set obj.[[TypedArrayName]] to constructorName.
392 
393     // 5. If constructorName is "BigInt64Array" or "BigUint64Array", set obj.[[ContentType]] to BigInt.
394     // 6. Otherwise, set obj.[[ContentType]] to Number.
395     JSTypedArray *jsTypedArray = JSTypedArray::Cast(*obj);
396     if (arrayType == DataViewType::BIGINT64 ||
397         arrayType == DataViewType::BIGUINT64) {
398         jsTypedArray->SetContentType(ContentType::BigInt);
399     } else {
400         jsTypedArray->SetContentType(ContentType::Number);
401     }
402     // 7. If length is not present, then
403     //   a. Set obj.[[ByteLength]] to 0.
404     //   b. Set obj.[[ByteOffset]] to 0.
405     //   c. Set obj.[[ArrayLength]] to 0.
406     jsTypedArray->SetTypedArrayName(thread, constructorName);
407     jsTypedArray->SetByteLength(0);
408     jsTypedArray->SetByteOffset(0);
409     jsTypedArray->SetArrayLength(0);
410     jsTypedArray->SetIsOnHeap(false);
411     // 9. Return obj.
412     return obj;
413 }
414 
415 // es11 22.2.4.2.1 Runtime Semantics: AllocateTypedArray ( constructorName, newTarget, defaultProto, length )
AllocateTypedArray(JSThread * thread,const JSHandle<JSTaggedValue> & constructorName,const JSHandle<JSTaggedValue> & newTarget,uint32_t length,const DataViewType arrayType)416 JSHandle<JSObject> TypedArrayHelper::AllocateTypedArray(JSThread *thread,
417                                                         const JSHandle<JSTaggedValue> &constructorName,
418                                                         const JSHandle<JSTaggedValue> &newTarget, uint32_t length,
419                                                         const DataViewType arrayType)
420 {
421     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
422     // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto).
423     // 2. Let obj be ! IntegerIndexedObjectCreate(proto).
424     JSHandle<JSFunction> typedArrayFunc = TypedArrayHelper::GetConstructorFromType(thread, arrayType);
425     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget);
426     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
427     // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined.
428     // 4. Set obj.[[TypedArrayName]] to constructorName.
429     JSTypedArray::Cast(*obj)->SetTypedArrayName(thread, constructorName);
430     // 7. If length is not present, then
431     // 8. Else,
432     //   a. Perform ? AllocateTypedArrayBuffer(obj, length).
433     TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, length, arrayType);
434     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
435     // 9. Return obj.
436     return obj;
437 }
438 
439 // es11 22.2.4.2.2 Runtime Semantics: AllocateTypedArrayBuffer ( O, length )
AllocateTypedArrayBuffer(JSThread * thread,const JSHandle<JSObject> & obj,uint64_t length,const DataViewType arrayType)440 JSHandle<JSObject> TypedArrayHelper::AllocateTypedArrayBuffer(JSThread *thread,
441                                                               const JSHandle<JSObject> &obj, uint64_t length,
442                                                               const DataViewType arrayType)
443 {
444     // 1. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
445     // 2. Assert: O.[[ViewedArrayBuffer]] is undefined.
446     // 3. Assert: ! IsNonNegativeInteger(length) is true.
447     JSHandle<JSObject> exception(thread, JSTaggedValue::Exception());
448     if (length > JSTypedArray::MAX_TYPED_ARRAY_INDEX) {
449         THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", exception);
450     }
451     // 4. Let constructorName be the String value of O.[[TypedArrayName]].
452     //     we use type to get size
453     // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName.
454     uint32_t elementSize = TypedArrayHelper::GetSizeFromType(arrayType);
455     // 6. Let byteLength be elementSize × length.
456     uint32_t arrayLength = static_cast<uint32_t>(length);
457     uint64_t byteLength = static_cast<uint64_t>(elementSize) * length;
458     // 7. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).
459     JSTaggedValue data;
460     if (byteLength > JSTypedArray::MAX_ONHEAP_LENGTH) {
461         JSHandle<JSTaggedValue> constructor = thread->GetEcmaVM()->GetGlobalEnv()->GetArrayBufferFunction();
462         data = BuiltinsArrayBuffer::AllocateArrayBuffer(thread, constructor, byteLength);
463         JSTypedArray::Cast(*obj)->SetIsOnHeap(false);
464     } else {
465         data = thread->GetEcmaVM()->GetFactory()->NewByteArray(arrayLength, elementSize).GetTaggedValue();
466         JSTypedArray::Cast(*obj)->SetIsOnHeap(true);
467     }
468     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, exception);
469     JSTypedArray *jsTypedArray = JSTypedArray::Cast(*obj);
470     if (arrayType == DataViewType::BIGINT64 ||
471         arrayType == DataViewType::BIGUINT64) {
472         jsTypedArray->SetContentType(ContentType::BigInt);
473     } else {
474         jsTypedArray->SetContentType(ContentType::Number);
475     }
476     // 8. Set O.[[ViewedArrayBuffer]] to data.
477     // 9. Set O.[[ByteLength]] to byteLength.
478     // 10. Set O.[[ByteOffset]] to 0.
479     // 11. Set O.[[ArrayLength]] to length.
480     jsTypedArray->SetViewedArrayBufferOrByteArray(thread, data);
481     jsTypedArray->SetByteLength(byteLength);
482     jsTypedArray->SetByteOffset(0);
483     jsTypedArray->SetArrayLength(arrayLength);
484     // 12. Return O.
485     return obj;
486 }
487 
488 // es11 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList )
TypedArraySpeciesCreate(JSThread * thread,const JSHandle<JSTypedArray> & obj,uint32_t argc,JSTaggedType argv[])489 JSHandle<JSObject> TypedArrayHelper::TypedArraySpeciesCreate(JSThread *thread, const JSHandle<JSTypedArray> &obj,
490                                                              uint32_t argc, JSTaggedType argv[])
491 {
492     // 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots.
493     // 2. Let defaultConstructor be the intrinsic object listed in column one of Table 61 for
494     // exemplar.[[TypedArrayName]].
495     JSHandle<JSTaggedValue> buffHandle(thread, JSTaggedValue(argv[0]));
496     JSHandle<JSTaggedValue> defaultConstructor =
497         TypedArrayHelper::GetConstructor(thread, JSHandle<JSTaggedValue>(obj));
498     // 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
499     JSHandle<JSTaggedValue> thisConstructor =
500         JSObject::SpeciesConstructor(thread, JSHandle<JSObject>::Cast(obj), defaultConstructor);
501     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
502     // 4. Let result be ? TypedArrayCreate(constructor, argumentList).
503     argv[0] = buffHandle.GetTaggedType();
504     JSHandle<JSObject> result = TypedArrayHelper::TypedArrayCreate(thread, thisConstructor, argc, argv);
505     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
506     // 5. If result.[[ContentType]] ≠ exemplar.[[ContentType]], throw a TypeError exception.
507     ContentType objContentType = obj->GetContentType();
508     ContentType resultContentType = JSHandle<JSTypedArray>::Cast(result)->GetContentType();
509     if (objContentType != resultContentType) {
510         JSHandle<JSObject> exception(thread, JSTaggedValue::Exception());
511         THROW_TYPE_ERROR_AND_RETURN(thread, "resultContentType is not equal objContentType.", exception);
512     }
513     return result;
514 }
515 
516 // es11 22.2.4.6 TypedArrayCreate ( constructor, argumentList )
TypedArrayCreate(JSThread * thread,const JSHandle<JSTaggedValue> & constructor,uint32_t argc,const JSTaggedType argv[])517 JSHandle<JSObject> TypedArrayHelper::TypedArrayCreate(JSThread *thread, const JSHandle<JSTaggedValue> &constructor,
518                                                       uint32_t argc, const JSTaggedType argv[])
519 {
520     // 1. Let newTypedArray be ? Construct(constructor, argumentList).
521     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
522     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, argc);
523     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
524     info->SetCallArg(argc, argv);
525     JSTaggedValue taggedArray = JSFunction::Construct(info);
526     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
527     if (!taggedArray.IsECMAObject()) {
528         THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the Typedarray.",
529                                     JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
530     }
531     JSHandle<JSTaggedValue> taggedArrayHandle(thread, taggedArray);
532     // 2. Perform ? ValidateTypedArray(newTypedArray).
533     TypedArrayHelper::ValidateTypedArray(thread, taggedArrayHandle);
534     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
535     JSHandle<JSObject> newTypedArray(taggedArrayHandle);
536     // 3. If argumentList is a List of a single Number, then
537     //   a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a TypeError exception.
538     if (argc == 1) {
539         if (JSHandle<JSTypedArray>::Cast(newTypedArray)->GetArrayLength() <
540             JSTaggedValue::ToUint32(thread, info->GetCallArg(0))) {
541             THROW_TYPE_ERROR_AND_RETURN(thread, "the length of newTypedArray is not a correct value.",
542                                         JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
543         }
544     }
545     // 4. Return newTypedArray.
546     return newTypedArray;
547 }
548 
549 // TypedArrayCreateSameType ( exemplar, argumentList )
TypedArrayCreateSameType(JSThread * thread,const JSHandle<JSTypedArray> & obj,uint32_t argc,JSTaggedType argv[])550 JSHandle<JSObject> TypedArrayHelper::TypedArrayCreateSameType(JSThread *thread, const JSHandle<JSTypedArray> &obj,
551                                                               uint32_t argc, JSTaggedType argv[])
552 {
553     // 1. Let constructor be the intrinsic object associated with the constructor name exemplar.[[TypedArrayName]]
554     // in Table 70.
555     JSHandle<JSTaggedValue> buffHandle(thread, JSTaggedValue(argv[0]));
556     JSHandle<JSTaggedValue> constructor =
557         TypedArrayHelper::GetConstructor(thread, JSHandle<JSTaggedValue>(obj));
558     argv[0] = buffHandle.GetTaggedType();
559     // 2. Let result be ? TypedArrayCreate(constructor, argumentList).
560     JSHandle<JSObject> result = TypedArrayHelper::TypedArrayCreate(thread, constructor, argc, argv);
561     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
562     // 3. Assert: result has [[TypedArrayName]] and [[ContentType]] internal slots.
563     // 4. Assert: result.[[ContentType]] is exemplar.[[ContentType]].
564     [[maybe_unused]] ContentType objContentType = obj->GetContentType();
565     [[maybe_unused]] ContentType resultContentType = JSHandle<JSTypedArray>::Cast(result)->GetContentType();
566     ASSERT(objContentType == resultContentType);
567     return result;
568 }
569 
570 // es11 22.2.3.5.1 Runtime Semantics: ValidateTypedArray ( O )
ValidateTypedArray(JSThread * thread,const JSHandle<JSTaggedValue> & value)571 JSTaggedValue TypedArrayHelper::ValidateTypedArray(JSThread *thread, const JSHandle<JSTaggedValue> &value)
572 {
573     // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
574     // 2. Assert: O has a [[ViewedArrayBuffer]] internal slot.
575     if (!value->IsTypedArray()) {
576         THROW_TYPE_ERROR_AND_RETURN(thread, "The O is not a TypedArray.", JSTaggedValue::Exception());
577     }
578     // 3. Let buffer be O.[[ViewedArrayBuffer]].
579     // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
580     JSTaggedValue buffer = JSHandle<JSTypedArray>::Cast(value)->GetViewedArrayBufferOrByteArray();
581     if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) {
582         THROW_TYPE_ERROR_AND_RETURN(thread, "The ViewedArrayBuffer of O is detached buffer.",
583                                     JSTaggedValue::Exception());
584     }
585     // 5. Return buffer.
586     return buffer;
587 }
588 
SortCompare(JSThread * thread,const JSHandle<JSTaggedValue> & callbackfnHandle,const JSHandle<JSTaggedValue> & buffer,const JSHandle<JSTaggedValue> & firstValue,const JSHandle<JSTaggedValue> & secondValue)589 int32_t TypedArrayHelper::SortCompare(JSThread *thread, const JSHandle<JSTaggedValue> &callbackfnHandle,
590                                       const JSHandle<JSTaggedValue> &buffer, const JSHandle<JSTaggedValue> &firstValue,
591                                       const JSHandle<JSTaggedValue> &secondValue)
592 {
593     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
594     // 1. Assert: Both Type(x) and Type(y) are Number or both are BigInt.
595     ASSERT((firstValue->IsNumber() && secondValue->IsNumber()) || (firstValue->IsBigInt() && secondValue->IsBigInt()));
596     // 2. If the argument comparefn is not undefined, then
597     //   a. Let v be Call(comparefn, undefined, «x, y»).
598     //   b. ReturnIfAbrupt(v).
599     //   c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
600     //   d. If v is NaN, return +0.
601     //   e. Return v.
602     if (!callbackfnHandle->IsUndefined()) {
603         const uint32_t argsLength = 2;
604         JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
605         EcmaRuntimeCallInfo *info =
606             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackfnHandle, undefined, undefined, argsLength);
607         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
608         info->SetCallArg(firstValue.GetTaggedValue(), secondValue.GetTaggedValue());
609         JSTaggedValue callResult = JSFunction::Call(info);
610         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
611         if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) {
612             THROW_TYPE_ERROR_AND_RETURN(thread, "The buffer is detached buffer.", 0);
613         }
614         JSHandle<JSTaggedValue> testResult(thread, callResult);
615         JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult);
616         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
617         double value = v.GetNumber();
618         if (std::isnan(value)) {
619             return +0;
620         }
621         return value;
622     }
623     if (firstValue->IsNumber()) {
624         // 3. If x and y are both NaN, return +0.
625         if (NumberHelper::IsNaN(firstValue.GetTaggedValue())) {
626             if (NumberHelper::IsNaN(secondValue.GetTaggedValue())) {
627                 return +0;
628             }
629             // 4. If x is NaN, return 1.
630             return 1;
631         }
632         // 5. If y is NaN, return -1.
633         if (NumberHelper::IsNaN(secondValue.GetTaggedValue())) {
634             return -1;
635         }
636         ComparisonResult compareResult = JSTaggedValue::Compare(thread, firstValue, secondValue);
637         // 6. If x < y, return -1.
638         // 7. If x > y, return 1.
639         // 8. If x is -0 and y is +0, return -1.
640         // 9. If x is +0 and y is -0, return 1.
641         // 10. Return +0.
642         if (compareResult == ComparisonResult::LESS) {
643             return -1;
644         }
645         if (compareResult == ComparisonResult::GREAT) {
646             return 1;
647         }
648         JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, firstValue);
649         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
650         JSTaggedNumber yNumber = JSTaggedValue::ToNumber(thread, secondValue);
651         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
652         double eZeroTemp = -0.0;
653         auto eZero = JSTaggedNumber(eZeroTemp);
654         double pZeroTemp = +0.0;
655         auto pZero = JSTaggedNumber(pZeroTemp);
656         if (JSTaggedNumber::SameValue(xNumber, eZero) && JSTaggedNumber::SameValue(yNumber, pZero)) {
657             return -1;
658         }
659         if (JSTaggedNumber::SameValue(xNumber, pZero) && JSTaggedNumber::SameValue(yNumber, eZero)) {
660             return 1;
661         }
662     } else {
663         ComparisonResult compareResult = JSTaggedValue::Compare(thread, firstValue, secondValue);
664         if (compareResult == ComparisonResult::LESS) {
665             return -1;
666         }
667         if (compareResult == ComparisonResult::GREAT) {
668             return 1;
669         }
670     }
671     return +0;
672 }
673 }  // namespace panda::ecmascript::base
674