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