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