• 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(thread, 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(thread, 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(thread, 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(thread, 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(thread, 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(thread, arrBuf.GetTaggedValue());
204         // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot.
205         void *toBuf = GetDataPointFromBuffer(thread, 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     uint32_t arrayByteLength = static_cast<uint32_t>(byteLength);
233     JSHandle<JSArrayBuffer> arrayBuffer(obj);
234     // 6. Set obj’s [[ArrayBufferData]] internal slot to block.
235     factory->NewJSArrayBufferData(arrayBuffer, arrayByteLength);
236     // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength.
237     arrayBuffer->SetArrayBufferByteLength(arrayByteLength);
238     // 8. Return obj.
239     return arrayBuffer.GetTaggedValue();
240 }
241 
242 // 24.1.1.2 IsDetachedBuffer()
IsDetachedBuffer(JSThread * thread,const JSHandle<JSTypedArray> & arrayBuffer)243 void BuiltinsArrayBuffer::IsDetachedBuffer(JSThread *thread, const JSHandle<JSTypedArray> &arrayBuffer)
244 {
245     JSTaggedValue detachedBuffer = arrayBuffer->GetViewedArrayBufferOrByteArray(thread);
246     if (IsDetachedBuffer(thread, detachedBuffer)) {
247         THROW_TYPE_ERROR(thread, "The ArrayBuffer of this value is detached buffer.");
248     }
249 }
250 
IsDetachedBuffer(JSThread * thread,JSTaggedValue arrayBuffer)251 bool BuiltinsArrayBuffer::IsDetachedBuffer(JSThread *thread, JSTaggedValue arrayBuffer)
252 {
253     if (arrayBuffer.IsByteArray()) {
254         return false;
255     }
256     // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
257     ASSERT(arrayBuffer.IsArrayBuffer() || arrayBuffer.IsSharedArrayBuffer());
258     JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject());
259     if (buffer == nullptr) {
260         LOG_ECMA(FATAL) << "BuiltinsArrayBuffer::IsDetachedBuffer:buffer is nullptr";
261     }
262     JSTaggedValue dataSlot = buffer->GetArrayBufferData(thread);
263     // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true.
264     // 3. Return false.
265     return dataSlot.IsNull();
266 }
267 
268 // 24.1.1.4
CloneArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & srcBuffer,uint32_t srcByteOffset,JSHandle<JSTaggedValue> constructor)269 JSTaggedValue BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &srcBuffer,
270                                                     uint32_t srcByteOffset, JSHandle<JSTaggedValue> constructor)
271 {
272     BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer);
273     // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
274     ASSERT(srcBuffer->IsArrayBuffer() || srcBuffer->IsSharedArrayBuffer() || srcBuffer->IsByteArray());
275     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
276     // 2. If cloneConstructor is not present
277     if (constructor->IsUndefined()) {
278         // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%).
279         JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
280         JSHandle<JSObject> objHandle(srcBuffer);
281         constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
282         // b. ReturnIfAbrupt(cloneConstructor).
283         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
284         // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
285         if (IsDetachedBuffer(thread, srcBuffer.GetTaggedValue())) {
286             THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
287         } else {
288             ASSERT(constructor->IsConstructor());
289         }
290     }
291     // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot.
292     uint32_t srcLen = 0;
293     int32_t cloneLen = 0;
294     if (srcBuffer->IsByteArray()) {
295         JSHandle<ByteArray> byteArrayBuf(srcBuffer);
296         srcLen = byteArrayBuf->GetArrayLength();
297         int32_t byteLen = static_cast<int32_t>(byteArrayBuf->GetByteLength());
298         // 5. Assert: srcByteOffset ≤ srcLength.
299         ASSERT(srcByteOffset <= srcLen);
300         // 6. Let cloneLength be (srcLength – srcByteOffset) * byteLen.
301         cloneLen = static_cast<int32_t>(srcLen - srcByteOffset) * byteLen;
302         srcByteOffset *= static_cast<uint32_t>(byteLen);
303     } else {
304         JSHandle<JSArrayBuffer> arrBuf(srcBuffer);
305         srcLen = arrBuf->GetArrayBufferByteLength();
306         // 5. Assert: srcByteOffset ≤ srcLength.
307         ASSERT(srcByteOffset <= srcLen);
308         // 6. Let cloneLength be srcLength – srcByteOffset.
309         cloneLen = static_cast<int32_t>(srcLen - srcByteOffset);
310     }
311     // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength).
312     JSTaggedValue taggedBuf = AllocateArrayBuffer(thread, constructor, cloneLen);
313     // 9. ReturnIfAbrupt(targetBuffer).
314     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
315     // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
316     if (IsDetachedBuffer(thread, srcBuffer.GetTaggedValue())) {
317         THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
318     }
319     // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot.
320     JSHandle<JSArrayBuffer> newArrBuf(thread, taggedBuf);
321     // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength).
322     // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot.
323     if (cloneLen > 0) {
324         void *fromBuf = GetDataPointFromBuffer(thread, srcBuffer.GetTaggedValue());
325         void *toBuf = GetDataPointFromBuffer(thread, taggedBuf);
326         JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, srcByteOffset, cloneLen);
327     }
328     return taggedBuf;
329 }
330 
331 // 24.1.1.5
332 // NOLINTNEXTLINE(readability-function-size)
GetValueFromBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,bool littleEndian)333 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
334                                                       DataViewType type, bool littleEndian)
335 {
336     void *pointer = GetDataPointFromBuffer(thread, arrBuf);
337     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
338     return GetValueFromBuffer(thread, byteIndex, block, type, littleEndian);
339 }
340 
GetValueFromBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,bool littleEndian)341 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block,
342                                                       DataViewType type, bool littleEndian)
343 {
344     ASSERT(block != nullptr);
345     switch (type) {
346         case DataViewType::UINT8:
347         case DataViewType::UINT8_CLAMPED: {
348             uint8_t res = block[byteIndex];  // NOLINT
349             return GetTaggedInt(res);
350         }
351         case DataViewType::INT8: {
352             uint8_t res = block[byteIndex];  // NOLINT
353             auto int8Res = static_cast<int8_t>(res);
354             return GetTaggedInt(int8Res);
355         }
356         case DataViewType::UINT16:
357             return GetValueFromBufferForInteger<uint16_t, NumberSize::UINT16>(block, byteIndex, littleEndian);
358         case DataViewType::INT16:
359             return GetValueFromBufferForInteger<int16_t, NumberSize::INT16>(block, byteIndex, littleEndian);
360         case DataViewType::UINT32:
361             return GetValueFromBufferForInteger<uint32_t, NumberSize::UINT32>(block, byteIndex, littleEndian);
362         case DataViewType::INT32:
363             return GetValueFromBufferForInteger<int32_t, NumberSize::INT32>(block, byteIndex, littleEndian);
364         case DataViewType::FLOAT32:
365             return GetValueFromBufferForFloat<float, UnionType32, NumberSize::FLOAT32>(block, byteIndex, littleEndian);
366         case DataViewType::FLOAT64:
367             return GetValueFromBufferForFloat<double, UnionType64, NumberSize::FLOAT64>(block, byteIndex, littleEndian);
368         case DataViewType::BIGINT64:
369             return GetValueFromBufferForBigInt<int64_t, NumberSize::BIGINT64>(thread, block, byteIndex, littleEndian);
370         case DataViewType::BIGUINT64:
371             return GetValueFromBufferForBigInt<uint64_t, NumberSize::BIGUINT64>(thread, block, byteIndex, littleEndian);
372         default:
373             break;
374     }
375     LOG_ECMA(FATAL) << "this branch is unreachable";
376     UNREACHABLE();
377 }
378 
379 // 24.1.1.6
SetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,const JSHandle<JSTaggedValue> & value,bool littleEndian)380 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
381                                                     DataViewType type, const JSHandle<JSTaggedValue> &value,
382                                                     bool littleEndian)
383 {
384     if (UNLIKELY(IsBigIntElementType(type))) {
385         JSHandle<JSTaggedValue> arrBufHandle(thread, arrBuf);
386         switch (type) {
387             case DataViewType::BIGINT64:
388                 SetValueInBufferForBigInt<int64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
389                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
390                 break;
391             case DataViewType::BIGUINT64:
392                 SetValueInBufferForBigInt<uint64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
393                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
394                 break;
395             default:
396                 LOG_ECMA(FATAL) << "this branch is unreachable";
397                 UNREACHABLE();
398         }
399         return JSTaggedValue::Undefined();
400     }
401     void *pointer = GetDataPointFromBuffer(thread, arrBuf);
402     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
403     JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, value.GetTaggedValue());
404     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
405     double val = numberValue.GetNumber();
406     return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
407 }
408 
409 // es12 25.1.2.7 IsBigIntElementType ( type )
IsBigIntElementType(DataViewType type)410 bool BuiltinsArrayBuffer::IsBigIntElementType(DataViewType type)
411 {
412     if (type == DataViewType::BIGINT64 || type == DataViewType::BIGUINT64) {
413         return true;
414     }
415     return false;
416 }
417 
418 // es12 25.1.2.6 IsUnclampedIntegerElementType ( type )
IsUnclampedIntegerElementType(DataViewType type)419 bool BuiltinsArrayBuffer::IsUnclampedIntegerElementType(DataViewType type)
420 {
421     switch (type) {
422         case DataViewType::INT8:
423         case DataViewType::INT16:
424         case DataViewType::INT32:
425         case DataViewType::UINT8:
426         case DataViewType::UINT16:
427         case DataViewType::UINT32:
428             return true;
429         default:
430             return false;
431     }
432 }
433 
434 template<typename T>
SetTypeData(uint8_t * block,T value,uint32_t index)435 void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, uint32_t index)
436 {
437     uint32_t sizeCount = sizeof(T);
438     uint8_t *res = reinterpret_cast<uint8_t *>(&value);
439     for (uint32_t i = 0; i < sizeCount; i++) {
440         *(block + index + i) = *(res + i);  // NOLINT
441     }
442 }
443 
444 template<typename T>
FastSetTypeData(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,T value)445 void BuiltinsArrayBuffer::FastSetTypeData(uint8_t *byteBeginOffset, uint8_t *byteEndOffset, T value)
446 {
447     ASSERT(byteBeginOffset != nullptr);
448     ASSERT(byteEndOffset != nullptr);
449     uint32_t sizeCount = sizeof(T);
450     if (sizeCount == 1) {
451         memset_s(byteBeginOffset, byteEndOffset-byteBeginOffset, value, byteEndOffset-byteBeginOffset);
452     } else {
453         uint8_t *resAddr = reinterpret_cast<uint8_t *>(&value);
454         for (uint8_t *addr = byteBeginOffset; addr < byteEndOffset; addr += sizeCount) {
455             for (uint32_t i = 0; i < sizeCount; ++i) {
456                 *(addr + i) = *(resAddr + i);
457             }
458         }
459     }
460 }
461 
462 template <typename T>
LittleEndianToBigEndian(T liValue)463 T BuiltinsArrayBuffer::LittleEndianToBigEndian(T liValue)
464 {
465     uint8_t sizeCount = sizeof(T);
466     T biValue;
467     switch (sizeCount) {
468         case NumberSize::UINT16:
469             biValue = ((liValue & 0x00FF) << BITS_EIGHT)     // NOLINT
470                       | ((liValue & 0xFF00) >> BITS_EIGHT);  // NOLINT
471             break;
472         case NumberSize::UINT32:
473             biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR)     // NOLINT
474                       | ((liValue & 0x0000FF00) << BITS_EIGHT)         // NOLINT
475                       | ((liValue & 0x00FF0000) >> BITS_EIGHT)         // NOLINT
476                       | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR);  // NOLINT
477             break;
478         default:
479             LOG_ECMA(FATAL) << "this branch is unreachable";
480             UNREACHABLE();
481             break;
482     }
483     return biValue;
484 }
485 template <typename T>
LittleEndianToBigEndian64Bit(T liValue)486 T BuiltinsArrayBuffer::LittleEndianToBigEndian64Bit(T liValue)
487 {
488     return ((liValue & 0x00000000000000FF) << BITS_FIFTY_SIX)      // NOLINT
489            | ((liValue & 0x000000000000FF00) << BITS_FORTY)        // NOLINT
490            | ((liValue & 0x0000000000FF0000) << BITS_TWENTY_FOUR)  // NOLINT
491            | ((liValue & 0x00000000FF000000) << BITS_EIGHT)        // NOLINT
492            | ((liValue & 0x000000FF00000000) >> BITS_EIGHT)        // NOLINT
493            | ((liValue & 0x0000FF0000000000) >> BITS_TWENTY_FOUR)  // NOLINT
494            | ((liValue & 0x00FF000000000000) >> BITS_FORTY)        // NOLINT
495            | ((liValue & 0xFF00000000000000) >> BITS_FIFTY_SIX);   // NOLINT
496 }
497 
498 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForInteger(uint8_t * block,uint32_t byteIndex,bool littleEndian)499 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, uint32_t byteIndex, bool littleEndian)
500 {
501     ASSERT(block != nullptr);
502     ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
503     ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
504     ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
505 
506     ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64);
507     T res = *reinterpret_cast<T *>(block + byteIndex);
508     if (!littleEndian) {
509         res = LittleEndianToBigEndian(res);
510     }
511 
512     // uint32_t maybe overflow with TaggedInt
513     // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
514     if constexpr (std::is_same_v<T, uint32_t>) {
515         // NOLINTNEXTLINE(clang-diagnostic-sign-compare)
516         if (res > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
517             return GetTaggedDouble(static_cast<double>(res));
518         }
519     }
520     return GetTaggedInt(res);
521 }
522 
523 template<typename T, typename UnionType, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForFloat(uint8_t * block,uint32_t byteIndex,bool littleEndian)524 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian)
525 {
526     ASSERT(block != nullptr);
527     ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be correct type");
528     ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
529 
530     UnionType unionValue = {0};
531     // NOLINTNEXTLINE(readability-braces-around-statements)
532     if constexpr (std::is_same_v<T, float>) {
533         unionValue.uValue = *reinterpret_cast<uint32_t *>(block + byteIndex);
534         uint32_t res = LittleEndianToBigEndian(unionValue.uValue);
535         return CommonConvert<T, uint32_t>(unionValue.value, res, littleEndian);
536     } else if constexpr (std::is_same_v<T, double>) {  // NOLINTNEXTLINE(readability-braces-around-statements)
537         unionValue.uValue = *reinterpret_cast<uint64_t *>(block + byteIndex);
538         uint64_t res = LittleEndianToBigEndian64Bit(unionValue.uValue);
539         return CommonConvert<T, uint64_t>(unionValue.value, res, littleEndian);
540     }
541 
542     return GetTaggedDouble(unionValue.value);
543 }
544 
545 template<typename T1, typename T2>
CommonConvert(T1 & value,T2 & res,bool littleEndian)546 JSTaggedValue BuiltinsArrayBuffer::CommonConvert(T1 &value, T2 &res, bool littleEndian)
547 {
548     if (!littleEndian) {
549         T1 d = base::bit_cast<T1>(res);
550         if (JSTaggedValue::IsImpureNaN(d)) {
551             return GetTaggedDouble(base::NAN_VALUE);
552         }
553         return GetTaggedDouble(d);
554     } else {
555         if (JSTaggedValue::IsImpureNaN(value)) {
556             return GetTaggedDouble(base::NAN_VALUE);
557         }
558     }
559     return GetTaggedDouble(value);
560 }
561 
562 
563 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForBigInt(JSThread * thread,uint8_t * block,uint32_t byteIndex,bool littleEndian)564 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block,
565                                                                uint32_t byteIndex, bool littleEndian)
566 {
567     ASSERT(block != nullptr);
568     ASSERT_PRINT((std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t>), "T must be uint64_t/int64_t");
569     auto pTmp = *reinterpret_cast<uint64_t *>(block + byteIndex);
570     if (!littleEndian) {
571         pTmp = LittleEndianToBigEndian64Bit(pTmp);
572     }
573     if constexpr (std::is_same_v<T, uint64_t>) {
574         return BigInt::Uint64ToBigInt(thread, pTmp).GetTaggedValue();
575     }
576     return BigInt::Int64ToBigInt(thread, pTmp).GetTaggedValue();
577 }
578 
579 
580 template<typename T>
SetValueInBufferForByte(double val,uint8_t * block,uint32_t byteIndex)581 void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex)
582 {
583     ASSERT_PRINT((std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>), "T must be int8/uint8");
584     T res;
585     if (std::isnan(val) || std::isinf(val)) {
586         res = 0;
587         SetTypeData(block, res, byteIndex);
588         return;
589     }
590     auto int32Val = base::NumberHelper::DoubleToInt(val, base::INT32_BITS);
591     auto *resArr = reinterpret_cast<T *>(&int32Val);
592     res = *resArr;
593     SetTypeData(block, res, byteIndex);
594 }
595 
SetValueInBufferForUint8Clamped(double val,uint8_t * block,uint32_t byteIndex)596 void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, uint32_t byteIndex)
597 {
598     uint8_t res;
599     if (std::isnan(val) || val <= 0) {
600         res = 0;
601     } else if (val > UINT8_MAX) {
602         res = UINT8_MAX;
603     } else {
604         // same as ToUint8Clamp
605         res = std::lrint(val);
606     }
607     SetTypeData(block, res, byteIndex);
608 }
609 
610 template<typename T>
SetValueInBufferForInteger(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)611 void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
612 {
613     ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
614     ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
615     T res;
616     if (std::isnan(val) || std::isinf(val)) {
617         res = 0;
618         SetTypeData(block, res, byteIndex);
619         return;
620     }
621     auto int32Val = base::NumberHelper::DoubleToInt(val, base::INT32_BITS);
622     // NOLINTNEXTLINE(readability-braces-around-statements)
623     if constexpr (std::is_same_v<T, uint16_t>) {
624         auto *pTmp = reinterpret_cast<int16_t *>(&int32Val);
625         int16_t tmp = *pTmp;
626         res = static_cast<T>(tmp);
627     } else {  // NOLINTNEXTLINE(readability-braces-around-statements)
628         auto *pTmp = reinterpret_cast<T *>(&int32Val);
629         res = *pTmp;
630     }
631 
632     if (!littleEndian) {
633         res = LittleEndianToBigEndian<T>(res);
634     }
635     SetTypeData(block, res, byteIndex);
636 }
637 
638 template<typename T>
SetValueInBufferForFloat(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)639 void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
640 {
641     ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be float type");
642     auto data = static_cast<T>(val);
643     if (!littleEndian) {
644         if constexpr (std::is_same_v<T, float>) {
645             uint32_t res = base::bit_cast<uint32_t>(data);
646             data = base::bit_cast<T>(LittleEndianToBigEndian(res));
647         } else if constexpr (std::is_same_v<T, double>) {
648             uint64_t res = base::bit_cast<uint64_t>(data);
649             data = base::bit_cast<T>(LittleEndianToBigEndian64Bit(res));
650         }
651     }
652     SetTypeData(block, data, byteIndex);
653 }
654 
655 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,const JSHandle<JSTaggedValue> & val,JSHandle<JSTaggedValue> & arrBuf,uint32_t byteIndex,bool littleEndian)656 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
657                                                     const JSHandle<JSTaggedValue> &val,
658                                                     JSHandle<JSTaggedValue> &arrBuf,
659                                                     uint32_t byteIndex, bool littleEndian)
660 {
661     ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
662     T value = 0;
663     bool lossless = true;
664     if constexpr(std::is_same_v<T, uint64_t>) {
665         BigInt::BigIntToUint64(thread, val, reinterpret_cast<uint64_t *>(&value), &lossless);
666     } else {
667         BigInt::BigIntToInt64(thread, val, reinterpret_cast<int64_t *>(&value), &lossless);
668     }
669     RETURN_IF_ABRUPT_COMPLETION(thread);
670     if (!littleEndian) {
671         value = LittleEndianToBigEndian64Bit<T>(value);
672     }
673     void *pointer = GetDataPointFromBuffer(thread, arrBuf.GetTaggedValue());
674     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
675     SetTypeData(block, value, byteIndex);
676 }
677 
678 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)679 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
680                                                     double val, uint8_t *block,
681                                                     uint32_t byteIndex, bool littleEndian)
682 {
683     ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
684     T value = 0;
685     bool lossless = true;
686 
687     JSHandle<JSTaggedValue> valHandle(thread, GetTaggedDouble(val));
688     if constexpr(std::is_same_v<T, uint64_t>) {
689         BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast<uint64_t *>(&value), &lossless);
690     } else {
691         BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast<int64_t *>(&value), &lossless);
692     }
693     RETURN_IF_ABRUPT_COMPLETION(thread);
694     if (!littleEndian) {
695         value = LittleEndianToBigEndian64Bit<T>(value);
696     }
697     SetTypeData(block, value, byteIndex);
698 }
699 
FastSetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,double val,bool littleEndian)700 JSTaggedValue BuiltinsArrayBuffer::FastSetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
701                                                         DataViewType type, double val, bool littleEndian)
702 {
703     // origin type maybe native JSType or shared JSType.
704     if (arrBuf.IsSendableArrayBuffer() && BuiltinsSendableArrayBuffer::IsDetachedBuffer(thread, arrBuf)) {
705         return JSTaggedValue::Undefined();
706     }
707     if (!arrBuf.IsSendableArrayBuffer() && BuiltinsArrayBuffer::IsDetachedBuffer(thread, arrBuf)) {
708         return JSTaggedValue::Undefined();
709     }
710     void *pointer = GetDataPointFromBuffer(thread, arrBuf);
711     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
712     return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
713 }
714 
SetValueInBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,double val,bool littleEndian)715 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread* thread, uint32_t byteIndex, uint8_t *block,
716                                                     DataViewType type, double val, bool littleEndian)
717 {
718     switch (type) {
719         case DataViewType::UINT8:
720             SetValueInBufferForByte<uint8_t>(val, block, byteIndex);
721             break;
722         case DataViewType::UINT8_CLAMPED:
723             SetValueInBufferForUint8Clamped(val, block, byteIndex);
724             break;
725         case DataViewType::INT8:
726             SetValueInBufferForByte<int8_t>(val, block, byteIndex);
727             break;
728         case DataViewType::UINT16:
729             SetValueInBufferForInteger<uint16_t>(val, block, byteIndex, littleEndian);
730             break;
731         case DataViewType::INT16:
732             SetValueInBufferForInteger<int16_t>(val, block, byteIndex, littleEndian);
733             break;
734         case DataViewType::UINT32:
735             SetValueInBufferForInteger<uint32_t>(val, block, byteIndex, littleEndian);
736             break;
737         case DataViewType::INT32:
738             SetValueInBufferForInteger<int32_t>(val, block, byteIndex, littleEndian);
739             break;
740         case DataViewType::FLOAT32:
741             SetValueInBufferForFloat<float>(val, block, byteIndex, littleEndian);
742             break;
743         case DataViewType::FLOAT64:
744             SetValueInBufferForFloat<double>(val, block, byteIndex, littleEndian);
745             break;
746         case DataViewType::BIGINT64:
747             SetValueInBufferForBigInt<int64_t>(thread, val, block, byteIndex, littleEndian);
748             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
749             break;
750         case DataViewType::BIGUINT64:
751             SetValueInBufferForBigInt<uint64_t>(thread, val, block, byteIndex, littleEndian);
752             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
753             break;
754         default:
755             LOG_ECMA(FATAL) << "this branch is unreachable";
756             UNREACHABLE();
757     }
758     return JSTaggedValue::Undefined();
759 }
760 
GetDataPointFromBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteOffset)761 void *BuiltinsArrayBuffer::GetDataPointFromBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteOffset)
762 {
763     if (arrBuf.IsByteArray()) {
764         return reinterpret_cast<void *>(ToUintPtr(ByteArray::Cast(arrBuf.GetTaggedObject())->GetData()) + byteOffset);
765     }
766 
767     JSArrayBuffer *arrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
768     if (arrayBuffer == nullptr) {
769         LOG_ECMA(FATAL) << "BuiltinsArrayBuffer::GetDataPointFromBuffer:arrayBuffer is nullptr";
770         UNREACHABLE();
771     }
772     if (arrayBuffer->GetArrayBufferByteLength() == 0) {
773         LOG_ECMA(FATAL) << "BuiltinsArrayBuffer::GetDataPointFromBuffer:arrayBuffer length is 0";
774         UNREACHABLE();
775     }
776 
777     JSTaggedValue data = arrayBuffer->GetArrayBufferData(thread);
778     return reinterpret_cast<void *>(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject())
779                                     ->GetExternalPointer()) + byteOffset);
780 }
781 
TypedArrayToList(JSThread * thread,JSHandle<JSTypedArray> & items)782 JSTaggedValue BuiltinsArrayBuffer::TypedArrayToList(JSThread *thread, JSHandle<JSTypedArray>& items)
783 {
784     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
785     JSHandle<JSTaggedValue> bufferHandle(thread, items->GetViewedArrayBufferOrByteArray(thread));
786     uint32_t arrayLen = items->GetArrayLength();
787     JSHandle<JSObject> newArrayHandle(thread, JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue());
788     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
789     JSHandle<TaggedArray> oldElements(thread, newArrayHandle->GetElements(thread));
790     JSHandle<TaggedArray> elements = (oldElements->GetLength() < arrayLen) ?
791         factory->ExtendArray(oldElements, arrayLen) : oldElements;
792     newArrayHandle->SetElements(thread, elements);
793     uint32_t offset = items->GetByteOffset();
794     uint32_t elementSize = TypedArrayHelper::GetElementSize(items);
795     DataViewType elementType = TypedArrayHelper::GetType(items);
796     uint32_t index = 0;
797     while (index < arrayLen) {
798         uint32_t byteIndex = index * elementSize + offset;
799         JSHandle<JSTaggedValue> result(thread, GetValueFromBuffer(thread, bufferHandle.GetTaggedValue(),
800                                        byteIndex, elementType, true));
801         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
802         ElementAccessor::Set(thread, newArrayHandle, index, result, true);
803         index++;
804     }
805     JSHandle<JSArray>(newArrayHandle)->SetArrayLength(thread, arrayLen);
806     return newArrayHandle.GetTaggedValue();
807 }
808 
809 template<typename T>
FastSetValueInBufferForByte(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val)810 void BuiltinsArrayBuffer::FastSetValueInBufferForByte(uint8_t *byteBeginOffset,
811                                                       uint8_t *byteEndOffset,
812                                                       double val)
813 {
814     ASSERT_PRINT(sizeof(T) == 1, "sizeof(T) must be one");
815     ASSERT_PRINT((std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>), "T must be int8/uint8");
816     T res;
817     if (std::isnan(val) || std::isinf(val)) {
818         res = 0;
819     } else {
820         auto int32Val = base::NumberHelper::DoubleToInt(val, base::INT32_BITS);
821         auto *resArr = reinterpret_cast<T *>(&int32Val);
822         res = *resArr;
823     }
824     FastSetTypeData(byteBeginOffset, byteEndOffset, res);
825 }
826 
FastSetValueInBufferForUint8Clamped(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val)827 void BuiltinsArrayBuffer::FastSetValueInBufferForUint8Clamped(uint8_t *byteBeginOffset,
828                                                               uint8_t *byteEndOffset,
829                                                               double val)
830 {
831     uint8_t res;
832     if (std::isnan(val) || val <= 0) {
833         res = 0;
834     } else {
835         val = val >= UINT8_MAX ? UINT8_MAX : val;
836         constexpr double HALF = 0.5;
837         val = val == HALF ? 0 : std::round(val);
838         res = static_cast<uint64_t>(val);
839     }
840     FastSetTypeData(byteBeginOffset, byteEndOffset, res);
841 }
842 
843 template<typename T>
FastSetValueInBufferForInteger(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val,bool littleEndian)844 void BuiltinsArrayBuffer::FastSetValueInBufferForInteger(uint8_t *byteBeginOffset,
845                                                          uint8_t *byteEndOffset,
846                                                          double val, bool littleEndian)
847 {
848     ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
849     ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
850     T res;
851     if (std::isnan(val) || std::isinf(val)) {
852         res = 0;
853     } else {
854         auto int32Val = base::NumberHelper::DoubleToInt(val, base::INT32_BITS);
855         // NOLINTNEXTLINE(readability-braces-around-statements)
856         if constexpr (std::is_same_v<T, uint16_t>) {
857             auto *pTmp = reinterpret_cast<int16_t *>(&int32Val);
858             int16_t tmp = *pTmp;
859             res = static_cast<T>(tmp);
860         } else {  // NOLINTNEXTLINE(readability-braces-around-statements)
861             auto *pTmp = reinterpret_cast<T *>(&int32Val);
862             res = *pTmp;
863         }
864         if (!littleEndian) {
865             res = LittleEndianToBigEndian<T>(res);
866         }
867     }
868     FastSetTypeData(byteBeginOffset, byteEndOffset, res);
869 }
870 
871 template<typename T>
FastSetValueInBufferForFloat(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val,bool littleEndian)872 void BuiltinsArrayBuffer::FastSetValueInBufferForFloat(uint8_t *byteBeginOffset,
873                                                        uint8_t *byteEndOffset,
874                                                        double val, bool littleEndian)
875 {
876     ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be float type");
877     auto data = static_cast<T>(val);
878     if (!std::isnan(val)) {
879         if (!littleEndian) {
880             if constexpr (std::is_same_v<T, float>) {
881                 uint32_t res = base::bit_cast<uint32_t>(data);
882                 data = base::bit_cast<T>(LittleEndianToBigEndian(res));
883             } else if constexpr (std::is_same_v<T, double>) {
884                 uint64_t res = base::bit_cast<uint64_t>(data);
885                 data = base::bit_cast<T>(LittleEndianToBigEndian64Bit(res));
886             }
887         }
888     }
889     FastSetTypeData(byteBeginOffset, byteEndOffset, data);
890 }
891 
892 template<typename T>
FastSetValueInBufferForBigInt(JSThread * thread,uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val,bool littleEndian)893 void BuiltinsArrayBuffer::FastSetValueInBufferForBigInt(JSThread *thread,
894                                                         uint8_t *byteBeginOffset,
895                                                         uint8_t *byteEndOffset,
896                                                         double val, bool littleEndian)
897 {
898     ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
899     T value = 0;
900     bool lossless = true;
901 
902     JSHandle<JSTaggedValue> valHandle(thread, GetTaggedDouble(val));
903     if constexpr(std::is_same_v<T, uint64_t>) {
904         BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast<uint64_t *>(&value), &lossless);
905     } else {
906         BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast<int64_t *>(&value), &lossless);
907     }
908     RETURN_IF_ABRUPT_COMPLETION(thread);
909     if (!littleEndian) {
910         value = LittleEndianToBigEndian64Bit<T>(value);
911     }
912     FastSetTypeData(byteBeginOffset, byteEndOffset, value);
913 }
914 
TryFastSetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteBeginOffset,uint32_t byteEndOffset,DataViewType type,double val,bool littleEndian)915 JSTaggedValue BuiltinsArrayBuffer::TryFastSetValueInBuffer([[maybe_unused]] JSThread *thread, JSTaggedValue arrBuf,
916                                                            uint32_t byteBeginOffset, uint32_t byteEndOffset,
917                                                            DataViewType type, double val, bool littleEndian)
918 {
919     uint8_t *beginPointer = reinterpret_cast<uint8_t *>(GetDataPointFromBuffer(thread, arrBuf, byteBeginOffset));
920     uint8_t *endPointer = reinterpret_cast<uint8_t *>(GetDataPointFromBuffer(thread, arrBuf, byteEndOffset));
921     switch (type) {
922         case DataViewType::UINT8:
923             FastSetValueInBufferForByte<uint8_t>(beginPointer, endPointer, val);
924             break;
925         case DataViewType::UINT8_CLAMPED:
926             FastSetValueInBufferForUint8Clamped(beginPointer, endPointer, val);
927             break;
928         case DataViewType::INT8:
929             FastSetValueInBufferForByte<int8_t>(beginPointer, endPointer, val);
930             break;
931         case DataViewType::UINT16:
932             FastSetValueInBufferForInteger<uint16_t>(beginPointer, endPointer, val, littleEndian);
933             break;
934         case DataViewType::INT16:
935             FastSetValueInBufferForInteger<int16_t>(beginPointer, endPointer, val, littleEndian);
936             break;
937         case DataViewType::UINT32:
938             FastSetValueInBufferForInteger<uint32_t>(beginPointer, endPointer, val, littleEndian);
939             break;
940         case DataViewType::INT32:
941             FastSetValueInBufferForInteger<int32_t>(beginPointer, endPointer, val, littleEndian);
942             break;
943         case DataViewType::FLOAT32:
944             FastSetValueInBufferForFloat<float>(beginPointer, endPointer, val, littleEndian);
945             break;
946         case DataViewType::FLOAT64:
947             FastSetValueInBufferForFloat<double>(beginPointer, endPointer, val, littleEndian);
948             break;
949         case DataViewType::BIGINT64:
950             FastSetValueInBufferForBigInt<int64_t>(thread, beginPointer, endPointer, val, littleEndian);
951             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
952             break;
953         case DataViewType::BIGUINT64:
954             FastSetValueInBufferForBigInt<uint64_t>(thread, beginPointer, endPointer, val, littleEndian);
955             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
956             break;
957         default:
958             LOG_ECMA(FATAL) << "this branch is unreachable";
959             UNREACHABLE();
960     }
961     return JSTaggedValue::Undefined();
962 }
963 }  // namespace panda::ecmascript::builtins
964