1/*
2 * Copyright (c) 2022-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_sharedarraybuffer.h"
17
18#include "ecmascript/global_env.h"
19#include "ecmascript/interpreter/interpreter.h"
20#include "ecmascript/js_arraybuffer.h"
21#include "ecmascript/js_function.h"
22
23namespace panda::ecmascript::builtins {
24// 25.2.2.1 SharedArrayBuffer ( [ length ] )
25JSTaggedValue BuiltinsSharedArrayBuffer::SharedArrayBufferConstructor(EcmaRuntimeCallInfo *argv)
26{
27    ASSERT(argv);
28    BUILTINS_API_TRACE(argv->GetThread(), SharedArrayBuffer, Constructor);
29    JSThread *thread = argv->GetThread();
30    [[maybe_unused]] EcmaHandleScope handleScope(thread);
31    JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
32    // 1. If NewTarget is undefined, throw a TypeError exception.
33    if (newTarget->IsUndefined()) {
34        THROW_TYPE_ERROR_AND_RETURN(thread, "newtarget is undefined", JSTaggedValue::Exception());
35    }
36    JSHandle<JSTaggedValue> lengthHandle = GetCallArg(argv, 0);
37    // 2. Let byteLength be ? ToIndex(length).
38    JSTaggedNumber lenNum = JSTaggedValue::ToIndex(thread, lengthHandle);
39    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
40    uint64_t byteLength = lenNum.GetNumber();
41    // 3. Return ? AllocateSharedArrayBuffer(NewTarget, byteLength).
42    return AllocateSharedArrayBuffer(thread, newTarget, byteLength);
43}
44
45// 25.2.1.2 IsSharedArrayBuffer ( obj )
46JSTaggedValue BuiltinsSharedArrayBuffer::IsSharedArrayBuffer(EcmaRuntimeCallInfo *argv)
47{
48    ASSERT(argv);
49    BUILTINS_API_TRACE(argv->GetThread(), SharedArrayBuffer, IsSharedArrayBuffer);
50    [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
51    JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
52    // 1. If Type(arg) is not Object,and it not has an [[ArrayBufferData]] internal slot return false.
53    if (!arg->IsECMAObject()) {
54        return BuiltinsSharedArrayBuffer::GetTaggedBoolean(false);
55    }
56    if (!arg->IsSharedArrayBuffer()) {
57        return BuiltinsSharedArrayBuffer::GetTaggedBoolean(false);
58    }
59    // 2. Let bufferData be obj.[[ArrayBufferData]].
60    JSHandle<JSArrayBuffer> buffer(arg);
61    JSTaggedValue bufferdata = buffer->GetArrayBufferData();
62    // 3. If bufferData is null, return false.
63    if (bufferdata.IsNull()) {
64        return BuiltinsSharedArrayBuffer::GetTaggedBoolean(false);
65    }
66    // 4. If this ArrayBuffer is not shared, return false.
67    if (buffer->GetShared() == false) {
68        return BuiltinsSharedArrayBuffer::GetTaggedBoolean(false);
69    }
70    return BuiltinsSharedArrayBuffer::GetTaggedBoolean(true);
71}
72
73bool BuiltinsSharedArrayBuffer::IsShared(JSTaggedValue arrayBuffer)
74{
75    if (!arrayBuffer.IsSharedArrayBuffer()) {
76        return false;
77    }
78    JSArrayBuffer *buffer = JSArrayBuffer::Cast(arrayBuffer.GetTaggedObject());
79    JSTaggedValue dataSlot = buffer->GetArrayBufferData();
80    // 2. If arrayBuffer’s [[ArrayBufferData]] internal slot is null, return false.
81    if (dataSlot.IsNull()) {
82        return false;
83    }
84    // 3. If this ArrayBuffer is not shared, return false.
85    return buffer->GetShared();
86}
87
88// 25.2.1.1 AllocateSharedArrayBuffer ( constructor, byteLength )
89JSTaggedValue BuiltinsSharedArrayBuffer::AllocateSharedArrayBuffer(
90    JSThread *thread, const JSHandle<JSTaggedValue> &newTarget, uint64_t byteLength)
91{
92    BUILTINS_API_TRACE(thread, SharedArrayBuffer, AllocateSharedArrayBuffer);
93    /**
94     * 1. Let obj be ? OrdinaryCreateFromConstructor(constructor, "%SharedArrayBuffer.prototype%",
95     * «[[ArrayBufferData]], [[ArrayBufferByteLength]] »).
96     * */
97    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
98    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
99    JSHandle<JSTaggedValue> shaArrBufFunc = env->GetSharedArrayBufferFunction();
100    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(shaArrBufFunc), newTarget);
101    // 2. ReturnIfAbrupt
102    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
103    // 4. Let block be CreateSharedByteDataBlock(byteLength).
104    if (byteLength > INT_MAX) {
105        THROW_RANGE_ERROR_AND_RETURN(thread, "Out of range", JSTaggedValue::Exception());
106    }
107    JSHandle<JSArrayBuffer> sharedArrayBuffer(obj);
108    // 6. Set obj’s [[ArrayBufferData]] internal slot to block.
109    factory->NewJSSharedArrayBufferData(sharedArrayBuffer, byteLength);
110    // 7. Set obj’s [[ArrayBufferByteLength]] internal slot to byteLength.
111    sharedArrayBuffer->SetArrayBufferByteLength(static_cast<uint32_t>(byteLength));
112    // 8. Return obj.
113    return sharedArrayBuffer.GetTaggedValue();
114}
115
116// 25.2.3.2 get SharedArrayBuffer [ @@species ]
117JSTaggedValue BuiltinsSharedArrayBuffer::Species(EcmaRuntimeCallInfo *argv)
118{
119    ASSERT(argv);
120    BUILTINS_API_TRACE(argv->GetThread(), SharedArrayBuffer, Species);
121    // 1. Return the this value.
122    return GetThis(argv).GetTaggedValue();
123}
124
125// 25.2.4.1 get SharedArrayBuffer.prototype.byteLength
126JSTaggedValue BuiltinsSharedArrayBuffer::GetByteLength(EcmaRuntimeCallInfo *argv)
127{
128    ASSERT(argv);
129    BUILTINS_API_TRACE(argv->GetThread(), SharedArrayBuffer, GetByteLength);
130    JSThread *thread = argv->GetThread();
131    [[maybe_unused]] EcmaHandleScope handleScope(thread);
132    // 1. Let O be the this value.
133    JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
134    // 2. If Type(O) is not Object, throw a TypeError exception.
135    if (!thisHandle->IsECMAObject()) {
136        THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
137    }
138    // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
139    if (!thisHandle->IsSharedArrayBuffer()) {
140        THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
141    }
142    JSHandle<JSArrayBuffer> shaArrBuf(thisHandle);
143    // 5. Let length be the value of O’s [[SharedArrayBufferByteLength]] internal slot.
144    uint32_t length = shaArrBuf->GetArrayBufferByteLength();
145    // 6. Return length.
146    return JSTaggedValue(length);
147}
148
149// 25.2.4.3 SharedArrayBuffer.prototype.slice ( start, end )
150JSTaggedValue BuiltinsSharedArrayBuffer::Slice(EcmaRuntimeCallInfo *argv)
151{
152    ASSERT(argv);
153    BUILTINS_API_TRACE(argv->GetThread(), SharedArrayBuffer, Slice);
154    JSThread *thread = argv->GetThread();
155    [[maybe_unused]] EcmaHandleScope handleScope(thread);
156    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
157    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
158    // 1. Let O be the this value.
159    JSHandle<JSTaggedValue> thisHandle = GetThis(argv);
160    // 2. If Type(O) is not Object, throw a TypeError exception.
161    if (!thisHandle->IsECMAObject()) {
162        THROW_TYPE_ERROR_AND_RETURN(thread, "this value is not an object", JSTaggedValue::Exception());
163    }
164    JSHandle<JSArrayBuffer> shaArrBuf(thisHandle);
165    // 3. If O does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
166    if (!thisHandle->IsSharedArrayBuffer()) {
167        THROW_TYPE_ERROR_AND_RETURN(thread, "don't have internal slot", JSTaggedValue::Exception());
168    }
169    // 4. If IsSharedArrayBuffer(O) is false, throw a TypeError exception.
170    if (!IsShared(thisHandle.GetTaggedValue())) {
171        THROW_TYPE_ERROR_AND_RETURN(thread, "this value not IsSharedArrayBuffer", JSTaggedValue::Exception());
172    }
173    // 5. Let len be the value of O’s [[ArrayBufferByteLength]] internal slot.
174    int32_t len = static_cast<int32_t>(shaArrBuf->GetArrayBufferByteLength());
175    JSHandle<JSTaggedValue> startHandle = GetCallArg(argv, 0);
176    // 6. Let relativeStart be ToInteger(start).
177    JSTaggedNumber relativeStart = JSTaggedValue::ToInteger(thread, startHandle);
178    // 7. ReturnIfAbrupt(relativeStart).
179    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
180    int32_t start = base::NumberHelper::DoubleInRangeInt32(relativeStart.GetNumber());
181    int32_t end = 0;
182    int32_t first = 0;
183    int32_t last = 0;
184    // 8. If relativeStart < 0, let first be max((len + relativeStart),0); else let first be min(relativeStart, len).
185    if (start < 0) {
186        first = std::max((len + start), 0);
187    } else {
188        first = std::min(start, len);
189    }
190    // 9. If end is undefined, let relativeEnd be len; else let relativeEnd be ToInteger(end).
191    JSHandle<JSTaggedValue> endHandle = GetCallArg(argv, 1);
192    if (endHandle->IsUndefined()) {
193        end = len;
194    } else {
195        JSTaggedNumber relativeEnd = JSTaggedValue::ToInteger(thread, endHandle);
196        // 10. ReturnIfAbrupt(relativeEnd).
197        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
198        end = base::NumberHelper::DoubleInRangeInt32(relativeEnd.GetNumber());
199    }
200    // 11. If relativeEnd < 0, let final be max((len + relativeEnd),0); else let final be min(relativeEnd, len).
201    last = end < 0 ? std::max((len + end), 0) : std::min(end, len);
202    // 12. Let newLen be max(final-first,0).
203    uint32_t newLen = std::max((last - first), 0);
204    // 13. Let ctor be SpeciesConstructor(O, %SharedArrayBuffer%).
205    JSHandle<JSTaggedValue> defaultConstructor = env->GetSharedArrayBufferFunction();
206    JSHandle<JSObject> objHandle(thisHandle);
207    JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, objHandle, defaultConstructor);
208    // 14. ReturnIfAbrupt(ctor).
209    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
210    // 15. Let new be Construct(ctor, «newLen»).
211    JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
212    EcmaRuntimeCallInfo *info =
213        EcmaInterpreter::NewRuntimeCallInfo(thread, constructor, undefined, undefined, 1);
214    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
215    info->SetCallArg(JSTaggedValue(newLen));
216    JSTaggedValue taggedNewArrBuf = JSFunction::Construct(info);
217    JSHandle<JSTaggedValue> newArrBuf(thread, taggedNewArrBuf);
218    // 16. ReturnIfAbrupt(new).
219    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
220    // 17. If new does not have an [[ArrayBufferData]] internal slot, throw a TypeError exception.
221    if (!newArrBuf->IsSharedArrayBuffer()) {
222        THROW_TYPE_ERROR_AND_RETURN(thread, "don't have bufferdata internal slot", JSTaggedValue::Exception());
223    }
224    // 18. If IsSharedArrayBuffer(new) is false, throw a TypeError exception.
225    if (!IsShared(newArrBuf.GetTaggedValue())) {
226        THROW_TYPE_ERROR_AND_RETURN(thread, "new arrayBuffer not IsSharedArrayBuffer", JSTaggedValue::Exception());
227    }
228    // 19. If SameValue(new, O) is true, throw a TypeError exception.
229    if (JSTaggedValue::SameValue(newArrBuf.GetTaggedValue(), thisHandle.GetTaggedValue())) {
230        THROW_TYPE_ERROR_AND_RETURN(thread, "value of new arraybuffer and this is same", JSTaggedValue::Exception());
231    }
232    JSHandle<JSArrayBuffer> newJsShaArrBuf(newArrBuf);
233    // 20. If the value of new’s [[ArrayBufferByteLength]] internal slot < newLen, throw a TypeError exception.
234    uint32_t newArrBufLen = newJsShaArrBuf->GetArrayBufferByteLength();
235    if (newArrBufLen < newLen) {
236        THROW_TYPE_ERROR_AND_RETURN(thread, "new array buffer length smaller than newlen", JSTaggedValue::Exception());
237    }
238    if (newLen > 0) {
239        // 23. Let fromBuf be the value of O’s [[ArrayBufferData]] internal slot.
240        JSTaggedValue from = shaArrBuf->GetArrayBufferData();
241        // 24. Let toBuf be the value of new’s [[ArrayBufferData]] internal slot.
242        JSTaggedValue to = newJsShaArrBuf->GetArrayBufferData();
243        // 25. Perform CopyDataBlockBytes(toBuf, fromBuf, first, newLen).
244        JSArrayBuffer::CopyDataBlockBytes(to, from, first, newLen);
245    }
246    // Return new.
247    return newArrBuf.GetTaggedValue();
248}
249}  // namespace panda::ecmascript::builtins