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