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