1 /* 2 * Copyright (c) 2025 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 #ifndef ECMASCRIPT_CONTAINERS_CONTAINERS_BUFFER_H 17 #define ECMASCRIPT_CONTAINERS_CONTAINERS_BUFFER_H 18 19 #include "ecmascript/base/builtins_base.h" 20 #include "ecmascript/ecma_runtime_call_info.h" 21 22 namespace panda::ecmascript::containers { 23 /** 24 * High performance container interface in jsapi. 25 * */ 26 #define CONTAINER_BUFFER_CHECK(name) \ 27 JSHandle<JSTaggedValue> self = GetThis(argv); \ 28 if (!self->IsJSAPIBuffer()) { \ 29 if (self->IsJSProxy() && JSHandle<JSProxy>::Cast(self)->GetTarget(thread).IsJSAPIBuffer()) { \ 30 self = JSHandle<JSTaggedValue>(thread, JSHandle<JSProxy>::Cast(self)->GetTarget(thread)); \ 31 } else { \ 32 JSTaggedValue error = \ 33 ContainerError::BusinessError(thread, BIND_ERROR, "The " #name " method cannot be bound"); \ 34 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \ 35 } \ 36 } 37 38 #define CONTAINER_BUFFER_ACCESSORS(name) \ 39 JSTaggedValue ContainersBuffer::Write##name##BE(EcmaRuntimeCallInfo *argv) \ 40 { \ 41 ASSERT(argv != nullptr); \ 42 JSThread *thread = argv->GetThread(); \ 43 BUILTINS_API_TRACE(thread, Buffer, Write##name##BE); \ 44 [[maybe_unused]] EcmaHandleScope handleScope(thread); \ 45 CONTAINER_BUFFER_CHECK(Write##name##BE) \ 46 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); \ 47 JSHandle<JSTaggedValue> offset = GetCallArg(argv, 1); \ 48 CHECK_NULL_OR_UNDEFINED(value); \ 49 uint32_t offsetIndex = 0; \ 50 if (offset->IsNumber()) { \ 51 if (IsNegetiveNumber(offset)) { \ 52 std::ostringstream oss; \ 53 oss << "The value of \"offset\" is out of range. It must be >= 0. Received value is: " \ 54 << offset->GetNumber(); \ 55 JSTaggedValue error = \ 56 ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \ 57 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \ 58 } \ 59 if (UNLIKELY(offset->IsDouble())) { \ 60 offsetIndex = static_cast<uint32_t>(offset->GetDouble()); \ 61 } else { \ 62 offsetIndex = static_cast<uint32_t>(offset->GetInt()); \ 63 } \ 64 } \ 65 if (value->IsUndefined() || value->IsNull()) { \ 66 return JSTaggedValue(offsetIndex); \ 67 } \ 68 JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self); \ 69 auto ret = JSAPIFastBuffer::Write##name(thread, buffer, value, offsetIndex, false); \ 70 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); \ 71 return ret; \ 72 } \ 73 JSTaggedValue ContainersBuffer::Write##name##LE(EcmaRuntimeCallInfo *argv) \ 74 { \ 75 ASSERT(argv != nullptr); \ 76 JSThread *thread = argv->GetThread(); \ 77 BUILTINS_API_TRACE(thread, Buffer, Write##name##LE); \ 78 [[maybe_unused]] EcmaHandleScope handleScope(thread); \ 79 CONTAINER_BUFFER_CHECK(Write##name##LE) \ 80 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0); \ 81 JSHandle<JSTaggedValue> offset = GetCallArg(argv, 1); \ 82 CHECK_NULL_OR_UNDEFINED(value); \ 83 uint32_t offsetIndex = 0; \ 84 if (offset->IsNumber()) { \ 85 if (IsNegetiveNumber(offset)) { \ 86 std::ostringstream oss; \ 87 oss << "The value of \"offset\" is out of range. It must be >= 0. Received value is: " \ 88 << offset->GetNumber(); \ 89 JSTaggedValue error = \ 90 ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \ 91 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \ 92 } \ 93 if (UNLIKELY(offset->IsDouble())) { \ 94 offsetIndex = static_cast<uint32_t>(offset->GetDouble()); \ 95 } else { \ 96 offsetIndex = static_cast<uint32_t>(offset->GetInt()); \ 97 } \ 98 } \ 99 if (value->IsUndefined() || value->IsNull()) { \ 100 return JSTaggedValue(offsetIndex); \ 101 } \ 102 JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self); \ 103 auto ret = JSAPIFastBuffer::Write##name(thread, buffer, value, offsetIndex, true); \ 104 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); \ 105 return ret; \ 106 } \ 107 JSTaggedValue ContainersBuffer::Read##name##BE(EcmaRuntimeCallInfo *argv) \ 108 { \ 109 ASSERT(argv != nullptr); \ 110 JSThread *thread = argv->GetThread(); \ 111 BUILTINS_API_TRACE(thread, Buffer, Read##name##BE); \ 112 [[maybe_unused]] EcmaHandleScope handleScope(thread); \ 113 CONTAINER_BUFFER_CHECK(Read##name##BE) \ 114 JSHandle<JSTaggedValue> offset = GetCallArg(argv, 0); \ 115 uint32_t offsetIndex = 0; \ 116 if (offset->IsNumber()) { \ 117 if (IsNegetiveNumber(offset)) { \ 118 std::ostringstream oss; \ 119 oss << "The value of \"offset\" is out of range. It must be >= 0. Received value is: " \ 120 << offset->GetNumber(); \ 121 JSTaggedValue error = \ 122 ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \ 123 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \ 124 } \ 125 if (UNLIKELY(offset->IsDouble())) { \ 126 offsetIndex = static_cast<uint32_t>(offset->GetDouble()); \ 127 } else { \ 128 offsetIndex = static_cast<uint32_t>(offset->GetInt()); \ 129 } \ 130 } \ 131 JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self); \ 132 auto ret = JSAPIFastBuffer::Read##name(thread, buffer, offsetIndex, false); \ 133 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); \ 134 return ret; \ 135 } \ 136 JSTaggedValue ContainersBuffer::Read##name##LE(EcmaRuntimeCallInfo *argv) \ 137 { \ 138 ASSERT(argv != nullptr); \ 139 JSThread *thread = argv->GetThread(); \ 140 BUILTINS_API_TRACE(thread, Buffer, Read##name##LE); \ 141 [[maybe_unused]] EcmaHandleScope handleScope(thread); \ 142 CONTAINER_BUFFER_CHECK(Read##name##LE) \ 143 JSHandle<JSTaggedValue> offset = GetCallArg(argv, 0); \ 144 uint32_t offsetIndex = 0; \ 145 if (offset->IsNumber()) { \ 146 if (IsNegetiveNumber(offset)) { \ 147 std::ostringstream oss; \ 148 oss << "The value of \"offset\" is out of range. It must be >= 0. Received value is: " \ 149 << offset->GetNumber(); \ 150 JSTaggedValue error = \ 151 ContainerError::BusinessError(thread, ErrorFlag::RANGE_ERROR, oss.str().c_str()); \ 152 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, JSTaggedValue::Exception()); \ 153 } \ 154 if (UNLIKELY(offset->IsDouble())) { \ 155 offsetIndex = static_cast<uint32_t>(offset->GetDouble()); \ 156 } else { \ 157 offsetIndex = static_cast<uint32_t>(offset->GetInt()); \ 158 } \ 159 } \ 160 JSHandle<JSAPIFastBuffer> buffer = JSHandle<JSAPIFastBuffer>::Cast(self); \ 161 auto ret = JSAPIFastBuffer::Read##name(thread, buffer, offsetIndex, true); \ 162 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); \ 163 return ret; \ 164 } 165 166 #define CONTAINER_FASTBUFFER_PROTOTYPE_FUNCTIONS(V) \ 167 V("toString", ToString, 3, INVALID) \ 168 V("write", Write, 3, INVALID) \ 169 V("writeUIntBE", WriteUIntBE, 3, INVALID) \ 170 V("writeUIntLE", WriteUIntLE, 3, INVALID) \ 171 V("writeIntBE", WriteIntBE, 3, INVALID) \ 172 V("writeIntLE", WriteIntLE, 3, INVALID) \ 173 V("writeUInt8", WriteUInt8, 2, INVALID) \ 174 V("writeUInt16BE", WriteUInt16BE, 2, INVALID) \ 175 V("writeUInt16LE", WriteUInt16LE, 2, INVALID) \ 176 V("writeUInt32BE", WriteUInt32BE, 2, INVALID) \ 177 V("writeUInt32LE", WriteUInt32LE, 2, INVALID) \ 178 V("writeInt8", WriteInt8, 2, INVALID) \ 179 V("writeInt16BE", WriteInt16BE, 2, INVALID) \ 180 V("writeInt16LE", WriteInt16LE, 2, INVALID) \ 181 V("writeInt32BE", WriteInt32BE, 2, INVALID) \ 182 V("writeInt32LE", WriteInt32LE, 2, INVALID) \ 183 V("writeFloatBE", WriteFloat32BE, 2, INVALID) \ 184 V("writeDoubleBE", WriteFloat64BE, 2, INVALID) \ 185 V("writeFloatLE", WriteFloat32LE, 2, INVALID) \ 186 V("writeDoubleLE", WriteFloat64LE, 2, INVALID) \ 187 V("writeBigUInt64BE", WriteBigUInt64BE, 2, INVALID) \ 188 V("writeBigUInt64LE", WriteBigUInt64LE, 2, INVALID) \ 189 V("writeBigInt64BE", WriteBigInt64BE, 2, INVALID) \ 190 V("writeBigInt64LE", WriteBigInt64LE, 2, INVALID) \ 191 V("readUIntBE", ReadUIntBE, 2, INVALID) \ 192 V("readUIntLE", ReadUIntLE, 2, INVALID) \ 193 V("readIntBE", ReadIntBE, 2, INVALID) \ 194 V("readIntLE", ReadIntLE, 2, INVALID) \ 195 V("readInt8", ReadInt8, 1, INVALID) \ 196 V("readUInt8", ReadUInt8, 1, INVALID) \ 197 V("readUInt16BE", ReadUInt16BE, 1, INVALID) \ 198 V("readUInt16LE", ReadUInt16LE, 1, INVALID) \ 199 V("readUInt32BE", ReadUInt32BE, 1, INVALID) \ 200 V("readUInt32LE", ReadUInt32LE, 1, INVALID) \ 201 V("readInt16BE", ReadInt16BE, 1, INVALID) \ 202 V("readInt16LE", ReadInt16LE, 1, INVALID) \ 203 V("readInt32BE", ReadInt32BE, 1, INVALID) \ 204 V("readInt32LE", ReadInt32LE, 1, INVALID) \ 205 V("readFloatBE", ReadFloat32BE, 1, INVALID) \ 206 V("readFloatLE", ReadFloat32LE, 1, INVALID) \ 207 V("readDoubleBE", ReadFloat64BE, 1, INVALID) \ 208 V("readDoubleLE", ReadFloat64LE, 1, INVALID) \ 209 V("readBigUInt64BE", ReadBigUInt64BE, 1, INVALID) \ 210 V("readBigUInt64LE", ReadBigUInt64LE, 1, INVALID) \ 211 V("readBigInt64BE", ReadBigInt64BE, 1, INVALID) \ 212 V("readBigInt64LE", ReadBigInt64LE, 1, INVALID) \ 213 V("entries", Entries, 0, INVALID) \ 214 V("keys", Keys, 0, INVALID) \ 215 V("values", Values, 0, INVALID) \ 216 V("copy", Copy, 4, INVALID) \ 217 V("fill", Fill, 4, INVALID) \ 218 V("includes", Includes, 3, INVALID) \ 219 V("indexOf", IndexOf, 3, INVALID) \ 220 V("lastIndexOf", LastIndexOf, 3, INVALID) \ 221 V("compare", Compare, 5, INVALID) \ 222 V("equals", Equals, 1, INVALID) 223 224 class ContainersBuffer : public base::BuiltinsBase { 225 public: 226 static JSTaggedValue BufferConstructor(EcmaRuntimeCallInfo *argv); 227 static JSTaggedValue Fill(EcmaRuntimeCallInfo *argv); 228 static JSTaggedValue Copy(EcmaRuntimeCallInfo *argv); 229 static JSTaggedValue Includes(EcmaRuntimeCallInfo *argv); 230 static JSTaggedValue Compare(EcmaRuntimeCallInfo *argv); 231 static JSTaggedValue Equals(EcmaRuntimeCallInfo *argv); 232 static JSTaggedValue IndexOf(EcmaRuntimeCallInfo *argv); 233 static JSTaggedValue LastIndexOf(EcmaRuntimeCallInfo *argv); 234 static JSTaggedValue Write(EcmaRuntimeCallInfo *argv); 235 static JSTaggedValue ToString(EcmaRuntimeCallInfo *argv); 236 static JSTaggedValue WriteIntBE(EcmaRuntimeCallInfo *argv); 237 static JSTaggedValue WriteIntLE(EcmaRuntimeCallInfo *argv); 238 static JSTaggedValue WriteUIntBE(EcmaRuntimeCallInfo *argv); 239 static JSTaggedValue WriteUIntLE(EcmaRuntimeCallInfo *argv); 240 static JSTaggedValue ReadIntBE(EcmaRuntimeCallInfo *argv); 241 static JSTaggedValue ReadIntLE(EcmaRuntimeCallInfo *argv); 242 static JSTaggedValue ReadUIntBE(EcmaRuntimeCallInfo *argv); 243 static JSTaggedValue ReadUIntLE(EcmaRuntimeCallInfo *argv); 244 static JSTaggedValue WriteInt8(EcmaRuntimeCallInfo *argv); 245 static JSTaggedValue WriteUInt8(EcmaRuntimeCallInfo *argv); 246 static JSTaggedValue ReadUInt8(EcmaRuntimeCallInfo *argv); 247 static JSTaggedValue ReadInt8(EcmaRuntimeCallInfo *argv); 248 249 #define DECL_CONTAINER_BUFFER_ACCESSORS(name) \ 250 static JSTaggedValue Write##name##BE(EcmaRuntimeCallInfo *argv); \ 251 static JSTaggedValue Write##name##LE(EcmaRuntimeCallInfo *argv); \ 252 static JSTaggedValue Read##name##BE(EcmaRuntimeCallInfo *argv); \ 253 static JSTaggedValue Read##name##LE(EcmaRuntimeCallInfo *argv); 254 DECL_CONTAINER_BUFFER_ACCESSORS(UInt8) 255 DECL_CONTAINER_BUFFER_ACCESSORS(UInt16) 256 DECL_CONTAINER_BUFFER_ACCESSORS(UInt32) 257 DECL_CONTAINER_BUFFER_ACCESSORS(BigInt64) 258 DECL_CONTAINER_BUFFER_ACCESSORS(Int8) 259 DECL_CONTAINER_BUFFER_ACCESSORS(Int16) 260 DECL_CONTAINER_BUFFER_ACCESSORS(Int32) 261 DECL_CONTAINER_BUFFER_ACCESSORS(BigUInt64) 262 DECL_CONTAINER_BUFFER_ACCESSORS(Float32) 263 DECL_CONTAINER_BUFFER_ACCESSORS(Float64) 264 #undef DECL_CONTAINER_BUFFER_ACCESSORS 265 266 static JSTaggedValue GetSize(EcmaRuntimeCallInfo *argv); 267 static JSTaggedValue GetByteOffset(EcmaRuntimeCallInfo *argv); 268 static JSTaggedValue GetArrayBuffer(EcmaRuntimeCallInfo *argv); 269 static JSTaggedValue Entries(EcmaRuntimeCallInfo *argv); 270 static JSTaggedValue Keys(EcmaRuntimeCallInfo *argv); 271 static JSTaggedValue Values(EcmaRuntimeCallInfo *argv); 272 273 // Excluding the constructor and '@@' internal properties. GetFastBufferPrototypeFunctions()274 static Span<const base::BuiltinFunctionEntry> GetFastBufferPrototypeFunctions() 275 { 276 return Span<const base::BuiltinFunctionEntry>(FASTBUFFER_PROTOTYPE_FUNCTIONS); 277 } 278 279 private: 280 #define CONTAINER_FASTBUFFER_FUNCTION_ENTRY(name, method, length, id) \ 281 base::BuiltinFunctionEntry::Create(name, ContainersBuffer::method, length, BUILTINS_STUB_ID(id)), 282 283 static constexpr std::array FASTBUFFER_PROTOTYPE_FUNCTIONS = { 284 CONTAINER_FASTBUFFER_PROTOTYPE_FUNCTIONS(CONTAINER_FASTBUFFER_FUNCTION_ENTRY)}; 285 #undef CONTAINER_FASTBUFFER_FUNCTION_ENTRY 286 }; 287 } // namespace panda::ecmascript::containers 288 #endif // ECMASCRIPT_CONTAINERS_CONTAINERS_BUFFER_H 289