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(thread, 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(thread, 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(thread, 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(thread, 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(thread, 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(thread, arrBuf.GetTaggedValue());
204 // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot.
205 void *toBuf = GetDataPointFromBuffer(thread, 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 uint32_t arrayByteLength = static_cast<uint32_t>(byteLength);
233 JSHandle<JSArrayBuffer> arrayBuffer(obj);
234 // 6. Set obj’s [[ArrayBufferData]] internal slot to block.
235 factory->NewJSArrayBufferData(arrayBuffer, arrayByteLength);
236 // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength.
237 arrayBuffer->SetArrayBufferByteLength(arrayByteLength);
238 // 8. Return obj.
239 return arrayBuffer.GetTaggedValue();
240 }
241
242 // 24.1.1.2 IsDetachedBuffer()
IsDetachedBuffer(JSThread * thread,const JSHandle<JSTypedArray> & arrayBuffer)243 void BuiltinsArrayBuffer::IsDetachedBuffer(JSThread *thread, const JSHandle<JSTypedArray> &arrayBuffer)
244 {
245 JSTaggedValue detachedBuffer = arrayBuffer->GetViewedArrayBufferOrByteArray(thread);
246 if (IsDetachedBuffer(thread, detachedBuffer)) {
247 THROW_TYPE_ERROR(thread, "The ArrayBuffer of this value is detached buffer.");
248 }
249 }
250
IsDetachedBuffer(JSThread * thread,JSTaggedValue arrayBuffer)251 bool BuiltinsArrayBuffer::IsDetachedBuffer(JSThread *thread, JSTaggedValue arrayBuffer)
252 {
253 if (arrayBuffer.IsByteArray()) {
254 return false;
255 }
256 // 1. Assert: Type(arrayBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
257 ASSERT(arrayBuffer.IsArrayBuffer() || arrayBuffer.IsSharedArrayBuffer());
258 JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject());
259 if (buffer == nullptr) {
260 LOG_ECMA(FATAL) << "BuiltinsArrayBuffer::IsDetachedBuffer:buffer is nullptr";
261 }
262 JSTaggedValue dataSlot = buffer->GetArrayBufferData(thread);
263 // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return true.
264 // 3. Return false.
265 return dataSlot.IsNull();
266 }
267
268 // 24.1.1.4
CloneArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & srcBuffer,uint32_t srcByteOffset,JSHandle<JSTaggedValue> constructor)269 JSTaggedValue BuiltinsArrayBuffer::CloneArrayBuffer(JSThread *thread, const JSHandle<JSTaggedValue> &srcBuffer,
270 uint32_t srcByteOffset, JSHandle<JSTaggedValue> constructor)
271 {
272 BUILTINS_API_TRACE(thread, ArrayBuffer, CloneArrayBuffer);
273 // 1. Assert: Type(srcBuffer) is Object and it has an [[ArrayBufferData]] internal slot.
274 ASSERT(srcBuffer->IsArrayBuffer() || srcBuffer->IsSharedArrayBuffer() || srcBuffer->IsByteArray());
275 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
276 // 2. If cloneConstructor is not present
277 if (constructor->IsUndefined()) {
278 // a. Let cloneConstructor be SpeciesConstructor(srcBuffer, %ArrayBuffer%).
279 JSHandle<JSTaggedValue> defaultConstructor = env->GetArrayBufferFunction();
280 JSHandle<JSObject> objHandle(srcBuffer);
281 constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
282 // b. ReturnIfAbrupt(cloneConstructor).
283 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
284 // c. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
285 if (IsDetachedBuffer(thread, srcBuffer.GetTaggedValue())) {
286 THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
287 } else {
288 ASSERT(constructor->IsConstructor());
289 }
290 }
291 // 4. Let srcLength be the value of srcBuffer’s [[ArrayBufferByteLength]] internal slot.
292 uint32_t srcLen = 0;
293 int32_t cloneLen = 0;
294 if (srcBuffer->IsByteArray()) {
295 JSHandle<ByteArray> byteArrayBuf(srcBuffer);
296 srcLen = byteArrayBuf->GetArrayLength();
297 int32_t byteLen = static_cast<int32_t>(byteArrayBuf->GetByteLength());
298 // 5. Assert: srcByteOffset ≤ srcLength.
299 ASSERT(srcByteOffset <= srcLen);
300 // 6. Let cloneLength be (srcLength – srcByteOffset) * byteLen.
301 cloneLen = static_cast<int32_t>(srcLen - srcByteOffset) * byteLen;
302 srcByteOffset *= static_cast<uint32_t>(byteLen);
303 } else {
304 JSHandle<JSArrayBuffer> arrBuf(srcBuffer);
305 srcLen = arrBuf->GetArrayBufferByteLength();
306 // 5. Assert: srcByteOffset ≤ srcLength.
307 ASSERT(srcByteOffset <= srcLen);
308 // 6. Let cloneLength be srcLength – srcByteOffset.
309 cloneLen = static_cast<int32_t>(srcLen - srcByteOffset);
310 }
311 // 8. Let targetBuffer be AllocateArrayBuffer(cloneConstructor, cloneLength).
312 JSTaggedValue taggedBuf = AllocateArrayBuffer(thread, constructor, cloneLen);
313 // 9. ReturnIfAbrupt(targetBuffer).
314 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
315 // 10. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
316 if (IsDetachedBuffer(thread, srcBuffer.GetTaggedValue())) {
317 THROW_TYPE_ERROR_AND_RETURN(thread, "Is Detached Buffer", JSTaggedValue::Exception());
318 }
319 // 11. Let targetBlock be the value of targetBuffer’s [[ArrayBufferData]] internal slot.
320 JSHandle<JSArrayBuffer> newArrBuf(thread, taggedBuf);
321 // Perform CopyDataBlockBytes(targetBlock, 0, srcBlock, srcByteOffset, cloneLength).
322 // 7. Let srcBlock be the value of srcBuffer’s [[ArrayBufferData]] internal slot.
323 if (cloneLen > 0) {
324 void *fromBuf = GetDataPointFromBuffer(thread, srcBuffer.GetTaggedValue());
325 void *toBuf = GetDataPointFromBuffer(thread, taggedBuf);
326 JSArrayBuffer::CopyDataPointBytes(toBuf, fromBuf, srcByteOffset, cloneLen);
327 }
328 return taggedBuf;
329 }
330
331 // 24.1.1.5
332 // NOLINTNEXTLINE(readability-function-size)
GetValueFromBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,bool littleEndian)333 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
334 DataViewType type, bool littleEndian)
335 {
336 void *pointer = GetDataPointFromBuffer(thread, arrBuf);
337 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
338 return GetValueFromBuffer(thread, byteIndex, block, type, littleEndian);
339 }
340
GetValueFromBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,bool littleEndian)341 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBuffer(JSThread *thread, uint32_t byteIndex, uint8_t *block,
342 DataViewType type, bool littleEndian)
343 {
344 ASSERT(block != nullptr);
345 switch (type) {
346 case DataViewType::UINT8:
347 case DataViewType::UINT8_CLAMPED: {
348 uint8_t res = block[byteIndex]; // NOLINT
349 return GetTaggedInt(res);
350 }
351 case DataViewType::INT8: {
352 uint8_t res = block[byteIndex]; // NOLINT
353 auto int8Res = static_cast<int8_t>(res);
354 return GetTaggedInt(int8Res);
355 }
356 case DataViewType::UINT16:
357 return GetValueFromBufferForInteger<uint16_t, NumberSize::UINT16>(block, byteIndex, littleEndian);
358 case DataViewType::INT16:
359 return GetValueFromBufferForInteger<int16_t, NumberSize::INT16>(block, byteIndex, littleEndian);
360 case DataViewType::UINT32:
361 return GetValueFromBufferForInteger<uint32_t, NumberSize::UINT32>(block, byteIndex, littleEndian);
362 case DataViewType::INT32:
363 return GetValueFromBufferForInteger<int32_t, NumberSize::INT32>(block, byteIndex, littleEndian);
364 case DataViewType::FLOAT32:
365 return GetValueFromBufferForFloat<float, UnionType32, NumberSize::FLOAT32>(block, byteIndex, littleEndian);
366 case DataViewType::FLOAT64:
367 return GetValueFromBufferForFloat<double, UnionType64, NumberSize::FLOAT64>(block, byteIndex, littleEndian);
368 case DataViewType::BIGINT64:
369 return GetValueFromBufferForBigInt<int64_t, NumberSize::BIGINT64>(thread, block, byteIndex, littleEndian);
370 case DataViewType::BIGUINT64:
371 return GetValueFromBufferForBigInt<uint64_t, NumberSize::BIGUINT64>(thread, block, byteIndex, littleEndian);
372 default:
373 break;
374 }
375 LOG_ECMA(FATAL) << "this branch is unreachable";
376 UNREACHABLE();
377 }
378
379 // 24.1.1.6
SetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,const JSHandle<JSTaggedValue> & value,bool littleEndian)380 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
381 DataViewType type, const JSHandle<JSTaggedValue> &value,
382 bool littleEndian)
383 {
384 if (UNLIKELY(IsBigIntElementType(type))) {
385 JSHandle<JSTaggedValue> arrBufHandle(thread, arrBuf);
386 switch (type) {
387 case DataViewType::BIGINT64:
388 SetValueInBufferForBigInt<int64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
389 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
390 break;
391 case DataViewType::BIGUINT64:
392 SetValueInBufferForBigInt<uint64_t>(thread, value, arrBufHandle, byteIndex, littleEndian);
393 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
394 break;
395 default:
396 LOG_ECMA(FATAL) << "this branch is unreachable";
397 UNREACHABLE();
398 }
399 return JSTaggedValue::Undefined();
400 }
401 void *pointer = GetDataPointFromBuffer(thread, arrBuf);
402 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
403 JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, value.GetTaggedValue());
404 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
405 double val = numberValue.GetNumber();
406 return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
407 }
408
409 // es12 25.1.2.7 IsBigIntElementType ( type )
IsBigIntElementType(DataViewType type)410 bool BuiltinsArrayBuffer::IsBigIntElementType(DataViewType type)
411 {
412 if (type == DataViewType::BIGINT64 || type == DataViewType::BIGUINT64) {
413 return true;
414 }
415 return false;
416 }
417
418 // es12 25.1.2.6 IsUnclampedIntegerElementType ( type )
IsUnclampedIntegerElementType(DataViewType type)419 bool BuiltinsArrayBuffer::IsUnclampedIntegerElementType(DataViewType type)
420 {
421 switch (type) {
422 case DataViewType::INT8:
423 case DataViewType::INT16:
424 case DataViewType::INT32:
425 case DataViewType::UINT8:
426 case DataViewType::UINT16:
427 case DataViewType::UINT32:
428 return true;
429 default:
430 return false;
431 }
432 }
433
434 template<typename T>
SetTypeData(uint8_t * block,T value,uint32_t index)435 void BuiltinsArrayBuffer::SetTypeData(uint8_t *block, T value, uint32_t index)
436 {
437 uint32_t sizeCount = sizeof(T);
438 uint8_t *res = reinterpret_cast<uint8_t *>(&value);
439 for (uint32_t i = 0; i < sizeCount; i++) {
440 *(block + index + i) = *(res + i); // NOLINT
441 }
442 }
443
444 template<typename T>
FastSetTypeData(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,T value)445 void BuiltinsArrayBuffer::FastSetTypeData(uint8_t *byteBeginOffset, uint8_t *byteEndOffset, T value)
446 {
447 ASSERT(byteBeginOffset != nullptr);
448 ASSERT(byteEndOffset != nullptr);
449 uint32_t sizeCount = sizeof(T);
450 if (sizeCount == 1) {
451 memset_s(byteBeginOffset, byteEndOffset-byteBeginOffset, value, byteEndOffset-byteBeginOffset);
452 } else {
453 uint8_t *resAddr = reinterpret_cast<uint8_t *>(&value);
454 for (uint8_t *addr = byteBeginOffset; addr < byteEndOffset; addr += sizeCount) {
455 for (uint32_t i = 0; i < sizeCount; ++i) {
456 *(addr + i) = *(resAddr + i);
457 }
458 }
459 }
460 }
461
462 template <typename T>
LittleEndianToBigEndian(T liValue)463 T BuiltinsArrayBuffer::LittleEndianToBigEndian(T liValue)
464 {
465 uint8_t sizeCount = sizeof(T);
466 T biValue;
467 switch (sizeCount) {
468 case NumberSize::UINT16:
469 biValue = ((liValue & 0x00FF) << BITS_EIGHT) // NOLINT
470 | ((liValue & 0xFF00) >> BITS_EIGHT); // NOLINT
471 break;
472 case NumberSize::UINT32:
473 biValue = ((liValue & 0x000000FF) << BITS_TWENTY_FOUR) // NOLINT
474 | ((liValue & 0x0000FF00) << BITS_EIGHT) // NOLINT
475 | ((liValue & 0x00FF0000) >> BITS_EIGHT) // NOLINT
476 | ((liValue & 0xFF000000) >> BITS_TWENTY_FOUR); // NOLINT
477 break;
478 default:
479 LOG_ECMA(FATAL) << "this branch is unreachable";
480 UNREACHABLE();
481 break;
482 }
483 return biValue;
484 }
485 template <typename T>
LittleEndianToBigEndian64Bit(T liValue)486 T BuiltinsArrayBuffer::LittleEndianToBigEndian64Bit(T liValue)
487 {
488 return ((liValue & 0x00000000000000FF) << BITS_FIFTY_SIX) // NOLINT
489 | ((liValue & 0x000000000000FF00) << BITS_FORTY) // NOLINT
490 | ((liValue & 0x0000000000FF0000) << BITS_TWENTY_FOUR) // NOLINT
491 | ((liValue & 0x00000000FF000000) << BITS_EIGHT) // NOLINT
492 | ((liValue & 0x000000FF00000000) >> BITS_EIGHT) // NOLINT
493 | ((liValue & 0x0000FF0000000000) >> BITS_TWENTY_FOUR) // NOLINT
494 | ((liValue & 0x00FF000000000000) >> BITS_FORTY) // NOLINT
495 | ((liValue & 0xFF00000000000000) >> BITS_FIFTY_SIX); // NOLINT
496 }
497
498 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForInteger(uint8_t * block,uint32_t byteIndex,bool littleEndian)499 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForInteger(uint8_t *block, uint32_t byteIndex, bool littleEndian)
500 {
501 ASSERT(block != nullptr);
502 ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
503 ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
504 ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
505
506 ASSERT(size >= NumberSize::UINT16 || size <= NumberSize::FLOAT64);
507 T res = *reinterpret_cast<T *>(block + byteIndex);
508 if (!littleEndian) {
509 res = LittleEndianToBigEndian(res);
510 }
511
512 // uint32_t maybe overflow with TaggedInt
513 // NOLINTNEXTLINE(readability-braces-around-statements,bugprone-suspicious-semicolon)
514 if constexpr (std::is_same_v<T, uint32_t>) {
515 // NOLINTNEXTLINE(clang-diagnostic-sign-compare)
516 if (res > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
517 return GetTaggedDouble(static_cast<double>(res));
518 }
519 }
520 return GetTaggedInt(res);
521 }
522
523 template<typename T, typename UnionType, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForFloat(uint8_t * block,uint32_t byteIndex,bool littleEndian)524 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForFloat(uint8_t *block, uint32_t byteIndex, bool littleEndian)
525 {
526 ASSERT(block != nullptr);
527 ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be correct type");
528 ASSERT_PRINT(sizeof(T) == size, "Invalid number size");
529
530 UnionType unionValue = {0};
531 // NOLINTNEXTLINE(readability-braces-around-statements)
532 if constexpr (std::is_same_v<T, float>) {
533 unionValue.uValue = *reinterpret_cast<uint32_t *>(block + byteIndex);
534 uint32_t res = LittleEndianToBigEndian(unionValue.uValue);
535 return CommonConvert<T, uint32_t>(unionValue.value, res, littleEndian);
536 } else if constexpr (std::is_same_v<T, double>) { // NOLINTNEXTLINE(readability-braces-around-statements)
537 unionValue.uValue = *reinterpret_cast<uint64_t *>(block + byteIndex);
538 uint64_t res = LittleEndianToBigEndian64Bit(unionValue.uValue);
539 return CommonConvert<T, uint64_t>(unionValue.value, res, littleEndian);
540 }
541
542 return GetTaggedDouble(unionValue.value);
543 }
544
545 template<typename T1, typename T2>
CommonConvert(T1 & value,T2 & res,bool littleEndian)546 JSTaggedValue BuiltinsArrayBuffer::CommonConvert(T1 &value, T2 &res, bool littleEndian)
547 {
548 if (!littleEndian) {
549 T1 d = base::bit_cast<T1>(res);
550 if (JSTaggedValue::IsImpureNaN(d)) {
551 return GetTaggedDouble(base::NAN_VALUE);
552 }
553 return GetTaggedDouble(d);
554 } else {
555 if (JSTaggedValue::IsImpureNaN(value)) {
556 return GetTaggedDouble(base::NAN_VALUE);
557 }
558 }
559 return GetTaggedDouble(value);
560 }
561
562
563 template<typename T, BuiltinsArrayBuffer::NumberSize size>
GetValueFromBufferForBigInt(JSThread * thread,uint8_t * block,uint32_t byteIndex,bool littleEndian)564 JSTaggedValue BuiltinsArrayBuffer::GetValueFromBufferForBigInt(JSThread *thread, uint8_t *block,
565 uint32_t byteIndex, bool littleEndian)
566 {
567 ASSERT(block != nullptr);
568 ASSERT_PRINT((std::is_same_v<T, uint64_t> || std::is_same_v<T, int64_t>), "T must be uint64_t/int64_t");
569 auto pTmp = *reinterpret_cast<uint64_t *>(block + byteIndex);
570 if (!littleEndian) {
571 pTmp = LittleEndianToBigEndian64Bit(pTmp);
572 }
573 if constexpr (std::is_same_v<T, uint64_t>) {
574 return BigInt::Uint64ToBigInt(thread, pTmp).GetTaggedValue();
575 }
576 return BigInt::Int64ToBigInt(thread, pTmp).GetTaggedValue();
577 }
578
579
580 template<typename T>
SetValueInBufferForByte(double val,uint8_t * block,uint32_t byteIndex)581 void BuiltinsArrayBuffer::SetValueInBufferForByte(double val, uint8_t *block, uint32_t byteIndex)
582 {
583 ASSERT_PRINT((std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>), "T must be int8/uint8");
584 T res;
585 if (std::isnan(val) || std::isinf(val)) {
586 res = 0;
587 SetTypeData(block, res, byteIndex);
588 return;
589 }
590 auto int32Val = base::NumberHelper::DoubleToInt(val, base::INT32_BITS);
591 auto *resArr = reinterpret_cast<T *>(&int32Val);
592 res = *resArr;
593 SetTypeData(block, res, byteIndex);
594 }
595
SetValueInBufferForUint8Clamped(double val,uint8_t * block,uint32_t byteIndex)596 void BuiltinsArrayBuffer::SetValueInBufferForUint8Clamped(double val, uint8_t *block, uint32_t byteIndex)
597 {
598 uint8_t res;
599 if (std::isnan(val) || val <= 0) {
600 res = 0;
601 } else if (val > UINT8_MAX) {
602 res = UINT8_MAX;
603 } else {
604 // same as ToUint8Clamp
605 res = std::lrint(val);
606 }
607 SetTypeData(block, res, byteIndex);
608 }
609
610 template<typename T>
SetValueInBufferForInteger(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)611 void BuiltinsArrayBuffer::SetValueInBufferForInteger(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
612 {
613 ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
614 ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
615 T res;
616 if (std::isnan(val) || std::isinf(val)) {
617 res = 0;
618 SetTypeData(block, res, byteIndex);
619 return;
620 }
621 auto int32Val = base::NumberHelper::DoubleToInt(val, base::INT32_BITS);
622 // NOLINTNEXTLINE(readability-braces-around-statements)
623 if constexpr (std::is_same_v<T, uint16_t>) {
624 auto *pTmp = reinterpret_cast<int16_t *>(&int32Val);
625 int16_t tmp = *pTmp;
626 res = static_cast<T>(tmp);
627 } else { // NOLINTNEXTLINE(readability-braces-around-statements)
628 auto *pTmp = reinterpret_cast<T *>(&int32Val);
629 res = *pTmp;
630 }
631
632 if (!littleEndian) {
633 res = LittleEndianToBigEndian<T>(res);
634 }
635 SetTypeData(block, res, byteIndex);
636 }
637
638 template<typename T>
SetValueInBufferForFloat(double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)639 void BuiltinsArrayBuffer::SetValueInBufferForFloat(double val, uint8_t *block, uint32_t byteIndex, bool littleEndian)
640 {
641 ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be float type");
642 auto data = static_cast<T>(val);
643 if (!littleEndian) {
644 if constexpr (std::is_same_v<T, float>) {
645 uint32_t res = base::bit_cast<uint32_t>(data);
646 data = base::bit_cast<T>(LittleEndianToBigEndian(res));
647 } else if constexpr (std::is_same_v<T, double>) {
648 uint64_t res = base::bit_cast<uint64_t>(data);
649 data = base::bit_cast<T>(LittleEndianToBigEndian64Bit(res));
650 }
651 }
652 SetTypeData(block, data, byteIndex);
653 }
654
655 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,const JSHandle<JSTaggedValue> & val,JSHandle<JSTaggedValue> & arrBuf,uint32_t byteIndex,bool littleEndian)656 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
657 const JSHandle<JSTaggedValue> &val,
658 JSHandle<JSTaggedValue> &arrBuf,
659 uint32_t byteIndex, bool littleEndian)
660 {
661 ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
662 T value = 0;
663 bool lossless = true;
664 if constexpr(std::is_same_v<T, uint64_t>) {
665 BigInt::BigIntToUint64(thread, val, reinterpret_cast<uint64_t *>(&value), &lossless);
666 } else {
667 BigInt::BigIntToInt64(thread, val, reinterpret_cast<int64_t *>(&value), &lossless);
668 }
669 RETURN_IF_ABRUPT_COMPLETION(thread);
670 if (!littleEndian) {
671 value = LittleEndianToBigEndian64Bit<T>(value);
672 }
673 void *pointer = GetDataPointFromBuffer(thread, arrBuf.GetTaggedValue());
674 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
675 SetTypeData(block, value, byteIndex);
676 }
677
678 template<typename T>
SetValueInBufferForBigInt(JSThread * thread,double val,uint8_t * block,uint32_t byteIndex,bool littleEndian)679 void BuiltinsArrayBuffer::SetValueInBufferForBigInt(JSThread *thread,
680 double val, uint8_t *block,
681 uint32_t byteIndex, bool littleEndian)
682 {
683 ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
684 T value = 0;
685 bool lossless = true;
686
687 JSHandle<JSTaggedValue> valHandle(thread, GetTaggedDouble(val));
688 if constexpr(std::is_same_v<T, uint64_t>) {
689 BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast<uint64_t *>(&value), &lossless);
690 } else {
691 BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast<int64_t *>(&value), &lossless);
692 }
693 RETURN_IF_ABRUPT_COMPLETION(thread);
694 if (!littleEndian) {
695 value = LittleEndianToBigEndian64Bit<T>(value);
696 }
697 SetTypeData(block, value, byteIndex);
698 }
699
FastSetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteIndex,DataViewType type,double val,bool littleEndian)700 JSTaggedValue BuiltinsArrayBuffer::FastSetValueInBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteIndex,
701 DataViewType type, double val, bool littleEndian)
702 {
703 // origin type maybe native JSType or shared JSType.
704 if (arrBuf.IsSendableArrayBuffer() && BuiltinsSendableArrayBuffer::IsDetachedBuffer(thread, arrBuf)) {
705 return JSTaggedValue::Undefined();
706 }
707 if (!arrBuf.IsSendableArrayBuffer() && BuiltinsArrayBuffer::IsDetachedBuffer(thread, arrBuf)) {
708 return JSTaggedValue::Undefined();
709 }
710 void *pointer = GetDataPointFromBuffer(thread, arrBuf);
711 uint8_t *block = reinterpret_cast<uint8_t *>(pointer);
712 return SetValueInBuffer(thread, byteIndex, block, type, val, littleEndian);
713 }
714
SetValueInBuffer(JSThread * thread,uint32_t byteIndex,uint8_t * block,DataViewType type,double val,bool littleEndian)715 JSTaggedValue BuiltinsArrayBuffer::SetValueInBuffer(JSThread* thread, uint32_t byteIndex, uint8_t *block,
716 DataViewType type, double val, bool littleEndian)
717 {
718 switch (type) {
719 case DataViewType::UINT8:
720 SetValueInBufferForByte<uint8_t>(val, block, byteIndex);
721 break;
722 case DataViewType::UINT8_CLAMPED:
723 SetValueInBufferForUint8Clamped(val, block, byteIndex);
724 break;
725 case DataViewType::INT8:
726 SetValueInBufferForByte<int8_t>(val, block, byteIndex);
727 break;
728 case DataViewType::UINT16:
729 SetValueInBufferForInteger<uint16_t>(val, block, byteIndex, littleEndian);
730 break;
731 case DataViewType::INT16:
732 SetValueInBufferForInteger<int16_t>(val, block, byteIndex, littleEndian);
733 break;
734 case DataViewType::UINT32:
735 SetValueInBufferForInteger<uint32_t>(val, block, byteIndex, littleEndian);
736 break;
737 case DataViewType::INT32:
738 SetValueInBufferForInteger<int32_t>(val, block, byteIndex, littleEndian);
739 break;
740 case DataViewType::FLOAT32:
741 SetValueInBufferForFloat<float>(val, block, byteIndex, littleEndian);
742 break;
743 case DataViewType::FLOAT64:
744 SetValueInBufferForFloat<double>(val, block, byteIndex, littleEndian);
745 break;
746 case DataViewType::BIGINT64:
747 SetValueInBufferForBigInt<int64_t>(thread, val, block, byteIndex, littleEndian);
748 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
749 break;
750 case DataViewType::BIGUINT64:
751 SetValueInBufferForBigInt<uint64_t>(thread, val, block, byteIndex, littleEndian);
752 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
753 break;
754 default:
755 LOG_ECMA(FATAL) << "this branch is unreachable";
756 UNREACHABLE();
757 }
758 return JSTaggedValue::Undefined();
759 }
760
GetDataPointFromBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteOffset)761 void *BuiltinsArrayBuffer::GetDataPointFromBuffer(JSThread *thread, JSTaggedValue arrBuf, uint32_t byteOffset)
762 {
763 if (arrBuf.IsByteArray()) {
764 return reinterpret_cast<void *>(ToUintPtr(ByteArray::Cast(arrBuf.GetTaggedObject())->GetData()) + byteOffset);
765 }
766
767 JSArrayBuffer *arrayBuffer = JSArrayBuffer::Cast(arrBuf.GetTaggedObject());
768 if (arrayBuffer == nullptr) {
769 LOG_ECMA(FATAL) << "BuiltinsArrayBuffer::GetDataPointFromBuffer:arrayBuffer is nullptr";
770 UNREACHABLE();
771 }
772 if (arrayBuffer->GetArrayBufferByteLength() == 0) {
773 LOG_ECMA(FATAL) << "BuiltinsArrayBuffer::GetDataPointFromBuffer:arrayBuffer length is 0";
774 UNREACHABLE();
775 }
776
777 JSTaggedValue data = arrayBuffer->GetArrayBufferData(thread);
778 return reinterpret_cast<void *>(ToUintPtr(JSNativePointer::Cast(data.GetTaggedObject())
779 ->GetExternalPointer()) + byteOffset);
780 }
781
TypedArrayToList(JSThread * thread,JSHandle<JSTypedArray> & items)782 JSTaggedValue BuiltinsArrayBuffer::TypedArrayToList(JSThread *thread, JSHandle<JSTypedArray>& items)
783 {
784 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
785 JSHandle<JSTaggedValue> bufferHandle(thread, items->GetViewedArrayBufferOrByteArray(thread));
786 uint32_t arrayLen = items->GetArrayLength();
787 JSHandle<JSObject> newArrayHandle(thread, JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue());
788 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
789 JSHandle<TaggedArray> oldElements(thread, newArrayHandle->GetElements(thread));
790 JSHandle<TaggedArray> elements = (oldElements->GetLength() < arrayLen) ?
791 factory->ExtendArray(oldElements, arrayLen) : oldElements;
792 newArrayHandle->SetElements(thread, elements);
793 uint32_t offset = items->GetByteOffset();
794 uint32_t elementSize = TypedArrayHelper::GetElementSize(items);
795 DataViewType elementType = TypedArrayHelper::GetType(items);
796 uint32_t index = 0;
797 while (index < arrayLen) {
798 uint32_t byteIndex = index * elementSize + offset;
799 JSHandle<JSTaggedValue> result(thread, GetValueFromBuffer(thread, bufferHandle.GetTaggedValue(),
800 byteIndex, elementType, true));
801 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
802 ElementAccessor::Set(thread, newArrayHandle, index, result, true);
803 index++;
804 }
805 JSHandle<JSArray>(newArrayHandle)->SetArrayLength(thread, arrayLen);
806 return newArrayHandle.GetTaggedValue();
807 }
808
809 template<typename T>
FastSetValueInBufferForByte(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val)810 void BuiltinsArrayBuffer::FastSetValueInBufferForByte(uint8_t *byteBeginOffset,
811 uint8_t *byteEndOffset,
812 double val)
813 {
814 ASSERT_PRINT(sizeof(T) == 1, "sizeof(T) must be one");
815 ASSERT_PRINT((std::is_same_v<T, uint8_t> || std::is_same_v<T, int8_t>), "T must be int8/uint8");
816 T res;
817 if (std::isnan(val) || std::isinf(val)) {
818 res = 0;
819 } else {
820 auto int32Val = base::NumberHelper::DoubleToInt(val, base::INT32_BITS);
821 auto *resArr = reinterpret_cast<T *>(&int32Val);
822 res = *resArr;
823 }
824 FastSetTypeData(byteBeginOffset, byteEndOffset, res);
825 }
826
FastSetValueInBufferForUint8Clamped(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val)827 void BuiltinsArrayBuffer::FastSetValueInBufferForUint8Clamped(uint8_t *byteBeginOffset,
828 uint8_t *byteEndOffset,
829 double val)
830 {
831 uint8_t res;
832 if (std::isnan(val) || val <= 0) {
833 res = 0;
834 } else {
835 val = val >= UINT8_MAX ? UINT8_MAX : val;
836 constexpr double HALF = 0.5;
837 val = val == HALF ? 0 : std::round(val);
838 res = static_cast<uint64_t>(val);
839 }
840 FastSetTypeData(byteBeginOffset, byteEndOffset, res);
841 }
842
843 template<typename T>
FastSetValueInBufferForInteger(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val,bool littleEndian)844 void BuiltinsArrayBuffer::FastSetValueInBufferForInteger(uint8_t *byteBeginOffset,
845 uint8_t *byteEndOffset,
846 double val, bool littleEndian)
847 {
848 ASSERT_PRINT(std::is_integral_v<T>, "T must be integral");
849 ASSERT_PRINT(sizeof(T) >= sizeof(uint16_t), "T must have a size more than uint8");
850 T res;
851 if (std::isnan(val) || std::isinf(val)) {
852 res = 0;
853 } else {
854 auto int32Val = base::NumberHelper::DoubleToInt(val, base::INT32_BITS);
855 // NOLINTNEXTLINE(readability-braces-around-statements)
856 if constexpr (std::is_same_v<T, uint16_t>) {
857 auto *pTmp = reinterpret_cast<int16_t *>(&int32Val);
858 int16_t tmp = *pTmp;
859 res = static_cast<T>(tmp);
860 } else { // NOLINTNEXTLINE(readability-braces-around-statements)
861 auto *pTmp = reinterpret_cast<T *>(&int32Val);
862 res = *pTmp;
863 }
864 if (!littleEndian) {
865 res = LittleEndianToBigEndian<T>(res);
866 }
867 }
868 FastSetTypeData(byteBeginOffset, byteEndOffset, res);
869 }
870
871 template<typename T>
FastSetValueInBufferForFloat(uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val,bool littleEndian)872 void BuiltinsArrayBuffer::FastSetValueInBufferForFloat(uint8_t *byteBeginOffset,
873 uint8_t *byteEndOffset,
874 double val, bool littleEndian)
875 {
876 ASSERT_PRINT((std::is_same_v<T, float> || std::is_same_v<T, double>), "T must be float type");
877 auto data = static_cast<T>(val);
878 if (!std::isnan(val)) {
879 if (!littleEndian) {
880 if constexpr (std::is_same_v<T, float>) {
881 uint32_t res = base::bit_cast<uint32_t>(data);
882 data = base::bit_cast<T>(LittleEndianToBigEndian(res));
883 } else if constexpr (std::is_same_v<T, double>) {
884 uint64_t res = base::bit_cast<uint64_t>(data);
885 data = base::bit_cast<T>(LittleEndianToBigEndian64Bit(res));
886 }
887 }
888 }
889 FastSetTypeData(byteBeginOffset, byteEndOffset, data);
890 }
891
892 template<typename T>
FastSetValueInBufferForBigInt(JSThread * thread,uint8_t * byteBeginOffset,uint8_t * byteEndOffset,double val,bool littleEndian)893 void BuiltinsArrayBuffer::FastSetValueInBufferForBigInt(JSThread *thread,
894 uint8_t *byteBeginOffset,
895 uint8_t *byteEndOffset,
896 double val, bool littleEndian)
897 {
898 ASSERT_PRINT((std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>), "T must be int64_t/uint64_t");
899 T value = 0;
900 bool lossless = true;
901
902 JSHandle<JSTaggedValue> valHandle(thread, GetTaggedDouble(val));
903 if constexpr(std::is_same_v<T, uint64_t>) {
904 BigInt::BigIntToUint64(thread, valHandle, reinterpret_cast<uint64_t *>(&value), &lossless);
905 } else {
906 BigInt::BigIntToInt64(thread, valHandle, reinterpret_cast<int64_t *>(&value), &lossless);
907 }
908 RETURN_IF_ABRUPT_COMPLETION(thread);
909 if (!littleEndian) {
910 value = LittleEndianToBigEndian64Bit<T>(value);
911 }
912 FastSetTypeData(byteBeginOffset, byteEndOffset, value);
913 }
914
TryFastSetValueInBuffer(JSThread * thread,JSTaggedValue arrBuf,uint32_t byteBeginOffset,uint32_t byteEndOffset,DataViewType type,double val,bool littleEndian)915 JSTaggedValue BuiltinsArrayBuffer::TryFastSetValueInBuffer([[maybe_unused]] JSThread *thread, JSTaggedValue arrBuf,
916 uint32_t byteBeginOffset, uint32_t byteEndOffset,
917 DataViewType type, double val, bool littleEndian)
918 {
919 uint8_t *beginPointer = reinterpret_cast<uint8_t *>(GetDataPointFromBuffer(thread, arrBuf, byteBeginOffset));
920 uint8_t *endPointer = reinterpret_cast<uint8_t *>(GetDataPointFromBuffer(thread, arrBuf, byteEndOffset));
921 switch (type) {
922 case DataViewType::UINT8:
923 FastSetValueInBufferForByte<uint8_t>(beginPointer, endPointer, val);
924 break;
925 case DataViewType::UINT8_CLAMPED:
926 FastSetValueInBufferForUint8Clamped(beginPointer, endPointer, val);
927 break;
928 case DataViewType::INT8:
929 FastSetValueInBufferForByte<int8_t>(beginPointer, endPointer, val);
930 break;
931 case DataViewType::UINT16:
932 FastSetValueInBufferForInteger<uint16_t>(beginPointer, endPointer, val, littleEndian);
933 break;
934 case DataViewType::INT16:
935 FastSetValueInBufferForInteger<int16_t>(beginPointer, endPointer, val, littleEndian);
936 break;
937 case DataViewType::UINT32:
938 FastSetValueInBufferForInteger<uint32_t>(beginPointer, endPointer, val, littleEndian);
939 break;
940 case DataViewType::INT32:
941 FastSetValueInBufferForInteger<int32_t>(beginPointer, endPointer, val, littleEndian);
942 break;
943 case DataViewType::FLOAT32:
944 FastSetValueInBufferForFloat<float>(beginPointer, endPointer, val, littleEndian);
945 break;
946 case DataViewType::FLOAT64:
947 FastSetValueInBufferForFloat<double>(beginPointer, endPointer, val, littleEndian);
948 break;
949 case DataViewType::BIGINT64:
950 FastSetValueInBufferForBigInt<int64_t>(thread, beginPointer, endPointer, val, littleEndian);
951 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
952 break;
953 case DataViewType::BIGUINT64:
954 FastSetValueInBufferForBigInt<uint64_t>(thread, beginPointer, endPointer, val, littleEndian);
955 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
956 break;
957 default:
958 LOG_ECMA(FATAL) << "this branch is unreachable";
959 UNREACHABLE();
960 }
961 return JSTaggedValue::Undefined();
962 }
963 } // namespace panda::ecmascript::builtins
964