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