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
23 namespace panda::ecmascript::builtins {
24 // 25.2.2.1 SharedArrayBuffer ( [ length ] )
SharedArrayBufferConstructor(EcmaRuntimeCallInfo * argv)25 JSTaggedValue 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 )
IsSharedArrayBuffer(EcmaRuntimeCallInfo * argv)46 JSTaggedValue 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
IsShared(JSTaggedValue arrayBuffer)73 bool 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 )
AllocateSharedArrayBuffer(JSThread * thread,const JSHandle<JSTaggedValue> & newTarget,uint64_t byteLength)89 JSTaggedValue 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 ]
Species(EcmaRuntimeCallInfo * argv)117 JSTaggedValue 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
GetByteLength(EcmaRuntimeCallInfo * argv)126 JSTaggedValue 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 )
Slice(EcmaRuntimeCallInfo * argv)150 JSTaggedValue 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