• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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/builtins/builtins_bigint.h"
23 #include "ecmascript/ecma_macros.h"
24 #include "ecmascript/ecma_vm.h"
25 #include "ecmascript/global_env.h"
26 #include "ecmascript/interpreter/interpreter.h"
27 #include "ecmascript/js_arraybuffer.h"
28 #include "ecmascript/js_object-inl.h"
29 #include "ecmascript/js_tagged_number.h"
30 #include "ecmascript/js_tagged_value-inl.h"
31 #include "ecmascript/js_tagged_value.h"
32 #include "ecmascript/object_factory.h"
33 #include "securec.h"
34 
35 namespace panda::ecmascript::builtins {
36 // 24.1.2.1 ArrayBuffer(length)
ArrayBufferConstructor(EcmaRuntimeCallInfo * argv)37 JSTaggedValue BuiltinsArrayBuffer::ArrayBufferConstructor(EcmaRuntimeCallInfo *argv)
38 {
39     ASSERT(argv);
40     BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Constructor);
41     JSThread *thread = argv->GetThread();
42     [[maybe_unused]] EcmaHandleScope handleScope(thread);
43     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
44     // 1. If NewTarget is undefined, throw a TypeError exception.
45     if (newTarget->IsUndefined()) {
46         THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception());
47     }
48     JSHandle<JSTaggedValue> lengthHandle = GetCallArg(argv, 0);
49     JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle);
50     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
51     uint64_t length = lenNum.GetNumber();
52     return AllocateArrayBuffer(thread, newTarget, length);
53 }
54 
55 // 24.1.3.1 ArrayBuffer.isView(arg)
IsView(EcmaRuntimeCallInfo * argv)56 JSTaggedValue BuiltinsArrayBuffer::IsView(EcmaRuntimeCallInfo *argv)
57 {
58     ASSERT(argv);
59     [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
60     JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
61     // 1. If Type(arg) is not Object, return false.
62     if (!arg->IsECMAObject()) {
63         return BuiltinsArrayBuffer::GetTaggedBoolean(false);
64     }
65     // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
66     if (arg->IsDataView() || arg->IsTypedArray()) {
67         return BuiltinsArrayBuffer::GetTaggedBoolean(true);
68     }
69     // 3. Return false.
70     return BuiltinsArrayBuffer::GetTaggedBoolean(false);
71 }
72 
73 // 24.1.3.3 get ArrayBuffer [ @@species ]
Species(EcmaRuntimeCallInfo * argv)74 JSTaggedValue BuiltinsArrayBuffer::Species(EcmaRuntimeCallInfo *argv)
75 {
76     ASSERT(argv);
77     return GetThis(argv).GetTaggedValue();
78 }
79 
80 // 24.1.4.1 get ArrayBuffer.prototype.byteLength
GetByteLength(EcmaRuntimeCallInfo * argv)81 JSTaggedValue BuiltinsArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv)
82 {
83     ASSERT(argv);
84     [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
85     JSThread *thread = argv->GetThread();
86     // 1. Let O be the this value.
87     JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
88     // 2. If Type(O) is not Object, throw a TypeError exception.
89     if (!thisHandle->IsECMAObject()) {
90         THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
91     }
92     // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
93     if (!thisHandle->IsArrayBuffer()) {
94         THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
95     }
96     // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
97     if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
98         THROW_TYPE_ERROR_AND_RETURN(thread, "IsDetachedBuffer", JSTaggedValue::Exception());
99     }
100     JSHandle<JSArrayBuffer> arrBuf(thisHandle);
101     // 5. Let length be the value of O’s [[ArrayBufferByteLength]] internal slot.
102     uint32_t length = arrBuf->GetArrayBufferByteLength();
103     // 6. Return length.
104     return JSTaggedValue(length);
105 }
106 
107 // 24.1.4.3 ArrayBuffer.prototype.slice(start, end)
Slice(EcmaRuntimeCallInfo * argv)108 JSTaggedValue BuiltinsArrayBuffer::Slice(EcmaRuntimeCallInfo *argv)
109 {
110     ASSERT(argv);
111     BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Slice);
112     JSThread *thread = argv->GetThread();
113     [[maybe_unused]] EcmaHandleScope handleScope(thread);
114     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
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->IsHeapObject()) {
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 = static_cast<int32_t>(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 = 0;
139     int32_t first = 0;
140     int32_t last = 0;
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 = thread->GlobalConstants()->GetHandledUndefined();
173     EcmaRuntimeCallInfo *info =
174         EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1);
175     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
176     info->SetCallArg(JSTaggedValue(newLen));
177     JSTaggedValue taggedNewArrBuf = JSFunction::Construct(info);
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(JSTaggedValue arrayBuffer)246 bool BuiltinsArrayBuffer::IsDetachedBuffer(JSTaggedValue arrayBuffer)
247 {
248     if (arrayBuffer.IsByteArray()) {
249         return false;
250     }
251     // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
252     ASSERT(arrayBuffer.IsArrayBuffer() || arrayBuffer.IsSharedArrayBuffer());
253     JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject());
254     JSTaggedValue dataSlot = buffer->GetArrayBufferData();
255     // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true.
256     // 3. Return false.
257     return dataSlot == JSTaggedValue::Null();
258 }
259 
260 // 24.1.1.4
CloneArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & srcBuffer,uint32_t srcByteOffset,JSHandle<JSTaggedValue> constructor)261 JSTaggedValue BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &srcBuffer,
262                                                     uint32_t srcByteOffset, JSHandle<JSTaggedValue> constructor)
263 {
264     BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer);
265     // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
266     ASSERT(srcBuffer->IsArrayBuffer() || srcBuffer->IsSharedArrayBuffer());
267     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
268     // 2. If cloneConstructor is not present
269     if (constructor->IsUndefined()) {
270         // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%).
271         JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
272         JSHandle<JSObject> objHandle(srcBuffer);
273         constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
274         // b. ReturnIfAbrupt(cloneConstructor).
275         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
276         // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
277         if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
278             THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
279         } else {
280             ASSERT(constructor->IsConstructor());
281         }
282     }
283     // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot.
284     JSHandle<JSArrayBuffer> arrBuf(srcBuffer);
285     uint32_t srcLen = arrBuf->GetArrayBufferByteLength();
286     // 5. Assert: srcByteOffset ≤ srcLength.
287     ASSERT(srcByteOffset <= srcLen);
288     // 6. Let cloneLength be srcLength – srcByteOffset.
289     int32_t cloneLen = static_cast<int32_t>(srcLen - srcByteOffset);
290     // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength).
291     JSTaggedValue taggedBuf = AllocateArrayBuffer(thread, constructor, cloneLen);
292     // 9. ReturnIfAbrupt(targetBuffer).
293     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
294     // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
295     if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
296         THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
297     }
298     // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot.
299     JSHandle<JSArrayBuffer> newArrBuf(thread, taggedBuf);
300     // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength).
301     // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot.
302     void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
303     void *toBuf = GetDataPointFromBuffer(taggedBuf);
304     if (cloneLen > 0) {
305         JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, srcByteOffset, cloneLen);
306     }
307     return taggedBuf;
308 }
309 
310 // 24.1.1.5
311 // NOLINTNEXTLINE(readability-function-size)
GetValueFromBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,bool littleEndian)312 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
313                                                       DataViewType type, bool littleEndian)
314 {
315     void *pointer = GetDataPointFromBuffer(arrBuf);
316     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
317     return GetValueFromBuffer(thread, byteIndex, block, type, littleEndian);
318 }
319 
GetValueFromBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,bool littleEndian)320 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block,
321                                                       DataViewType type, bool littleEndian)
322 {
323     switch (type) {
324         case DataViewType::UINT8:
325         case DataViewType::UINT8_CLAMPED: {
326             uint8_t res = block[byteIndex];  // NOLINT
327             return GetTaggedInt(res);
328         }
329         case DataViewType::INT8: {
330             uint8_t res = block[byteIndex];  // NOLINT
331             auto int8Res = static_cast<int8_t>(res);
332             return GetTaggedInt(int8Res);
333         }
334         case DataViewType::UINT16:
335             return GetValueFromBufferForInteger<uint16_t, NumberSize::UINT16>(block, byteIndex, littleEndian);
336         case DataViewType::INT16:
337             return GetValueFromBufferForInteger<int16_t, NumberSize::INT16>(block, byteIndex, littleEndian);
338         case DataViewType::UINT32:
339             return GetValueFromBufferForInteger<uint32_t, NumberSize::UINT32>(block, byteIndex, littleEndian);
340         case DataViewType::INT32:
341             return GetValueFromBufferForInteger<int32_t, NumberSize::INT32>(block, byteIndex, littleEndian);
342         case DataViewType::FLOAT32:
343             return GetValueFromBufferForFloat<float, UnionType32, NumberSize::FLOAT32>(block, byteIndex, littleEndian);
344         case DataViewType::FLOAT64:
345             return GetValueFromBufferForFloat<double, UnionType64, NumberSize::FLOAT64>(block, byteIndex, littleEndian);
346         case DataViewType::BIGINT64:
347             return GetValueFromBufferForBigInt<int64_t, NumberSize::BIGINT64>(thread, block, byteIndex, littleEndian);
348         case DataViewType::BIGUINT64:
349             return GetValueFromBufferForBigInt<uint64_t, NumberSize::BIGUINT64>(thread, block, byteIndex, littleEndian);
350         default:
351             break;
352     }
353 
354     UNREACHABLE();
355 }
356 
357 // 24.1.1.6
SetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,const JSHandle<JSTaggedValue> & value,bool littleEndian)358 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
359                                                     DataViewType type, const JSHandle<JSTaggedValue> &value,
360                                                     bool littleEndian)
361 {
362     if (UNLIKELY(IsBigIntElementType(type))) {
363         JSHandle<JSTaggedValue> arrBufHandle(thread, arrBuf);
364         switch (type) {
365             case DataViewType::BIGINT64:
366                 SetValueInBufferForBigInt<int64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
367                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
368                 break;
369             case DataViewType::BIGUINT64:
370                 SetValueInBufferForBigInt<uint64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
371                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
372                 break;
373             default:
374                 UNREACHABLE();
375         }
376         return JSTaggedValue::Undefined();
377     }
378     void *pointer = GetDataPointFromBuffer(arrBuf);
379     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
380     double val = value.GetTaggedValue().GetNumber();
381     return SetValueInBuffer(byteIndex, block, type, val, littleEndian);
382 }
383 
384 // es12 25.1.2.7 IsBigIntElementType ( type )
IsBigIntElementType(DataViewType type)385 bool BuiltinsArrayBuffer::IsBigIntElementType(DataViewType type)
386 {
387     if (type == DataViewType::BIGINT64 || type == DataViewType::BIGUINT64) {
388         return true;
389     }
390     return false;
391 }
392 
393 // es12 25.1.2.6 IsUnclampedIntegerElementType ( type )
IsUnclampedIntegerElementType(DataViewType type)394 bool BuiltinsArrayBuffer::IsUnclampedIntegerElementType(DataViewType type)
395 {
396     switch (type) {
397         case DataViewType::INT8:
398         case DataViewType::INT16:
399         case DataViewType::INT32:
400         case DataViewType::UINT8:
401         case DataViewType::UINT16:
402         case DataViewType::UINT32:
403             return true;
404         default:
405             return false;
406     }
407 }
408 
409 template<typename T>
SetTypeData(uint8_t * block,T value,uint32_t index)410 void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, uint32_t index)
411 {
412     uint32_t sizeCount = sizeof(T);
413     uint8_t *res = reinterpret_cast<uint8_t *>(&value);
414     for (uint32_t i = 0; i < sizeCount; i++) {
415         *(block + index + i) = *(res + i);  // NOLINT
416     }
417 }
418 
419 template <typename T>
LittleEndianToBigEndian(T liValue)420 T BuiltinsArrayBuffer::LittleEndianToBigEndian(T liValue)
421 {
422     uint8_t sizeCount = sizeof(T);
423     T biValue;
424     switch (sizeCount) {
425         case NumberSize::UINT16:
426             biValue = ((liValue & 0x00FF) << BITS_EIGHT)     // NOLINT
427                       | ((liValue & 0xFF00) >> BITS_EIGHT);  // NOLINT
428             break;
429         case NumberSize::UINT32:
430             biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR)     // NOLINT
431                       | ((liValue & 0x0000FF00) << BITS_EIGHT)         // NOLINT
432                       | ((liValue & 0x00FF0000) >> BITS_EIGHT)         // NOLINT
433                       | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR);  // NOLINT
434             break;
435         default:
436             UNREACHABLE();
437             break;
438     }
439     return biValue;
440 }
441 template <typename T>
LittleEndianToBigEndian64Bit(T liValue)442 T BuiltinsArrayBuffer::LittleEndianToBigEndian64Bit(T liValue)
443 {
444     return ((liValue & 0x00000000000000FF) << BITS_FIFTY_SIX)      // NOLINT
445            | ((liValue & 0x000000000000FF00) << BITS_FORTY)        // NOLINT
446            | ((liValue & 0x0000000000FF0000) << BITS_TWENTY_FOUR)  // NOLINT
447            | ((liValue & 0x00000000FF000000) << BITS_EIGHT)        // NOLINT
448            | ((liValue & 0x000000FF00000000) >> BITS_EIGHT)        // NOLINT
449            | ((liValue & 0x0000FF0000000000) >> BITS_TWENTY_FOUR)  // NOLINT
450            | ((liValue & 0x00FF000000000000) >> BITS_FORTY)        // NOLINT
451            | ((liValue & 0xFF00000000000000) >> BITS_FIFTY_SIX);   // NOLINT
452 }
453 
454 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForInteger(uint8_t * block,uint32_t byteIndex,bool littleEndian)455 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, uint32_t byteIndex, bool littleEndian)
456 {
457     static_assert(std::is_integral_v<T>, "T must be integral");
458     static_assert(sizeof(T) == size, "Invalid number size");
459     static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
460 
461     ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64);
462     T res = *reinterpret_cast<T *>(block + byteIndex);
463     if (!littleEndian) {
464         res = LittleEndianToBigEndian(res);
465     }
466 
467     // uint32_t maybe overflow with TaggedInt
468     // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
469     if constexpr (std::is_same_v<T, uint32_t>) {
470         // NOLINTNEXTLINE(clang-diagnostic-sign-compare)
471         if (res > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
472             return GetTaggedDouble(static_cast<double>(res));
473         }
474     }
475     return GetTaggedInt(res);
476 }
477 
478 template<typename T, typename UnionType, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForFloat(uint8_t * block,uint32_t byteIndex,bool littleEndian)479 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian)
480 {
481     static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "T must be correct type");
482     static_assert(sizeof(T) == size, "Invalid number size");
483 
484     UnionType unionValue = {0};
485     // NOLINTNEXTLINE(readability-braces-around-statements)
486     if constexpr (std::is_same_v<T, float>) {
487         unionValue.uValue = *reinterpret_cast<uint32_t *>(block + byteIndex);
488         if (std::isnan(unionValue.value)) {
489             return GetTaggedDouble(unionValue.value);
490         }
491         if (!littleEndian) {
492             uint32_t res = LittleEndianToBigEndian(unionValue.uValue);
493             return GetTaggedDouble(bit_cast<T>(res));
494         }
495     } else if constexpr (std::is_same_v<T, double>) {  // NOLINTNEXTLINE(readability-braces-around-statements)
496         unionValue.uValue = *reinterpret_cast<uint64_t *>(block + byteIndex);
497         if (std::isnan(unionValue.value) && !JSTaggedValue::IsImpureNaN(unionValue.value)) {
498             return GetTaggedDouble(unionValue.value);
499         }
500         if (!littleEndian) {
501             uint64_t res = LittleEndianToBigEndian64Bit(unionValue.uValue);
502             return GetTaggedDouble(bit_cast<T>(res));
503         }
504     }
505 
506     return GetTaggedDouble(unionValue.value);
507 }
508 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForBigInt(JSThread * thread,uint8_t * block,uint32_t byteIndex,bool littleEndian)509 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block,
510                                                                uint32_t byteIndex, bool littleEndian)
511 {
512     static_assert(std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t>, "T must be uint64_t/int64_t");
513     auto pTmp = *reinterpret_cast<uint64_t *>(block + byteIndex);
514     if (!littleEndian) {
515         pTmp = LittleEndianToBigEndian64Bit(pTmp);
516     }
517     if constexpr (std::is_same_v<T, uint64_t>) {
518         return BigInt::Uint64ToBigInt(thread, pTmp).GetTaggedValue();
519     }
520     return BigInt::Int64ToBigInt(thread, pTmp).GetTaggedValue();
521 }
522 
523 
524 template<typename T>
SetValueInBufferForByte(double val,uint8_t * block,uint32_t byteIndex)525 void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex)
526 {
527     static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>, "T must be int8/uint8");
528     T res;
529     if (std::isnan(val) || std::isinf(val)) {
530         res = 0;
531         SetTypeData(block, res, byteIndex);
532         return;
533     }
534     auto int64Val = static_cast<int64_t>(val);
535     auto *resArr = reinterpret_cast<T *>(&int64Val);
536     res = *resArr;
537     SetTypeData(block, res, byteIndex);
538 }
539 
SetValueInBufferForUint8Clamped(double val,uint8_t * block,uint32_t byteIndex)540 void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, uint32_t byteIndex)
541 {
542     uint8_t res;
543     if (std::isnan(val) || val <= 0) {
544         res = 0;
545         SetTypeData(block, res, byteIndex);
546         return;
547     }
548     val = val >= UINT8_MAX ? UINT8_MAX : val;
549     constexpr double HALF = 0.5;
550     val = val == HALF ? 0 : std::round(val);
551     res = static_cast<uint64_t>(val);
552     SetTypeData(block, res, byteIndex);
553 }
554 
555 template<typename T>
SetValueInBufferForInteger(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)556 void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
557 {
558     static_assert(std::is_integral_v<T>, "T must be integral");
559     static_assert(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
560     T res;
561     if (std::isnan(val) || std::isinf(val)) {
562         res = 0;
563         SetTypeData(block, res, byteIndex);
564         return;
565     }
566     auto int64Val = static_cast<int64_t>(val);
567     // NOLINTNEXTLINE(readability-braces-around-statements)
568     if constexpr (std::is_same_v<T, uint16_t>) {
569         auto *pTmp = reinterpret_cast<int16_t *>(&int64Val);
570         int16_t tmp = *pTmp;
571         res = static_cast<T>(tmp);
572     } else {  // NOLINTNEXTLINE(readability-braces-around-statements)
573         auto *pTmp = reinterpret_cast<T *>(&int64Val);
574         res = *pTmp;
575     }
576 
577     if (!littleEndian) {
578         res = LittleEndianToBigEndian<T>(res);
579     }
580     SetTypeData(block, res, byteIndex);
581 }
582 
583 template<typename T>
SetValueInBufferForFloat(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)584 void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
585 {
586     static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>, "T must be float type");
587     auto data = static_cast<T>(val);
588     if (std::isnan(val)) {
589         SetTypeData(block, data, byteIndex);
590         return;
591     }
592     if (!littleEndian) {
593         if constexpr (std::is_same_v<T, float>) {
594             uint32_t res = bit_cast<uint32_t>(data);
595             data = bit_cast<T>(LittleEndianToBigEndian(res));
596         } else if constexpr (std::is_same_v<T, double>) {
597             uint64_t res = bit_cast<uint64_t>(data);
598             data = bit_cast<T>(LittleEndianToBigEndian64Bit(res));
599         }
600     }
601     SetTypeData(block, data, byteIndex);
602 }
603 
604 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,const JSHandle<JSTaggedValue> & val,JSHandle<JSTaggedValue> & arrBuf,uint32_t byteIndex,bool littleEndian)605 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
606                                                     const JSHandle<JSTaggedValue> &val,
607                                                     JSHandle<JSTaggedValue> &arrBuf,
608                                                     uint32_t byteIndex, bool littleEndian)
609 {
610     static_assert(std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>, "T must be int64_t/uint64_t");
611     T value = 0;
612     bool lossless = true;
613     if constexpr(std::is_same_v<T, uint64_t>) {
614         BigInt::BigIntToUint64(thread, val, reinterpret_cast<uint64_t *>(&value), &lossless);
615     } else {
616         BigInt::BigIntToInt64(thread, val, reinterpret_cast<int64_t *>(&value), &lossless);
617     }
618     RETURN_IF_ABRUPT_COMPLETION(thread);
619     if (!littleEndian) {
620         value = LittleEndianToBigEndian64Bit<T>(value);
621     }
622     void *pointer = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
623     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
624     SetTypeData(block, value, byteIndex);
625 }
626 
FastSetValueInBuffer(JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,double val,bool littleEndian)627 JSTaggedValue BuiltinsArrayBuffer::FastSetValueInBuffer(JSTaggedValue arrBuf, uint32_t byteIndex,
628                                                         DataViewType type, double val, bool littleEndian)
629 {
630     void *pointer = GetDataPointFromBuffer(arrBuf);
631     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
632     return SetValueInBuffer(byteIndex, block, type, val, littleEndian);
633 }
634 
SetValueInBuffer(uint32_t byteIndex,uint8_t * block,DataViewType type,double val,bool littleEndian)635 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(uint32_t byteIndex, uint8_t *block, DataViewType type, double val,
636                                                     bool littleEndian)
637 {
638     switch (type) {
639         case DataViewType::UINT8:
640             SetValueInBufferForByte<uint8_t>(val, block, byteIndex);
641             break;
642         case DataViewType::UINT8_CLAMPED:
643             SetValueInBufferForUint8Clamped(val, block, byteIndex);
644             break;
645         case DataViewType::INT8:
646             SetValueInBufferForByte<int8_t>(val, block, byteIndex);
647             break;
648         case DataViewType::UINT16:
649             SetValueInBufferForInteger<uint16_t>(val, block, byteIndex, littleEndian);
650             break;
651         case DataViewType::INT16:
652             SetValueInBufferForInteger<int16_t>(val, block, byteIndex, littleEndian);
653             break;
654         case DataViewType::UINT32:
655             SetValueInBufferForInteger<uint32_t>(val, block, byteIndex, littleEndian);
656             break;
657         case DataViewType::INT32:
658             SetValueInBufferForInteger<int32_t>(val, block, byteIndex, littleEndian);
659             break;
660         case DataViewType::FLOAT32:
661             SetValueInBufferForFloat<float>(val, block, byteIndex, littleEndian);
662             break;
663         case DataViewType::FLOAT64:
664             SetValueInBufferForFloat<double>(val, block, byteIndex, littleEndian);
665             break;
666         default:
667             UNREACHABLE();
668     }
669     return JSTaggedValue::Undefined();
670 }
671 
GetDataPointFromBuffer(JSTaggedValue arrBuf,uint32_t byteOffset)672 void *BuiltinsArrayBuffer::GetDataPointFromBuffer(JSTaggedValue arrBuf, uint32_t byteOffset)
673 {
674     if (arrBuf.IsByteArray()) {
675         return reinterpret_cast<void *>(ToUintPtr(ByteArray::Cast(arrBuf.GetTaggedObject())->GetData()) + byteOffset);
676     } else {
677         JSArrayBuffer *arrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
678         if (arrayBuffer->GetArrayBufferByteLength() == 0) {
679             return nullptr;
680         }
681         JSTaggedValue data = arrayBuffer->GetArrayBufferData();
682         return reinterpret_cast<void *>(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject())
683                                                   ->GetExternalPointer()) + byteOffset);
684     }
685 }
686 }  // namespace panda::ecmascript::builtins
687