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/builtins/builtins_arraybuffer.h"
17
18 #include <typeinfo>
19
20 #include "ecmascript/base/builtins_base.h"
21 #include "ecmascript/base/number_helper.h"
22 #include "ecmascript/builtins/builtins_bigint.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_arraybuffer.h"
28 #include "ecmascript/js_object-inl.h"
29 #include "ecmascript/js_tagged_number.h"
30 #include "ecmascript/js_tagged_value-inl.h"
31 #include "ecmascript/js_tagged_value.h"
32 #include "ecmascript/object_factory.h"
33 #include "ecmascript/base/typed_array_helper-inl.h"
34 #include "ecmascript/base/typed_array_helper.h"
35
36 #include "securec.h"
37 #include "cstdio"
38 #include "cstring"
39
40 namespace panda::ecmascript::builtins {
41 using TypedArrayHelper = base::TypedArrayHelper;
42 // 24.1.2.1 ArrayBuffer(length)
ArrayBufferConstructor(EcmaRuntimeCallInfo * argv)43 JSTaggedValue BuiltinsArrayBuffer::ArrayBufferConstructor(EcmaRuntimeCallInfo *argv)
44 {
45 ASSERT(argv);
46 JSThread *thread = argv->GetThread();
47 BUILTINS_API_TRACE(thread, ArrayBuffer, Constructor);
48 [[maybe_unused]] EcmaHandleScope handleScope(thread);
49 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
50 // 1. If NewTarget is undefined, throw a TypeError exception.
51 if (newTarget->IsUndefined()) {
52 THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception());
53 }
54 JSHandle<JSTaggedValue> lengthHandle = GetCallArg(argv, 0);
55 JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle);
56 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
57 uint64_t length = lenNum.GetNumber();
58 return AllocateArrayBuffer(thread, newTarget, length);
59 }
60
61 // 24.1.3.1 ArrayBuffer.isView(arg)
IsView(EcmaRuntimeCallInfo * argv)62 JSTaggedValue BuiltinsArrayBuffer::IsView(EcmaRuntimeCallInfo *argv)
63 {
64 ASSERT(argv);
65 JSThread *thread = argv->GetThread();
66 BUILTINS_API_TRACE(thread, ArrayBuffer, IsView);
67 [[maybe_unused]] EcmaHandleScope handleScope(thread);
68 JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
69 // 1. If Type(arg) is not Object, return false.
70 if (!arg->IsECMAObject()) {
71 return BuiltinsArrayBuffer::GetTaggedBoolean(false);
72 }
73 // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
74 if (arg->IsDataView() || arg->IsTypedArray()) {
75 return BuiltinsArrayBuffer::GetTaggedBoolean(true);
76 }
77 // 3. Return false.
78 return BuiltinsArrayBuffer::GetTaggedBoolean(false);
79 }
80
81 // 24.1.3.3 get ArrayBuffer [ @@species ]
Species(EcmaRuntimeCallInfo * argv)82 JSTaggedValue BuiltinsArrayBuffer::Species(EcmaRuntimeCallInfo *argv)
83 {
84 ASSERT(argv);
85 BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Species);
86 return GetThis(argv).GetTaggedValue();
87 }
88
89 // 24.1.4.1 get ArrayBuffer.prototype.byteLength
GetByteLength(EcmaRuntimeCallInfo * argv)90 JSTaggedValue BuiltinsArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv)
91 {
92 ASSERT(argv);
93 JSThread *thread = argv->GetThread();
94 BUILTINS_API_TRACE(thread, ArrayBuffer, GetByteLength);
95 [[maybe_unused]] EcmaHandleScope handleScope(thread);
96
97 // 1. Let O be the this value.
98 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
99 // 2. If Type(O) is not Object, throw a TypeError exception.
100 if (!thisHandle->IsECMAObject()) {
101 THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
102 }
103 // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
104 if (!thisHandle->IsArrayBuffer()) {
105 THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
106 }
107 // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
108 if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
109 THROW_TYPE_ERROR_AND_RETURN(thread, "IsDetachedBuffer", JSTaggedValue::Exception());
110 }
111 JSHandle<JSArrayBuffer> arrBuf(thisHandle);
112 // 5. Let length be the value of O’s [[ArrayBufferByteLength]] internal slot.
113 uint32_t length = arrBuf->GetArrayBufferByteLength();
114 // 6. Return length.
115 return JSTaggedValue(length);
116 }
117
118 // 24.1.4.3 ArrayBuffer.prototype.slice(start, end)
Slice(EcmaRuntimeCallInfo * argv)119 JSTaggedValue BuiltinsArrayBuffer::Slice(EcmaRuntimeCallInfo *argv)
120 {
121 ASSERT(argv);
122 JSThread *thread = argv->GetThread();
123 BUILTINS_API_TRACE(thread, ArrayBuffer, Slice);
124 [[maybe_unused]] EcmaHandleScope handleScope(thread);
125 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
126 // 1. Let O be the this value.
127 JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
128 // 2. If Type(O) is not Object, throw a TypeError exception.
129 if (!thisHandle->IsHeapObject()) {
130 THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
131 }
132 JSHandle<JSArrayBuffer> arrBuf(thisHandle);
133 // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
134 if (!thisHandle->IsArrayBuffer()) {
135 THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
136 }
137 // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
138 if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
139 THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception());
140 }
141 // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot.
142 int32_t len = static_cast<int32_t>(arrBuf->GetArrayBufferByteLength());
143 JSHandle<JSTaggedValue> startHandle = GetCallArg(argv, 0);
144 // 6. Let relativeStart be ToInteger(start).
145 JSTaggedNumber relativeStart = JSTaggedValue::ToInteger(thread, startHandle);
146 // 7. ReturnIfAbrupt(relativeStart).
147 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
148 int32_t start = base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber());
149 int32_t end = 0;
150 int32_t first = 0;
151 int32_t last = 0;
152 // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else let first be min(relativeStart, len).
153 if (start < 0) {
154 first = std::max((len + start), 0);
155 } else {
156 first = std::min(start, len);
157 }
158 // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end).
159 JSHandle<JSTaggedValue> endHandle = GetCallArg(argv, 1);
160 if (endHandle->IsUndefined()) {
161 end = len;
162 } else {
163 JSTaggedNumber relativeEnd = JSTaggedValue::ToInteger(thread, endHandle);
164 // 10. ReturnIfAbrupt(relativeEnd).
165 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
166 end = base::NumberHelper::DoubleInRangeInt32(relativeEnd.GetNumber());
167 }
168 // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len).
169 if (end < 0) {
170 last = std::max((len + end), 0);
171 } else {
172 last = std::min(end, len);
173 }
174 // 12. Let newLen be max(final-first,0).
175 uint32_t newLen = std::max((last - first), 0);
176 // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%).
177 JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
178 JSHandle<JSObject> objHandle(thisHandle);
179 JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
180 // 14. ReturnIfAbrupt(ctor).
181 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
182 // 15. Let new be Construct(ctor, «newLen»).
183 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
184 EcmaRuntimeCallInfo *info =
185 EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1);
186 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
187 info->SetCallArg(JSTaggedValue(newLen));
188 JSTaggedValue taggedNewArrBuf = JSFunction::Construct(info);
189 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
190 JSHandle<JSTaggedValue> newArrBuf(thread, taggedNewArrBuf);
191 // 16. ReturnIfAbrupt(new).
192 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
193 // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
194 if (!newArrBuf->IsArrayBuffer()) {
195 THROW_TYPE_ERROR_AND_RETURN(thread, "don't have bufferdata internal slot", JSTaggedValue::Exception());
196 }
197 // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception.
198 if (IsDetachedBuffer(newArrBuf.GetTaggedValue())) {
199 THROW_TYPE_ERROR_AND_RETURN(thread, "new arrayBuffer IsDetachedBuffer", JSTaggedValue::Exception());
200 }
201 // 19. If SameValue(new, O) is true, throw a TypeError exception.
202 if (JSTaggedValue::SameValue(newArrBuf.GetTaggedValue(), thisHandle.GetTaggedValue())) {
203 THROW_TYPE_ERROR_AND_RETURN(thread, "value of new arraybuffer and this is same", JSTaggedValue::Exception());
204 }
205 JSHandle<JSArrayBuffer> newJsArrBuf(newArrBuf);
206 // 20. If the value of new’s [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception.
207 uint32_t newArrBufLen = newJsArrBuf->GetArrayBufferByteLength();
208 if (newArrBufLen < newLen) {
209 THROW_TYPE_ERROR_AND_RETURN(thread, "new array buffer length smaller than newlen", JSTaggedValue::Exception());
210 }
211 // 21. NOTE: Side-effects of the above steps may have detached O.
212 // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception.
213 if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
214 THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception());
215 }
216 if (newLen > 0) {
217 // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot.
218 void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
219 // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot.
220 void *toBuf = GetDataPointFromBuffer(newJsArrBuf.GetTaggedValue());
221 // 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen).
222 JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, first, newLen);
223 }
224 // Return new.
225 return newArrBuf.GetTaggedValue();
226 }
227
228 // 24.1.1.1 AllocateArrayBuffer(constructor, byteLength)
AllocateArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & newTarget,uint64_t byteLength)229 JSTaggedValue BuiltinsArrayBuffer::AllocateArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget,
230 uint64_t byteLength)
231 {
232 BUILTINS_API_TRACE(thread, ArrayBuffer, AllocateArrayBuffer);
233 /**
234 * 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%",
235 * «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ).
236 * */
237 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
238 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
239 JSHandle<JSTaggedValue> arrBufFunc = env->GetArrayBufferFunction();
240 JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(arrBufFunc), newTarget);
241 // 2. ReturnIfAbrupt
242 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
243 // 4. Let block be CreateByteDataBlock(byteLength).
244 if (byteLength > INT_MAX) {
245 THROW_RANGE_ERROR_AND_RETURN(thread, "Out of range", JSTaggedValue::Exception());
246 }
247 uint32_t arrayByteLength = static_cast<uint32_t>(byteLength);
248 JSHandle<JSArrayBuffer> arrayBuffer(obj);
249 // 6. Set obj’s [[ArrayBufferData]] internal slot to block.
250 factory->NewJSArrayBufferData(arrayBuffer, arrayByteLength);
251 // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength.
252 arrayBuffer->SetArrayBufferByteLength(arrayByteLength);
253 // 8. Return obj.
254 return arrayBuffer.GetTaggedValue();
255 }
256
257 // 24.1.1.2 IsDetachedBuffer()
IsDetachedBuffer(JSTaggedValue arrayBuffer)258 bool BuiltinsArrayBuffer::IsDetachedBuffer(JSTaggedValue arrayBuffer)
259 {
260 if (arrayBuffer.IsByteArray()) {
261 return false;
262 }
263 // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
264 ASSERT(arrayBuffer.IsArrayBuffer() || arrayBuffer.IsSharedArrayBuffer());
265 JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject());
266 JSTaggedValue dataSlot = buffer->GetArrayBufferData();
267 // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true.
268 // 3. Return false.
269 return dataSlot.IsNull();
270 }
271
272 // 24.1.1.4
CloneArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & srcBuffer,uint32_t srcByteOffset,JSHandle<JSTaggedValue> constructor)273 JSTaggedValue BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &srcBuffer,
274 uint32_t srcByteOffset, JSHandle<JSTaggedValue> constructor)
275 {
276 BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer);
277 // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
278 ASSERT(srcBuffer->IsArrayBuffer() || srcBuffer->IsSharedArrayBuffer());
279 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
280 // 2. If cloneConstructor is not present
281 if (constructor->IsUndefined()) {
282 // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%).
283 JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
284 JSHandle<JSObject> objHandle(srcBuffer);
285 constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
286 // b. ReturnIfAbrupt(cloneConstructor).
287 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
288 // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
289 if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
290 THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
291 } else {
292 ASSERT(constructor->IsConstructor());
293 }
294 }
295 // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot.
296 JSHandle<JSArrayBuffer> arrBuf(srcBuffer);
297 uint32_t srcLen = arrBuf->GetArrayBufferByteLength();
298 // 5. Assert: srcByteOffset ≤ srcLength.
299 ASSERT(srcByteOffset <= srcLen);
300 // 6. Let cloneLength be srcLength – srcByteOffset.
301 int32_t cloneLen = static_cast<int32_t>(srcLen - srcByteOffset);
302 // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength).
303 JSTaggedValue taggedBuf = AllocateArrayBuffer(thread, constructor, cloneLen);
304 // 9. ReturnIfAbrupt(targetBuffer).
305 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
306 // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
307 if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
308 THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
309 }
310 // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot.
311 JSHandle<JSArrayBuffer> newArrBuf(thread, taggedBuf);
312 // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength).
313 // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot.
314 void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
315 void *toBuf = GetDataPointFromBuffer(taggedBuf);
316 if (cloneLen > 0) {
317 JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, srcByteOffset, cloneLen);
318 }
319 return taggedBuf;
320 }
321
322 // 24.1.1.5
323 // NOLINTNEXTLINE(readability-function-size)
GetValueFromBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,bool littleEndian)324 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
325 DataViewType type, bool littleEndian)
326 {
327 void *pointer = GetDataPointFromBuffer(arrBuf);
328 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
329 return GetValueFromBuffer(thread, byteIndex, block, type, littleEndian);
330 }
331
GetValueFromBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,bool littleEndian)332 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block,
333 DataViewType type, bool littleEndian)
334 {
335 switch (type) {
336 case DataViewType::UINT8:
337 case DataViewType::UINT8_CLAMPED: {
338 uint8_t res = block[byteIndex]; // NOLINT
339 return GetTaggedInt(res);
340 }
341 case DataViewType::INT8: {
342 uint8_t res = block[byteIndex]; // NOLINT
343 auto int8Res = static_cast<int8_t>(res);
344 return GetTaggedInt(int8Res);
345 }
346 case DataViewType::UINT16:
347 return GetValueFromBufferForInteger<uint16_t, NumberSize::UINT16>(block, byteIndex, littleEndian);
348 case DataViewType::INT16:
349 return GetValueFromBufferForInteger<int16_t, NumberSize::INT16>(block, byteIndex, littleEndian);
350 case DataViewType::UINT32:
351 return GetValueFromBufferForInteger<uint32_t, NumberSize::UINT32>(block, byteIndex, littleEndian);
352 case DataViewType::INT32:
353 return GetValueFromBufferForInteger<int32_t, NumberSize::INT32>(block, byteIndex, littleEndian);
354 case DataViewType::FLOAT32:
355 return GetValueFromBufferForFloat<float, UnionType32, NumberSize::FLOAT32>(block, byteIndex, littleEndian);
356 case DataViewType::FLOAT64:
357 return GetValueFromBufferForFloat<double, UnionType64, NumberSize::FLOAT64>(block, byteIndex, littleEndian);
358 case DataViewType::BIGINT64:
359 return GetValueFromBufferForBigInt<int64_t, NumberSize::BIGINT64>(thread, block, byteIndex, littleEndian);
360 case DataViewType::BIGUINT64:
361 return GetValueFromBufferForBigInt<uint64_t, NumberSize::BIGUINT64>(thread, block, byteIndex, littleEndian);
362 default:
363 break;
364 }
365 LOG_ECMA(FATAL) << "this branch is unreachable";
366 UNREACHABLE();
367 }
368
369 // 24.1.1.6
SetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,const JSHandle<JSTaggedValue> & value,bool littleEndian)370 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
371 DataViewType type, const JSHandle<JSTaggedValue> &value,
372 bool littleEndian)
373 {
374 if (UNLIKELY(IsBigIntElementType(type))) {
375 JSHandle<JSTaggedValue> arrBufHandle(thread, arrBuf);
376 switch (type) {
377 case DataViewType::BIGINT64:
378 SetValueInBufferForBigInt<int64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
379 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
380 break;
381 case DataViewType::BIGUINT64:
382 SetValueInBufferForBigInt<uint64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
383 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
384 break;
385 default:
386 LOG_ECMA(FATAL) << "this branch is unreachable";
387 UNREACHABLE();
388 }
389 return JSTaggedValue::Undefined();
390 }
391 void *pointer = GetDataPointFromBuffer(arrBuf);
392 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
393 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, value.GetTaggedValue());
394 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
395 double val = numberValue.GetNumber();
396 return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
397 }
398
399 // es12 25.1.2.7 IsBigIntElementType ( type )
IsBigIntElementType(DataViewType type)400 bool BuiltinsArrayBuffer::IsBigIntElementType(DataViewType type)
401 {
402 if (type == DataViewType::BIGINT64 || type == DataViewType::BIGUINT64) {
403 return true;
404 }
405 return false;
406 }
407
408 // es12 25.1.2.6 IsUnclampedIntegerElementType ( type )
IsUnclampedIntegerElementType(DataViewType type)409 bool BuiltinsArrayBuffer::IsUnclampedIntegerElementType(DataViewType type)
410 {
411 switch (type) {
412 case DataViewType::INT8:
413 case DataViewType::INT16:
414 case DataViewType::INT32:
415 case DataViewType::UINT8:
416 case DataViewType::UINT16:
417 case DataViewType::UINT32:
418 return true;
419 default:
420 return false;
421 }
422 }
423
424 template<typename T>
SetTypeData(uint8_t * block,T value,uint32_t index)425 void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, uint32_t index)
426 {
427 uint32_t sizeCount = sizeof(T);
428 uint8_t *res = reinterpret_cast<uint8_t *>(&value);
429 for (uint32_t i = 0; i < sizeCount; i++) {
430 *(block + index + i) = *(res + i); // NOLINT
431 }
432 }
433
434 template<typename T>
FastSetTypeData(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,T value)435 void BuiltinsArrayBuffer::FastSetTypeData(uint8_t *byteBeginOffset, uint8_t *byteEndOffset, T value)
436 {
437 uint32_t sizeCount = sizeof(T);
438 if (sizeCount == 1) {
439 memset_s(byteBeginOffset, byteEndOffset-byteBeginOffset, value, byteEndOffset-byteBeginOffset);
440 } else {
441 uint8_t *resAddr = reinterpret_cast<uint8_t *>(&value);
442 for (uint8_t *addr = byteBeginOffset; addr < byteEndOffset; addr += sizeCount) {
443 for (uint32_t i = 0; i < sizeCount; ++i) {
444 *(addr + i) = *(resAddr + i);
445 }
446 }
447 }
448 }
449
450 template <typename T>
LittleEndianToBigEndian(T liValue)451 T BuiltinsArrayBuffer::LittleEndianToBigEndian(T liValue)
452 {
453 uint8_t sizeCount = sizeof(T);
454 T biValue;
455 switch (sizeCount) {
456 case NumberSize::UINT16:
457 biValue = ((liValue & 0x00FF) << BITS_EIGHT) // NOLINT
458 | ((liValue & 0xFF00) >> BITS_EIGHT); // NOLINT
459 break;
460 case NumberSize::UINT32:
461 biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR) // NOLINT
462 | ((liValue & 0x0000FF00) << BITS_EIGHT) // NOLINT
463 | ((liValue & 0x00FF0000) >> BITS_EIGHT) // NOLINT
464 | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR); // NOLINT
465 break;
466 default:
467 LOG_ECMA(FATAL) << "this branch is unreachable";
468 UNREACHABLE();
469 break;
470 }
471 return biValue;
472 }
473 template <typename T>
LittleEndianToBigEndian64Bit(T liValue)474 T BuiltinsArrayBuffer::LittleEndianToBigEndian64Bit(T liValue)
475 {
476 return ((liValue & 0x00000000000000FF) << BITS_FIFTY_SIX) // NOLINT
477 | ((liValue & 0x000000000000FF00) << BITS_FORTY) // NOLINT
478 | ((liValue & 0x0000000000FF0000) << BITS_TWENTY_FOUR) // NOLINT
479 | ((liValue & 0x00000000FF000000) << BITS_EIGHT) // NOLINT
480 | ((liValue & 0x000000FF00000000) >> BITS_EIGHT) // NOLINT
481 | ((liValue & 0x0000FF0000000000) >> BITS_TWENTY_FOUR) // NOLINT
482 | ((liValue & 0x00FF000000000000) >> BITS_FORTY) // NOLINT
483 | ((liValue & 0xFF00000000000000) >> BITS_FIFTY_SIX); // NOLINT
484 }
485
486 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForInteger(uint8_t * block,uint32_t byteIndex,bool littleEndian)487 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, uint32_t byteIndex, bool littleEndian)
488 {
489 ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
490 ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
491 ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
492
493 ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64);
494 T res = *reinterpret_cast<T *>(block + byteIndex);
495 if (!littleEndian) {
496 res = LittleEndianToBigEndian(res);
497 }
498
499 // uint32_t maybe overflow with TaggedInt
500 // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
501 if constexpr (std::is_same_v<T, uint32_t>) {
502 // NOLINTNEXTLINE(clang-diagnostic-sign-compare)
503 if (res > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
504 return GetTaggedDouble(static_cast<double>(res));
505 }
506 }
507 return GetTaggedInt(res);
508 }
509
510 template<typename T, typename UnionType, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForFloat(uint8_t * block,uint32_t byteIndex,bool littleEndian)511 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian)
512 {
513 ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be correct type");
514 ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
515
516 UnionType unionValue = {0};
517 // NOLINTNEXTLINE(readability-braces-around-statements)
518 if constexpr (std::is_same_v<T, float>) {
519 unionValue.uValue = *reinterpret_cast<uint32_t *>(block + byteIndex);
520 uint32_t res = LittleEndianToBigEndian(unionValue.uValue);
521 return CommonConvert<T, uint32_t>(unionValue.value, res, littleEndian);
522 } else if constexpr (std::is_same_v<T, double>) { // NOLINTNEXTLINE(readability-braces-around-statements)
523 unionValue.uValue = *reinterpret_cast<uint64_t *>(block + byteIndex);
524 uint64_t res = LittleEndianToBigEndian64Bit(unionValue.uValue);
525 return CommonConvert<T, uint64_t>(unionValue.value, res, littleEndian);
526 }
527
528 return GetTaggedDouble(unionValue.value);
529 }
530
531 template<typename T1, typename T2>
CommonConvert(T1 & value,T2 & res,bool littleEndian)532 JSTaggedValue BuiltinsArrayBuffer::CommonConvert(T1 &value, T2 &res, bool littleEndian)
533 {
534 if (std::isnan(value) && !JSTaggedValue::IsImpureNaN(value)) {
535 return GetTaggedDouble(value);
536 }
537 if (!littleEndian) {
538 T1 d = base::bit_cast<T1>(res);
539 if (JSTaggedValue::IsImpureNaN(d)) {
540 return GetTaggedDouble(base::NAN_VALUE);
541 }
542 return GetTaggedDouble(d);
543 } else {
544 if (JSTaggedValue::IsImpureNaN(value)) {
545 return GetTaggedDouble(base::NAN_VALUE);
546 }
547 }
548 return GetTaggedDouble(value);
549 }
550
551
552 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForBigInt(JSThread * thread,uint8_t * block,uint32_t byteIndex,bool littleEndian)553 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block,
554 uint32_t byteIndex, bool littleEndian)
555 {
556 ASSERT_PRINT((std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t>), "T must be uint64_t/int64_t");
557 auto pTmp = *reinterpret_cast<uint64_t *>(block + byteIndex);
558 if (!littleEndian) {
559 pTmp = LittleEndianToBigEndian64Bit(pTmp);
560 }
561 if constexpr (std::is_same_v<T, uint64_t>) {
562 return BigInt::Uint64ToBigInt(thread, pTmp).GetTaggedValue();
563 }
564 return BigInt::Int64ToBigInt(thread, pTmp).GetTaggedValue();
565 }
566
567
568 template<typename T>
SetValueInBufferForByte(double val,uint8_t * block,uint32_t byteIndex)569 void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex)
570 {
571 ASSERT_PRINT((std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>), "T must be int8/uint8");
572 T res;
573 if (std::isnan(val) || std::isinf(val)) {
574 res = 0;
575 SetTypeData(block, res, byteIndex);
576 return;
577 }
578 auto int64Val = static_cast<int64_t>(val);
579 auto *resArr = reinterpret_cast<T *>(&int64Val);
580 res = *resArr;
581 SetTypeData(block, res, byteIndex);
582 }
583
SetValueInBufferForUint8Clamped(double val,uint8_t * block,uint32_t byteIndex)584 void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, uint32_t byteIndex)
585 {
586 uint8_t res;
587 if (std::isnan(val) || val <= 0) {
588 res = 0;
589 } else if (val > UINT8_MAX) {
590 res = UINT8_MAX;
591 } else {
592 // same as ToUint8Clamp
593 res = std::lrint(val);
594 }
595 SetTypeData(block, res, byteIndex);
596 }
597
598 template<typename T>
SetValueInBufferForInteger(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)599 void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
600 {
601 ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
602 ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
603 T res;
604 if (std::isnan(val) || std::isinf(val)) {
605 res = 0;
606 SetTypeData(block, res, byteIndex);
607 return;
608 }
609 auto int64Val = static_cast<int64_t>(val);
610 // NOLINTNEXTLINE(readability-braces-around-statements)
611 if constexpr (std::is_same_v<T, uint16_t>) {
612 auto *pTmp = reinterpret_cast<int16_t *>(&int64Val);
613 int16_t tmp = *pTmp;
614 res = static_cast<T>(tmp);
615 } else { // NOLINTNEXTLINE(readability-braces-around-statements)
616 auto *pTmp = reinterpret_cast<T *>(&int64Val);
617 res = *pTmp;
618 }
619
620 if (!littleEndian) {
621 res = LittleEndianToBigEndian<T>(res);
622 }
623 SetTypeData(block, res, byteIndex);
624 }
625
626 template<typename T>
SetValueInBufferForFloat(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)627 void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
628 {
629 ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be float type");
630 auto data = static_cast<T>(val);
631 if (std::isnan(val)) {
632 SetTypeData(block, data, byteIndex);
633 return;
634 }
635 if (!littleEndian) {
636 if constexpr (std::is_same_v<T, float>) {
637 uint32_t res = base::bit_cast<uint32_t>(data);
638 data = base::bit_cast<T>(LittleEndianToBigEndian(res));
639 } else if constexpr (std::is_same_v<T, double>) {
640 uint64_t res = base::bit_cast<uint64_t>(data);
641 data = base::bit_cast<T>(LittleEndianToBigEndian64Bit(res));
642 }
643 }
644 SetTypeData(block, data, byteIndex);
645 }
646
647 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,const JSHandle<JSTaggedValue> & val,JSHandle<JSTaggedValue> & arrBuf,uint32_t byteIndex,bool littleEndian)648 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
649 const JSHandle<JSTaggedValue> &val,
650 JSHandle<JSTaggedValue> &arrBuf,
651 uint32_t byteIndex, bool littleEndian)
652 {
653 ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
654 T value = 0;
655 bool lossless = true;
656 if constexpr(std::is_same_v<T, uint64_t>) {
657 BigInt::BigIntToUint64(thread, val, reinterpret_cast<uint64_t *>(&value), &lossless);
658 } else {
659 BigInt::BigIntToInt64(thread, val, reinterpret_cast<int64_t *>(&value), &lossless);
660 }
661 RETURN_IF_ABRUPT_COMPLETION(thread);
662 if (!littleEndian) {
663 value = LittleEndianToBigEndian64Bit<T>(value);
664 }
665 void *pointer = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
666 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
667 SetTypeData(block, value, byteIndex);
668 }
669
670 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)671 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
672 double val, uint8_t *block,
673 uint32_t byteIndex, bool littleEndian)
674 {
675 ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
676 T value = 0;
677 bool lossless = true;
678
679 JSHandle<JSTaggedValue> valHandle(thread, GetTaggedDouble(val));
680 if constexpr(std::is_same_v<T, uint64_t>) {
681 BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast<uint64_t *>(&value), &lossless);
682 } else {
683 BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast<int64_t *>(&value), &lossless);
684 }
685 RETURN_IF_ABRUPT_COMPLETION(thread);
686 if (!littleEndian) {
687 value = LittleEndianToBigEndian64Bit<T>(value);
688 }
689 SetTypeData(block, value, byteIndex);
690 }
691
FastSetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,double val,bool littleEndian)692 JSTaggedValue BuiltinsArrayBuffer::FastSetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
693 DataViewType type, double val, bool littleEndian)
694 {
695 void *pointer = GetDataPointFromBuffer(arrBuf);
696 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
697 return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
698 }
699
SetValueInBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,double val,bool littleEndian)700 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread* thread, uint32_t byteIndex, uint8_t *block,
701 DataViewType type, double val, bool littleEndian)
702 {
703 switch (type) {
704 case DataViewType::UINT8:
705 SetValueInBufferForByte<uint8_t>(val, block, byteIndex);
706 break;
707 case DataViewType::UINT8_CLAMPED:
708 SetValueInBufferForUint8Clamped(val, block, byteIndex);
709 break;
710 case DataViewType::INT8:
711 SetValueInBufferForByte<int8_t>(val, block, byteIndex);
712 break;
713 case DataViewType::UINT16:
714 SetValueInBufferForInteger<uint16_t>(val, block, byteIndex, littleEndian);
715 break;
716 case DataViewType::INT16:
717 SetValueInBufferForInteger<int16_t>(val, block, byteIndex, littleEndian);
718 break;
719 case DataViewType::UINT32:
720 SetValueInBufferForInteger<uint32_t>(val, block, byteIndex, littleEndian);
721 break;
722 case DataViewType::INT32:
723 SetValueInBufferForInteger<int32_t>(val, block, byteIndex, littleEndian);
724 break;
725 case DataViewType::FLOAT32:
726 SetValueInBufferForFloat<float>(val, block, byteIndex, littleEndian);
727 break;
728 case DataViewType::FLOAT64:
729 SetValueInBufferForFloat<double>(val, block, byteIndex, littleEndian);
730 break;
731 case DataViewType::BIGINT64:
732 SetValueInBufferForBigInt<int64_t>(thread, val, block, byteIndex, littleEndian);
733 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
734 break;
735 case DataViewType::BIGUINT64:
736 SetValueInBufferForBigInt<uint64_t>(thread, val, block, byteIndex, littleEndian);
737 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
738 break;
739 default:
740 LOG_ECMA(FATAL) << "this branch is unreachable";
741 UNREACHABLE();
742 }
743 return JSTaggedValue::Undefined();
744 }
745
GetDataPointFromBuffer(JSTaggedValue arrBuf,uint32_t byteOffset)746 void *BuiltinsArrayBuffer::GetDataPointFromBuffer(JSTaggedValue arrBuf, uint32_t byteOffset)
747 {
748 if (arrBuf.IsByteArray()) {
749 return reinterpret_cast<void *>(ToUintPtr(ByteArray::Cast(arrBuf.GetTaggedObject())->GetData()) + byteOffset);
750 }
751
752 JSArrayBuffer *arrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
753 if (arrayBuffer->GetArrayBufferByteLength() == 0) {
754 return nullptr;
755 }
756
757 JSTaggedValue data = arrayBuffer->GetArrayBufferData();
758 return reinterpret_cast<void *>(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject())
759 ->GetExternalPointer()) + byteOffset);
760 }
761
TypedArrayToList(JSThread * thread,JSHandle<JSTypedArray> & items)762 JSTaggedValue BuiltinsArrayBuffer::TypedArrayToList(JSThread *thread, JSHandle<JSTypedArray>& items)
763 {
764 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
765 JSHandle<JSTaggedValue> bufferHandle(thread, items->GetViewedArrayBufferOrByteArray());
766 uint32_t arrayLen = items->GetArrayLength();
767 JSTaggedValue newArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue();
768 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
769 JSHandle<JSObject> newArrayHandle(thread, newArray);
770 JSHandle<TaggedArray> oldElements(thread, newArrayHandle->GetElements());
771 JSHandle<TaggedArray> elements = (oldElements->GetLength() < arrayLen) ?
772 factory->ExtendArray(oldElements, arrayLen) : oldElements;
773 newArrayHandle->SetElements(thread, elements);
774 uint32_t offset = items->GetByteOffset();
775 uint32_t elementSize = TypedArrayHelper::GetElementSize(items);
776 DataViewType elementType = TypedArrayHelper::GetType(items);
777 uint32_t index = 0;
778 while (index < arrayLen) {
779 uint32_t byteIndex = index * elementSize + offset;
780 JSTaggedValue result = GetValueFromBuffer(thread, bufferHandle.GetTaggedValue(),
781 byteIndex, elementType, true);
782 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
783 ElementAccessor::Set(thread, newArrayHandle, index, result, true);
784 index++;
785 }
786 JSHandle<JSArray>(newArrayHandle)->SetArrayLength(thread, arrayLen);
787 return newArrayHandle.GetTaggedValue();
788 }
789
790 template<typename T>
FastSetValueInBufferForByte(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val)791 void BuiltinsArrayBuffer::FastSetValueInBufferForByte(uint8_t *byteBeginOffset,
792 uint8_t *byteEndOffset,
793 double val)
794 {
795 ASSERT_PRINT(sizeof(T) == 1, "sizeof(T) must be one");
796 ASSERT_PRINT((std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>), "T must be int8/uint8");
797 T res;
798 if (std::isnan(val) || std::isinf(val)) {
799 res = 0;
800 } else {
801 auto int64Val = static_cast<int64_t>(val);
802 auto *resArr = reinterpret_cast<T *>(&int64Val);
803 res = *resArr;
804 }
805 FastSetTypeData(byteBeginOffset, byteEndOffset, res);
806 }
807
FastSetValueInBufferForUint8Clamped(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val)808 void BuiltinsArrayBuffer::FastSetValueInBufferForUint8Clamped(uint8_t *byteBeginOffset,
809 uint8_t *byteEndOffset,
810 double val)
811 {
812 uint8_t res;
813 if (std::isnan(val) || val <= 0) {
814 res = 0;
815 } else {
816 val = val >= UINT8_MAX ? UINT8_MAX : val;
817 constexpr double HALF = 0.5;
818 val = val == HALF ? 0 : std::round(val);
819 res = static_cast<uint64_t>(val);
820 }
821 FastSetTypeData(byteBeginOffset, byteEndOffset, res);
822 }
823
824 template<typename T>
FastSetValueInBufferForInteger(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val,bool littleEndian)825 void BuiltinsArrayBuffer::FastSetValueInBufferForInteger(uint8_t *byteBeginOffset,
826 uint8_t *byteEndOffset,
827 double val, bool littleEndian)
828 {
829 ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
830 ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
831 T res;
832 if (std::isnan(val) || std::isinf(val)) {
833 res = 0;
834 } else {
835 auto int64Val = static_cast<int64_t>(val);
836 // NOLINTNEXTLINE(readability-braces-around-statements)
837 if constexpr (std::is_same_v<T, uint16_t>) {
838 auto *pTmp = reinterpret_cast<int16_t *>(&int64Val);
839 int16_t tmp = *pTmp;
840 res = static_cast<T>(tmp);
841 } else { // NOLINTNEXTLINE(readability-braces-around-statements)
842 auto *pTmp = reinterpret_cast<T *>(&int64Val);
843 res = *pTmp;
844 }
845 if (!littleEndian) {
846 res = LittleEndianToBigEndian<T>(res);
847 }
848 }
849 FastSetTypeData(byteBeginOffset, byteEndOffset, res);
850 }
851
852 template<typename T>
FastSetValueInBufferForFloat(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val,bool littleEndian)853 void BuiltinsArrayBuffer::FastSetValueInBufferForFloat(uint8_t *byteBeginOffset,
854 uint8_t *byteEndOffset,
855 double val, bool littleEndian)
856 {
857 ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be float type");
858 auto data = static_cast<T>(val);
859 if (!std::isnan(val)) {
860 if (!littleEndian) {
861 if constexpr (std::is_same_v<T, float>) {
862 uint32_t res = base::bit_cast<uint32_t>(data);
863 data = base::bit_cast<T>(LittleEndianToBigEndian(res));
864 } else if constexpr (std::is_same_v<T, double>) {
865 uint64_t res = base::bit_cast<uint64_t>(data);
866 data = base::bit_cast<T>(LittleEndianToBigEndian64Bit(res));
867 }
868 }
869 }
870 FastSetTypeData(byteBeginOffset, byteEndOffset, data);
871 }
872
873 template<typename T>
FastSetValueInBufferForBigInt(JSThread * thread,uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val,bool littleEndian)874 void BuiltinsArrayBuffer::FastSetValueInBufferForBigInt(JSThread *thread,
875 uint8_t *byteBeginOffset,
876 uint8_t *byteEndOffset,
877 double val, bool littleEndian)
878 {
879 ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
880 T value = 0;
881 bool lossless = true;
882
883 JSHandle<JSTaggedValue> valHandle(thread, GetTaggedDouble(val));
884 if constexpr(std::is_same_v<T, uint64_t>) {
885 BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast<uint64_t *>(&value), &lossless);
886 } else {
887 BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast<int64_t *>(&value), &lossless);
888 }
889 RETURN_IF_ABRUPT_COMPLETION(thread);
890 if (!littleEndian) {
891 value = LittleEndianToBigEndian64Bit<T>(value);
892 }
893 FastSetTypeData(byteBeginOffset, byteEndOffset, value);
894 }
895
TryFastSetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteBeginOffset,uint32_t byteEndOffset,DataViewType type,double val,bool littleEndian)896 JSTaggedValue BuiltinsArrayBuffer::TryFastSetValueInBuffer([[maybe_unused]] JSThread *thread, JSTaggedValue arrBuf,
897 uint32_t byteBeginOffset, uint32_t byteEndOffset,
898 DataViewType type, double val, bool littleEndian)
899 {
900 uint8_t *beginPointer = reinterpret_cast<uint8_t *>(GetDataPointFromBuffer(arrBuf, byteBeginOffset));
901 uint8_t *endPointer = reinterpret_cast<uint8_t *>(GetDataPointFromBuffer(arrBuf, byteEndOffset));
902 switch (type) {
903 case DataViewType::UINT8:
904 FastSetValueInBufferForByte<uint8_t>(beginPointer, endPointer, val);
905 break;
906 case DataViewType::UINT8_CLAMPED:
907 FastSetValueInBufferForUint8Clamped(beginPointer, endPointer, val);
908 break;
909 case DataViewType::INT8:
910 FastSetValueInBufferForByte<int8_t>(beginPointer, endPointer, val);
911 break;
912 case DataViewType::UINT16:
913 FastSetValueInBufferForInteger<uint16_t>(beginPointer, endPointer, val, littleEndian);
914 break;
915 case DataViewType::INT16:
916 FastSetValueInBufferForInteger<int16_t>(beginPointer, endPointer, val, littleEndian);
917 break;
918 case DataViewType::UINT32:
919 FastSetValueInBufferForInteger<uint32_t>(beginPointer, endPointer, val, littleEndian);
920 break;
921 case DataViewType::INT32:
922 FastSetValueInBufferForInteger<int32_t>(beginPointer, endPointer, val, littleEndian);
923 break;
924 case DataViewType::FLOAT32:
925 FastSetValueInBufferForFloat<float>(beginPointer, endPointer, val, littleEndian);
926 break;
927 case DataViewType::FLOAT64:
928 FastSetValueInBufferForFloat<double>(beginPointer, endPointer, val, littleEndian);
929 break;
930 case DataViewType::BIGINT64:
931 FastSetValueInBufferForBigInt<int64_t>(thread, beginPointer, endPointer, val, littleEndian);
932 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
933 break;
934 case DataViewType::BIGUINT64:
935 FastSetValueInBufferForBigInt<uint64_t>(thread, beginPointer, endPointer, val, littleEndian);
936 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
937 break;
938 default:
939 LOG_ECMA(FATAL) << "this branch is unreachable";
940 UNREACHABLE();
941 }
942 return JSTaggedValue::Undefined();
943 }
944 } // namespace panda::ecmascript::builtins
945