• 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     uint64_t newLength = 0;
328     if (!length->IsUndefined()) {
329         index = JSTaggedValue::ToIndex(thread, length);
330         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
331         newLength = static_cast<uint64_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     uint64_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         newByteLength = newLength * elementSize;
359         if (offset + newByteLength > bufferByteLength) {
360             THROW_RANGE_ERROR_AND_RETURN(thread, "The newByteLength is out of range.", JSTaggedValue::Exception());
361         }
362     }
363     // 13. Set O.[[ViewedArrayBuffer]] to buffer.
364     // 14. Set O.[[ByteLength]] to newByteLength.
365     // 15. Set O.[[ByteOffset]] to offset.
366     // 16. Set O.[[ArrayLength]] to newByteLength / elementSize.
367     JSTypedArray *jsTypedArray = JSTypedArray::Cast(*obj);
368     jsTypedArray->SetViewedArrayBufferOrByteArray(thread, buffer);
369     jsTypedArray->SetByteLength(newByteLength);
370     jsTypedArray->SetByteOffset(offset);
371     jsTypedArray->SetArrayLength(newByteLength / elementSize);
372     // 17. Return O.
373     return obj.GetTaggedValue();
374 }
375 
376 // 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)377 JSHandle<JSObject> TypedArrayHelper::AllocateTypedArray(JSThread *thread,
378                                                         const JSHandle<JSTaggedValue> &constructorName,
379                                                         const JSHandle<JSTaggedValue> &newTarget,
380                                                         const DataViewType arrayType)
381 {
382     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
383     // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto).
384     // 2. Let obj be ! IntegerIndexedObjectCreate(proto).
385     JSHandle<JSFunction> typedArrayFunc = TypedArrayHelper::GetConstructorFromType(thread, arrayType);
386     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget);
387     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
388     // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined.
389     // 4. Set obj.[[TypedArrayName]] to constructorName.
390 
391     // 5. If constructorName is "BigInt64Array" or "BigUint64Array", set obj.[[ContentType]] to BigInt.
392     // 6. Otherwise, set obj.[[ContentType]] to Number.
393     JSTypedArray *jsTypedArray = JSTypedArray::Cast(*obj);
394     if (arrayType == DataViewType::BIGINT64 ||
395         arrayType == DataViewType::BIGUINT64) {
396         jsTypedArray->SetContentType(ContentType::BigInt);
397     } else {
398         jsTypedArray->SetContentType(ContentType::Number);
399     }
400     // 7. If length is not present, then
401     //   a. Set obj.[[ByteLength]] to 0.
402     //   b. Set obj.[[ByteOffset]] to 0.
403     //   c. Set obj.[[ArrayLength]] to 0.
404     jsTypedArray->SetTypedArrayName(thread, constructorName);
405     jsTypedArray->SetByteLength(0);
406     jsTypedArray->SetByteOffset(0);
407     jsTypedArray->SetArrayLength(0);
408     jsTypedArray->SetIsOnHeap(false);
409     // 9. Return obj.
410     return obj;
411 }
412 
413 // 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)414 JSHandle<JSObject> TypedArrayHelper::AllocateTypedArray(JSThread *thread,
415                                                         const JSHandle<JSTaggedValue> &constructorName,
416                                                         const JSHandle<JSTaggedValue> &newTarget, uint32_t length,
417                                                         const DataViewType arrayType)
418 {
419     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
420     // 1. Let proto be ? GetPrototypeFromConstructor(newTarget, defaultProto).
421     // 2. Let obj be ! IntegerIndexedObjectCreate(proto).
422     JSHandle<JSFunction> typedArrayFunc = TypedArrayHelper::GetConstructorFromType(thread, arrayType);
423     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(typedArrayFunc, newTarget);
424     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
425     // 3. Assert: obj.[[ViewedArrayBuffer]] is undefined.
426     // 4. Set obj.[[TypedArrayName]] to constructorName.
427     JSTypedArray::Cast(*obj)->SetTypedArrayName(thread, constructorName);
428     // 7. If length is not present, then
429     // 8. Else,
430     //   a. Perform ? AllocateTypedArrayBuffer(obj, length).
431     TypedArrayHelper::AllocateTypedArrayBuffer(thread, obj, length, arrayType);
432     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
433     // 9. Return obj.
434     return obj;
435 }
436 
437 // es11 22.2.4.2.2 Runtime Semantics: AllocateTypedArrayBuffer ( O, length )
AllocateTypedArrayBuffer(JSThread * thread,const JSHandle<JSObject> & obj,uint64_t length,const DataViewType arrayType)438 JSHandle<JSObject> TypedArrayHelper::AllocateTypedArrayBuffer(JSThread *thread,
439                                                               const JSHandle<JSObject> &obj, uint64_t length,
440                                                               const DataViewType arrayType)
441 {
442     // 1. Assert: O is an Object that has a [[ViewedArrayBuffer]] internal slot.
443     // 2. Assert: O.[[ViewedArrayBuffer]] is undefined.
444     // 3. Assert: ! IsNonNegativeInteger(length) is true.
445     JSHandle<JSObject> exception(thread, JSTaggedValue::Exception());
446     if (length > JSTypedArray::MAX_TYPED_ARRAY_INDEX) {
447         THROW_RANGE_ERROR_AND_RETURN(thread, "array length must less than 2^32 - 1", exception);
448     }
449     // 4. Let constructorName be the String value of O.[[TypedArrayName]].
450     //     we use type to get size
451     // 5. Let elementSize be the Element Size value specified in Table 61 for constructorName.
452     uint32_t elementSize = TypedArrayHelper::GetSizeFromType(arrayType);
453     // 6. Let byteLength be elementSize × length.
454     uint32_t arrayLength = static_cast<uint32_t>(length);
455     uint64_t byteLength = static_cast<uint64_t>(elementSize) * length;
456     // 7. Let data be ? AllocateArrayBuffer(%ArrayBuffer%, byteLength).
457     JSTaggedValue data;
458     if (byteLength > JSTypedArray::MAX_ONHEAP_LENGTH) {
459         JSHandle<JSTaggedValue> constructor = thread->GetEcmaVM()->GetGlobalEnv()->GetArrayBufferFunction();
460         data = BuiltinsArrayBuffer::AllocateArrayBuffer(thread, constructor, byteLength);
461         JSTypedArray::Cast(*obj)->SetIsOnHeap(false);
462     } else {
463         data = thread->GetEcmaVM()->GetFactory()->NewByteArray(arrayLength, elementSize).GetTaggedValue();
464         JSTypedArray::Cast(*obj)->SetIsOnHeap(true);
465     }
466     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, exception);
467     JSTypedArray *jsTypedArray = JSTypedArray::Cast(*obj);
468     if (arrayType == DataViewType::BIGINT64 ||
469         arrayType == DataViewType::BIGUINT64) {
470         jsTypedArray->SetContentType(ContentType::BigInt);
471     } else {
472         jsTypedArray->SetContentType(ContentType::Number);
473     }
474     // 8. Set O.[[ViewedArrayBuffer]] to data.
475     // 9. Set O.[[ByteLength]] to byteLength.
476     // 10. Set O.[[ByteOffset]] to 0.
477     // 11. Set O.[[ArrayLength]] to length.
478     jsTypedArray->SetViewedArrayBufferOrByteArray(thread, data);
479     jsTypedArray->SetByteLength(byteLength);
480     jsTypedArray->SetByteOffset(0);
481     jsTypedArray->SetArrayLength(arrayLength);
482     // 12. Return O.
483     return obj;
484 }
485 
486 // es11 22.2.4.7 TypedArraySpeciesCreate ( exemplar, argumentList )
TypedArraySpeciesCreate(JSThread * thread,const JSHandle<JSTypedArray> & obj,uint32_t argc,JSTaggedType argv[])487 JSHandle<JSObject> TypedArrayHelper::TypedArraySpeciesCreate(JSThread *thread, const JSHandle<JSTypedArray> &obj,
488                                                              uint32_t argc, JSTaggedType argv[])
489 {
490     // 1. Assert: exemplar is an Object that has [[TypedArrayName]] and [[ContentType]] internal slots.
491     // 2. Let defaultConstructor be the intrinsic object listed in column one of Table 61 for
492     // exemplar.[[TypedArrayName]].
493     JSHandle<JSTaggedValue> buffHandle(thread, JSTaggedValue(argv[0]));
494     JSHandle<JSTaggedValue> defaultConstructor =
495         TypedArrayHelper::GetConstructor(thread, JSHandle<JSTaggedValue>(obj));
496     // 3. Let constructor be ? SpeciesConstructor(exemplar, defaultConstructor).
497     JSHandle<JSTaggedValue> thisConstructor =
498         JSObject::SpeciesConstructor(thread, JSHandle<JSObject>::Cast(obj), defaultConstructor);
499     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
500     // 4. Let result be ? TypedArrayCreate(constructor, argumentList).
501     argv[0] = buffHandle.GetTaggedType();
502     JSHandle<JSObject> result = TypedArrayHelper::TypedArrayCreate(thread, thisConstructor, argc, argv);
503     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
504     // 5. If result.[[ContentType]] ≠ exemplar.[[ContentType]], throw a TypeError exception.
505     ContentType objContentType = obj->GetContentType();
506     ContentType resultContentType = JSHandle<JSTypedArray>::Cast(result)->GetContentType();
507     if (objContentType != resultContentType) {
508         JSHandle<JSObject> exception(thread, JSTaggedValue::Exception());
509         THROW_TYPE_ERROR_AND_RETURN(thread, "resultContentType is not equal objContentType.", exception);
510     }
511     return result;
512 }
513 
514 // es11 22.2.4.6 TypedArrayCreate ( constructor, argumentList )
TypedArrayCreate(JSThread * thread,const JSHandle<JSTaggedValue> & constructor,uint32_t argc,const JSTaggedType argv[])515 JSHandle<JSObject> TypedArrayHelper::TypedArrayCreate(JSThread *thread, const JSHandle<JSTaggedValue> &constructor,
516                                                       uint32_t argc, const JSTaggedType argv[])
517 {
518     // 1. Let newTypedArray be ? Construct(constructor, argumentList).
519     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
520     EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, argc);
521     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
522     info->SetCallArg(argc, argv);
523     JSTaggedValue taggedArray = JSFunction::Construct(info);
524     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
525     if (!taggedArray.IsECMAObject()) {
526         THROW_TYPE_ERROR_AND_RETURN(thread, "Failed to construct the Typedarray.",
527                                     JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
528     }
529     JSHandle<JSTaggedValue> taggedArrayHandle(thread, taggedArray);
530     // 2. Perform ? ValidateTypedArray(newTypedArray).
531     TypedArrayHelper::ValidateTypedArray(thread, taggedArrayHandle);
532     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
533     JSHandle<JSObject> newTypedArray(taggedArrayHandle);
534     // 3. If argumentList is a List of a single Number, then
535     //   a. If newTypedArray.[[ArrayLength]] < argumentList[0], throw a TypeError exception.
536     if (argc == 1) {
537         if (JSHandle<JSTypedArray>::Cast(newTypedArray)->GetArrayLength() <
538             JSTaggedValue::ToUint32(thread, info->GetCallArg(0))) {
539             THROW_TYPE_ERROR_AND_RETURN(thread, "the length of newTypedArray is not a correct value.",
540                                         JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
541         }
542     }
543     // 4. Return newTypedArray.
544     return newTypedArray;
545 }
546 
547 // TypedArrayCreateSameType ( exemplar, argumentList )
TypedArrayCreateSameType(JSThread * thread,const JSHandle<JSTypedArray> & obj,uint32_t argc,JSTaggedType argv[])548 JSHandle<JSObject> TypedArrayHelper::TypedArrayCreateSameType(JSThread *thread, const JSHandle<JSTypedArray> &obj,
549                                                               uint32_t argc, JSTaggedType argv[])
550 {
551     // 1. Let constructor be the intrinsic object associated with the constructor name exemplar.[[TypedArrayName]]
552     // in Table 70.
553     JSHandle<JSTaggedValue> buffHandle(thread, JSTaggedValue(argv[0]));
554     JSHandle<JSTaggedValue> constructor =
555         TypedArrayHelper::GetConstructor(thread, JSHandle<JSTaggedValue>(obj));
556     argv[0] = buffHandle.GetTaggedType();
557     // 2. Let result be ? TypedArrayCreate(constructor, argumentList).
558     JSHandle<JSObject> result = TypedArrayHelper::TypedArrayCreate(thread, constructor, argc, argv);
559     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSHandle<JSObject>(thread, JSTaggedValue::Exception()));
560     // 3. Assert: result has [[TypedArrayName]] and [[ContentType]] internal slots.
561     // 4. Assert: result.[[ContentType]] is exemplar.[[ContentType]].
562     [[maybe_unused]] ContentType objContentType = obj->GetContentType();
563     [[maybe_unused]] ContentType resultContentType = JSHandle<JSTypedArray>::Cast(result)->GetContentType();
564     ASSERT(objContentType == resultContentType);
565     return result;
566 }
567 
568 // es11 22.2.3.5.1 Runtime Semantics: ValidateTypedArray ( O )
ValidateTypedArray(JSThread * thread,const JSHandle<JSTaggedValue> & value)569 JSTaggedValue TypedArrayHelper::ValidateTypedArray(JSThread *thread, const JSHandle<JSTaggedValue> &value)
570 {
571     // 1. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
572     // 2. Assert: O has a [[ViewedArrayBuffer]] internal slot.
573     if (!value->IsTypedArray()) {
574         THROW_TYPE_ERROR_AND_RETURN(thread, "The O is not a TypedArray.", JSTaggedValue::Exception());
575     }
576     // 3. Let buffer be O.[[ViewedArrayBuffer]].
577     // 4. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
578     JSTaggedValue buffer = JSHandle<JSTypedArray>::Cast(value)->GetViewedArrayBufferOrByteArray();
579     if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer)) {
580         THROW_TYPE_ERROR_AND_RETURN(thread, "The ViewedArrayBuffer of O is detached buffer.",
581                                     JSTaggedValue::Exception());
582     }
583     // 5. Return buffer.
584     return buffer;
585 }
586 
SortCompare(JSThread * thread,const JSHandle<JSTaggedValue> & callbackfnHandle,const JSHandle<JSTaggedValue> & buffer,const JSHandle<JSTaggedValue> & firstValue,const JSHandle<JSTaggedValue> & secondValue)587 int32_t TypedArrayHelper::SortCompare(JSThread *thread, const JSHandle<JSTaggedValue> &callbackfnHandle,
588                                       const JSHandle<JSTaggedValue> &buffer, const JSHandle<JSTaggedValue> &firstValue,
589                                       const JSHandle<JSTaggedValue> &secondValue)
590 {
591     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
592     // 1. Assert: Both Type(x) and Type(y) are Number or both are BigInt.
593     ASSERT((firstValue->IsNumber() && secondValue->IsNumber()) || (firstValue->IsBigInt() && secondValue->IsBigInt()));
594     // 2. If the argument comparefn is not undefined, then
595     //   a. Let v be Call(comparefn, undefined, «x, y»).
596     //   b. ReturnIfAbrupt(v).
597     //   c. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
598     //   d. If v is NaN, return +0.
599     //   e. Return v.
600     if (!callbackfnHandle->IsUndefined()) {
601         const uint32_t argsLength = 2;
602         JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
603         EcmaRuntimeCallInfo *info =
604             EcmaInterpreter::NewRuntimeCallInfo(thread, callbackfnHandle, undefined, undefined, argsLength);
605         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
606         info->SetCallArg(firstValue.GetTaggedValue(), secondValue.GetTaggedValue());
607         JSTaggedValue callResult = JSFunction::Call(info);
608         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
609         if (BuiltinsArrayBuffer::IsDetachedBuffer(buffer.GetTaggedValue())) {
610             THROW_TYPE_ERROR_AND_RETURN(thread, "The buffer is detached buffer.", 0);
611         }
612         JSHandle<JSTaggedValue> testResult(thread, callResult);
613         JSTaggedNumber v = JSTaggedValue::ToNumber(thread, testResult);
614         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
615         double value = v.GetNumber();
616         if (std::isnan(value)) {
617             return +0;
618         }
619         return value;
620     }
621     if (firstValue->IsNumber()) {
622         // 3. If x and y are both NaN, return +0.
623         if (NumberHelper::IsNaN(firstValue.GetTaggedValue())) {
624             if (NumberHelper::IsNaN(secondValue.GetTaggedValue())) {
625                 return +0;
626             }
627             // 4. If x is NaN, return 1.
628             return 1;
629         }
630         // 5. If y is NaN, return -1.
631         if (NumberHelper::IsNaN(secondValue.GetTaggedValue())) {
632             return -1;
633         }
634         ComparisonResult compareResult = JSTaggedValue::Compare(thread, firstValue, secondValue);
635         // 6. If x < y, return -1.
636         // 7. If x > y, return 1.
637         // 8. If x is -0 and y is +0, return -1.
638         // 9. If x is +0 and y is -0, return 1.
639         // 10. Return +0.
640         if (compareResult == ComparisonResult::LESS) {
641             return -1;
642         }
643         if (compareResult == ComparisonResult::GREAT) {
644             return 1;
645         }
646         JSTaggedNumber xNumber = JSTaggedValue::ToNumber(thread, firstValue);
647         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
648         JSTaggedNumber yNumber = JSTaggedValue::ToNumber(thread, secondValue);
649         RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, 0);
650         double eZeroTemp = -0.0;
651         auto eZero = JSTaggedNumber(eZeroTemp);
652         double pZeroTemp = +0.0;
653         auto pZero = JSTaggedNumber(pZeroTemp);
654         if (JSTaggedNumber::SameValue(xNumber, eZero) && JSTaggedNumber::SameValue(yNumber, pZero)) {
655             return -1;
656         }
657         if (JSTaggedNumber::SameValue(xNumber, pZero) && JSTaggedNumber::SameValue(yNumber, eZero)) {
658             return 1;
659         }
660     } else {
661         ComparisonResult compareResult = JSTaggedValue::Compare(thread, firstValue, secondValue);
662         if (compareResult == ComparisonResult::LESS) {
663             return -1;
664         }
665         if (compareResult == ComparisonResult::GREAT) {
666             return 1;
667         }
668     }
669     return +0;
670 }
671 }  // namespace panda::ecmascript::base
672