• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/builtins/builtins_arraybuffer.h"
17 
18 #include <typeinfo>
19 
20 #include "ecmascript/base/builtins_base.h"
21 #include "ecmascript/base/number_helper.h"
22 #include "ecmascript/ecma_macros.h"
23 #include "ecmascript/ecma_vm.h"
24 #include "ecmascript/global_env.h"
25 #include "ecmascript/internal_call_params.h"
26 #include "ecmascript/js_arraybuffer.h"
27 #include "ecmascript/js_object-inl.h"
28 #include "ecmascript/js_tagged_number.h"
29 #include "ecmascript/js_tagged_value-inl.h"
30 #include "ecmascript/js_tagged_value.h"
31 #include "ecmascript/object_factory.h"
32 #include "securec.h"
33 
34 namespace panda::ecmascript::builtins {
35 // 24.1.2.1 ArrayBuffer(length)
ArrayBufferConstructor(EcmaRuntimeCallInfo * argv)36 JSTaggedValue BuiltinsArrayBuffer::ArrayBufferConstructor(EcmaRuntimeCallInfo *argv)
37 {
38     ASSERT(argv);
39     BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Constructor);
40     JSThread *thread = argv->GetThread();
41     [[maybe_unused]] EcmaHandleScope handleScope(thread);
42     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
43     // 1. If NewTarget is undefined, throw a TypeError exception.
44     if (newTarget->IsUndefined()) {
45         THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception());
46     }
47     JSHandle<JSTaggedValue> lengthHandle = GetCallArg(argv, 0);
48     JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle);
49     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
50     double length = lenNum.GetNumber();
51     return AllocateArrayBuffer(thread, newTarget, length);
52 }
53 
54 // 24.1.3.1 ArrayBuffer.isView(arg)
IsView(EcmaRuntimeCallInfo * argv)55 JSTaggedValue BuiltinsArrayBuffer::IsView(EcmaRuntimeCallInfo *argv)
56 {
57     ASSERT(argv);
58     [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
59     JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
60     // 1. If Type(arg) is not Object, return false.
61     if (!arg->IsECMAObject()) {
62         return BuiltinsArrayBuffer::GetTaggedBoolean(false);
63     }
64     // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
65     if (arg->IsDataView() || arg->IsTypedArray()) {
66         return BuiltinsArrayBuffer::GetTaggedBoolean(true);
67     }
68     // 3. Return false.
69     return BuiltinsArrayBuffer::GetTaggedBoolean(false);
70 }
71 
72 // 24.1.3.3 get ArrayBuffer [ @@species ]
Species(EcmaRuntimeCallInfo * argv)73 JSTaggedValue BuiltinsArrayBuffer::Species(EcmaRuntimeCallInfo *argv)
74 {
75     ASSERT(argv);
76     return GetThis(argv).GetTaggedValue();
77 }
78 
79 // 24.1.4.1 get ArrayBuffer.prototype.byteLength
GetByteLength(EcmaRuntimeCallInfo * argv)80 JSTaggedValue BuiltinsArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv)
81 {
82     ASSERT(argv);
83     [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
84     JSThread *thread = argv->GetThread();
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     BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Slice);
111     JSThread *thread = argv->GetThread();
112     [[maybe_unused]] EcmaHandleScope handleScope(thread);
113     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
114     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
115     // 1. Let O be the this value.
116     JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
117     // 2. If Type(O) is not Object, throw a TypeError exception.
118     if (!thisHandle->IsObject()) {
119         THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
120     }
121     JSHandle<JSArrayBuffer> arrBuf(thisHandle);
122     // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
123     if (!thisHandle->IsArrayBuffer()) {
124         THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
125     }
126     // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
127     if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
128         THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception());
129     }
130     // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot.
131     int32_t len = arrBuf->GetArrayBufferByteLength();
132     JSHandle<JSTaggedValue> startHandle = GetCallArg(argv, 0);
133     // 6. Let relativeStart be ToInteger(start).
134     JSTaggedNumber relativeStart = JSTaggedValue::ToInteger(thread, startHandle);
135     // 7. ReturnIfAbrupt(relativeStart).
136     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
137     int32_t start = base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber());
138     int32_t end;
139     int32_t first;
140     int32_t last;
141     // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else let first be min(relativeStart, len).
142     if (start < 0) {
143         first = std::max((len + start), 0);
144     } else {
145         first = std::min(start, len);
146     }
147     // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end).
148     JSHandle<JSTaggedValue> endHandle = GetCallArg(argv, 1);
149     if (endHandle->IsUndefined()) {
150         end = len;
151     } else {
152         JSTaggedNumber relativeEnd = JSTaggedValue::ToInteger(thread, endHandle);
153         // 10. ReturnIfAbrupt(relativeEnd).
154         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
155         end = base::NumberHelper::DoubleInRangeInt32(relativeEnd.GetNumber());
156     }
157     // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len).
158     if (end < 0) {
159         last = std::max((len + end), 0);
160     } else {
161         last = std::min(end, len);
162     }
163     // 12. Let newLen be max(final-first,0).
164     uint32_t newLen = std::max((last - first), 0);
165     // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%).
166     JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
167     JSHandle<JSObject> objHandle(thisHandle);
168     JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
169     // 14. ReturnIfAbrupt(ctor).
170     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
171     // 15. Let new be Construct(ctor, «newLen»).
172     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
173     InternalCallParams *arguments = thread->GetInternalCallParams();
174     arguments->MakeArgv(JSTaggedValue(newLen));
175     JSTaggedValue taggedNewArrBuf = JSFunction::Construct(thread, constructor, 1, arguments->GetArgv(), undefined);
176     JSHandle<JSTaggedValue> newArrBuf(thread, taggedNewArrBuf);
177     // 16. ReturnIfAbrupt(new).
178     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
179     // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
180     if (!newArrBuf->IsArrayBuffer()) {
181         THROW_TYPE_ERROR_AND_RETURN(thread, "don't have bufferdata internal slot", JSTaggedValue::Exception());
182     }
183     // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception.
184     if (IsDetachedBuffer(newArrBuf.GetTaggedValue())) {
185         THROW_TYPE_ERROR_AND_RETURN(thread, "new arrayBuffer IsDetachedBuffer", JSTaggedValue::Exception());
186     }
187     // 19. If SameValue(new, O) is true, throw a TypeError exception.
188     if (JSTaggedValue::SameValue(newArrBuf.GetTaggedValue(), thisHandle.GetTaggedValue())) {
189         THROW_TYPE_ERROR_AND_RETURN(thread, "value of new arraybuffer and this is same", JSTaggedValue::Exception());
190     }
191     JSHandle<JSArrayBuffer> newJsArrBuf(newArrBuf);
192     // 20. If the value of new’s [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception.
193     uint32_t newArrBufLen = newJsArrBuf->GetArrayBufferByteLength();
194     if (newArrBufLen < newLen) {
195         THROW_TYPE_ERROR_AND_RETURN(thread, "new array buffer length smaller than newlen", JSTaggedValue::Exception());
196     }
197     // 21. NOTE: Side-effects of the above steps may have detached O.
198     // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception.
199     if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
200         THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception());
201     }
202     if (newLen > 0) {
203         // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot.
204         JSTaggedValue from = arrBuf->GetArrayBufferData();
205         // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot.
206         JSTaggedValue to = newJsArrBuf->GetArrayBufferData();
207         // 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen).
208         JSArrayBuffer::CopyDataBlockBytes(to, from, first, newLen);
209     }
210     // Return new.
211     return newArrBuf.GetTaggedValue();
212 }
213 
214 // 24.1.1.1 AllocateArrayBuffer(constructor, byteLength)
AllocateArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & newTarget,double byteLength)215 JSTaggedValue BuiltinsArrayBuffer::AllocateArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget,
216                                                        double byteLength)
217 {
218     BUILTINS_API_TRACE(thread, ArrayBuffer, AllocateArrayBuffer);
219     /**
220      * 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%",
221      * «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ).
222      * */
223     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
224     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
225     JSHandle<JSTaggedValue> arrBufFunc = env->GetArrayBufferFunction();
226     JSHandle<JSObject> obj;
227     if (!newTarget->IsBoundFunction()) {
228         obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(arrBufFunc), newTarget);
229         // 2. ReturnIfAbrupt
230         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
231     } else {
232         JSHandle<JSTaggedValue> prototypeKey = thread->GlobalConstants()->GetHandledPrototypeString();
233         JSHandle<JSTaggedValue> constructTag(newTarget);
234         JSHandle<JSTaggedValue> constructProto =
235             JSTaggedValue::GetProperty(thread, constructTag, prototypeKey).GetValue();
236         obj = JSObject::ObjectCreate(thread, JSHandle<JSObject>(constructProto));
237         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
238     }
239     // 3. Assert: byteLength is a positive integer.
240     ASSERT(JSTaggedValue(byteLength).IsInteger());
241     ASSERT(byteLength >= 0);
242     // 4. Let block be CreateByteDataBlock(byteLength).
243     if (byteLength > INT_MAX) {
244         THROW_RANGE_ERROR_AND_RETURN(thread, "Out of range", JSTaggedValue::Exception());
245     }
246     JSHandle<JSArrayBuffer> arrayBuffer(obj);
247     // 6. Set obj’s [[ArrayBufferData]] internal slot to block.
248     factory->NewJSArrayBufferData(arrayBuffer, byteLength);
249     // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength.
250     arrayBuffer->SetArrayBufferByteLength(static_cast<uint32_t>(byteLength));
251     // 8. Return obj.
252     return arrayBuffer.GetTaggedValue();
253 }
254 
255 // 24.1.1.2 IsDetachedBuffer()
IsDetachedBuffer(JSTaggedValue arrayBuffer)256 bool BuiltinsArrayBuffer::IsDetachedBuffer(JSTaggedValue arrayBuffer)
257 {
258     // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
259     ASSERT(arrayBuffer.IsArrayBuffer());
260     JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject());
261     JSTaggedValue dataSlot = buffer->GetArrayBufferData();
262     // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true.
263     // 3. Return false.
264     return dataSlot == JSTaggedValue::Null();
265 }
266 
267 // 24.1.1.4
CloneArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & srcBuffer,uint32_t srcByteOffset,JSHandle<JSTaggedValue> constructor)268 JSTaggedValue BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &srcBuffer,
269                                                     uint32_t srcByteOffset, JSHandle<JSTaggedValue> constructor)
270 {
271     BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer);
272     // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
273     ASSERT(srcBuffer->IsArrayBuffer());
274     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
275     // 2. If cloneConstructor is not present
276     if (constructor->IsUndefined()) {
277         // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%).
278         JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
279         JSHandle<JSObject> objHandle(srcBuffer);
280         constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
281         // b. ReturnIfAbrupt(cloneConstructor).
282         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
283         // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
284         if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
285             THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
286         } else {
287             ASSERT(constructor->IsConstructor());
288         }
289     }
290     // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot.
291     JSHandle<JSArrayBuffer> arrBuf(srcBuffer);
292     uint32_t srcLen = arrBuf->GetArrayBufferByteLength();
293     // 5. Assert: srcByteOffset ≤ srcLength.
294     ASSERT(srcByteOffset <= srcLen);
295     // 6. Let cloneLength be srcLength – srcByteOffset.
296     int32_t cloneLen = srcLen - srcByteOffset;
297     // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength).
298     JSTaggedValue taggedBuf = AllocateArrayBuffer(thread, constructor, cloneLen);
299     // 9. ReturnIfAbrupt(targetBuffer).
300     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
301     // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
302     if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
303         THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
304     }
305     // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot.
306     JSHandle<JSArrayBuffer> newArrBuf(thread, taggedBuf);
307     // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength).
308     // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot.
309     JSTaggedValue srcBlock = arrBuf->GetArrayBufferData();
310     JSTaggedValue targetBlock = newArrBuf->GetArrayBufferData();
311     if (cloneLen > 0) {
312         JSArrayBuffer::CopyDataBlockBytes(targetBlock, srcBlock, srcByteOffset, cloneLen);
313     }
314     return taggedBuf;
315 }
316 
317 // 24.1.1.5
318 // NOLINTNEXTLINE(readability-function-size)
GetValueFromBuffer(JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,bool littleEndian)319 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSTaggedValue arrBuf, uint32_t byteIndex, DataViewType type,
320                                                       bool littleEndian)
321 {
322     JSArrayBuffer *jsArrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
323     JSTaggedValue data = jsArrayBuffer->GetArrayBufferData();
324     void *pointer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer();
325     auto *block = reinterpret_cast<uint8_t *>(pointer);
326     switch (type) {
327         case DataViewType::UINT8:
328         case DataViewType::UINT8_CLAMPED: {
329             uint8_t res = block[byteIndex];  // NOLINT
330             return GetTaggedInt(res);
331         }
332         case DataViewType::INT8: {
333             uint8_t res = block[byteIndex];  // NOLINT
334             auto int8Res = static_cast<int8_t>(res);
335             return GetTaggedInt(int8Res);
336         }
337         case DataViewType::UINT16:
338             return GetValueFromBufferForInteger<uint16_t, NumberSize::UINT16>(block, byteIndex, littleEndian);
339         case DataViewType::INT16:
340             return GetValueFromBufferForInteger<int16_t, NumberSize::INT16>(block, byteIndex, littleEndian);
341         case DataViewType::UINT32:
342             return GetValueFromBufferForInteger<uint32_t, NumberSize::UINT32>(block, byteIndex, littleEndian);
343         case DataViewType::INT32:
344             return GetValueFromBufferForInteger<int32_t, NumberSize::INT32>(block, byteIndex, littleEndian);
345         case DataViewType::FLOAT32:
346             return GetValueFromBufferForFloat<float, UnionType32, NumberSize::FLOAT32>(block, byteIndex, littleEndian);
347         case DataViewType::FLOAT64:
348             return GetValueFromBufferForFloat<double, UnionType64, NumberSize::FLOAT64>(block, byteIndex, littleEndian);
349         default:
350             break;
351     }
352 
353     UNREACHABLE();
354 }
355 
356 // 24.1.1.6
SetValueInBuffer(JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,JSTaggedNumber value,bool littleEndian)357 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSTaggedValue arrBuf, uint32_t byteIndex, DataViewType type,
358                                                     JSTaggedNumber value, bool littleEndian)
359 {
360     JSArrayBuffer *jsArrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
361     JSTaggedValue data = jsArrayBuffer->GetArrayBufferData();
362     void *pointer = JSNativePointer::Cast(data.GetTaggedObject())->GetExternalPointer();
363     auto *block = reinterpret_cast<uint8_t *>(pointer);
364     double val = value.GetNumber();
365     switch (type) {
366         case DataViewType::UINT8:
367             SetValueInBufferForByte<uint8_t>(val, block, byteIndex);
368             break;
369         case DataViewType::UINT8_CLAMPED:
370             SetValueInBufferForUint8Clamped(val, block, byteIndex);
371             break;
372         case DataViewType::INT8:
373             SetValueInBufferForByte<int8_t>(val, block, byteIndex);
374             break;
375         case DataViewType::UINT16:
376             SetValueInBufferForInteger<uint16_t>(val, block, byteIndex, littleEndian);
377             break;
378         case DataViewType::INT16:
379             SetValueInBufferForInteger<int16_t>(val, block, byteIndex, littleEndian);
380             break;
381         case DataViewType::UINT32:
382             SetValueInBufferForInteger<uint32_t>(val, block, byteIndex, littleEndian);
383             break;
384         case DataViewType::INT32:
385             SetValueInBufferForInteger<int32_t>(val, block, byteIndex, littleEndian);
386             break;
387         case DataViewType::FLOAT32:
388             SetValueInBufferForFloat<float>(val, block, byteIndex, littleEndian);
389             break;
390         case DataViewType::FLOAT64:
391             SetValueInBufferForFloat<double>(val, block, byteIndex, littleEndian);
392             break;
393         default:
394             UNREACHABLE();
395     }
396     return JSTaggedValue::Undefined();
397 }
398 
399 template<typename T>
SetTypeData(uint8_t * block,T value,uint32_t index)400 void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, uint32_t index)
401 {
402     uint32_t sizeCount = sizeof(T);
403     auto *res = reinterpret_cast<uint8_t *>(&value);
404     for (uint32_t i = 0; i < sizeCount; i++) {
405         *(block + index + i) = *(res + i);  // NOLINT
406     }
407 }
408 
409 template <typename T>
LittleEndianToBigEndian(T liValue)410 T BuiltinsArrayBuffer::LittleEndianToBigEndian(T liValue)
411 {
412     uint8_t sizeCount = sizeof(T);
413     T biValue;
414     switch (sizeCount) {
415         case NumberSize::UINT16:
416             biValue = ((liValue & 0x00FF) << BITS_EIGHT)     // NOLINT
417                       | ((liValue & 0xFF00) >> BITS_EIGHT);  // NOLINT
418             break;
419         case NumberSize::UINT32:
420             biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR)     // NOLINT
421                       | ((liValue & 0x0000FF00) << BITS_EIGHT)         // NOLINT
422                       | ((liValue & 0x00FF0000) >> BITS_EIGHT)         // NOLINT
423                       | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR);  // NOLINT
424             break;
425         default:
426             UNREACHABLE();
427             break;
428     }
429     return biValue;
430 }
431 
LittleEndianToBigEndianUint64(uint64_t liValue)432 uint64_t BuiltinsArrayBuffer::LittleEndianToBigEndianUint64(uint64_t liValue)
433 {
434     return ((liValue & 0x00000000000000FF) << BITS_FIFTY_SIX)      // NOLINT
435            | ((liValue & 0x000000000000FF00) << BITS_FORTY)        // NOLINT
436            | ((liValue & 0x0000000000FF0000) << BITS_TWENTY_FOUR)  // NOLINT
437            | ((liValue & 0x00000000FF000000) << BITS_EIGHT)        // NOLINT
438            | ((liValue & 0x000000FF00000000) >> BITS_EIGHT)        // NOLINT
439            | ((liValue & 0x0000FF0000000000) >> BITS_TWENTY_FOUR)  // NOLINT
440            | ((liValue & 0x00FF000000000000) >> BITS_FORTY)        // NOLINT
441            | ((liValue & 0xFF00000000000000) >> BITS_FIFTY_SIX);   // NOLINT
442 }
443 
444 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForInteger(uint8_t * block,uint32_t byteIndex,bool littleEndian)445 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, uint32_t byteIndex, bool littleEndian)
446 {
447     static_assert(std::is_integral_v<T>, "T must be integral");
448     static_assert(sizeof(T) == size, "Invalid number size");
449     static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
450 
451     ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64);
452     T res = *reinterpret_cast<T *>(block + byteIndex);
453     if (!littleEndian) {
454         res = LittleEndianToBigEndian(res);
455     }
456 
457     // uint32_t maybe overflow with TaggedInt
458     // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
459     if constexpr (std::is_same_v<T, uint32_t>) {
460         // NOLINTNEXTLINE(clang-diagnostic-sign-compare)
461         if (res > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
462             return GetTaggedDouble(static_cast<double>(res));
463         }
464     }
465     return GetTaggedInt(res);
466 }
467 
468 template<typename T, typename UnionType, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForFloat(uint8_t * block,uint32_t byteIndex,bool littleEndian)469 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian)
470 {
471     static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "T must be correct type");
472     static_assert(sizeof(T) == size, "Invalid number size");
473 
474     UnionType unionValue = {0};
475     // NOLINTNEXTLINE(readability-braces-around-statements)
476     if constexpr (std::is_same_v<T, float>) {
477         unionValue.uValue = *reinterpret_cast<uint32_t *>(block + byteIndex);
478         if (std::isnan(unionValue.value)) {
479             return GetTaggedDouble(unionValue.value);
480         }
481         if (!littleEndian) {
482             uint32_t res = LittleEndianToBigEndian(unionValue.uValue);
483             return GetTaggedDouble(bit_cast<T>(res));
484         }
485     } else if constexpr (std::is_same_v<T, double>) {  // NOLINTNEXTLINE(readability-braces-around-statements)
486         unionValue.uValue = *reinterpret_cast<uint64_t *>(block + byteIndex);
487         if (std::isnan(unionValue.value) && !JSTaggedValue::IsImpureNaN(unionValue.value)) {
488             return GetTaggedDouble(unionValue.value);
489         }
490         if (!littleEndian) {
491             uint64_t res = LittleEndianToBigEndianUint64(unionValue.uValue);
492             return GetTaggedDouble(bit_cast<T>(res));
493         }
494     }
495 
496     return GetTaggedDouble(unionValue.value);
497 }
498 
499 template<typename T>
SetValueInBufferForByte(double val,uint8_t * block,uint32_t byteIndex)500 void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex)
501 {
502     static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>, "T must be int8/uint8");
503     T res;
504     if (std::isnan(val) || std::isinf(val)) {
505         res = 0;
506         SetTypeData(block, res, byteIndex);
507         return;
508     }
509     auto int64Val = static_cast<int64_t>(val);
510     auto *resArr = reinterpret_cast<T *>(&int64Val);
511     res = *resArr;
512     SetTypeData(block, res, byteIndex);
513 }
514 
SetValueInBufferForUint8Clamped(double val,uint8_t * block,uint32_t byteIndex)515 void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, uint32_t byteIndex)
516 {
517     uint8_t res;
518     if (std::isnan(val) || val <= 0) {
519         res = 0;
520         SetTypeData(block, res, byteIndex);
521         return;
522     }
523     val = val >= UINT8_MAX ? UINT8_MAX : val;
524     constexpr double HALF = 0.5;
525     val = val == HALF ? 0 : std::round(val);
526     res = static_cast<int64_t>(val);
527     SetTypeData(block, res, byteIndex);
528 }
529 
530 template<typename T>
SetValueInBufferForInteger(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)531 void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
532 {
533     static_assert(std::is_integral_v<T>, "T must be integral");
534     static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
535     T res;
536     if (std::isnan(val) || std::isinf(val)) {
537         res = 0;
538         SetTypeData(block, res, byteIndex);
539         return;
540     }
541     auto int64Val = static_cast<int64_t>(val);
542     // NOLINTNEXTLINE(readability-braces-around-statements)
543     if constexpr (std::is_same_v<T, uint16_t>) {
544         auto *pTmp = reinterpret_cast<int16_t *>(&int64Val);
545         int16_t tmp = *pTmp;
546         res = static_cast<T>(tmp);
547     } else {  // NOLINTNEXTLINE(readability-braces-around-statements)
548         auto *pTmp = reinterpret_cast<T *>(&int64Val);
549         res = *pTmp;
550     }
551 
552     if (!littleEndian) {
553         res = LittleEndianToBigEndian<T>(res);
554     }
555     SetTypeData(block, res, byteIndex);
556 }
557 
558 template<typename T>
SetValueInBufferForFloat(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)559 void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
560 {
561     static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "T must be float type");
562     auto data = static_cast<T>(val);
563     if (std::isnan(val)) {
564         SetTypeData(block, data, byteIndex);
565         return;
566     }
567     if (!littleEndian) {
568         if constexpr (std::is_same_v<T, float>) {
569             uint32_t res = bit_cast<uint32_t>(data);
570             data = bit_cast<T>(LittleEndianToBigEndian(res));
571         } else if constexpr (std::is_same_v<T, double>) {
572             uint64_t res = bit_cast<uint64_t>(data);
573             data = bit_cast<T>(LittleEndianToBigEndianUint64(res));
574         }
575     }
576     SetTypeData(block, data, byteIndex);
577 }
578 }  // namespace panda::ecmascript::builtins
579