1 /* 2 * Copyright (c) 2021 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_ECMA_MACROS_H 17 #define ECMASCRIPT_ECMA_MACROS_H 18 19 #include "ecmascript/common.h" 20 #include "ecmascript/log_wrapper.h" 21 #include "ecmascript/mem/barriers_get-inl.h" 22 23 #if defined(ENABLE_BYTRACE) 24 #include "hitrace_meter.h" 25 #endif 26 27 #if defined(__cplusplus) 28 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 29 30 #define OPTIONAL_LOG(vm, level) LOG_ECMA_IF(vm->IsOptionalLogEnabled(), level) 31 #define OPTIONAL_LOG_COMPILER(level) LOG_ECMA_IF(IsLogEnabled(), level) 32 33 #if !defined(ENABLE_BYTRACE) 34 #define ECMA_BYTRACE_NAME(level, tag, name, customArgs) 35 #define ECMA_BYTRACE_START_TRACE(level, tag, msg) 36 #define ECMA_BYTRACE_FINISH_TRACE(level, tag) 37 #define ECMA_BYTRACE_COUNT_TRACE(level, tag, name, count) 38 #else 39 #define ECMA_BYTRACE_NAME(level, tag, name, customArgs) HITRACE_METER_NAME_EX(level, tag, name, customArgs) 40 #define ECMA_BYTRACE_START_TRACE(level, tag, msg) StartTraceEx(level, tag, msg) 41 #define ECMA_BYTRACE_FINISH_TRACE(level, tag) FinishTraceEx(level, tag) 42 #define ECMA_BYTRACE_COUNT_TRACE(level, tag, name, count) CountTraceEx(level, tag, name, count) 43 #endif 44 45 #if defined(ENABLE_HITRACE) 46 #define ENQUEUE_JOB_HITRACE(pendingJob, queueType) job::EnqueueJobScope hitraceScope(pendingJob, queueType) 47 #define EXECUTE_JOB_HITRACE(pendingJob) job::ExecuteJobScope hitraceScope(pendingJob) 48 #define ENQUEUE_JOB_TRACE(thread, pendingJob) job::EnqueueJobTrace enqueueJobTrace(thread, pendingJob) 49 #define EXECUTE_JOB_TRACE(thread, pendingJob) job::ExecuteJobTrace executeJobTrace(thread, pendingJob) 50 #else 51 #define ENQUEUE_JOB_HITRACE(pendingJob, queueType) 52 #define EXECUTE_JOB_HITRACE(pendingJob) 53 #define ENQUEUE_JOB_TRACE(thread, pendingJob) 54 #define EXECUTE_JOB_TRACE(thread, pendingJob) 55 #endif 56 57 /* Note: We can't statically decide the element type is a primitive or heap object, especially for */ 58 /* dynamically-typed languages like JavaScript. So we simply skip the read-barrier. */ 59 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 60 #define GET_VALUE(thread, addr, offset) Barriers::GetTaggedValue((thread), (addr), (offset)) 61 62 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 63 #define SET_VALUE_WITH_BARRIER(thread, addr, offset, value) \ 64 if ((value).IsHeapObject()) { \ 65 Barriers::SetObject<true>(thread, addr, offset, (value).GetRawData()); \ 66 } else { \ 67 Barriers::SetPrimitive<JSTaggedType>(addr, offset, (value).GetRawData()); \ 68 } 69 70 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 71 #define SET_VALUE_PRIMITIVE(addr, offset, value) \ 72 Barriers::SetPrimitive<JSTaggedType>(this, offset, (value).GetRawData()) 73 74 #if !defined(NDEBUG) 75 #define FIELD_ACCESS_CHECK(needCheck, name, check) \ 76 if constexpr (needCheck) { \ 77 if (g_isEnableCMCGC && check()) { \ 78 LOG_FULL(FATAL) << #name" field can't be used under check: "#check; \ 79 UNREACHABLE(); \ 80 }; \ 81 } 82 #else 83 #define FIELD_ACCESS_CHECK(needCheck, name, check) 84 #endif 85 86 #define DUMMY_FUNC() false 87 88 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 89 #define ACCESSORS_WITH_DCHECK_BASE(name, offset, endOffset, needCheck, check) \ 90 static constexpr size_t endOffset = (offset) + JSTaggedValue::TaggedTypeSize(); \ 91 JSTaggedValue Get##name(const JSThread *thread) const \ 92 { \ 93 FIELD_ACCESS_CHECK(needCheck, name, check); \ 94 /* Note: We can't statically decide the element type is a primitive or heap object, especially for */ \ 95 /* dynamically-typed languages like JavaScript. So we simply skip the read-barrier. */ \ 96 return JSTaggedValue(Barriers::GetTaggedValue(thread, this, offset)); \ 97 } \ 98 template<BarrierMode mode = WRITE_BARRIER, typename T> \ 99 void Set##name(const JSThread *thread, JSHandle<T> value) \ 100 { \ 101 FIELD_ACCESS_CHECK(needCheck, name, check); \ 102 if constexpr (mode == WRITE_BARRIER) { \ 103 if (value.GetTaggedValue().IsHeapObject()) { \ 104 Barriers::SetObject<true>(thread, this, offset, value.GetTaggedValue().GetRawData()); \ 105 } else { \ 106 Barriers::SetPrimitive<JSTaggedType>(this, offset, value.GetTaggedValue().GetRawData()); \ 107 } \ 108 } else { \ 109 static_assert(mode == SKIP_BARRIER); \ 110 Barriers::SetPrimitive<JSTaggedType>(this, offset, value.GetTaggedValue().GetRawData()); \ 111 } \ 112 } \ 113 template<BarrierMode mode = WRITE_BARRIER> \ 114 void Set##name(const JSThread *thread, JSTaggedValue value) \ 115 { \ 116 FIELD_ACCESS_CHECK(needCheck, name, check); \ 117 if constexpr (mode == WRITE_BARRIER) { \ 118 if (value.IsHeapObject()) { \ 119 Barriers::SetObject<true>(thread, this, offset, value.GetRawData()); \ 120 } else { \ 121 Barriers::SetPrimitive<JSTaggedType>(this, offset, value.GetRawData()); \ 122 } \ 123 } else { \ 124 static_assert(mode == SKIP_BARRIER); \ 125 Barriers::SetPrimitive<JSTaggedType>(this, offset, value.GetRawData()); \ 126 } \ 127 } 128 129 #define ACCESSORS(name, offset, endOffset) \ 130 ACCESSORS_WITH_DCHECK_BASE(name, offset, endOffset, false, DUMMY_FUNC) 131 132 #define ACCESSORS_WITH_RB_MODE(name, offset, endOffset) \ 133 ACCESSORS(name, offset, endOffset) \ 134 template <RBMode mode = RBMode::DEFAULT_RB> \ 135 JSTaggedValue Get##name(const JSThread *thread) const \ 136 { \ 137 return JSTaggedValue(Barriers::GetTaggedValue<mode>(thread, this, offset)); \ 138 } \ 139 140 #define ACCESSORS_DCHECK(name, offset, endOffset, check) \ 141 ACCESSORS_WITH_DCHECK_BASE(name, offset, endOffset, true, check) 142 143 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 144 #define ACCESSORS_SYNCHRONIZED_PRIMITIVE_FIELD(name, type, offset, endOffset) \ 145 static constexpr size_t endOffset = (offset) + sizeof(type); \ 146 inline type Get##name() const \ 147 { \ 148 return reinterpret_cast<volatile std::atomic<type> *>(ToUintPtr(this) + offset) \ 149 ->load(std::memory_order_acquire); \ 150 } \ 151 inline void Set##name(type value) \ 152 { \ 153 reinterpret_cast<volatile std::atomic<type> *>(ToUintPtr(this) + offset) \ 154 ->store(value, std::memory_order_release); \ 155 } 156 157 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 158 #define ACCESSORS_SYNCHRONIZED_WITH_DCHECK_BASE(name, offset, endOffset, needCheck, check) \ 159 static constexpr size_t endOffset = (offset) + JSTaggedValue::TaggedTypeSize(); \ 160 JSTaggedValue Get##name(const JSThread *thread) const \ 161 { \ 162 FIELD_ACCESS_CHECK(needCheck, name, check); \ 163 /* Note: We can't statically decide the element type is a primitive or heap object, especially for */ \ 164 /* dynamically-typed languages like JavaScript. So we simply skip the read-barrier. */ \ 165 /* Synchronized means it will restrain the store and load in atomic. */ \ 166 return JSTaggedValue(Barriers::GetTaggedValueAtomic(thread, this, offset)); \ 167 } \ 168 template<typename T> \ 169 void Set##name(const JSThread *thread, JSHandle<T> value) \ 170 { \ 171 FIELD_ACCESS_CHECK(needCheck, name, check); \ 172 bool isPrimitive = !value.GetTaggedValue().IsHeapObject(); \ 173 Barriers::SynchronizedSetObject(thread, this, offset, value.GetTaggedValue().GetRawData(), isPrimitive); \ 174 } \ 175 void Set##name(const JSThread *thread, JSTaggedValue value) \ 176 { \ 177 FIELD_ACCESS_CHECK(needCheck, name, check); \ 178 bool isPrimitive = !value.IsHeapObject(); \ 179 Barriers::SynchronizedSetObject(thread, this, offset, value.GetRawData(), isPrimitive); \ 180 } 181 182 #define ACCESSORS_SYNCHRONIZED(name, offset, endOffset) \ 183 ACCESSORS_SYNCHRONIZED_WITH_DCHECK_BASE(name, offset, endOffset, false, DUMMY_FUNC) 184 185 #define ACCESSORS_SYNCHRONIZED_DCHECK(name, offset, endOffset, check) \ 186 ACCESSORS_SYNCHRONIZED_WITH_DCHECK_BASE(name, offset, endOffset, true, check) 187 188 #define ACCESSORS_SYNCHRONIZED_DCHECK_WITH_RB_MODE(name, offset, endOffset, check) \ 189 ACCESSORS_SYNCHRONIZED_DCHECK(name, offset, endOffset, check) \ 190 template <RBMode mode = RBMode::DEFAULT_RB> \ 191 JSTaggedValue Get##name(const JSThread *thread) const \ 192 { \ 193 FIELD_ACCESS_CHECK(true, name, check); \ 194 return JSTaggedValue(Barriers::GetTaggedValueAtomic<mode>(thread, this, offset)); \ 195 } 196 197 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 198 #define DEFINE_ALIGN_SIZE(offset) \ 199 static constexpr size_t SIZE = ((offset) + sizeof(JSTaggedType) - 1U) & (~(sizeof(JSTaggedType) - 1U)) 200 201 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 202 #define ACCESSORS_FIXED_SIZE_FIELD_DCHECK_BASE(name, type, sizeType, offset, endOffset, needCheck, check) \ 203 static_assert(sizeof(type) <= sizeof(sizeType)); \ 204 static constexpr size_t endOffset = (offset) + sizeof(sizeType); \ 205 inline void Set##name(type value) \ 206 { \ 207 FIELD_ACCESS_CHECK(needCheck, name, check); \ 208 Barriers::SetPrimitive<type>(this, offset, value); \ 209 } \ 210 inline type Get##name() const \ 211 { \ 212 FIELD_ACCESS_CHECK(needCheck, name, check); \ 213 return Barriers::GetPrimitive<type>(this, offset); \ 214 } 215 216 #define ACCESSORS_FIXED_SIZE_FIELD(name, type, sizeType, offset, endOffset) \ 217 ACCESSORS_FIXED_SIZE_FIELD_DCHECK_BASE(name, type, sizeType, offset, endOffset, false, DUMMY_FUNC) 218 219 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 220 #define ACCESSORS_NATIVE_FIELD(name, type, offset, endOffset) \ 221 ACCESSORS_FIXED_SIZE_FIELD(name, type *, type *, offset, endOffset) 222 223 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 224 #define ACCESSORS_PRIMITIVE_FIELD(name, type, offset, endOffset) \ 225 ACCESSORS_FIXED_SIZE_FIELD(name, type, type, offset, endOffset) 226 227 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 228 #define ACCESSORS_PRIMITIVE_FIELD_DCHECK(name, type, offset, endOffset, check) \ 229 ACCESSORS_FIXED_SIZE_FIELD_DCHECK_BASE(name, type, type, offset, endOffset, true, check) 230 231 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 232 #define ACCESSORS_BIT_FIELD(name, offset, endOffset) \ 233 ACCESSORS_FIXED_SIZE_FIELD(name, uint32_t, uint32_t, offset, endOffset) \ 234 inline void Clear##name() \ 235 { \ 236 Set##name(0UL); \ 237 } 238 239 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 240 #define SET_GET_BIT_FIELD(bitFieldName, name, type) \ 241 inline type Get##name() const \ 242 { \ 243 return name##Bits::Decode(Get##bitFieldName()); \ 244 } \ 245 inline void Set##name(type t) \ 246 { \ 247 Set##bitFieldName(name##Bits::Update(Get##bitFieldName(), t)); \ 248 } 249 250 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 251 #define ACCESSORS_PRIMITIVE_FIELD_ATOMIC(name, type, offset) \ 252 inline void AtomicSet##name(type value) \ 253 { \ 254 volatile auto *atomicField = reinterpret_cast<volatile std::atomic<type> *>(ToUintPtr(this) + (offset)); \ 255 atomicField->store(value, std::memory_order_release); \ 256 } \ 257 inline type AtomicGet##name() const \ 258 { \ 259 volatile auto *atomicField = reinterpret_cast<volatile std::atomic<type> *>(ToUintPtr(this) + (offset)); \ 260 return atomicField->load(std::memory_order_acquire); \ 261 } 262 263 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 264 #define ACCESSORS_PRIMITIVE_FIELD_HAS_ATOMIC_INTERFACE(name, type, offset, endOffset) \ 265 ACCESSORS_PRIMITIVE_FIELD(name, type, offset, endOffset) \ 266 ACCESSORS_PRIMITIVE_FIELD_ATOMIC(name, type, offset) 267 268 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 269 #define FIRST_BIT_FIELD(bitFieldName, name, type, bits) \ 270 using name##Bits = BitField<type, 0, bits>; \ 271 SET_GET_BIT_FIELD(bitFieldName, name, type) 272 273 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 274 #define NEXT_BIT_FIELD(bitFieldName, name, type, bits, lastName) \ 275 using name##Bits = lastName##Bits::NextField<type, bits>; \ 276 SET_GET_BIT_FIELD(bitFieldName, name, type) 277 278 #if !defined(NDEBUG) 279 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 280 #define DASSERT(cond) assert(cond) 281 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 282 #define DASSERT_PRINT(cond, message) \ 283 if (auto cond_val = (cond); UNLIKELY(!(cond_val))) { \ 284 std::cerr << (message) << std::endl; \ 285 ASSERT(#cond &&cond_val); \ 286 } 287 #else // NDEBUG 288 #define DASSERT(cond) static_cast<void>(0) // NOLINT(cppcoreguidelines-macro-usage) 289 #define DASSERT_PRINT(cond, message) static_cast<void>(0) // NOLINT(cppcoreguidelines-macro-usage) 290 #endif // !NDEBUG 291 292 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 293 #define RASSERT(cond) assert(cond) 294 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 295 #define RASSERT_PRINT(cond, message) \ 296 if (auto cond_val = cond; UNLIKELY(!(cond_val))) { \ 297 std::cerr << message << std::endl; \ 298 RASSERT(#cond &&cond_val); \ 299 } 300 301 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 302 #define RETURN_IF_ABRUPT_COMPLETION(thread) \ 303 do { \ 304 if ((thread)->HasPendingException()) { \ 305 return; \ 306 } \ 307 } while (false) 308 309 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 310 #define RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, value) \ 311 do { \ 312 if ((thread)->HasPendingException()) { \ 313 return (value); \ 314 } \ 315 } while (false) 316 317 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 318 #define RETURN_VALUE_IF_ABRUPT_COMPLETION_WITH_DATA_DELETE(thread, value, flagsStr) \ 319 do { \ 320 if ((thread)->HasPendingException()) { \ 321 delete[] flagsStr; \ 322 return (value); \ 323 } \ 324 } while (false) 325 326 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 327 #define RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread) \ 328 do { \ 329 if ((thread)->HasPendingException()) { \ 330 return JSTaggedValue::Exception(); \ 331 } \ 332 } while (false) 333 334 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 335 #define RETURN_EXCEPTION_AND_POP_JOINSTACK(thread, value) \ 336 do { \ 337 if ((thread)->HasPendingException()) { \ 338 ArrayJoinStack::Pop(thread, value); \ 339 return JSTaggedValue::Exception(); \ 340 } \ 341 } while (false) 342 343 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 344 #define RETURN_HANDLE_IF_ABRUPT_COMPLETION(type, thread) \ 345 do { \ 346 if ((thread)->HasPendingException()) { \ 347 return JSHandle<type>(thread, JSTaggedValue::Exception()); \ 348 } \ 349 } while (false) 350 351 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 352 #define ASSERT_NO_ABRUPT_COMPLETION(thread) ASSERT(!(thread)->HasPendingException()); 353 354 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 355 #define SET_DATE_VALUE(name, code, isLocal) \ 356 static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ 357 { \ 358 ASSERT(argv); \ 359 JSThread *thread = argv->GetThread(); \ 360 [[maybe_unused]] EcmaHandleScope handleScope(thread); \ 361 JSHandle<JSTaggedValue> msg = GetThis(argv); \ 362 if (!msg->IsDate()) { \ 363 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ 364 } \ 365 JSHandle<JSDate> jsDate(msg); \ 366 JSTaggedValue result = jsDate->SetDateValue(argv, code, isLocal); \ 367 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); \ 368 jsDate->SetTimeValue(thread, result); \ 369 return result; \ 370 } 371 372 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 373 #define DATE_TO_STRING(name) \ 374 static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ 375 { \ 376 ASSERT(argv); \ 377 JSThread *thread = argv->GetThread(); \ 378 [[maybe_unused]] EcmaHandleScope handleScope(thread); \ 379 JSHandle<JSTaggedValue> msg = GetThis(argv); \ 380 if (!msg->IsDate()) { \ 381 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ 382 } \ 383 if (std::isnan(JSDate::Cast(msg->GetTaggedObject())->GetTimeValue(thread).GetDouble())) { \ 384 THROW_RANGE_ERROR_AND_RETURN(thread, "range error", JSTaggedValue::Exception()); \ 385 } \ 386 return JSDate::Cast(msg->GetTaggedObject())->name(thread); \ 387 } 388 389 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 390 #define DATE_STRING(name) \ 391 static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ 392 { \ 393 ASSERT(argv); \ 394 JSThread *thread = argv->GetThread(); \ 395 [[maybe_unused]] EcmaHandleScope handleScope(thread); \ 396 JSHandle<JSTaggedValue> msg = GetThis(argv); \ 397 if (!msg->IsDate()) { \ 398 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ 399 } \ 400 if (std::isnan(JSDate::Cast(msg->GetTaggedObject())->GetTimeValue(thread).GetDouble())) { \ 401 return thread->GetEcmaVM()->GetFactory()->NewFromASCII("Invalid Date").GetTaggedValue(); \ 402 } \ 403 return JSDate::Cast(msg->GetTaggedObject())->name(thread); \ 404 } 405 406 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 407 #define GET_DATE_VALUE(name, code, isLocal) \ 408 static JSTaggedValue name(EcmaRuntimeCallInfo *argv) \ 409 { \ 410 ASSERT(argv); \ 411 JSThread *thread = argv->GetThread(); \ 412 [[maybe_unused]] EcmaHandleScope handleScope(thread); \ 413 JSHandle<JSTaggedValue> msg = GetThis(argv); \ 414 if (!msg->IsDate()) { \ 415 THROW_TYPE_ERROR_AND_RETURN(thread, "Not a Date Object", JSTaggedValue::Exception()); \ 416 } \ 417 JSHandle<JSDate> jsDate(msg); \ 418 double result = jsDate->GetDateValue(thread, jsDate->GetTimeValue(thread).GetDouble(), code, isLocal); \ 419 return GetTaggedDouble(result); \ 420 } 421 422 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 423 #define THROW_NEW_ERROR_AND_RETURN(thread, error) \ 424 do { \ 425 if (!(thread)->HasPendingException()) { \ 426 (thread)->SetException(error); \ 427 } \ 428 return; \ 429 } while (false) 430 431 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 432 #define THROW_ERROR(thread, type, message) \ 433 do { \ 434 if ((thread)->HasPendingException()) { \ 435 return; \ 436 } \ 437 ObjectFactory *_factory = (thread)->GetEcmaVM()->GetFactory(); \ 438 JSHandle<JSObject> _error = _factory->GetJSError(type, message, StackCheck::NO); \ 439 (thread)->SetException(_error.GetTaggedValue()); \ 440 return; \ 441 } while (false) 442 443 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 444 #define THROW_UNCATCHABLE_ERROR(thread, type, message) \ 445 do { \ 446 EcmaVM *_ecmaVm = (thread)->GetEcmaVM(); \ 447 ObjectFactory *_factory = _ecmaVm->GetFactory(); \ 448 JSHandle<JSObject> _error = _factory->GetJSError(type, message, StackCheck::NO); \ 449 (thread)->SetException(_error.GetTaggedValue()); \ 450 _ecmaVm->HandleUncatchableError(); \ 451 } while (false) 452 453 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 454 #define THROW_NEW_ERROR_AND_RETURN_VALUE(thread, error, value) \ 455 do { \ 456 if (!(thread)->HasPendingException()) { \ 457 (thread)->SetException(error); \ 458 } \ 459 return (value); \ 460 } while (false) 461 462 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 463 #define THROW_NEW_ERROR_AND_RETURN_HANDLE(thread, errorType, type, message) \ 464 do { \ 465 if ((thread)->HasPendingException()) { \ 466 return JSHandle<type>(thread, JSTaggedValue::Exception()); \ 467 } \ 468 ObjectFactory *_factory = (thread)->GetEcmaVM()->GetFactory(); \ 469 JSHandle<JSObject> _error = _factory->GetJSError(errorType, message, StackCheck::NO); \ 470 (thread)->SetException(_error.GetTaggedValue()); \ 471 return JSHandle<type>(thread, JSTaggedValue::Exception()); \ 472 } while (false) 473 474 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 475 #define THROW_NEW_ERROR_WITH_MSG_AND_RETURN_VALUE(thread, errorType, message, value) \ 476 do { \ 477 if ((thread)->HasPendingException()) { \ 478 return (value); \ 479 } \ 480 ObjectFactory *_factory = (thread)->GetEcmaVM()->GetFactory(); \ 481 JSHandle<JSObject> _error = _factory->GetJSError(errorType, message, ecmascript::StackCheck::NO); \ 482 (thread)->SetException(_error.GetTaggedValue()); \ 483 return (value); \ 484 } while (false) 485 486 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 487 #define THROW_MODULE_NOT_FOUND_ERROR_WITH_RETURN_VALUE(thread, requestStr, currentRecord, value) \ 488 do { \ 489 if ((thread)->HasPendingException()) { \ 490 return (value); \ 491 } \ 492 ObjectFactory *_factory = (thread)->GetEcmaVM()->GetFactory(); \ 493 CString normalizeStr = ModulePathHelper::ReformatPath(currentRecord); \ 494 CString msg = "Cannot find module '" + requestStr + "' imported from '" + normalizeStr + "'."; \ 495 JSHandle<JSObject> _error = _factory->GetJSError(ErrorType::REFERENCE_ERROR, \ 496 msg.c_str(), StackCheck::NO); \ 497 (thread)->SetException(_error.GetTaggedValue()); \ 498 return (value); \ 499 } while (false) 500 501 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 502 #define THROW_TYPE_ERROR_AND_RETURN(thread, message, value) \ 503 THROW_NEW_ERROR_WITH_MSG_AND_RETURN_VALUE(thread, ErrorType::TYPE_ERROR, message, value) 504 505 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 506 #define THROW_RANGE_ERROR_AND_RETURN(thread, message, value) \ 507 THROW_NEW_ERROR_WITH_MSG_AND_RETURN_VALUE(thread, ErrorType::RANGE_ERROR, message, value) 508 509 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 510 #define THROW_URI_ERROR_AND_RETURN(thread, message, value) \ 511 THROW_NEW_ERROR_WITH_MSG_AND_RETURN_VALUE(thread, ErrorType::URI_ERROR, message, value) 512 513 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 514 #define THROW_SYNTAX_ERROR_AND_RETURN(thread, message, value) \ 515 THROW_NEW_ERROR_WITH_MSG_AND_RETURN_VALUE(thread, ErrorType::SYNTAX_ERROR, message, value) 516 517 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 518 #define THROW_REFERENCE_ERROR_AND_RETURN(thread, message, value) \ 519 THROW_NEW_ERROR_WITH_MSG_AND_RETURN_VALUE(thread, ErrorType::REFERENCE_ERROR, message, value) 520 521 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 522 #define THROW_TYPE_ERROR(thread, message) \ 523 THROW_ERROR(thread, ErrorType::TYPE_ERROR, message) 524 525 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 526 #define THROW_OOM_ERROR(thread, message) \ 527 THROW_UNCATCHABLE_ERROR(thread, ErrorType::OOM_ERROR, message) 528 529 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 530 #define THROW_TERMINATION_ERROR(thread, message) \ 531 THROW_ERROR(thread, ErrorType::TERMINATION_ERROR, message) 532 533 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 534 #define RETURN_STACK_BEFORE_THROW_IF_ASM(thread) \ 535 do { \ 536 if ((thread)->IsAsmInterpreter()) { \ 537 FrameIterator it(const_cast<JSTaggedType *>((thread)->GetCurrentSPFrame()), (thread)); \ 538 it.Advance(); \ 539 (thread)->SetCurrentSPFrame(it.GetSp()); \ 540 } \ 541 } while (false) 542 543 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 544 #define RETURN_REJECT_PROMISE_IF_ABRUPT(thread, value, capability) \ 545 do { \ 546 const GlobalEnvConstants *globalConst = (thread)->GlobalConstants(); \ 547 if ((value).GetTaggedValue().IsCompletionRecord()) { \ 548 JSHandle<CompletionRecord> record = JSHandle<CompletionRecord>::Cast(value); \ 549 if (record->IsThrow()) { \ 550 JSHandle<JSTaggedValue> reject(thread, (capability)->GetReject(thread)); \ 551 JSHandle<JSTaggedValue> undefine = globalConst->GetHandledUndefined(); \ 552 EcmaRuntimeCallInfo *info = \ 553 EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefine, undefine, 1); \ 554 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); \ 555 info->SetCallArg(record->GetValue(thread)); \ 556 JSTaggedValue res = JSFunction::Call(info); \ 557 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, res); \ 558 return (capability)->GetPromise(thread); \ 559 } \ 560 } \ 561 if ((thread)->HasPendingException()) { \ 562 (thread)->ClearException(); \ 563 JSHandle<JSTaggedValue> reject(thread, (capability)->GetReject(thread)); \ 564 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined(); \ 565 EcmaRuntimeCallInfo *info = \ 566 EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1); \ 567 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); \ 568 info->SetCallArg(value.GetTaggedValue()); \ 569 JSTaggedValue res = JSFunction::Call(info); \ 570 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, res); \ 571 return (capability)->GetPromise(thread); \ 572 } \ 573 } while (false) 574 575 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 576 #define RETURN_COMPLETION_IF_ABRUPT(thread, value) \ 577 do { \ 578 if ((thread)->HasPendingException()) { \ 579 JSHandle<CompletionRecord> completionRecord = \ 580 factory->NewCompletionRecord(CompletionRecordType::THROW, value); \ 581 return (completionRecord); \ 582 } \ 583 } while (false) 584 585 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 586 #define RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, value) \ 587 do { \ 588 if ((thread)->HasPendingException()) { \ 589 JSHandle<CompletionRecord> completionRecord = \ 590 factory->NewCompletionRecord(CompletionRecordType::THROW, value); \ 591 return (completionRecord).GetTaggedValue(); \ 592 } \ 593 } while (false) 594 595 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 596 #define DECL_DUMP() \ 597 void Dump(const JSThread *thread, std::ostream &os) const DUMP_API_ATTR; \ 598 void Dump(const JSThread *thread) const DUMP_API_ATTR \ 599 { \ 600 Dump(thread, std::cout); \ 601 } \ 602 void DumpForSnapshot(const JSThread *thread, std::vector<Reference> &vec) const; 603 604 #endif // defined(__cplusplus) 605 606 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 607 #define DECL_CAST(TYPE) \ 608 static TYPE *Cast(TaggedObject *object) \ 609 { \ 610 ASSERT(JSTaggedValue(object).Is##TYPE()); \ 611 return reinterpret_cast<TYPE *>(object); \ 612 } 613 614 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 615 #define DECL_VISIT_ARRAY(BEGIN_OFFSET, REF_LENGTH, LENGTH) \ 616 template <VisitType visitType, class DerivedVisitor> \ 617 void VisitRangeSlot(BaseObjectVisitor<DerivedVisitor> &visitor) \ 618 { \ 619 ArrayBodyIterator<visitType, (BEGIN_OFFSET)>::IterateBody(this, visitor, (REF_LENGTH), (LENGTH)); \ 620 } 621 622 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 623 #define DECL_VISIT_OBJECT(BEGIN_OFFSET, END_OFFSET) \ 624 template <VisitType visitType, class DerivedVisitor> \ 625 void VisitRangeSlot(BaseObjectVisitor<DerivedVisitor> &visitor) \ 626 { \ 627 ObjectBodyIterator<visitType, (BEGIN_OFFSET), (END_OFFSET), SIZE>::IterateRefBody(this, visitor); \ 628 } 629 630 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 631 #define DECL_VISIT_PRIMITIVE_OBJECT() \ 632 template <VisitType visitType, class DerivedVisitor> \ 633 void VisitRangeSlot(BaseObjectVisitor<DerivedVisitor> &visitor) \ 634 { \ 635 PrimitiveObjectBodyIterator<visitType, SIZE>::IterateBody(this, visitor); \ 636 } 637 638 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 639 #define DECL_VISIT_NATIVE_FIELD(BEGIN_OFFSET, END_OFFSET) \ 640 template <VisitType visitType, class DerivedVisitor> \ 641 void VisitRangeSlot(BaseObjectVisitor<DerivedVisitor> &visitor) \ 642 { \ 643 ObjectBodyIterator<visitType, (BEGIN_OFFSET), (END_OFFSET), SIZE>::IterateNativeBody(this, visitor); \ 644 } \ 645 646 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 647 #define DECL_VISIT_OBJECT_FOR_JS_OBJECT(PARENTCLASS, BEGIN_OFFSET, END_OFFSET) \ 648 template <VisitType visitType, class DerivedVisitor> \ 649 void VisitRangeSlot(BaseObjectVisitor<DerivedVisitor> &visitor) \ 650 { \ 651 VisitObjects<visitType>(visitor); \ 652 JSObjectBodyIterator<visitType, SIZE>::IterateBody(this, visitor); \ 653 } \ 654 template <VisitType visitType, class DerivedVisitor> \ 655 void VisitObjects(BaseObjectVisitor<DerivedVisitor> &visitor) \ 656 { \ 657 PARENTCLASS::VisitObjects<visitType>(visitor); \ 658 static constexpr size_t PARENT_SIZE = PARENTCLASS::SIZE; \ 659 ObjectBodyIterator<visitType, (BEGIN_OFFSET), \ 660 (END_OFFSET), SIZE, PARENT_SIZE>::IterateDerivedRefBody(this, visitor); \ 661 } 662 663 #if ECMASCRIPT_ENABLE_CAST_CHECK 664 #define CAST_CHECK(CAST_TYPE, CHECK_METHOD) \ 665 static inline CAST_TYPE *Cast(TaggedObject *object) \ 666 { \ 667 if (!JSTaggedValue(object).CHECK_METHOD()) { \ 668 LOG_ECMA(ERROR) << "cast failed, dump: " << JSTaggedValue(object); \ 669 std::abort(); \ 670 } \ 671 return static_cast<CAST_TYPE *>(object); \ 672 } \ 673 static inline const CAST_TYPE *ConstCast(const TaggedObject *object) \ 674 { \ 675 if (!JSTaggedValue(object).CHECK_METHOD()) { \ 676 LOG_ECMA(ERROR) << "cast failed, dump: " << JSTaggedValue(object); \ 677 std::abort(); \ 678 } \ 679 return static_cast<const CAST_TYPE *>(object); \ 680 } \ 681 static inline CAST_TYPE *Cast(JSTaggedValue value) \ 682 { \ 683 if (!value.CHECK_METHOD()) { \ 684 LOG_ECMA(ERROR) << "cast failed, dump: " << value; \ 685 std::abort(); \ 686 } \ 687 return static_cast<CAST_TYPE *>(value.GetTaggedObject()); \ 688 } 689 #else 690 #define CAST_CHECK(CAST_TYPE, CHECK_METHOD) \ 691 static inline CAST_TYPE *Cast(TaggedObject *object) \ 692 { \ 693 ASSERT(JSTaggedValue(object).CHECK_METHOD()); \ 694 return static_cast<CAST_TYPE *>(object); \ 695 } \ 696 static const inline CAST_TYPE *ConstCast(const TaggedObject *object) \ 697 { \ 698 ASSERT(JSTaggedValue(object).CHECK_METHOD()); \ 699 return static_cast<const CAST_TYPE *>(object); \ 700 } \ 701 static inline CAST_TYPE *Cast(JSTaggedValue value) \ 702 { \ 703 ASSERT(value.CHECK_METHOD()); \ 704 return static_cast<CAST_TYPE *>(value.GetTaggedObject()); \ 705 } 706 #endif 707 708 #define CAST_NO_CHECK(CAST_TYPE) \ 709 static inline CAST_TYPE *Cast(TaggedObject *object) \ 710 { \ 711 return static_cast<CAST_TYPE *>(object); \ 712 } \ 713 static const inline CAST_TYPE *ConstCast(const TaggedObject *object) \ 714 { \ 715 return static_cast<const CAST_TYPE *>(object); \ 716 } 717 718 #define CHECK_OBJECT_SIZE(size) \ 719 if ((size) == 0) { \ 720 LOG_FULL(FATAL) << __func__ << ":" << __LINE__ << " objectSize is " << (size); \ 721 } 722 723 #define CHECK_REGION_END(begin, end) \ 724 if ((begin) > (end)) { \ 725 LOG_FULL(FATAL) << __func__ << ":" << __LINE__ << " begin: " << (begin) << " end: " << (end); \ 726 } 727 728 #define CHECK_JS_THREAD(vm) \ 729 if (!(vm)->GetJSThread()->IsCrossThreadExecutionEnable()) { \ 730 ASSERT((vm)->GetJSThread()->GetThreadId() == JSThread::GetCurrentThreadId()); \ 731 } 732 #define CHECK_DAEMON_THREAD() \ 733 ASSERT(os::thread::GetCurrentThreadId() == DaemonThread::GetInstance()->GetThreadId()) 734 735 #if !defined(NDEBUG) 736 #define STACK_ASSERT_SCOPE(thread) [[maybe_unused]] StackAssertScope stackAssertScope = StackAssertScope(thread) 737 #else 738 #define STACK_ASSERT_SCOPE(thread) static_cast<void>(0) 739 #endif 740 741 #if !defined(NDEBUG) 742 #define BUILTINS_ENTRY_DEBUG_LOG() LOG_BUILTINS(DEBUG) << "Builtins C++ " << __func__ 743 #else 744 #define BUILTINS_ENTRY_DEBUG_LOG() static_cast<void>(0) 745 #endif 746 747 #if defined(ARK_NOT_SUPPORT_INTL_GLOBAL) 748 #define ARK_SUPPORT_INTL_RETURN_STR(msg) "Please use import intl lib "#msg 749 #define ARK_SUPPORT_INTL_RETURN(thread, message) \ 750 THROW_TYPE_ERROR(thread, ARK_SUPPORT_INTL_RETURN_STR(message)) 751 #define ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, message) \ 752 THROW_TYPE_ERROR_AND_RETURN(thread, \ 753 ARK_SUPPORT_INTL_RETURN_STR(message), JSTaggedValue::Exception()) 754 #else 755 #define ARK_SUPPORT_INTL_RETURN(thread, message) static_cast<void>(0) 756 #define ARK_SUPPORT_INTL_RETURN_JSVALUE(thread, message) static_cast<void>(0) 757 #endif 758 759 #define STACK_LIMIT_CHECK(thread, retVal) \ 760 do { \ 761 if ((thread)->DoStackLimitCheck()) { \ 762 return (retVal); \ 763 } \ 764 } while (0) 765 766 #define STACK_LIMIT_CHECK_VOID(thread) \ 767 do { \ 768 if ((thread)->DoStackLimitCheck()) { \ 769 return; \ 770 } \ 771 } while (0) 772 773 #define CHECK_SLOTID_BREAK(slotId) \ 774 if ((slotId) == 0xff) { \ 775 break; \ 776 } 777 778 #define CHECK_INPUT_NULLPTR(ptr, msg) \ 779 if ((ptr) == nullptr) { \ 780 LOG_FULL(FATAL) << (msg); \ 781 UNREACHABLE(); \ 782 } 783 784 #endif // ECMASCRIPT_ECMA_MACROS_H 785