diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index a61038398dde4a2caf40b949197f3c2fbf4108b5..fedad1bf5de297a835dfe941fcd73cb2193604ff 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -143,12 +143,41 @@ namespace { enum IsolateDataSlot { kIsolateData = 0, kIsolateSnapshotCreatorSlot = 1, + kIsolateHandlerPoolSlot = 2, }; +// Always compare the final element of IsolateDataSlot with v8 limit. +static_assert(kIsolateHandlerPoolSlot < v8::internal::Internals::kNumIsolateDataSlots); + +struct IsolateHandlerPool { + JSVM_HandlerForOOMError handlerForOOMError = nullptr; + JSVM_HandlerForFatalError handlerForFatalError = nullptr; + JSVM_HandlerForPromiseReject handlerForPromiseReject = nullptr; +}; + +static IsolateHandlerPool* GetIsolateHandlerPool(v8::Isolate* isolate) { + auto pool = isolate->GetData(v8impl::kIsolateHandlerPoolSlot); + return reinterpret_cast(pool); +} + +static IsolateHandlerPool* GetOrCreateIsolateHandlerPool(v8::Isolate* isolate) { + auto *pool = isolate->GetData(v8impl::kIsolateHandlerPoolSlot); + if (pool != nullptr) { + return reinterpret_cast(pool); + } + auto *createdPool = new v8impl::IsolateHandlerPool(); + isolate->SetData(v8impl::kIsolateHandlerPoolSlot, createdPool); + return createdPool; +} enum ContextEmbedderIndex { kContextEnvIndex = 1, }; +static JSVM_Env GetEnvByContext(v8::Local context) { + auto env = context->GetAlignedPointerFromEmbedderData(v8impl::kContextEnvIndex); + return reinterpret_cast(env); +} + struct IsolateData { IsolateData(v8::StartupData* blob) : blob(blob) {} @@ -1480,6 +1509,8 @@ OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, JSVM_VM* result) { } v8impl::CreateIsolateData(isolate, snapshotBlob); *result = reinterpret_cast(isolate); + // Create nullptr placeholder + isolate->SetData(v8impl::kIsolateHandlerPoolSlot, nullptr); return JSVM_OK; } @@ -1493,6 +1524,7 @@ OH_JSVM_DestroyVM(JSVM_VM vm) { auto creator = v8impl::GetIsolateSnapshotCreator(isolate); auto data = v8impl::GetIsolateData(isolate); + auto *handlerPool = v8impl::GetIsolateHandlerPool(isolate); if (creator != nullptr) { delete creator; } else { @@ -1502,6 +1534,9 @@ OH_JSVM_DestroyVM(JSVM_VM vm) { delete data; } + if (handlerPool != nullptr) { + delete handlerPool; + } return JSVM_OK; } @@ -2791,6 +2826,109 @@ JSVM_Status JSVM_CDECL OH_JSVM_ObjectSeal(JSVM_Env env, JSVM_Value object) { return GET_RETURN_STATUS(env); } +static void OnOOMError(const char* location, const v8::OOMDetails& details) { + auto* isolate = v8::Isolate::GetCurrent(); + auto* pool = v8impl::GetIsolateHandlerPool(isolate); + if (pool == nullptr) { + return; + } + auto* handler = pool->handlerForOOMError; + if (handler == nullptr) { + return; + } + (*handler)(location, details.detail, details.is_heap_oom); +} + +static void OnFatalError(const char* location, const char* message) { + auto* isolate = v8::Isolate::GetCurrent(); + auto* pool = v8impl::GetIsolateHandlerPool(isolate); + if (pool == nullptr) { + return; + } + auto* handler = pool->handlerForFatalError; + if (handler == nullptr) { + return; + } + (*handler)(location, message); +} + +JSVM_Status JSVM_CDECL OH_JSVM_SetHandlerForOOMError(JSVM_VM vm, JSVM_HandlerForOOMError handler) { + if (vm == nullptr) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + pool->handlerForOOMError = handler; + isolate->SetOOMErrorHandler(OnOOMError); + return JSVM_OK; +} + +static void OnPromiseReject(v8::PromiseRejectMessage rejectMessage) { + auto* isolate = v8::Isolate::GetCurrent(); + auto* pool = v8impl::GetIsolateHandlerPool(isolate); + if (pool == nullptr) { + return; + } + auto* handler = pool->handlerForPromiseReject; + if (handler == nullptr) { + return; + } + auto context = isolate->GetCurrentContext(); + auto env = v8impl::GetEnvByContext(context); + v8::HandleScope scope(isolate); + v8::Local rejectInfo = v8::Object::New(isolate); + auto strPromise = + v8::String::NewFromUtf8(isolate, "promise").ToLocalChecked(); + (void)rejectInfo->Set(context, strPromise, rejectMessage.GetPromise()); + auto strValue = v8::String::NewFromUtf8(isolate, "value").ToLocalChecked(); + (void)rejectInfo->Set(context, strValue, rejectMessage.GetValue()); + JSVM_Value jsvmRejectInfo = v8impl::JsValueFromV8LocalValue(rejectInfo); + JSVM_PromiseRejectEvent rejectEvent = JSVM_PROMISE_REJECT_OTHER_REASONS; + switch (rejectMessage.GetEvent()) { + case v8::kPromiseRejectWithNoHandler: { + rejectEvent = JSVM_PROMISE_REJECT_WITH_NO_HANDLER; + break; + } + case v8::kPromiseHandlerAddedAfterReject: { + rejectEvent = JSVM_PROMISE_ADD_HANDLER_AFTER_REJECTED; + break; + } + case v8::kPromiseRejectAfterResolved: { + rejectEvent = JSVM_PROMISE_REJECT_AFTER_RESOLVED; + break; + } + case v8::kPromiseResolveAfterResolved: { + rejectEvent = JSVM_PROMISE_RESOLVE_AFTER_RESOLVED; + break; + } + } + (*handler)(env, rejectEvent, jsvmRejectInfo); +} + +JSVM_Status JSVM_CDECL OH_JSVM_SetHandlerForFatalError(JSVM_VM vm, + JSVM_HandlerForFatalError handler) { + if (vm == nullptr) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + pool->handlerForFatalError = handler; + isolate->SetFatalErrorHandler(OnFatalError); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_SetHandlerForPromiseReject( + JSVM_VM vm, JSVM_HandlerForPromiseReject handler) { + if (vm == nullptr) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + pool->handlerForPromiseReject = handler; + isolate->SetPromiseRejectCallback(OnPromiseReject); + return JSVM_OK; +} + JSVM_Status JSVM_CDECL OH_JSVM_IsArray(JSVM_Env env, JSVM_Value value, bool* result) { diff --git a/src/jsvm.h b/src/jsvm.h index 89ce7ffbd5c26454502241c182535d4dec7143b4..f0ce3cb87b29aeede58dd1ed457dc4bf401f54a2 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -102,7 +102,7 @@ EXTERN_C_START /** * @brief Init a JavaScript vm. * - * @param options: The options for initialize the JavaScript VM. + * @param options The options for initialize the JavaScript VM. * @return Returns JSVM funtions result code. * Returns {@link JSVM_OK } in all cases.\n * @since 11 @@ -1515,6 +1515,51 @@ JSVM_EXTERN JSVM_Status OH_JSVM_Instanceof(JSVM_Env env, JSVM_Value constructor, bool* result); +/** + * @brief Set Handler For OOM Error. If this function is invoked repeatedly, + * only the last time takes effect. When handler is null, the previous setting is canceled. + * + * @param vm The environment that the API is invoked under. + * @param handler The handler for OOM Error. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if vm is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetHandlerForOOMError(JSVM_VM vm, + JSVM_HandlerForOOMError handler); + +/** + * @brief Set Handler For Fatal Error. If this function is invoked repeatedly, + * only the last time takes effect. When handler is null, the previous setting is canceled. + * + * @param vm The environment that the API is invoked under. + * @param handler The handler for Fatal Error. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if vm is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetHandlerForFatalError(JSVM_VM vm, + JSVM_HandlerForFatalError handler); + +/** + * @brief Set Handler For Promise Reject. If this function is invoked repeatedly, + * only the last time takes effect. When handler is null, the previous setting is canceled. + * + * @param vm The environment that the API is invoked under. + * @param handler The handler for Promise Reject. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if vm is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetHandlerForPromiseReject(JSVM_VM vm, + JSVM_HandlerForPromiseReject handler); + /** * @brief This API represents invoking the IsArray operation on the object * @@ -1546,9 +1591,9 @@ JSVM_EXTERN JSVM_Status OH_JSVM_IsArraybuffer(JSVM_Env env, /** * @brief This API checks if the Object passed in is a date. * - * @param env: The environment that the API is invoked under. - * @param value: The JavaScript value to check. - * @param result: Whether the given JSVM_Value represents a JavaScript Date object. + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param isDate Whether the given JSVM_Value represents a JavaScript Date object. * @return Returns JSVM funtions result code. * {@link JSVM_OK } If the function executed successfully.\n * @since 11 @@ -2522,8 +2567,8 @@ JSVM_EXTERN JSVM_Status OH_JSVM_ReleaseLock(JSVM_Env env); * @brief Starts the running of the task queue inside the VM. * This task queue can be executed by an external event loop. * - * @param env: The VM instance on which to start the task queue. - * @param result: Whether the task queue was successfully started. + * @param vm The VM instance on which to start the task queue. + * @param result Whether the task queue was successfully started. * @return Returns JSVM funtions result code. * {@link JSVM_OK } If the function executed successfully.\n * @since 12 @@ -2534,7 +2579,7 @@ JSVM_EXTERN JSVM_Status OH_JSVM_PumpMessageLoop(JSVM_VM vm, /** * @brief Check to see if there are any microtasks waiting in the queue, and if there are, execute them. * - * @param env: The VM instance on which to check microtasks. + * @param vm The VM instance on which to check microtasks. * @return Returns JSVM funtions result code. * {@link JSVM_OK } If the function executed successfully.\n * @since 12 diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 4ee1793b311032cf2d6dca111180ba4f7ad2468d..7b27df393953031d5295c2093d8e0fe19cd14655 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -770,6 +770,84 @@ typedef enum { /** WebAssembly cache, generated by OH_JSVM_CreateWasmCache */ JSVM_CACHE_TYPE_WASM, } JSVM_CacheType; + +/** + * @brief The promise-reject event. + * + * @since 16 + */ +typedef enum { + /** Promise is rejected for other reasons. */ + JSVM_PROMISE_REJECT_OTHER_REASONS = 0, + /** Promise rejected with no handler. */ + JSVM_PROMISE_REJECT_WITH_NO_HANDLER = 1, + /** Add the handler after Promise has been rejected. */ + JSVM_PROMISE_ADD_HANDLER_AFTER_REJECTED = 2, + /** After the Promise has been resolved, try to reject the Promise again. */ + JSVM_PROMISE_REJECT_AFTER_RESOLVED = 3, + /** After the Promise has been resolved, try resolving the Promise again. */ + JSVM_PROMISE_RESOLVE_AFTER_RESOLVED = 4, +} JSVM_PromiseRejectEvent; + +/** + * @brief The level of message error. + * + * @since 16 + */ +typedef enum { + /** Log level message. */ + JSVM_MESSAGE_LOG = (1 << 0), + /** Debug level message. */ + JSVM_MESSAGE_DEBUG = (1 << 1), + /** Info level message. */ + JSVM_MESSAGE_INFO = (1 << 2), + /** Error level message. */ + JSVM_MESSAGE_ERROR = (1 << 3), + /** Warning level message. */ + JSVM_MESSAGE_WARNING = (1 << 4), + /** All level message. */ + JSVM_MESSAGE_ALL = JSVM_MESSAGE_LOG | JSVM_MESSAGE_DEBUG | JSVM_MESSAGE_INFO | JSVM_MESSAGE_ERROR | + JSVM_MESSAGE_WARNING, +} JSVM_MessageErrorLevel; + +/** + * @brief Function pointer type of OOM-Error callback. + * + * @param location The location information of the OOM error. + * @param detail The detail of the OOM error. + * @param isHeapOOM Determine whether the OOM type is Heap OOM. + * + * @since 16 + */ +typedef void(JSVM_CDECL* JSVM_HandlerForOOMError)(const char* location, + const char* detail, + bool isHeapOOM); + +/** + * @brief Function pointer type of Fatal-Error callback. + * + * @param location The location information of the Fatal error. + * @param message The message of the Fatal error. + * + * @since 16 + */ +typedef void(JSVM_CDECL* JSVM_HandlerForFatalError)(const char* location, + const char* message); + +/** + * @brief Function pointer type of Promise-Reject callback. + * + * @param env The environment that the function is invoked under. + * @param rejectEvent The promise-reject event. + * @param rejectInfo An JS-object containing two properties: 'promise' and 'value'. + * The 'promise' represents a reference to the Promise object that was rejected. + * The 'value' represents the rejection reason associated with that promise. + * + * @since 16 + */ +typedef void(JSVM_CDECL* JSVM_HandlerForPromiseReject)(JSVM_Env env, + JSVM_PromiseRejectEvent rejectEvent, + JSVM_Value rejectInfo); /** @} */ #endif /* ARK_RUNTIME_JSVM_JSVM_TYPE_H */