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