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