• 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     JSThread *thread = argv->GetThread();
41     BUILTINS_API_TRACE(thread, ArrayBuffer, Constructor);
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     JSThread *thread = argv->GetThread();
60     BUILTINS_API_TRACE(thread, ArrayBuffer, IsView);
61     [[maybe_unused]] EcmaHandleScope handleScope(thread);
62     JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
63     // 1. If Type(arg) is not Object, return false.
64     if (!arg->IsECMAObject()) {
65         return BuiltinsArrayBuffer::GetTaggedBoolean(false);
66     }
67     // 2. If arg has a [[ViewedArrayBuffer]] internal slot, return true.
68     if (arg->IsDataView() || arg->IsTypedArray()) {
69         return BuiltinsArrayBuffer::GetTaggedBoolean(true);
70     }
71     // 3. Return false.
72     return BuiltinsArrayBuffer::GetTaggedBoolean(false);
73 }
74 
75 // 24.1.3.3 get ArrayBuffer [ @@species ]
Species(EcmaRuntimeCallInfo * argv)76 JSTaggedValue BuiltinsArrayBuffer::Species(EcmaRuntimeCallInfo *argv)
77 {
78     ASSERT(argv);
79     BUILTINS_API_TRACE(argv->GetThread(), ArrayBuffer, Species);
80     return GetThis(argv).GetTaggedValue();
81 }
82 
83 // 24.1.4.1 get ArrayBuffer.prototype.byteLength
GetByteLength(EcmaRuntimeCallInfo * argv)84 JSTaggedValue BuiltinsArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv)
85 {
86     ASSERT(argv);
87     JSThread *thread = argv->GetThread();
88     BUILTINS_API_TRACE(thread, ArrayBuffer, GetByteLength);
89     [[maybe_unused]] EcmaHandleScope handleScope(thread);
90 
91     // 1. Let O be the this value.
92     JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
93     // 2. If Type(O) is not Object, throw a TypeError exception.
94     if (!thisHandle->IsECMAObject()) {
95         THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
96     }
97     // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
98     if (!thisHandle->IsArrayBuffer()) {
99         THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
100     }
101     // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
102     if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
103         THROW_TYPE_ERROR_AND_RETURN(thread, "IsDetachedBuffer", JSTaggedValue::Exception());
104     }
105     JSHandle<JSArrayBuffer> arrBuf(thisHandle);
106     // 5. Let length be the value of O’s [[ArrayBufferByteLength]] internal slot.
107     uint32_t length = arrBuf->GetArrayBufferByteLength();
108     // 6. Return length.
109     return JSTaggedValue(length);
110 }
111 
112 // 24.1.4.3 ArrayBuffer.prototype.slice(start, end)
Slice(EcmaRuntimeCallInfo * argv)113 JSTaggedValue BuiltinsArrayBuffer::Slice(EcmaRuntimeCallInfo *argv)
114 {
115     ASSERT(argv);
116     JSThread *thread = argv->GetThread();
117     BUILTINS_API_TRACE(thread, ArrayBuffer, Slice);
118     [[maybe_unused]] EcmaHandleScope handleScope(thread);
119     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
120     // 1. Let O be the this value.
121     JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
122     // 2. If Type(O) is not Object, throw a TypeError exception.
123     if (!thisHandle->IsHeapObject()) {
124         THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
125     }
126     JSHandle<JSArrayBuffer> arrBuf(thisHandle);
127     // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
128     if (!thisHandle->IsArrayBuffer()) {
129         THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
130     }
131     // 4. If IsDetachedBuffer(O) is true, throw a TypeError exception.
132     if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
133         THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception());
134     }
135     // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot.
136     int32_t len = static_cast<int32_t>(arrBuf->GetArrayBufferByteLength());
137     JSHandle<JSTaggedValue> startHandle = GetCallArg(argv, 0);
138     // 6. Let relativeStart be ToInteger(start).
139     JSTaggedNumber relativeStart = JSTaggedValue::ToInteger(thread, startHandle);
140     // 7. ReturnIfAbrupt(relativeStart).
141     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
142     int32_t start = base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber());
143     int32_t end = 0;
144     int32_t first = 0;
145     int32_t last = 0;
146     // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else let first be min(relativeStart, len).
147     if (start < 0) {
148         first = std::max((len + start), 0);
149     } else {
150         first = std::min(start, len);
151     }
152     // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end).
153     JSHandle<JSTaggedValue> endHandle = GetCallArg(argv, 1);
154     if (endHandle->IsUndefined()) {
155         end = len;
156     } else {
157         JSTaggedNumber relativeEnd = JSTaggedValue::ToInteger(thread, endHandle);
158         // 10. ReturnIfAbrupt(relativeEnd).
159         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
160         end = base::NumberHelper::DoubleInRangeInt32(relativeEnd.GetNumber());
161     }
162     // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len).
163     if (end < 0) {
164         last = std::max((len + end), 0);
165     } else {
166         last = std::min(end, len);
167     }
168     // 12. Let newLen be max(final-first,0).
169     uint32_t newLen = std::max((last - first), 0);
170     // 13. Let ctor be SpeciesConstructor(O, %ArrayBuffer%).
171     JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
172     JSHandle<JSObject> objHandle(thisHandle);
173     JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
174     // 14. ReturnIfAbrupt(ctor).
175     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
176     // 15. Let new be Construct(ctor, «newLen»).
177     JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
178     EcmaRuntimeCallInfo *info =
179         EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1);
180     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
181     info->SetCallArg(JSTaggedValue(newLen));
182     JSTaggedValue taggedNewArrBuf = JSFunction::Construct(info);
183     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
184     JSHandle<JSTaggedValue> newArrBuf(thread, taggedNewArrBuf);
185     // 16. ReturnIfAbrupt(new).
186     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
187     // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
188     if (!newArrBuf->IsArrayBuffer()) {
189         THROW_TYPE_ERROR_AND_RETURN(thread, "don't have bufferdata internal slot", JSTaggedValue::Exception());
190     }
191     // 18. If IsDetachedBuffer(new) is true, throw a TypeError exception.
192     if (IsDetachedBuffer(newArrBuf.GetTaggedValue())) {
193         THROW_TYPE_ERROR_AND_RETURN(thread, "new arrayBuffer IsDetachedBuffer", JSTaggedValue::Exception());
194     }
195     // 19. If SameValue(new, O) is true, throw a TypeError exception.
196     if (JSTaggedValue::SameValue(newArrBuf.GetTaggedValue(), thisHandle.GetTaggedValue())) {
197         THROW_TYPE_ERROR_AND_RETURN(thread, "value of new arraybuffer and this is same", JSTaggedValue::Exception());
198     }
199     JSHandle<JSArrayBuffer> newJsArrBuf(newArrBuf);
200     // 20. If the value of new’s [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception.
201     uint32_t newArrBufLen = newJsArrBuf->GetArrayBufferByteLength();
202     if (newArrBufLen < newLen) {
203         THROW_TYPE_ERROR_AND_RETURN(thread, "new array buffer length smaller than newlen", JSTaggedValue::Exception());
204     }
205     // 21. NOTE: Side-effects of the above steps may have detached O.
206     // 22. If IsDetachedBuffer(O) is true, throw a TypeError exception.
207     if (IsDetachedBuffer(thisHandle.GetTaggedValue())) {
208         THROW_TYPE_ERROR_AND_RETURN(thread, "this value IsDetachedBuffer", JSTaggedValue::Exception());
209     }
210     if (newLen > 0) {
211         // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot.
212         void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
213         // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot.
214         void *toBuf = GetDataPointFromBuffer(newJsArrBuf.GetTaggedValue());
215         // 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen).
216         JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, first, newLen);
217     }
218     // Return new.
219     return newArrBuf.GetTaggedValue();
220 }
221 
222 // 24.1.1.1 AllocateArrayBuffer(constructor, byteLength)
AllocateArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & newTarget,uint64_t byteLength)223 JSTaggedValue BuiltinsArrayBuffer::AllocateArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &newTarget,
224                                                        uint64_t byteLength)
225 {
226     BUILTINS_API_TRACE(thread, ArrayBuffer, AllocateArrayBuffer);
227     /**
228      * 1. Let obj be OrdinaryCreateFromConstructor(constructor, "%ArrayBufferPrototype%",
229      * «[[ArrayBufferData]], [[ArrayBufferByteLength]]» ).
230      * */
231     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
232     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
233     JSHandle<JSTaggedValue> arrBufFunc = env->GetArrayBufferFunction();
234     JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(arrBufFunc), newTarget);
235     // 2. ReturnIfAbrupt
236     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
237     // 4. Let block be CreateByteDataBlock(byteLength).
238     if (byteLength > INT_MAX) {
239         THROW_RANGE_ERROR_AND_RETURN(thread, "Out of range", JSTaggedValue::Exception());
240     }
241     uint32_t arrayByteLength = static_cast<uint32_t>(byteLength);
242     JSHandle<JSArrayBuffer> arrayBuffer(obj);
243     // 6. Set obj’s [[ArrayBufferData]] internal slot to block.
244     factory->NewJSArrayBufferData(arrayBuffer, arrayByteLength);
245     // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength.
246     arrayBuffer->SetArrayBufferByteLength(arrayByteLength);
247     // 8. Return obj.
248     return arrayBuffer.GetTaggedValue();
249 }
250 
251 // 24.1.1.2 IsDetachedBuffer()
IsDetachedBuffer(JSTaggedValue arrayBuffer)252 bool BuiltinsArrayBuffer::IsDetachedBuffer(JSTaggedValue arrayBuffer)
253 {
254     if (arrayBuffer.IsByteArray()) {
255         return false;
256     }
257     // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
258     ASSERT(arrayBuffer.IsArrayBuffer() || arrayBuffer.IsSharedArrayBuffer());
259     JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject());
260     JSTaggedValue dataSlot = buffer->GetArrayBufferData();
261     // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true.
262     // 3. Return false.
263     return dataSlot.IsNull();
264 }
265 
266 // 24.1.1.4
CloneArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & srcBuffer,uint32_t srcByteOffset,JSHandle<JSTaggedValue> constructor)267 JSTaggedValue BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &srcBuffer,
268                                                     uint32_t srcByteOffset, JSHandle<JSTaggedValue> constructor)
269 {
270     BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer);
271     // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
272     ASSERT(srcBuffer->IsArrayBuffer() || srcBuffer->IsSharedArrayBuffer());
273     JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
274     // 2. If cloneConstructor is not present
275     if (constructor->IsUndefined()) {
276         // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%).
277         JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
278         JSHandle<JSObject> objHandle(srcBuffer);
279         constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
280         // b. ReturnIfAbrupt(cloneConstructor).
281         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
282         // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
283         if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
284             THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
285         } else {
286             ASSERT(constructor->IsConstructor());
287         }
288     }
289     // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot.
290     JSHandle<JSArrayBuffer> arrBuf(srcBuffer);
291     uint32_t srcLen = arrBuf->GetArrayBufferByteLength();
292     // 5. Assert: srcByteOffset ≤ srcLength.
293     ASSERT(srcByteOffset <= srcLen);
294     // 6. Let cloneLength be srcLength – srcByteOffset.
295     int32_t cloneLen = static_cast<int32_t>(srcLen - srcByteOffset);
296     // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength).
297     JSTaggedValue taggedBuf = AllocateArrayBuffer(thread, constructor, cloneLen);
298     // 9. ReturnIfAbrupt(targetBuffer).
299     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
300     // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
301     if (IsDetachedBuffer(srcBuffer.GetTaggedValue())) {
302         THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
303     }
304     // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot.
305     JSHandle<JSArrayBuffer> newArrBuf(thread, taggedBuf);
306     // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength).
307     // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot.
308     void *fromBuf = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
309     void *toBuf = GetDataPointFromBuffer(taggedBuf);
310     if (cloneLen > 0) {
311         JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, srcByteOffset, cloneLen);
312     }
313     return taggedBuf;
314 }
315 
316 // 24.1.1.5
317 // NOLINTNEXTLINE(readability-function-size)
GetValueFromBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,bool littleEndian)318 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
319                                                       DataViewType type, bool littleEndian)
320 {
321     void *pointer = GetDataPointFromBuffer(arrBuf);
322     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
323     return GetValueFromBuffer(thread, byteIndex, block, type, littleEndian);
324 }
325 
GetValueFromBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,bool littleEndian)326 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block,
327                                                       DataViewType type, bool littleEndian)
328 {
329     switch (type) {
330         case DataViewType::UINT8:
331         case DataViewType::UINT8_CLAMPED: {
332             uint8_t res = block[byteIndex];  // NOLINT
333             return GetTaggedInt(res);
334         }
335         case DataViewType::INT8: {
336             uint8_t res = block[byteIndex];  // NOLINT
337             auto int8Res = static_cast<int8_t>(res);
338             return GetTaggedInt(int8Res);
339         }
340         case DataViewType::UINT16:
341             return GetValueFromBufferForInteger<uint16_t, NumberSize::UINT16>(block, byteIndex, littleEndian);
342         case DataViewType::INT16:
343             return GetValueFromBufferForInteger<int16_t, NumberSize::INT16>(block, byteIndex, littleEndian);
344         case DataViewType::UINT32:
345             return GetValueFromBufferForInteger<uint32_t, NumberSize::UINT32>(block, byteIndex, littleEndian);
346         case DataViewType::INT32:
347             return GetValueFromBufferForInteger<int32_t, NumberSize::INT32>(block, byteIndex, littleEndian);
348         case DataViewType::FLOAT32:
349             return GetValueFromBufferForFloat<float, UnionType32, NumberSize::FLOAT32>(block, byteIndex, littleEndian);
350         case DataViewType::FLOAT64:
351             return GetValueFromBufferForFloat<double, UnionType64, NumberSize::FLOAT64>(block, byteIndex, littleEndian);
352         case DataViewType::BIGINT64:
353             return GetValueFromBufferForBigInt<int64_t, NumberSize::BIGINT64>(thread, block, byteIndex, littleEndian);
354         case DataViewType::BIGUINT64:
355             return GetValueFromBufferForBigInt<uint64_t, NumberSize::BIGUINT64>(thread, block, byteIndex, littleEndian);
356         default:
357             break;
358     }
359     LOG_ECMA(FATAL) << "this branch is unreachable";
360     UNREACHABLE();
361 }
362 
363 // 24.1.1.6
SetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,const JSHandle<JSTaggedValue> & value,bool littleEndian)364 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
365                                                     DataViewType type, const JSHandle<JSTaggedValue> &value,
366                                                     bool littleEndian)
367 {
368     if (UNLIKELY(IsBigIntElementType(type))) {
369         JSHandle<JSTaggedValue> arrBufHandle(thread, arrBuf);
370         switch (type) {
371             case DataViewType::BIGINT64:
372                 SetValueInBufferForBigInt<int64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
373                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
374                 break;
375             case DataViewType::BIGUINT64:
376                 SetValueInBufferForBigInt<uint64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
377                 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
378                 break;
379             default:
380                 LOG_ECMA(FATAL) << "this branch is unreachable";
381                 UNREACHABLE();
382         }
383         return JSTaggedValue::Undefined();
384     }
385     void *pointer = GetDataPointFromBuffer(arrBuf);
386     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
387     double val = value.GetTaggedValue().GetNumber();
388     return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
389 }
390 
391 // es12 25.1.2.7 IsBigIntElementType ( type )
IsBigIntElementType(DataViewType type)392 bool BuiltinsArrayBuffer::IsBigIntElementType(DataViewType type)
393 {
394     if (type == DataViewType::BIGINT64 || type == DataViewType::BIGUINT64) {
395         return true;
396     }
397     return false;
398 }
399 
400 // es12 25.1.2.6 IsUnclampedIntegerElementType ( type )
IsUnclampedIntegerElementType(DataViewType type)401 bool BuiltinsArrayBuffer::IsUnclampedIntegerElementType(DataViewType type)
402 {
403     switch (type) {
404         case DataViewType::INT8:
405         case DataViewType::INT16:
406         case DataViewType::INT32:
407         case DataViewType::UINT8:
408         case DataViewType::UINT16:
409         case DataViewType::UINT32:
410             return true;
411         default:
412             return false;
413     }
414 }
415 
416 template<typename T>
SetTypeData(uint8_t * block,T value,uint32_t index)417 void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, uint32_t index)
418 {
419     uint32_t sizeCount = sizeof(T);
420     uint8_t *res = reinterpret_cast<uint8_t *>(&value);
421     for (uint32_t i = 0; i < sizeCount; i++) {
422         *(block + index + i) = *(res + i);  // NOLINT
423     }
424 }
425 
426 template <typename T>
LittleEndianToBigEndian(T liValue)427 T BuiltinsArrayBuffer::LittleEndianToBigEndian(T liValue)
428 {
429     uint8_t sizeCount = sizeof(T);
430     T biValue;
431     switch (sizeCount) {
432         case NumberSize::UINT16:
433             biValue = ((liValue & 0x00FF) << BITS_EIGHT)     // NOLINT
434                       | ((liValue & 0xFF00) >> BITS_EIGHT);  // NOLINT
435             break;
436         case NumberSize::UINT32:
437             biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR)     // NOLINT
438                       | ((liValue & 0x0000FF00) << BITS_EIGHT)         // NOLINT
439                       | ((liValue & 0x00FF0000) >> BITS_EIGHT)         // NOLINT
440                       | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR);  // NOLINT
441             break;
442         default:
443             LOG_ECMA(FATAL) << "this branch is unreachable";
444             UNREACHABLE();
445             break;
446     }
447     return biValue;
448 }
449 template <typename T>
LittleEndianToBigEndian64Bit(T liValue)450 T BuiltinsArrayBuffer::LittleEndianToBigEndian64Bit(T liValue)
451 {
452     return ((liValue & 0x00000000000000FF) << BITS_FIFTY_SIX)      // NOLINT
453            | ((liValue & 0x000000000000FF00) << BITS_FORTY)        // NOLINT
454            | ((liValue & 0x0000000000FF0000) << BITS_TWENTY_FOUR)  // NOLINT
455            | ((liValue & 0x00000000FF000000) << BITS_EIGHT)        // NOLINT
456            | ((liValue & 0x000000FF00000000) >> BITS_EIGHT)        // NOLINT
457            | ((liValue & 0x0000FF0000000000) >> BITS_TWENTY_FOUR)  // NOLINT
458            | ((liValue & 0x00FF000000000000) >> BITS_FORTY)        // NOLINT
459            | ((liValue & 0xFF00000000000000) >> BITS_FIFTY_SIX);   // NOLINT
460 }
461 
462 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForInteger(uint8_t * block,uint32_t byteIndex,bool littleEndian)463 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, uint32_t byteIndex, bool littleEndian)
464 {
465     ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
466     ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
467     ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
468 
469     ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64);
470     T res = *reinterpret_cast<T *>(block + byteIndex);
471     if (!littleEndian) {
472         res = LittleEndianToBigEndian(res);
473     }
474 
475     // uint32_t maybe overflow with TaggedInt
476     // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
477     if constexpr (std::is_same_v<T, uint32_t>) {
478         // NOLINTNEXTLINE(clang-diagnostic-sign-compare)
479         if (res > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
480             return GetTaggedDouble(static_cast<double>(res));
481         }
482     }
483     return GetTaggedInt(res);
484 }
485 
486 template<typename T, typename UnionType, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForFloat(uint8_t * block,uint32_t byteIndex,bool littleEndian)487 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian)
488 {
489     ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be correct type");
490     ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
491 
492     UnionType unionValue = {0};
493     // NOLINTNEXTLINE(readability-braces-around-statements)
494     if constexpr (std::is_same_v<T, float>) {
495         unionValue.uValue = *reinterpret_cast<uint32_t *>(block + byteIndex);
496         if (std::isnan(unionValue.value)) {
497             if (!JSTaggedValue::IsImpureNaN(unionValue.value)) {
498                 return GetTaggedDouble(unionValue.value);
499             } else {
500                 return GetTaggedDouble(base::NAN_VALUE);
501             }
502         }
503         if (!littleEndian) {
504             uint32_t res = LittleEndianToBigEndian(unionValue.uValue);
505             return GetTaggedDouble(base::bit_cast<T>(res));
506         }
507     } else if constexpr (std::is_same_v<T, double>) {  // NOLINTNEXTLINE(readability-braces-around-statements)
508         unionValue.uValue = *reinterpret_cast<uint64_t *>(block + byteIndex);
509         if (std::isnan(unionValue.value) && !JSTaggedValue::IsImpureNaN(unionValue.value)) {
510             return GetTaggedDouble(unionValue.value);
511         }
512         if (!littleEndian) {
513             uint64_t res = LittleEndianToBigEndian64Bit(unionValue.uValue);
514             T d = base::bit_cast<T>(res);
515             if (JSTaggedValue::IsImpureNaN(d)) {
516                 return GetTaggedDouble(base::bit_cast<T>(base::pureNaN));
517             }
518             return GetTaggedDouble(d);
519         } else {
520             if (JSTaggedValue::IsImpureNaN(unionValue.value)) {
521                 return GetTaggedDouble(base::NAN_VALUE);
522             }
523         }
524     }
525 
526     return GetTaggedDouble(unionValue.value);
527 }
528 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForBigInt(JSThread * thread,uint8_t * block,uint32_t byteIndex,bool littleEndian)529 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block,
530                                                                uint32_t byteIndex, bool littleEndian)
531 {
532     ASSERT_PRINT((std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t>), "T must be uint64_t/int64_t");
533     auto pTmp = *reinterpret_cast<uint64_t *>(block + byteIndex);
534     if (!littleEndian) {
535         pTmp = LittleEndianToBigEndian64Bit(pTmp);
536     }
537     if constexpr (std::is_same_v<T, uint64_t>) {
538         return BigInt::Uint64ToBigInt(thread, pTmp).GetTaggedValue();
539     }
540     return BigInt::Int64ToBigInt(thread, pTmp).GetTaggedValue();
541 }
542 
543 
544 template<typename T>
SetValueInBufferForByte(double val,uint8_t * block,uint32_t byteIndex)545 void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex)
546 {
547     ASSERT_PRINT((std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>), "T must be int8/uint8");
548     T res;
549     if (std::isnan(val) || std::isinf(val)) {
550         res = 0;
551         SetTypeData(block, res, byteIndex);
552         return;
553     }
554     auto int64Val = static_cast<int64_t>(val);
555     auto *resArr = reinterpret_cast<T *>(&int64Val);
556     res = *resArr;
557     SetTypeData(block, res, byteIndex);
558 }
559 
SetValueInBufferForUint8Clamped(double val,uint8_t * block,uint32_t byteIndex)560 void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, uint32_t byteIndex)
561 {
562     uint8_t res;
563     if (std::isnan(val) || val <= 0) {
564         res = 0;
565         SetTypeData(block, res, byteIndex);
566         return;
567     }
568     val = val >= UINT8_MAX ? UINT8_MAX : val;
569     constexpr double HALF = 0.5;
570     val = val == HALF ? 0 : std::round(val);
571     res = static_cast<uint64_t>(val);
572     SetTypeData(block, res, byteIndex);
573 }
574 
575 template<typename T>
SetValueInBufferForInteger(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)576 void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
577 {
578     ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
579     ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
580     T res;
581     if (std::isnan(val) || std::isinf(val)) {
582         res = 0;
583         SetTypeData(block, res, byteIndex);
584         return;
585     }
586     auto int64Val = static_cast<int64_t>(val);
587     // NOLINTNEXTLINE(readability-braces-around-statements)
588     if constexpr (std::is_same_v<T, uint16_t>) {
589         auto *pTmp = reinterpret_cast<int16_t *>(&int64Val);
590         int16_t tmp = *pTmp;
591         res = static_cast<T>(tmp);
592     } else {  // NOLINTNEXTLINE(readability-braces-around-statements)
593         auto *pTmp = reinterpret_cast<T *>(&int64Val);
594         res = *pTmp;
595     }
596 
597     if (!littleEndian) {
598         res = LittleEndianToBigEndian<T>(res);
599     }
600     SetTypeData(block, res, byteIndex);
601 }
602 
603 template<typename T>
SetValueInBufferForFloat(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)604 void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
605 {
606     ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be float type");
607     auto data = static_cast<T>(val);
608     if (std::isnan(val)) {
609         SetTypeData(block, data, byteIndex);
610         return;
611     }
612     if (!littleEndian) {
613         if constexpr (std::is_same_v<T, float>) {
614             uint32_t res = base::bit_cast<uint32_t>(data);
615             data = base::bit_cast<T>(LittleEndianToBigEndian(res));
616         } else if constexpr (std::is_same_v<T, double>) {
617             uint64_t res = base::bit_cast<uint64_t>(data);
618             data = base::bit_cast<T>(LittleEndianToBigEndian64Bit(res));
619         }
620     }
621     SetTypeData(block, data, byteIndex);
622 }
623 
624 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,const JSHandle<JSTaggedValue> & val,JSHandle<JSTaggedValue> & arrBuf,uint32_t byteIndex,bool littleEndian)625 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
626                                                     const JSHandle<JSTaggedValue> &val,
627                                                     JSHandle<JSTaggedValue> &arrBuf,
628                                                     uint32_t byteIndex, bool littleEndian)
629 {
630     ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
631     T value = 0;
632     bool lossless = true;
633     if constexpr(std::is_same_v<T, uint64_t>) {
634         BigInt::BigIntToUint64(thread, val, reinterpret_cast<uint64_t *>(&value), &lossless);
635     } else {
636         BigInt::BigIntToInt64(thread, val, reinterpret_cast<int64_t *>(&value), &lossless);
637     }
638     RETURN_IF_ABRUPT_COMPLETION(thread);
639     if (!littleEndian) {
640         value = LittleEndianToBigEndian64Bit<T>(value);
641     }
642     void *pointer = GetDataPointFromBuffer(arrBuf.GetTaggedValue());
643     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
644     SetTypeData(block, value, byteIndex);
645 }
646 
647 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)648 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
649                                                     double val, uint8_t *block,
650                                                     uint32_t byteIndex, bool littleEndian)
651 {
652     ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
653     T value = 0;
654     bool lossless = true;
655 
656     JSHandle<JSTaggedValue> valHandle(thread, GetTaggedDouble(val));
657     if constexpr(std::is_same_v<T, uint64_t>) {
658         BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast<uint64_t *>(&value), &lossless);
659     } else {
660         BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast<int64_t *>(&value), &lossless);
661     }
662     RETURN_IF_ABRUPT_COMPLETION(thread);
663     if (!littleEndian) {
664         value = LittleEndianToBigEndian64Bit<T>(value);
665     }
666     SetTypeData(block, value, byteIndex);
667 }
668 
FastSetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,double val,bool littleEndian)669 JSTaggedValue BuiltinsArrayBuffer::FastSetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
670                                                         DataViewType type, double val, bool littleEndian)
671 {
672     void *pointer = GetDataPointFromBuffer(arrBuf);
673     uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
674     return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
675 }
676 
SetValueInBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,double val,bool littleEndian)677 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread* thread, uint32_t byteIndex, uint8_t *block,
678                                                     DataViewType type, double val, bool littleEndian)
679 {
680     switch (type) {
681         case DataViewType::UINT8:
682             SetValueInBufferForByte<uint8_t>(val, block, byteIndex);
683             break;
684         case DataViewType::UINT8_CLAMPED:
685             SetValueInBufferForUint8Clamped(val, block, byteIndex);
686             break;
687         case DataViewType::INT8:
688             SetValueInBufferForByte<int8_t>(val, block, byteIndex);
689             break;
690         case DataViewType::UINT16:
691             SetValueInBufferForInteger<uint16_t>(val, block, byteIndex, littleEndian);
692             break;
693         case DataViewType::INT16:
694             SetValueInBufferForInteger<int16_t>(val, block, byteIndex, littleEndian);
695             break;
696         case DataViewType::UINT32:
697             SetValueInBufferForInteger<uint32_t>(val, block, byteIndex, littleEndian);
698             break;
699         case DataViewType::INT32:
700             SetValueInBufferForInteger<int32_t>(val, block, byteIndex, littleEndian);
701             break;
702         case DataViewType::FLOAT32:
703             SetValueInBufferForFloat<float>(val, block, byteIndex, littleEndian);
704             break;
705         case DataViewType::FLOAT64:
706             SetValueInBufferForFloat<double>(val, block, byteIndex, littleEndian);
707             break;
708         case DataViewType::BIGINT64:
709             SetValueInBufferForBigInt<int64_t>(thread, val, block, byteIndex, littleEndian);
710             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
711             break;
712         case DataViewType::BIGUINT64:
713             SetValueInBufferForBigInt<uint64_t>(thread, val, block, byteIndex, littleEndian);
714             RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
715             break;
716         default:
717             LOG_ECMA(FATAL) << "this branch is unreachable";
718             UNREACHABLE();
719     }
720     return JSTaggedValue::Undefined();
721 }
722 
GetDataPointFromBuffer(JSTaggedValue arrBuf,uint32_t byteOffset)723 void *BuiltinsArrayBuffer::GetDataPointFromBuffer(JSTaggedValue arrBuf, uint32_t byteOffset)
724 {
725     if (arrBuf.IsByteArray()) {
726         return reinterpret_cast<void *>(ToUintPtr(ByteArray::Cast(arrBuf.GetTaggedObject())->GetData()) + byteOffset);
727     }
728 
729     JSArrayBuffer *arrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
730     if (arrayBuffer->GetArrayBufferByteLength() == 0) {
731         return nullptr;
732     }
733 
734     JSTaggedValue data = arrayBuffer->GetArrayBufferData();
735     return reinterpret_cast<void *>(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject())
736                                     ->GetExternalPointer()) + byteOffset);
737 }
738 }  // namespace panda::ecmascript::builtins
739