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