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