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