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