From db92f7e18262837d45523a57cd3c73f90ae4dce7 Mon Sep 17 00:00:00 2001 From: liuweili Date: Thu, 28 Nov 2024 11:32:48 +0800 Subject: [PATCH 1/9] =?UTF-8?q?jsvm=E6=96=B0=E5=A2=9EAPI=20external=20stri?= =?UTF-8?q?ng&&private=20=EF=BC=88cherry=20picked=20commit=20from=20=20Signed-off-by:=20liuweili=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liuweili --- src/js_native_api_v8.cc | 247 ++++++++++++++++++++++++++++++++++++---- src/js_native_api_v8.h | 42 ++++--- src/jsvm.h | 161 ++++++++++++++++++++++++++ src/jsvm_types.h | 7 ++ 4 files changed, 422 insertions(+), 35 deletions(-) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index df11b358..ce6095ca 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -728,7 +728,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->namedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->namedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -761,7 +761,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->namedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->namedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; JSVM_Value result = nullptr; @@ -792,7 +792,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->namedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->namedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -826,7 +826,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->namedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->namedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -859,7 +859,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->indexedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->indexedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -892,7 +892,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->indexedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->indexedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } JSVM_Value result = nullptr; @@ -924,7 +924,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->indexedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->indexedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -958,7 +958,7 @@ class PropertyCallbackWrapperBase : public CallbackWrapper { JSVM_Value innerData = nullptr; if (_cb->indexedPropertyData_ != nullptr) { v8impl::Reference* reference = reinterpret_cast(_cb->indexedPropertyData_); - innerData = v8impl::JsValueFromV8LocalValue(reference->Get()); + innerData = v8impl::JsValueFromV8LocalValue(reference->GetValue()); } bool exceptionOccurred = false; @@ -1117,7 +1117,11 @@ inline JSVM_Status Wrap(JSVM_Env env, // Currently, V8 has no API to detect if a symbol is local or global. // Until we have a V8 API for it, we consider that all symbols can be weak. // This matches the current Node-API behavior. -inline bool CanBeHeldWeakly(v8::Local value) { +inline bool CanBeHeldWeakly(v8::Local data) { + if (data->IsPrivate()) { + return false; + } + auto value = data.As(); return value->IsObject() || value->IsSymbol(); } @@ -1233,10 +1237,11 @@ void RefBase::Finalize() { } template -Reference::Reference(JSVM_Env env, v8::Local value, Args&&... args) +Reference::Reference(JSVM_Env env, v8::Local data, bool isValue, Args&&... args) : RefBase(env, std::forward(args)...), - persistent_(env->isolate, value), - can_be_weak_(CanBeHeldWeakly(value)), + persistent_(env->isolate, data), + is_value(isValue), + can_be_weak_(isValue && CanBeHeldWeakly(data.As())), deleted_by_user(false), wait_callback(false) { if (RefCount() == 0) { @@ -1250,14 +1255,16 @@ Reference::~Reference() { } Reference* Reference::New(JSVM_Env env, - v8::Local value, + v8::Local data, uint32_t initialRefcount, Ownership ownership, JSVM_Finalize finalizeCallback, void* finalizeData, - void* finalizeHint) { + void* finalizeHint, + bool isValue) { return new Reference(env, - value, + data, + isValue, initialRefcount, ownership, finalizeCallback, @@ -1293,11 +1300,20 @@ uint32_t Reference::Unref() { return refcount; } -v8::Local Reference::Get() { +v8::Local Reference::GetValue() { + DCHECK(is_value); if (persistent_.IsEmpty()) { return v8::Local(); } else { - return v8::Local::New(env_->isolate, persistent_); + return v8::Local::New(env_->isolate, persistent_).As(); + } +} + +v8::Local Reference::GetData() { + if (persistent_.IsEmpty()) { + return v8::Local(); + } else { + return v8::Local::New(env_->isolate, persistent_); } } @@ -1416,11 +1432,13 @@ OH_JSVM_Init(const JSVM_InitOptions* options) { #endif v8::V8::InitializePlatform(v8impl::g_platform.get()); +#ifdef TARGET_OHOS if (node::ReadSystemXpmState()) { int secArgc = SECARGCNT; char *secArgv[SECARGCNT] = {const_cast("jsvm"), const_cast("--jitless")}; v8::V8::SetFlagsFromCommandLine(&secArgc, secArgv, false); } +#endif if (options && options->argc && options->argv) { v8::V8::SetFlagsFromCommandLine(options->argc, options->argv, options->removeFlags); @@ -1877,7 +1895,7 @@ OH_JSVM_CreateCodeCache(JSVM_Env env, CHECK_ARG(env, script); CHECK_ARG(env, data); CHECK_ARG(env, length); - auto jsvmData = reinterpret_cast(script); + auto jsvmData = reinterpret_cast(script); auto v8script = jsvmData->ToV8Local(env->isolate); v8::ScriptCompiler::CachedData* cache; cache = v8::ScriptCompiler::CreateCodeCache(v8script->GetUnboundScript()); @@ -1900,7 +1918,7 @@ OH_JSVM_RunScript(JSVM_Env env, JSVM_Script script, JSVM_Value* result) { CHECK_ARG(env, script); CHECK_ARG(env, result); - auto jsvmData = reinterpret_cast(script); + auto jsvmData = reinterpret_cast(script); auto v8script = jsvmData->ToV8Local(env->isolate); auto script_result = v8script->Run(env->context()); CHECK_MAYBE_EMPTY(env, script_result, JSVM_GENERIC_FAILURE); @@ -2913,6 +2931,60 @@ JSVM_Status JSVM_CDECL OH_JSVM_CreateArrayWithLength(JSVM_Env env, return jsvm_clear_last_error(env); } +JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringLatin1(JSVM_Env env, + char* str, + size_t length, + JSVM_Finalize finalizeCallback, + void* finalizeHint, + JSVM_Value* result, + bool* copied) { + CHECK_ARG(env, copied); + return v8impl::NewExternalString( + env, + str, + length, + finalizeCallback, + finalizeHint, + result, + copied, + OH_JSVM_CreateStringLatin1, + [&](v8::Isolate* isolate) { + if (length == JSVM_AUTO_LENGTH) { + length = (std::string_view(str)).length(); + } + auto resource = new v8impl::ExternalOneByteStringResource( + env, str, length, finalizeCallback, finalizeHint); + return v8::String::NewExternalOneByte(isolate, resource); + }); +} + +JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringUtf16(JSVM_Env env, + char16_t* str, + size_t length, + JSVM_Finalize finalizeCallback, + void* finalizeHint, + JSVM_Value* result, + bool* copied) { + CHECK_ARG(env, copied); + return v8impl::NewExternalString( + env, + str, + length, + finalizeCallback, + finalizeHint, + result, + copied, + OH_JSVM_CreateStringUtf16, + [&](v8::Isolate* isolate) { + if (length == JSVM_AUTO_LENGTH) { + length = (std::u16string_view(str)).length(); + } + auto resource = new v8impl::ExternalStringResource( + env, str, length, finalizeCallback, finalizeHint); + return v8::String::NewExternalTwoByte(isolate, resource); + }); +} + JSVM_Status JSVM_CDECL OH_JSVM_CreateStringLatin1(JSVM_Env env, const char* str, size_t length, @@ -3098,6 +3170,90 @@ JSVM_Status JSVM_CDECL OH_JSVM_SymbolFor(JSVM_Env env, return jsvm_clear_last_error(env); } +JSVM_Status JSVM_CDECL OH_JSVM_CreatePrivate(JSVM_Env env, + JSVM_Value description, + JSVM_Data* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Isolate* isolate = env->isolate; + + if (description == nullptr) { + *result = v8impl::JsDataFromV8LocalData(v8::Private::New(isolate)); + } else { + v8::Local v8Name = v8impl::V8LocalValueFromJsValue(description); + RETURN_STATUS_IF_FALSE(env, v8Name->IsString(), JSVM_STRING_EXPECTED); + + *result = v8impl::JsDataFromV8LocalData(v8::Private::New(isolate, v8Name.As())); + } + + return jsvm_clear_last_error(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_SetPrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key, + JSVM_Value value) { + JSVM_PREAMBLE(env); + CHECK_ARG(env, object); + CHECK_ARG(env, key); + CHECK_ARG(env, value); + + auto context = env->context(); + auto obj = v8impl::V8LocalValueFromJsValue(object); + RETURN_STATUS_IF_FALSE(env, obj->IsObject(), JSVM_OBJECT_EXPECTED); + auto privateKey = v8impl::V8LocalDataFromJsData(key); + RETURN_STATUS_IF_FALSE(env, privateKey->IsPrivate(), JSVM_INVALID_ARG); + auto val = v8impl::V8LocalValueFromJsValue(value); + + auto set_maybe = obj.As()->SetPrivate(context, privateKey.As(), val); + + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, set_maybe.FromMaybe(false), JSVM_GENERIC_FAILURE); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_GetPrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key, + JSVM_Value *result) { + JSVM_PREAMBLE(env); + CHECK_ARG(env, object); + CHECK_ARG(env, key); + CHECK_ARG(env, result); + + auto context = env->context(); + auto obj = v8impl::V8LocalValueFromJsValue(object); + RETURN_STATUS_IF_FALSE(env, obj->IsObject(), JSVM_OBJECT_EXPECTED); + auto privateKey = v8impl::V8LocalDataFromJsData(key); + RETURN_STATUS_IF_FALSE(env, privateKey->IsPrivate(), JSVM_INVALID_ARG); + + auto getMaybe = obj.As()->GetPrivate(context, privateKey.As()); + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, getMaybe, JSVM_GENERIC_FAILURE); + + v8::Local val = getMaybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(val); + return GET_RETURN_STATUS(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_DeletePrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key) { + JSVM_PREAMBLE(env); + CHECK_ARG(env, object); + CHECK_ARG(env, key); + + auto context = env->context(); + auto obj = v8impl::V8LocalValueFromJsValue(object); + RETURN_STATUS_IF_FALSE(env, obj->IsObject(), JSVM_OBJECT_EXPECTED); + auto privateKey = v8impl::V8LocalDataFromJsData(key); + RETURN_STATUS_IF_FALSE(env, privateKey->IsPrivate(), JSVM_INVALID_ARG); + + auto deleteMaybe = obj.As()->DeletePrivate(context, privateKey.As()); + auto success = deleteMaybe.IsJust() && deleteMaybe.FromMaybe(false); + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, success, JSVM_GENERIC_FAILURE); + return GET_RETURN_STATUS(env); +} + static inline JSVM_Status set_error_code(JSVM_Env env, v8::Local error, JSVM_Value code, @@ -3945,6 +4101,33 @@ JSVM_Status JSVM_CDECL OH_JSVM_CreateReference(JSVM_Env env, return jsvm_clear_last_error(env); } +// ref for data can not be weak, so initialRefcount must be greater than 0 +JSVM_Status JSVM_CDECL OH_JSVM_CreateDataReference(JSVM_Env env, + JSVM_Data data, + uint32_t initialRefcount, + JSVM_Ref* result) { + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, data); + CHECK_ARG(env, result); + RETURN_STATUS_IF_FALSE(env, initialRefcount != 0, JSVM_INVALID_ARG); + + v8::Local v8_value = v8impl::V8LocalDataFromJsData(data); + v8impl::Reference* reference = v8impl::Reference::New( + env, + v8_value, + initialRefcount, + v8impl::Ownership::kUserland, + nullptr, + nullptr, + nullptr, + false); + + *result = reinterpret_cast(reference); + return jsvm_clear_last_error(env); +} + // Deletes a reference. The referenced value is released, and may be GC'd unless // there are other references to it. JSVM_Status JSVM_CDECL OH_JSVM_DeleteReference(JSVM_Env env, JSVM_Ref ref) { @@ -4024,7 +4207,27 @@ JSVM_Status JSVM_CDECL OH_JSVM_GetReferenceValue(JSVM_Env env, CHECK_ARG(env, result); v8impl::Reference* reference = reinterpret_cast(ref); - *result = v8impl::JsValueFromV8LocalValue(reference->Get()); + RETURN_STATUS_IF_FALSE(env, reference->IsValue(), JSVM_INVALID_ARG); + *result = v8impl::JsValueFromV8LocalValue(reference->GetValue()); + + return jsvm_clear_last_error(env); +} + +// Attempts to get a referenced value. If the reference is weak, the value might +// no longer be available, in that case the call is still successful but the +// result is NULL. +JSVM_Status JSVM_CDECL OH_JSVM_GetReferenceData(JSVM_Env env, + JSVM_Ref ref, + JSVM_Data* result) { + // Omit JSVM_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw + // JS exceptions. + CHECK_ENV(env); + CHECK_ARG(env, ref); + CHECK_ARG(env, result); + + v8impl::Reference* reference = reinterpret_cast(ref); + RETURN_STATUS_IF_FALSE(env, !reference->IsValue(), JSVM_INVALID_ARG); + *result = v8impl::JsDataFromV8LocalData(reference->GetData()); return jsvm_clear_last_error(env); } @@ -5207,7 +5410,7 @@ JSVM_Status JSVM_CDECL OH_JSVM_ObjectSetPrototypeOf(JSVM_Env env, JSVM_Status JSVM_CDECL OH_JSVM_RetainScript(JSVM_Env env, JSVM_Script script) { CHECK_ENV(env); - auto jsvmData = reinterpret_cast(script); + auto jsvmData = reinterpret_cast(script); RETURN_STATUS_IF_FALSE(env, jsvmData && !jsvmData->isGlobal, JSVM_INVALID_ARG); @@ -5220,7 +5423,7 @@ JSVM_Status JSVM_CDECL OH_JSVM_RetainScript(JSVM_Env env, JSVM_Script script) { JSVM_Status JSVM_CDECL OH_JSVM_ReleaseScript(JSVM_Env env, JSVM_Script script) { CHECK_ENV(env); - auto jsvmData = reinterpret_cast(script); + auto jsvmData = reinterpret_cast(script); RETURN_STATUS_IF_FALSE(env, jsvmData && jsvmData->isGlobal, JSVM_INVALID_ARG); diff --git a/src/js_native_api_v8.h b/src/js_native_api_v8.h index 7e799c7c..540766b6 100644 --- a/src/js_native_api_v8.h +++ b/src/js_native_api_v8.h @@ -58,13 +58,13 @@ class Finalizer; class Agent; } // end of namespace v8impl -struct JSVM_Data__ { +struct JSVM_Script_Data__ { public: using SourcePtr = std::variant, v8::Global>; using DataType = enum { kJsvmScript }; template - JSVM_Data__(T ptr, bool retained, DataType type = kJsvmScript) + JSVM_Script_Data__(T ptr, bool retained, DataType type = kJsvmScript) : taggedPointer(ptr), isGlobal(retained), type(type) {} @@ -193,11 +193,11 @@ struct JSVM_Env__ { } template - JSVM_Data__ *NewJsvmData(T srcPtr, JSVM_Data__::DataType type = JSVM_Data__::kJsvmScript) { + JSVM_Script_Data__ *NewJsvmData(T srcPtr, JSVM_Script_Data__::DataType type = JSVM_Script_Data__::kJsvmScript) { if (dataStack.empty() || open_handle_scopes != dataStack.top().first) { - dataStack.emplace(open_handle_scopes, std::vector()); + dataStack.emplace(open_handle_scopes, std::vector()); } - auto newData = new JSVM_Data__(srcPtr, false, type); + auto newData = new JSVM_Script_Data__(srcPtr, false, type); dataStack.top().second.push_back(newData); return newData; } @@ -234,7 +234,7 @@ struct JSVM_Env__ { int32_t module_api_version = NODE_API_DEFAULT_MODULE_API_VERSION; bool in_gc_finalizer = false; v8::Locker* locker = nullptr; - std::stack>> dataStack; + std::stack>> dataStack; private: v8impl::Agent* inspector_agent_; @@ -395,9 +395,19 @@ inline JSVM_Value JsValueFromV8LocalValue(v8::Local local) { return reinterpret_cast(*local); } -inline v8::Local V8LocalValueFromJsValue(JSVM_Value v) { +inline JSVM_Data JsDataFromV8LocalData(v8::Local local) { + return reinterpret_cast(*local); +} + +inline v8::Local V8LocalValueFromJsValue(JSVM_Value value) { v8::Local local; - memcpy(static_cast(&local), &v, sizeof(v)); + memcpy(static_cast(&local), &value, sizeof(JSVM_Value)); + return local; +} + +inline v8::Local V8LocalDataFromJsData(JSVM_Data data) { + v8::Local local; + memcpy(static_cast(&local), &data, sizeof(JSVM_Data)); return local; } @@ -517,16 +527,17 @@ class RefBase : public TrackedFinalizer { class Reference : public RefBase { protected: template - Reference(JSVM_Env env, v8::Local value, Args&&... args); + Reference(JSVM_Env env, v8::Local value, bool isValue, Args&&... args); public: static Reference* New(JSVM_Env env, - v8::Local value, + v8::Local value, uint32_t initialRefcount, Ownership ownership, JSVM_Finalize finalizeCallback = nullptr, void* finalizeData = nullptr, - void* finalizeHint = nullptr); + void* finalizeHint = nullptr, + bool isValue = true); virtual ~Reference(); bool HasDeletedByUser() { @@ -534,8 +545,12 @@ class Reference : public RefBase { } uint32_t Ref(); uint32_t Unref(); - v8::Local Get(); + v8::Local GetValue(); + v8::Local GetData(); void Delete(); + bool IsValue() const { + return is_value; + } protected: void Finalize() override; @@ -545,7 +560,8 @@ class Reference : public RefBase { void SetWeak(); - v8impl::Persistent persistent_; + v8impl::Persistent persistent_; + bool is_value; bool can_be_weak_; bool deleted_by_user; bool wait_callback; diff --git a/src/jsvm.h b/src/jsvm.h index b5771f10..8ec58836 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -611,6 +611,23 @@ JSVM_EXTERN JSVM_Status OH_JSVM_CreateReference(JSVM_Env env, uint32_t initialRefcount, JSVM_Ref* result); +/** + * @brief This API creates a new reference with the specified reference count to the data passed in. + * + * @param env The environment that the API is invoked under. + * @param value The JSVM_Data for which a reference is being created. + * @param initialRefcount Initial reference count for the new reference. + * @param result JSVM_Ref pointing to the new reference. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any parameter is null or the value of initialRefcount is 0.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateDataReference(JSVM_Env env, + JSVM_Data data, + uint32_t initialRefcount, + JSVM_Ref* result); + /** * @brief his API deletes the reference passed in. * @@ -668,6 +685,23 @@ JSVM_EXTERN JSVM_Status OH_JSVM_GetReferenceValue(JSVM_Env env, JSVM_Ref ref, JSVM_Value* result); +/** + * @brief If still valid, this API returns the JSVM_Data representing the + * JavaScript data associated with the JSVM_Ref. Otherwise, result will be NULL. + * + * @param env The environment that the API is invoked under. + * @param ref The JSVM_Ref for which the corresponding value is being requested. + * @param result The JSVM_Data referenced by the JSVM_Ref. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any parameter is null or the ref is not a reference to JSVM_Data.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetReferenceData(JSVM_Env env, + JSVM_Ref ref, + JSVM_Data* result); + /** * @brief This API returns a JSVM-API value corresponding to a JavaScript Array type. * @@ -851,6 +885,81 @@ JSVM_EXTERN JSVM_Status OH_JSVM_SymbolFor(JSVM_Env env, size_t length, JSVM_Value* result); +/** + * @brief This API creates a JavaScript private key. + * + * @param env The environment that the API is invoked under. + * @param description Optional JSVM_Value which refers to a JavaScript string to be set as the description + * for the private key. + * @param result A JSVM_Data representing a JavaScript private key. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if env or result is NULL.\n + * {@link JSVM_STRING_EXPECTED } if the description is not a string.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreatePrivate(JSVM_Env env, + JSVM_Value description, + JSVM_Data* result); + +/** + * @brief This API set a private property on the Object passed in. + * + * @param env The environment that the API is invoked under. + * @param object The object on which to set the private property. + * @param key The private key of the property. + * @param value The private property value. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the arguments is NULL or the key is not a private key.\n + * {@link JSVM_OBJECT_EXPECTED } object passed in is not a real object.\n + * {@link JSVM_GENERIC_FAILURE } if failed to set the private key but no exception is pending.\n + * {@link JSVM_PENDING_EXCPTION } if an exception occurs.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetPrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key, + JSVM_Value value); + +/** + * @brief This API gets the requested private property from the Object passed in. + * + * @param env The environment that the API is invoked under. + * @param object The object from which to retrieve the private property. + * @param key The private key of the property. + * @param result The value of the private property. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the arguments is NULL or the key is not a private key.\n + * {@link JSVM_OBJECT_EXPECTED } object passed in is not a real object.\n + * {@link JSVM_GENERIC_FAILURE } if failed to get the private key but no exception is pending.\n + * {@link JSVM_PENDING_EXCPTION } if an exception occurs.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetPrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key, + JSVM_Value *result); + +/** + * @brief This API attempts to delete the property of the private key from object. + * + * @param env The environment that the API is invoked under. + * @param object The object to query. + * @param key The private key of the property to delete. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the arguments is NULL or the key is not a private key.\n + * {@link JSVM_OBJECT_EXPECTED } object passed in is not a real object.\n + * {@link JSVM_GENERIC_FAILURE } if failed to delete the private key but no exception is pending.\n + * {@link JSVM_PENDING_EXCPTION } if an exception occurs.\n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DeletePrivate(JSVM_Env env, + JSVM_Value object, + JSVM_Data key); + /** * @brief This API creates a JavaScript TypedArray object over an existing ArrayBuffer. TypedArray * objects provide an array-like view over an underlying data buffer where each element has the @@ -1050,6 +1159,58 @@ JSVM_EXTERN JSVM_Status OH_JSVM_CreateStringUtf8(JSVM_Env env, const char* str, size_t length, JSVM_Value* result); +/** + * @brief This API creates an external JavaScript string value from an ISO-8859-1-encoded C + * string. The native string is copied when failed to create external string. + * + * @param env The environment that the API is invoked under. + * @param str Character buffer representing an ISO-8859-1-encoded string. + * @param length The length of the string in bytes, or JSVM_AUTO_LENGTH if it is null-terminated. + * @param finalizeCallback Optional callback to call when the external value is being collected. + * JSVM_Finalize provides more details. + * @param finalizeHint Optional hint to pass to the finalize callback during collection. + * @param result A JSVM_Value representing a JavaScript external string. + * @param copied flag indicate whether the external string is successfully created, + * true for faild to create external ones and fall back to non-external strings, false for success. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if one of env, str and copied is NULL.\n + * @since 16 + */ +JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringLatin1(JSVM_Env env, + char* str, + size_t length, + JSVM_Finalize finalizeCallback, + void* finalizeHint, + JSVM_Value* result, + bool* copied); + +/** + * @brief This API creates an external JavaScript string value from an UTF16-LE-encoded C + * string. The native string is copied when failed to create external string. + * + * @param env The environment that the API is invoked under. + * @param str Character buffer representing an UTF16-LE-encoded string. + * @param length The length of the string in bytes, or JSVM_AUTO_LENGTH if it is null-terminated. + * @param finalizeCallback Optional callback to call when the external value is being collected. + * JSVM_Finalize provides more details. + * @param finalizeHint Optional hint to pass to the finalize callback during collection. + * @param result A JSVM_Value representing a JavaScript external string. + * @param copied flag indicate whether the external string is successfully created, + * true for faild to create external ones and fall back to non-external strings, false for success. + * @return Returns JSVM funtions result code. + * Returns {@link JSVM_OK } if the function executed successfully.\n + * Returns {@link JSVM_INVALID_ARG } if one of env, str and copied is NULL.\n + * @since 16 + */ + +JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringUtf16(JSVM_Env env, + char16_t* str, + size_t length, + JSVM_Finalize finalizeCallback, + void* finalizeHint, + JSVM_Value* result, + bool* copied); /** * @brief This API returns the length of an array. diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 4ee1793b..6e0812ca 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -108,6 +108,13 @@ typedef struct JSVM_CpuProfiler__* JSVM_CpuProfiler; */ typedef struct JSVM_Value__* JSVM_Value; +/** + * @brief To represent a JavaScript Data type. + * + * @since 16 + */ +typedef struct JSVM_Data__* JSVM_Data; + /** * @brief To represent a JavaScript value references. * -- Gitee From d58d602e0b009055983b43e1cfb0e8733d517d78 Mon Sep 17 00:00:00 2001 From: chenraozhong Date: Tue, 26 Nov 2024 19:59:48 +0800 Subject: [PATCH 2/9] cherry pick 3c5f75c from https://gitee.com/wang-yimin15/third_party_node/pulls/189 support microtask policy Signed-off-by: wangyimin --- src/js_native_api_v8.cc | 18 ++++++++++++++++++ src/jsvm.h | 12 ++++++++++++ src/jsvm_types.h | 14 ++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index ce6095ca..f09b2d20 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -1502,6 +1502,24 @@ OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, JSVM_VM* result) { return JSVM_OK; } +JSVM_Status OH_JSVM_SetMicrotaskPolicy(JSVM_VM vm, + JSVM_MicrotaskPolicy policy) { + static constexpr v8::MicrotasksPolicy converter[] = { + v8::MicrotasksPolicy::kExplicit, + v8::MicrotasksPolicy::kAuto + }; + constexpr size_t policyCount = node::arraysize(converter); + + if (!vm || policy >= policyCount) { + return JSVM_INVALID_ARG; + } + + auto isolate = reinterpret_cast(vm); + isolate->SetMicrotasksPolicy(converter[policy]); + + return JSVM_OK; +} + JSVM_Status JSVM_CDECL OH_JSVM_DestroyVM(JSVM_VM vm) { if (vm == nullptr) { diff --git a/src/jsvm.h b/src/jsvm.h index 8ec58836..a35a7365 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -121,6 +121,18 @@ JSVM_EXTERN JSVM_Status OH_JSVM_Init(const JSVM_InitOptions* options); JSVM_EXTERN JSVM_Status OH_JSVM_CreateVM(const JSVM_CreateVMOptions* options, JSVM_VM* result); +/** + * @brief This function controls how Microtasks are invoked of the vm. If the method is not + * called, the default microtask policy of vm is JSVM_MicrotaskPolicy::JSVM_MICROTASK_AUTO. + * + * @param vm The VM instance to set mircrotasks policy. + * @param policy Policy for running microtasks. + * @return Returns JSVM_OK if the API succeeded. + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_SetMicrotaskPolicy(JSVM_VM vm, + JSVM_MicrotaskPolicy policy); + /** * @brief Destroys VM instance. * diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 6e0812ca..70b58e39 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -778,5 +778,19 @@ typedef enum { JSVM_CACHE_TYPE_WASM, } JSVM_CacheType; /** @} */ + +/** + * @brief Microtask policies of JSVM. + * + * @since 16 + */ +typedef enum { + /** Microtasks are invoked with the OH_JSVM_PerformMicrotaskCheckpoint() method. */ + JSVM_MICROTASK_EXPLICIT = 0, + /** Microtasks are invoked when the script call depth decrements to zero. + * Default mode. + */ + JSVM_MICROTASK_AUTO, +} JSVM_MicrotaskPolicy; #endif /* ARK_RUNTIME_JSVM_JSVM_TYPE_H */ -- Gitee From 2c3fa2a97b27534386bda8b121cfb181d47e6917 Mon Sep 17 00:00:00 2001 From: chenraozhong Date: Thu, 28 Nov 2024 17:28:57 +0800 Subject: [PATCH 3/9] set Handler for OOM | Fatal | PromiseReject Signed-off-by: chenraozhong --- src/js_native_api_v8.cc | 141 ++++++++++++++++++++++++++++++++++++++++ src/jsvm.h | 45 +++++++++++++ src/jsvm_types.h | 78 ++++++++++++++++++++++ 3 files changed, 264 insertions(+) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index f09b2d20..aba45471 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) {} @@ -1498,6 +1527,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; } @@ -1529,6 +1560,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 { @@ -1538,6 +1570,9 @@ OH_JSVM_DestroyVM(JSVM_VM vm) { delete data; } + if (handlerPool != nullptr) { + delete handlerPool; + } return JSVM_OK; } @@ -2828,6 +2863,112 @@ 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); +} + +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 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_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; +} + +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; + } + default: { + rejectEvent = JSVM_PROMISE_REJECT_OTHER_REASONS; + } + } + (*handler)(env, rejectEvent, jsvmRejectInfo); +} + +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 a35a7365..dbffede3 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -1688,6 +1688,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 * diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 70b58e39..598b99a7 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -777,6 +777,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); /** @} */ /** -- Gitee From 1dcc403b4ea405238161d78ddb22598802c61b54 Mon Sep 17 00:00:00 2001 From: chenraozhong Date: Thu, 28 Nov 2024 18:55:05 +0800 Subject: [PATCH 4/9] Add Handler For GC Signed-off-by: chenraozhong --- src/js_native_api_v8.cc | 156 ++++++++++++++++++++++++++++++++++++++++ src/jsvm.h | 40 +++++++++++ src/jsvm_types.h | 70 ++++++++++++++++++ 3 files changed, 266 insertions(+) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index aba45471..69cd272f 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -148,10 +148,34 @@ enum IsolateDataSlot { // Always compare the final element of IsolateDataSlot with v8 limit. static_assert(kIsolateHandlerPoolSlot < v8::internal::Internals::kNumIsolateDataSlots); +struct GCHandlerWrapper { + GCHandlerWrapper(JSVM_GCType gcType, JSVM_HandlerForGC handler, void* userData) + : gcType(gcType), handler(handler), userData(userData) {} + + JSVM_GCType gcType; + JSVM_HandlerForGC handler; + void *userData; +}; + +using GCHandlerWrappers = std::list; + struct IsolateHandlerPool { + GCHandlerWrappers handlerWrappersBeforeGC; + GCHandlerWrappers handlerWrappersAfterGC; JSVM_HandlerForOOMError handlerForOOMError = nullptr; JSVM_HandlerForFatalError handlerForFatalError = nullptr; JSVM_HandlerForPromiseReject handlerForPromiseReject = nullptr; + + ~IsolateHandlerPool() { + for (auto *handler : handlerWrappersBeforeGC) { + delete handler; + } + handlerWrappersBeforeGC.clear(); + for (auto *handler : handlerWrappersAfterGC) { + delete handler; + } + handlerWrappersAfterGC.clear(); + } }; static IsolateHandlerPool* GetIsolateHandlerPool(v8::Isolate* isolate) { @@ -2969,6 +2993,138 @@ JSVM_Status JSVM_CDECL OH_JSVM_SetHandlerForPromiseReject( return JSVM_OK; } +JSVM_GCType GetJSVMGCType(v8::GCType gcType) { + switch (gcType) { + case v8::GCType::kGCTypeScavenge: + return JSVM_GC_TYPE_SCAVENGE; + case v8::GCType::kGCTypeMinorMarkCompact: + return JSVM_GC_TYPE_MINOR_MARK_COMPACT; + case v8::GCType::kGCTypeMarkSweepCompact: + return JSVM_GC_TYPE_MARK_SWEEP_COMPACT; + case v8::GCType::kGCTypeIncrementalMarking: + return JSVM_GC_TYPE_INCREMENTAL_MARKING; + case v8::GCType::kGCTypeProcessWeakCallbacks: + return JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS; + default: + return JSVM_GC_TYPE_ALL; + } +} + +static v8::GCType GetV8GCType(JSVM_GCType gcType) { + switch(gcType) { + case JSVM_GC_TYPE_SCAVENGE: + return v8::GCType::kGCTypeScavenge; + case JSVM_GC_TYPE_MINOR_MARK_COMPACT: + return v8::GCType::kGCTypeMinorMarkCompact; + case JSVM_GC_TYPE_MARK_SWEEP_COMPACT: + return v8::GCType::kGCTypeMarkSweepCompact; + case JSVM_GC_TYPE_INCREMENTAL_MARKING: + return v8::GCType::kGCTypeIncrementalMarking; + case JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS: + return v8::GCType::kGCTypeProcessWeakCallbacks; + default: + return v8::GCType::kGCTypeAll; + } +} + +JSVM_GCCallbackFlags GetJSVMGCCallbackFlags(v8::GCCallbackFlags flag) { + switch (flag) { + case v8::GCCallbackFlags::kGCCallbackFlagConstructRetainedObjectInfos: + return JSVM_GC_CALLBACK_CONSTRUCT_RETAINED_OBJECT_INFOS; + case v8::GCCallbackFlags::kGCCallbackFlagForced: + return JSVM_GC_CALLBACK_FORCED; + case v8::GCCallbackFlags::kGCCallbackFlagSynchronousPhantomCallbackProcessing: + return JSVM_GC_CALLBACK_SYNCHRONOUS_PHANTOM_CALLBACK_PROCESSING; + case v8::GCCallbackFlags::kGCCallbackFlagCollectAllAvailableGarbage: + return JSVM_GC_CALLBACK_COLLECT_ALL_AVAILABLE_GARBAGE; + case v8::GCCallbackFlags::kGCCallbackFlagCollectAllExternalMemory: + return JSVM_GC_CALLBACK_COLLECT_ALL_EXTERNAL_MEMORY; + case v8::GCCallbackFlags::kGCCallbackScheduleIdleGarbageCollection: + return JSVM_GC_CALLBACK_SCHEDULE_IDLE_GARBAGE_COLLECTION; + default: + return JSVM_NO_GC_CALLBACK_FLAGS; + } +} + +static void OnBeforeGC(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags, void* data) { + auto *pool = v8impl::GetIsolateHandlerPool(isolate); + DCHECK_NOT_NULL(pool); + JSVM_GCType gcType = GetJSVMGCType(type); + JSVM_GCCallbackFlags gcFlags = GetJSVMGCCallbackFlags(flags); + + auto *gcHandlerWrapper = (v8impl::GCHandlerWrapper*)data; + gcHandlerWrapper->handler(reinterpret_cast(isolate), gcType, gcFlags, gcHandlerWrapper->userData); +} + +static void OnAfterGC(v8::Isolate* isolate, v8::GCType type, v8::GCCallbackFlags flags, void* data) { + auto *pool = v8impl::GetIsolateHandlerPool(isolate); + DCHECK_NOT_NULL(pool); + JSVM_GCType gcType = GetJSVMGCType(type); + JSVM_GCCallbackFlags gcFlags = GetJSVMGCCallbackFlags(flags); + + auto *gcHandlerWrapper = (v8impl::GCHandlerWrapper*)data; + gcHandlerWrapper->handler(reinterpret_cast(isolate), gcType, gcFlags, gcHandlerWrapper->userData); +} + +JSVM_Status JSVM_CDECL OH_JSVM_AddHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + JSVM_GCType gcType, + void* data) { + if (!vm || !handler) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; + auto it = std::find_if(handlers.begin(), handlers.end(), [handler, data](v8impl::GCHandlerWrapper *callbackData) { + return callbackData->handler == handler && callbackData->userData == data; + }); + if (it != handlers.end()) { + return JSVM_INVALID_ARG; + } + auto *callbackData = new v8impl::GCHandlerWrapper(gcType, handler, data); + handlers.push_back(callbackData); + + if (triggerTime == JSVM_CB_TRIGGER_BEFORE_GC) { + isolate->AddGCPrologueCallback( + OnBeforeGC, callbackData, GetV8GCType(gcType)); + } else { + isolate->AddGCEpilogueCallback( + OnAfterGC, callbackData, GetV8GCType(gcType)); + } + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_RemoveHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + void* userData) { + if (!vm || !handler) { + return JSVM_INVALID_ARG; + } + auto* isolate = reinterpret_cast(vm); + auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); + if (pool == nullptr) { + return JSVM_INVALID_ARG; + } + auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; + auto it = std::find_if(handlers.begin(), handlers.end(), [handler, userData](v8impl::GCHandlerWrapper *callbackData) { + return callbackData->handler == handler && callbackData->userData == userData; + }); + if (it == handlers.end()) { + return JSVM_INVALID_ARG; + } + handlers.erase(it); + if (triggerTime == JSVM_CB_TRIGGER_BEFORE_GC) { + isolate->RemoveGCPrologueCallback(OnBeforeGC, (*it)); + } else { + isolate->RemoveGCEpilogueCallback(OnAfterGC, (*it)); + } + delete (*it); + 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 dbffede3..a7fffcbf 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -1688,6 +1688,46 @@ JSVM_EXTERN JSVM_Status OH_JSVM_Instanceof(JSVM_Env env, JSVM_Value constructor, bool* result); + +/** +* @brief Add VM GC Callback. +* +* @param vm The environment that the API is invoked under. +* @param triggerTime The timing of GC callback trigger. +* @param handler When Trigger gc, the callback function will be called. +* @param gcType The type of gc. +* @param userData The native pointer data. +* @return Returns JSVM funtions result code. +* {@link JSVM_OK } if the function executed successfully.\n +* {@link JSVM_INVALID_ARG } if the vm or the handler is NULL or the handler has been added before.\n +* +* @since 16 +*/ +JSVM_EXTERN JSVM_Status OH_JSVM_AddHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + JSVM_GCType gcType, + void* userData); + +/** +* @brief Remove VM GC Callback. +* +* @param vm The environment that the API is invoked under. +* @param triggerTime The timing of GC callback trigger. +* @param handler When Trigger gc, the callback function will be called. +* @param userData The native pointer data. +* @return Returns JSVM funtions result code. +* {@link JSVM_OK } if the function executed successfully.\n +* {@link JSVM_INVALID_ARG } if the vm or the handler is NULL, or the handler has been removed, +* or the handler has never been added.\n +* +* @since 16 +*/ +JSVM_EXTERN JSVM_Status OH_JSVM_RemoveHandlerForGC(JSVM_VM vm, + JSVM_CBTriggerTimeForGC triggerTime, + JSVM_HandlerForGC handler, + void* userData); + /** * @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. diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 598b99a7..19a08c59 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -855,6 +855,76 @@ typedef void(JSVM_CDECL* JSVM_HandlerForFatalError)(const char* location, typedef void(JSVM_CDECL* JSVM_HandlerForPromiseReject)(JSVM_Env env, JSVM_PromiseRejectEvent rejectEvent, JSVM_Value rejectInfo); +/** + * @brief The timing of GC callback trigger. + * + * @since 16 + */ +typedef enum { + /** Trigger GC callback before GC. */ + JSVM_CB_TRIGGER_BEFORE_GC, + /** Trigger GC callback after GC. */ + JSVM_CB_TRIGGER_AFTER_GC, +} JSVM_CBTriggerTimeForGC; + +/** + * @brief The GC type. + * + * @since 16 + */ +typedef enum { + /** The GC algorithm is Scavenge. */ + JSVM_GC_TYPE_SCAVENGE = 1 << 0, + /** The GC algorithm is Minor-Mark-Compact. */ + JSVM_GC_TYPE_MINOR_MARK_COMPACT = 1 << 1, + /** The GC algorithm is Mark-Sweep-Compact. */ + JSVM_GC_TYPE_MARK_SWEEP_COMPACT = 1 << 2, + /** The GC algorithm is Incremental-Marking. */ + JSVM_GC_TYPE_INCREMENTAL_MARKING = 1 << 3, + /** The GC algorithm is Weak-Callbacks. */ + JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS = 1 << 4, + /** All GC algorithm. */ + JSVM_GC_TYPE_ALL = JSVM_GC_TYPE_SCAVENGE | JSVM_GC_TYPE_MINOR_MARK_COMPACT | + JSVM_GC_TYPE_MARK_SWEEP_COMPACT | JSVM_GC_TYPE_INCREMENTAL_MARKING | + JSVM_GC_TYPE_PROCESS_WEAK_CALLBACKS, +} JSVM_GCType; + +/** + * @brief The GC callback flags. + * + * @since 16 + */ +typedef enum { + /** No GC callback falgs. */ + JSVM_NO_GC_CALLBACK_FLAGS, + /** Reserved object information will be built in the garbage collection callback. */ + JSVM_GC_CALLBACK_CONSTRUCT_RETAINED_OBJECT_INFOS, + /** Enforce Garbage Collection Callback. */ + JSVM_GC_CALLBACK_FORCED, + /** Synchronous phantom callback processing. */ + JSVM_GC_CALLBACK_SYNCHRONOUS_PHANTOM_CALLBACK_PROCESSING, + /** All available garbage objects are collected during garbage collection. */ + JSVM_GC_CALLBACK_COLLECT_ALL_AVAILABLE_GARBAGE, + /** Garbage collection collects all external memory. */ + JSVM_GC_CALLBACK_COLLECT_ALL_EXTERNAL_MEMORY, + /** Schedule Garbage Collection at Idle Time. */ + JSVM_GC_CALLBACK_SCHEDULE_IDLE_GARBAGE_COLLECTION, +} JSVM_GCCallbackFlags; + +/** + * @brief Function pointer type of GC callback. + * + * @param vm The VM instance that the JSVM-API call is invoked under. + * @param gcType The gc type. + * @param flags The GC callback flags. + * @param data The native pointer data. + * + * @since 16 + */ +typedef void(JSVM_CDECL* JSVM_HandlerForGC)(JSVM_VM vm, + JSVM_GCType gcType, + JSVM_GCCallbackFlags flags, + void* data); /** @} */ /** -- Gitee From b48407792bc2ec68541b88ee11b1e081eb4f404a Mon Sep 17 00:00:00 2001 From: chenraozhong Date: Thu, 28 Nov 2024 21:02:02 +0800 Subject: [PATCH 5/9] Add OH_JSVM_DefineClassWithOptions Signed-off-by: chenraozhong --- src/js_native_api_v8.cc | 226 ++++++++++++++++++++++++++++++++++++++++ src/jsvm.h | 40 +++++++ src/jsvm_types.h | 44 ++++++++ 3 files changed, 310 insertions(+) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 69cd272f..ca5fd666 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -5410,6 +5410,232 @@ OH_JSVM_DefineClassWithPropertyHandler(JSVM_Env env, return GET_RETURN_STATUS(env); } +JSVM_Status ProcessPropertyHandler(JSVM_Env env, + v8::Local tpl, + JSVM_PropertyHandlerCfg propertyHandlerCfg, + JSVM_Callback callAsFunctionCallback, + v8impl::JSVM_PropertyHandlerCfgStruct** propertyHandlerCfgStruct) { + CHECK_ARG(env, propertyHandlerCfg); + *propertyHandlerCfgStruct = v8impl::CreatePropertyCfg(env, propertyHandlerCfg); + if (*propertyHandlerCfgStruct == nullptr) { + return JSVM_GENERIC_FAILURE; + } + v8::Local cbdata = v8impl::CallbackBundle::New(env, *propertyHandlerCfgStruct); + + // register named property handler + v8::NamedPropertyHandlerConfiguration namedPropertyHandler; + if (propertyHandlerCfg->genericNamedPropertyGetterCallback) { + namedPropertyHandler.getter = v8impl::PropertyCallbackWrapper::NameGetterInvoke; + } + if (propertyHandlerCfg->genericNamedPropertySetterCallback) { + namedPropertyHandler.setter = v8impl::PropertyCallbackWrapper::NameSetterInvoke; + } + if (propertyHandlerCfg->genericNamedPropertyDeleterCallback) { + namedPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::NameDeleterInvoke; + } + if (propertyHandlerCfg->genericNamedPropertyEnumeratorCallback) { + namedPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::NameEnumeratorInvoke; + } + namedPropertyHandler.data = cbdata; + tpl->InstanceTemplate()->SetHandler(namedPropertyHandler); + + // register indexed property handle + v8::IndexedPropertyHandlerConfiguration indexPropertyHandler; + if (propertyHandlerCfg->genericIndexedPropertyGetterCallback) { + indexPropertyHandler.getter = v8impl::PropertyCallbackWrapper::IndexGetterInvoke; + } + if (propertyHandlerCfg->genericIndexedPropertySetterCallback) { + indexPropertyHandler.setter = v8impl::PropertyCallbackWrapper::IndexSetterInvoke; + } + if (propertyHandlerCfg->genericIndexedPropertyDeleterCallback) { + indexPropertyHandler.deleter = v8impl::PropertyCallbackWrapper::IndexDeleterInvoke; + } + if (propertyHandlerCfg->genericIndexedPropertyEnumeratorCallback) { + indexPropertyHandler.enumerator = v8impl::PropertyCallbackWrapper::IndexEnumeratorInvoke; + } + indexPropertyHandler.data = cbdata; + tpl->InstanceTemplate()->SetHandler(indexPropertyHandler); + + // register call as function + if (callAsFunctionCallback && callAsFunctionCallback->callback) { + v8::Local funcCbdata = v8impl::CallbackBundle::New(env, callAsFunctionCallback); + tpl->InstanceTemplate()->SetCallAsFunctionHandler(v8impl::FunctionCallbackWrapper::Invoke, funcCbdata); + } + return JSVM_OK; +} + +class DefineClassOptionsResolver { + public: + void ProcessOptions(size_t length, JSVM_DefineClassOptions options[], JSVM_Env env, + v8::Local tpl) { + for (int32_t i = 0; i < length; i++) { + if (status != JSVM_OK) { + break; + } + switch(options[i].id) { + case JSVM_DEFINE_CLASS_NORMAL: + break; + case JSVM_DEFINE_CLASS_WITH_COUNT: { + auto count = options[i].content.num; + v8::Local instance_templ = tpl->InstanceTemplate(); + instance_templ->SetInternalFieldCount(count); + break; + } + case JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER: { + hasPropertyHandle = true; + auto* propertyHandle = static_cast(options[i].content.ptr); + propertyHandlerCfg = propertyHandle->propertyHandlerCfg; + callAsFunctionCallback = propertyHandle->callAsFunctionCallback; + status = ProcessPropertyHandler(env, tpl, propertyHandlerCfg, callAsFunctionCallback, + &propertyHandlerCfgStruct); + break; + } + default: { + status = JSVM_INVALID_ARG; + } + } + } + } + + JSVM_Status GetStatus() { + return status; + } + + v8impl::JSVM_PropertyHandlerCfgStruct* GetPropertyHandler() { + return propertyHandlerCfgStruct; + } + + bool HasPropertyHandler() { + return hasPropertyHandle; + } + + private: + JSVM_PropertyHandlerCfg propertyHandlerCfg = nullptr; + JSVM_Callback callAsFunctionCallback = nullptr; + bool hasPropertyHandle = false; + JSVM_Status status = JSVM_OK; + v8impl::JSVM_PropertyHandlerCfgStruct* propertyHandlerCfgStruct = nullptr; +}; + +JSVM_Status JSVM_CDECL +OH_JSVM_DefineClassWithOptions(JSVM_Env env, + const char* utf8name, + size_t length, + JSVM_Callback constructor, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_Value parentClass, + size_t option_count, + JSVM_DefineClassOptions options[], + JSVM_Value* result) { + JSVM_PREAMBLE(env); + CHECK_ARG(env, result); + CHECK_ARG(env, constructor); + CHECK_ARG(env, constructor->callback); + + if (propertyCount > 0) { + CHECK_ARG(env, properties); + } + + v8::Isolate* isolate = env->isolate; + v8::EscapableHandleScope scope(isolate); + v8::Local tpl; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, constructor, &tpl)); + + v8::Local name_string; + CHECK_NEW_FROM_UTF8_LEN(env, name_string, utf8name, length); + tpl->SetClassName(name_string); + + size_t static_property_count = 0; + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = properties + i; + + if ((p->attributes & JSVM_STATIC) != 0) { // attributes + // Static properties are handled separately below. + static_property_count++; + continue; + } + + v8::Local property_name; + STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name)); + v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); + + // This code is similar to that in OH_JSVM_DefineProperties(); the + // difference is it applies to a template instead of an object, + // and preferred PropertyAttribute for lack of PropertyDescriptor + // support on ObjectTemplate. + if (p->getter != nullptr || p->setter != nullptr) { + v8::Local getter_tpl; + v8::Local setter_tpl; + if (p->getter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, p->getter, &getter_tpl)); + } + if (p->setter != nullptr) { + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, p->setter, &setter_tpl)); + } + + tpl->PrototypeTemplate()->SetAccessorProperty(property_name, + getter_tpl, + setter_tpl, + attributes, + v8::AccessControl::DEFAULT); + } else if (p->method != nullptr) { + v8::Local t; + STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( + env, p->method, &t, v8::Signature::New(isolate, tpl))); + + tpl->PrototypeTemplate()->Set(property_name, t, attributes); + } else { + v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); + tpl->PrototypeTemplate()->Set(property_name, value, attributes); + } + } + + if (parentClass != nullptr) { + v8::Local parentFunc; + CHECK_TO_FUNCTION(env, parentFunc, parentClass); + if (!tpl->Inherit(parentFunc)) { + return JSVM_INVALID_ARG; + } + } + + DefineClassOptionsResolver optionResolver; + optionResolver.ProcessOptions(option_count, options, env, tpl); + + if (optionResolver.GetStatus() != JSVM_OK) { + return optionResolver.GetStatus(); + } + + v8::Local context = env->context(); + *result = v8impl::JsValueFromV8LocalValue( + scope.Escape(tpl->GetFunction(context).ToLocalChecked())); + + if (optionResolver.HasPropertyHandler()) { + v8impl::Reference::New(env, v8impl::V8LocalValueFromJsValue(*result), 0, v8impl::Ownership::kRuntime, + v8impl::CfgFinalizedCallback, optionResolver.GetPropertyHandler(), nullptr); + } + + if (static_property_count > 0) { + std::vector static_descriptors; + static_descriptors.reserve(static_property_count); + + for (size_t i = 0; i < propertyCount; i++) { + const JSVM_PropertyDescriptor* p = properties + i; + if ((p->attributes & JSVM_STATIC) != 0) { + static_descriptors.push_back(*p); + } + } + + STATUS_CALL(OH_JSVM_DefineProperties( + env, *result, static_descriptors.size(), static_descriptors.data())); + } + + return GET_RETURN_STATUS(env); +} + JSVM_Status JSVM_CDECL OH_JSVM_IsLocked(JSVM_Env env, bool* isLocked) { CHECK_ENV(env); diff --git a/src/jsvm.h b/src/jsvm.h index a7fffcbf..90f1bd37 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -3303,6 +3303,46 @@ JSVM_EXTERN JSVM_Status OH_JSVM_CreateWasmCache(JSVM_Env env, JSVM_EXTERN JSVM_Status OH_JSVM_ReleaseCache(JSVM_Env env, const uint8_t* cacheData, JSVM_CacheType cacheType); + +/** + * @brief When wrapping a C++ class, the C++ constructor callback passed via constructor + * should be a static method on the class that calls the actual class constructor, then + * wraps the new C++ instance in a JavaScript object according to the different Options + * passed in, and returns the wrapper object. + * + * @param env The environment that the API is invoked under. + * @param utf8name Name of the JavaScript constructor function. For clarity, it is + * recommended to use the C++ class name when wrapping a C++ class. + * @param length The length of the utf8name in bytes, or JSVM_AUTO_LENGTH if it + * is null-terminated. + * @param constructor Struct include callback function that handles constructing instances of the class. + * When wrapping a C++ class, this method must be a static member with the JSVM_Callback.callback + * signature. A C++ class constructor cannot be used. + * Include Optional data to be passed to the constructor callback as the data + * property of the callback info. JSVM_Callback provides more details. + * @param propertyCount Number of items in the properties array argument. + * @param properties Array of property descriptors describing static and instance data + * properties, accessors, and methods on the class See JSVM_PropertyDescriptor. + * @param parentClass The parent-class of the currently defined class. + * @param option_count Number of items in an option array argument. + * @param options DefineClass options to be passed. + * @param result A JSVM_Value representing the constructor function for the class. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the function executed successfully. \n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL. \n + * {@link JSVM_GENERIC_FAILURE} if the input utf8name | constructor | properties is invalid. \n + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_DefineClassWithOptions(JSVM_Env env, + const char* utf8name, + size_t length, + JSVM_Callback constructor, + size_t propertyCount, + const JSVM_PropertyDescriptor* properties, + JSVM_Value parentClass, + size_t option_count, + JSVM_DefineClassOptions options[], + JSVM_Value* result); EXTERN_C_END /** @} */ #endif /* ARK_RUNTIME_JSVM_JSVM_H */ diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 19a08c59..70a7b53c 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -925,6 +925,50 @@ typedef void(JSVM_CDECL* JSVM_HandlerForGC)(JSVM_VM vm, JSVM_GCType gcType, JSVM_GCCallbackFlags flags, void* data); +/** + * @brief The property-handler used to define class. + * + * @since 16 + */ +typedef struct { + /** The instance object triggers the corresponding callback function. */ + JSVM_PropertyHandlerCfg propertyHandlerCfg; + /** Calling an instance object as a function will trigger this callback. */ + JSVM_Callback callAsFunctionCallback; +} JSVM_PropertyHandler; + +/** + * @brief DefineClass options id. + * + * @since 16 + */ +typedef enum { + /** Defining a class in normal mode. */ + JSVM_DEFINE_CLASS_NORMAL, + /** Defining a class with count. */ + JSVM_DEFINE_CLASS_WITH_COUNT, + /** Defining a class with property handler. */ + JSVM_DEFINE_CLASS_WITH_PROPERTY_HANDLER, +} JSVM_DefineClassOptionsId; + +/** + * @brief DefineClass options. + * + * @since 16 + */ +typedef struct { + /** DefineClass options id. */ + JSVM_DefineClassOptionsId id; + /** option content. */ + union { + /** for option value with pointer type. */ + void* ptr; + /** for option value with integer type */ + int num; + /** for option value with bool type */ + bool boolean; + } content; +} JSVM_DefineClassOptions; /** @} */ /** -- Gitee From 0e60dcdec7c17fc2db7abff3be8d1fb6ebe661bd Mon Sep 17 00:00:00 2001 From: wangzhongqi5 Date: Thu, 28 Nov 2024 16:45:11 +0800 Subject: [PATCH 6/9] add jsvm trace Signed-off-by: wangzhongqi5 --- src/js_native_api_v8.cc | 102 ++++++++++++++++++++++++++++++++++++++++ src/jsvm.h | 28 +++++++++++ src/jsvm_types.h | 23 +++++++++ 3 files changed, 153 insertions(+) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index ca5fd666..b0f328e0 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -16,6 +16,7 @@ #include "js_native_api_v8.h" #include "js_native_api_v8_inspector.h" #include "libplatform/libplatform.h" +#include "libplatform/v8-tracing.h" #include "util-inl.h" #include "util.h" #include "sourcemap.def" @@ -24,6 +25,10 @@ #include "hisysevent.h" #endif +#ifdef V8_USE_PERFETTO +#error Unsupported Perfetto. +#endif // V8_USE_PERFETTO + #define SECARGCNT 2 #define CHECK_MAYBE_NOTHING(env, maybe, status) \ @@ -292,6 +297,23 @@ static v8::ArrayBuffer::Allocator *GetOrCreateDefaultArrayBufferAllocator() { return defaultArrayBufferAllocator.get(); } +static std::unique_ptr g_trace_stream; + +constexpr uint32_t g_trace_catrgory_count = 7; +static constexpr const char* g_internal_trace_categories[] = { + "v8", + TRACE_DISABLED_BY_DEFAULT("v8.compile"), + "v8.execute", + TRACE_DISABLED_BY_DEFAULT("v8.runtime"), + TRACE_DISABLED_BY_DEFAULT("v8.stack_trace"), + "v8.wasm", + TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"), +}; + +constexpr uint32_t g_default_catrgory_count = 4; +static constexpr JSVM_TraceCategory g_default_categories[] = { + JSVM_TRACE_VM, JSVM_TRACE_EXECUTE, JSVM_TRACE_COMPILE, JSVM_TRACE_RUNTIME }; + static void SetFileToSourceMapMapping(std::string &&file, std::string &&sourceMapUrl) { auto it = sourceMapUrlMap.find(file); if (it == sourceMapUrlMap.end()) { @@ -2419,6 +2441,86 @@ OH_JSVM_DefineClass(JSVM_Env env, return GET_RETURN_STATUS(env); } +JSVM_EXTERN JSVM_Status OH_JSVM_TraceStart(size_t count, + const JSVM_TraceCategory* categories, + const char* tag, + size_t eventsCount) { + if (count > v8impl::g_trace_catrgory_count || + ((count != 0) != (categories != nullptr))) { + return JSVM_INVALID_ARG; + } + + for (size_t i = 0; i < count; ++i) { + if (categories[i] >= v8impl::g_trace_catrgory_count) { + return JSVM_INVALID_ARG; + } + } + + using namespace v8::platform::tracing; + TraceConfig* trace_config = new TraceConfig(); + + if (count == 0) { + count = v8impl::g_default_catrgory_count; + categories = v8impl::g_default_categories; + } + + for (size_t i = 0; i < count; ++i) { + trace_config->AddIncludedCategory( + v8impl::g_internal_trace_categories[categories[i]]); + } + + v8::Platform* platform = v8impl::g_platform.get(); + TracingController* controller = + static_cast(platform->GetTracingController()); + v8impl::g_trace_stream.reset(new std::stringstream()); + auto stream = v8impl::g_trace_stream.get(); + + TraceWriter* writer = nullptr; + if (tag != nullptr) { + writer = TraceWriter::CreateJSONTraceWriter(*stream, tag); + } else { + writer = TraceWriter::CreateJSONTraceWriter(*stream); + } + + size_t max_chunks; + if (eventsCount != 0) { + size_t chunk_size = TraceBufferChunk::kChunkSize; + max_chunks = (eventsCount + chunk_size - 1) / chunk_size; + } else { + max_chunks = TraceBuffer::kRingBufferChunks; + } + + TraceBuffer* ring_buffer = + TraceBuffer::CreateTraceBufferRingBuffer(max_chunks, writer); + controller->Initialize(ring_buffer); + controller->StartTracing(trace_config); + return JSVM_OK; +} + +JSVM_Status JSVM_CDECL OH_JSVM_TraceStop(JSVM_OutputStream stream, + void* streamData) { + if (stream == nullptr || streamData == nullptr || + v8impl::g_trace_stream.get() == nullptr) { + return JSVM_INVALID_ARG; + } + + using namespace v8::platform::tracing; + v8::Platform* platform = v8impl::g_platform.get(); + auto controller = + static_cast(platform->GetTracingController()); + DCHECK(controller != nullptr); + controller->StopTracing(); + + // Call the destructor of TraceBuffer to print the JSON end. + controller->Initialize(nullptr); + + std::string output = v8impl::g_trace_stream.get()->rdbuf()->str(); + stream(output.c_str(), output.size(), streamData); + + v8impl::g_trace_stream.reset(nullptr); + return JSVM_OK; +} + JSVM_Status JSVM_CDECL OH_JSVM_GetPropertyNames(JSVM_Env env, JSVM_Value object, JSVM_Value* result) { diff --git a/src/jsvm.h b/src/jsvm.h index 90f1bd37..6547cb87 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -3343,6 +3343,34 @@ JSVM_EXTERN JSVM_Status OH_JSVM_DefineClassWithOptions(JSVM_Env env, size_t option_count, JSVM_DefineClassOptions options[], JSVM_Value* result); +/** + * @brief Trace start with specified categories for all JSVM VM.(Non-thread-safe) + * + * @param count The count of trace categories. + * @param categories Select internal trace events for tracing by categories. + * @param tag User-defined tag of trace data. + * @param eventsCount Number of trace events. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if categories or count is illegal.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_TraceStart(size_t count, const JSVM_TraceCategory* categories, + const char* tag, size_t eventsCount); + +/** + * @brief Trace stop for specified categories for all JSVM VM.(Non-thread-safe) + * + * @param stream The output stream callback for receiving the data. + * @param streamData Data passed to the stream callback. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if stream or streamData is NULL\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_TraceStop(JSVM_OutputStream stream, void* streamData); EXTERN_C_END /** @} */ #endif /* ARK_RUNTIME_JSVM_JSVM_H */ diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 70a7b53c..82900acd 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -984,5 +984,28 @@ typedef enum { */ JSVM_MICROTASK_AUTO, } JSVM_MicrotaskPolicy; + + +/** + * @brief Trace category for jsvm internal trace events. + * + * @since 16 + */ +typedef enum { + /** Tracing main interface invoking of JSVM, such as run scripts. */ + JSVM_TRACE_VM, + /** Tracing interface invoking about compilation, such as CompileCodeBackground. */ + JSVM_TRACE_COMPILE, + /** Tracing interface invoking about execution status, such as Interrupts and Microtasks. */ + JSVM_TRACE_EXECUTE, + /** Tracing external callback */ + JSVM_TRACE_RUNTIME, + /** Tracing stack trace in JSVM. */ + JSVM_TRACE_STACK_TRACE, + /** Tracing main interface invoking of WASM, such as Compile Wasm Module and Instantiate. */ + JSVM_TRACE_WASM, + /** Tracing more detailed interface invoking of WASM, such as background compilation and wrappers. */ + JSVM_TRACE_WASM_DETAILED +} JSVM_TraceCategory; #endif /* ARK_RUNTIME_JSVM_JSVM_TYPE_H */ -- Gitee From 7074505a1eab52b732098d4465d5501740c18d59 Mon Sep 17 00:00:00 2001 From: liuweili Date: Tue, 26 Nov 2024 20:48:45 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=E6=96=B0=E5=A2=9Ewell=20know=20symbols=20a?= =?UTF-8?q?nd=20wrapper=20object=20API=20=EF=BC=88cherry=20picked=20commit?= =?UTF-8?q?=20from=20=20Signed-off-by:=20liu?= =?UTF-8?q?weili=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liuweili --- src/js_native_api_v8.cc | 165 +++++++++++++++++++++++++++++ src/jsvm.h | 223 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 388 insertions(+) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index b0f328e0..93de3936 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -1485,6 +1485,171 @@ static bool ProcessBundleName(std::string& bundleName) return true; } +JSVM_Status OH_JSVM_IsBooleanObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsBooleanObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_IsBigIntObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsBigIntObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_IsStringObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsStringObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_IsNumberObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsNumberObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_IsSymbolObject(JSVM_Env env, JSVM_Value value, bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local v = v8impl::V8LocalValueFromJsValue(value); + *result = v->IsSymbolObject(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolToStringTag(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolToStringTag = v8::Symbol::GetToStringTag(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolToStringTag); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolIterator(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolIterator = v8::Symbol::GetIterator(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolIterator); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolAsyncIterator(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolAsyncIterator = v8::Symbol::GetAsyncIterator(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolAsyncIterator); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolHasInstance(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolHasInstance = v8::Symbol::GetHasInstance(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolHasInstance); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolUnscopables(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolUnscopables = v8::Symbol::GetUnscopables(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolUnscopables); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolIsConcatSpreadable(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolIsConcatSpreadable = v8::Symbol::GetIsConcatSpreadable(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolIsConcatSpreadable); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolMatch(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolMatch = v8::Symbol::GetMatch(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolMatch); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolReplace(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolReplace = v8::Symbol::GetReplace(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolReplace); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolSearch(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolSearch = v8::Symbol::GetSearch(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolSearch); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolSplit(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolSplit = v8::Symbol::GetSplit(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolSplit); + + return jsvm_clear_last_error(env); +} + +JSVM_Status OH_JSVM_GetSymbolToPrimitive(JSVM_Env env, JSVM_Value* result) { + CHECK_ENV(env); + CHECK_ARG(env, result); + + v8::Local symbolToPrimitive = v8::Symbol::GetToPrimitive(env->isolate); + *result = v8impl::JsValueFromV8LocalValue(symbolToPrimitive); + + return jsvm_clear_last_error(env); +} + JSVM_Status JSVM_CDECL OH_JSVM_Init(const JSVM_InitOptions* options) { static std::atomic initialized(false); diff --git a/src/jsvm.h b/src/jsvm.h index 6547cb87..736c1edf 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -99,6 +99,229 @@ EXTERN_C_START +/** + * @brief Check whether the given JSVM_Value is a BigInt Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a BigInt Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsBigIntObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Check whether the given JSVM_Value is a Boolean Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a Boolean Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsBooleanObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Check whether the given JSVM_Value is a String Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a String Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsStringObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Check whether the given JSVM_Value is a Number Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a Number Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsNumberObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief Check whether the given JSVM_Value is a Symbol Object. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param result Whether the given value is a Symbol Object. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsSymbolObject(JSVM_Env env, + JSVM_Value value, + bool* result); + +/** + * @brief This API returns the Symbol.asyncIterator of Well-Known Symbols. + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.asyncIterator of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolAsyncIterator(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.hasInstance of Well-Known Symbols. + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.hasInstance of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolHasInstance(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.isConcatSpreadable of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.isConcatSpreadable of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolIsConcatSpreadable(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.match of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.match of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolMatch(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.replace of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.replace of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolReplace(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.search of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.search of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolSearch(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.split of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.split of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolSplit(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.toPrimitive of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.toPrimitive of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolToPrimitive(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.unscopables of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.unscopables of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolUnscopables(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.toStringTag of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.toStringTag of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolToStringTag(JSVM_Env env, JSVM_Value* result); + +/** + * @brief This API returns the Symbol.iterator of Well-Known Symbols + * + * @param env The environment that the API is invoked under. + * @param result The Symbol.iterator of Well-Known Symbols. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if any of the pointer arguments is NULL.\n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_GetSymbolIterator(JSVM_Env env, JSVM_Value* result); + /** * @brief Init a JavaScript vm. * -- Gitee From c5c843b771449c04be726092e630ff0a40ea03de Mon Sep 17 00:00:00 2001 From: wangyimin Date: Tue, 26 Nov 2024 20:03:10 +0800 Subject: [PATCH 8/9] cherry pick f3de1df from https://gitee.com/wang-yimin15/third_party_node/pulls/188 add OH_JSVM_PromiseRegisterHandler Signed-off-by: wangyimin --- src/js_native_api_v8.cc | 42 +++++++++++++++++++++++++++++++++++++++++ src/jsvm.h | 22 +++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 93de3936..b1a335bd 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -2118,6 +2118,48 @@ class CompileOptionResolver { bool hasInvalidOption = false; }; +JSVM_Status JSVM_CDECL OH_JSVM_PromiseRegisterHandler( + JSVM_Env env, JSVM_Value promise, JSVM_Value onFulfilled, JSVM_Value onRejected, JSVM_Value *result) +{ + JSVM_PREAMBLE(env); + CHECK_ARG(env, promise); + RETURN_STATUS_IF_FALSE(env, onFulfilled || onRejected, JSVM_INVALID_ARG); + + v8::Local value = v8impl::V8LocalValueFromJsValue(promise); + RETURN_STATUS_IF_FALSE(env, value->IsPromise(), JSVM_INVALID_TYPE); + auto localPromise = value.As(); + + v8::Local ctx = env->context(); + v8::MaybeLocal maybe; + if (!onFulfilled) { + // Only pass onRejected, call v8::Promise::Catch + auto rejectedHandler = v8impl::V8LocalValueFromJsValue(onRejected); + RETURN_STATUS_IF_FALSE(env, rejectedHandler->IsFunction(), JSVM_INVALID_TYPE); + maybe = localPromise->Catch(ctx, rejectedHandler.As()); + } else if (!onRejected) { + // Only pass onFulfilled, call v8::Promise::Then + auto fulfiledHandler = v8impl::V8LocalValueFromJsValue(onFulfilled); + RETURN_STATUS_IF_FALSE(env, fulfiledHandler->IsFunction(), JSVM_INVALID_TYPE); + maybe = value.As()->Then(ctx, fulfiledHandler.As()); + } else { + // Pass onFulfilled and onRejected, call v8::Promise::Then + auto fulfiledHandler = v8impl::V8LocalValueFromJsValue(onFulfilled); + RETURN_STATUS_IF_FALSE(env, fulfiledHandler->IsFunction(), JSVM_INVALID_TYPE); + auto rejectedHandler = v8impl::V8LocalValueFromJsValue(onRejected); + RETURN_STATUS_IF_FALSE(env, rejectedHandler->IsFunction(), JSVM_INVALID_TYPE); + maybe = value.As()->Then(ctx, fulfiledHandler.As(), rejectedHandler.As()); + } + + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, JSVM_GENERIC_FAILURE); + + if (result) { + auto retPromise = maybe.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(retPromise); + } + + return jsvm_clear_last_error(env); +} + size_t CompileOptionResolver::compileCount = 0; JSVM_Status JSVM_CDECL diff --git a/src/jsvm.h b/src/jsvm.h index 736c1edf..99aea441 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -2795,6 +2795,28 @@ JSVM_EXTERN JSVM_Status OH_JSVM_IsPromise(JSVM_Env env, JSVM_Value value, bool* isPromise); +/** + * @brief This API register a resolution/rejection handler with a promise. + * @param env The environment that the API is invoked under. + * @param promise The promise to be handled. + * @param onFulfilled The function to be invoked if promise is resolved. + * @param onRejected The function to be invoked if promise is rejected. + * @param result Another promise returned from promise then/catch method. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the arguments are invalid. \n + * {@link JSVM_INVALID_TYPE } if the arguments are invalid Javascript type. \n + * {@link JSVM_PENDING_EXCEPTION} if an exception occurs. \n + * {@link JSVM_GENERIC_FAILURE} if the API failed. \n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_PromiseRegisterHandler(JSVM_Env env, + JSVM_Value promise, + JSVM_Value onFulfilled, + JSVM_Value onRejected, + JSVM_Value* result); + /** * @brief This API parses a JSON string and returns it as value if successful. * @param env The environment that the API is invoked under. -- Gitee From 793e54812e1adc0535d11ba5668bdddbed307b12 Mon Sep 17 00:00:00 2001 From: wangyimin Date: Tue, 26 Nov 2024 20:06:01 +0800 Subject: [PATCH 9/9] cherry pick a4b9107 from https://gitee.com/wang-yimin15/third_party_node/pulls/187 add proxy Signed-off-by: wangyimin --- src/js_native_api_v8.cc | 101 ++++++++++++++++++++++++++++-------- src/jsvm.h | 111 ++++++++++++++++++++++++++++++---------- src/jsvm_types.h | 8 ++- 3 files changed, 168 insertions(+), 52 deletions(-) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index b1a335bd..89e61a9f 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -10,6 +10,7 @@ #include "v8-primitive.h" #include "v8-statistics.h" #include "v8-version-string.h" +#include "v8-proxy.h" #define JSVM_EXPERIMENTAL #include "env-inl.h" #include "jsvm.h" @@ -1787,6 +1788,59 @@ OH_JSVM_DestroyVM(JSVM_VM vm) { return JSVM_OK; } +JSVM_Status JSVM_CDECL OH_JSVM_CreateProxy(JSVM_Env env, JSVM_Value target, JSVM_Value handler, JSVM_Value *result) +{ + // Check args is not null + JSVM_PREAMBLE(env); + CHECK_ARG(env, target); + CHECK_ARG(env, handler); + CHECK_ARG(env, result); + + // Check target and handler are v8 Object + auto localTarget = v8impl::V8LocalValueFromJsValue(target); + RETURN_STATUS_IF_FALSE(env, localTarget->IsObject(), JSVM_OBJECT_EXPECTED); + auto localHandler = v8impl::V8LocalValueFromJsValue(handler); + RETURN_STATUS_IF_FALSE(env, localHandler->IsObject(), JSVM_OBJECT_EXPECTED); + + v8::Local context = env->context(); + + v8::MaybeLocal maybeProxy = + v8::Proxy::New(context, localTarget.As(), localHandler.As()); + + CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybeProxy, JSVM_GENERIC_FAILURE); + + v8::Local proxy = maybeProxy.ToLocalChecked(); + *result = v8impl::JsValueFromV8LocalValue(proxy); + + return jsvm_clear_last_error(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_IsProxy(JSVM_Env env, JSVM_Value value, bool *isProxy) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, isProxy); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + *isProxy = val->IsProxy(); + + return jsvm_clear_last_error(env); +} + +JSVM_Status JSVM_CDECL OH_JSVM_ProxyGetTarget(JSVM_Env env, JSVM_Value value, JSVM_Value *result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + RETURN_STATUS_IF_FALSE(env, val->IsProxy(), JSVM_INVALID_TYPE); + + *result = v8impl::JsValueFromV8LocalValue(val.As()->GetTarget()); + return jsvm_clear_last_error(env); +} + JSVM_Status JSVM_CDECL OH_JSVM_OpenVMScope(JSVM_VM vm, JSVM_VMScope* result) { auto isolate = reinterpret_cast(vm); auto scope = new v8::Isolate::Scope(isolate); @@ -2445,6 +2499,7 @@ static const char* error_messages[] = { "Main thread would deadlock", "External buffers are not allowed", "Cannot run JavaScript", + "Invalid type" }; JSVM_Status JSVM_CDECL OH_JSVM_GetLastErrorInfo( @@ -2456,7 +2511,7 @@ JSVM_Status JSVM_CDECL OH_JSVM_GetLastErrorInfo( // message in the `JSVM_Status` enum each time a new error message is added. // We don't have a jsvm_status_last as this would result in an ABI // change each time a message was added. - const int last_status = JSVM_CANNOT_RUN_JS; + const int last_status = JSVM_INVALID_TYPE; static_assert(JSVM_ARRAYSIZE(error_messages) == last_status + 1, "Count of error messages must match count of error values"); @@ -3234,7 +3289,7 @@ static void OnFatalError(const char* location, const char* message) { } JSVM_Status JSVM_CDECL OH_JSVM_SetHandlerForFatalError(JSVM_VM vm, - JSVM_HandlerForFatalError handler) { + JSVM_HandlerForFatalError handler) { if (vm == nullptr) { return JSVM_INVALID_ARG; } @@ -3320,7 +3375,7 @@ JSVM_GCType GetJSVMGCType(v8::GCType gcType) { } static v8::GCType GetV8GCType(JSVM_GCType gcType) { - switch(gcType) { + switch (gcType) { case JSVM_GC_TYPE_SCAVENGE: return v8::GCType::kGCTypeScavenge; case JSVM_GC_TYPE_MINOR_MARK_COMPACT: @@ -3379,20 +3434,21 @@ JSVM_Status JSVM_CDECL OH_JSVM_AddHandlerForGC(JSVM_VM vm, JSVM_CBTriggerTimeForGC triggerTime, JSVM_HandlerForGC handler, JSVM_GCType gcType, - void* data) { + void* userData) { if (!vm || !handler) { return JSVM_INVALID_ARG; } auto* isolate = reinterpret_cast(vm); auto* pool = v8impl::GetOrCreateIsolateHandlerPool(isolate); - auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; - auto it = std::find_if(handlers.begin(), handlers.end(), [handler, data](v8impl::GCHandlerWrapper *callbackData) { - return callbackData->handler == handler && callbackData->userData == data; + auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? + pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; + auto it = std::find_if(handlers.begin(), handlers.end(), [handler, userData](v8impl::GCHandlerWrapper *callbackData) { + return callbackData->handler == handler && callbackData->userData == userData; }); if (it != handlers.end()) { return JSVM_INVALID_ARG; } - auto *callbackData = new v8impl::GCHandlerWrapper(gcType, handler, data); + auto *callbackData = new v8impl::GCHandlerWrapper(gcType, handler, userData); handlers.push_back(callbackData); if (triggerTime == JSVM_CB_TRIGGER_BEFORE_GC) { @@ -3417,7 +3473,8 @@ JSVM_Status JSVM_CDECL OH_JSVM_RemoveHandlerForGC(JSVM_VM vm, if (pool == nullptr) { return JSVM_INVALID_ARG; } - auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; + auto &handlers = triggerTime == JSVM_CB_TRIGGER_BEFORE_GC ? + pool->handlerWrappersBeforeGC : pool->handlerWrappersAfterGC; auto it = std::find_if(handlers.begin(), handlers.end(), [handler, userData](v8impl::GCHandlerWrapper *callbackData) { return callbackData->handler == handler && callbackData->userData == userData; }); @@ -5866,8 +5923,8 @@ OH_JSVM_DefineClassWithOptions(JSVM_Env env, continue; } - v8::Local property_name; - STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &property_name)); + v8::Local propertyName; + STATUS_CALL(v8impl::V8NameFromPropertyDescriptor(env, p, &propertyName)); v8::PropertyAttribute attributes = v8impl::V8PropertyAttributesFromDescriptor(p); // This code is similar to that in OH_JSVM_DefineProperties(); the @@ -5875,31 +5932,31 @@ OH_JSVM_DefineClassWithOptions(JSVM_Env env, // and preferred PropertyAttribute for lack of PropertyDescriptor // support on ObjectTemplate. if (p->getter != nullptr || p->setter != nullptr) { - v8::Local getter_tpl; - v8::Local setter_tpl; + v8::Local getterTpl; + v8::Local setterTpl; if (p->getter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( - env, p->getter, &getter_tpl)); + env, p->getter, &getterTpl)); } if (p->setter != nullptr) { STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( - env, p->setter, &setter_tpl)); + env, p->setter, &setterTpl)); } - tpl->PrototypeTemplate()->SetAccessorProperty(property_name, - getter_tpl, - setter_tpl, + tpl->PrototypeTemplate()->SetAccessorProperty(propertyName, + getterTpl, + setterTpl, attributes, v8::AccessControl::DEFAULT); } else if (p->method != nullptr) { - v8::Local t; + v8::Local temp; STATUS_CALL(v8impl::FunctionCallbackWrapper::NewTemplate( - env, p->method, &t, v8::Signature::New(isolate, tpl))); + env, p->method, &temp, v8::Signature::New(isolate, tpl))); - tpl->PrototypeTemplate()->Set(property_name, t, attributes); + tpl->PrototypeTemplate()->Set(propertyName, temp, attributes); } else { v8::Local value = v8impl::V8LocalValueFromJsValue(p->value); - tpl->PrototypeTemplate()->Set(property_name, value, attributes); + tpl->PrototypeTemplate()->Set(propertyName, value, attributes); } } diff --git a/src/jsvm.h b/src/jsvm.h index 99aea441..5b645e5c 100644 --- a/src/jsvm.h +++ b/src/jsvm.h @@ -366,6 +366,61 @@ JSVM_EXTERN JSVM_Status OH_JSVM_SetMicrotaskPolicy(JSVM_VM vm, */ JSVM_EXTERN JSVM_Status OH_JSVM_DestroyVM(JSVM_VM vm); +/** + * @brief This API allocates a default JavaScript Proxy. It is the equivalent of + * doing new Proxy(target, handler) in JavaScript. + * + * @param env The environment that the API is invoked under. + * @param target A JSVM_Value representing the JavaScript Object which you want to proxy. + * @param handler A JSVM_Value representing the JavaScript Object that defines which + * operations will be intercepted and how to redefine intercepted operations. + * @param result A JSVM_Value representing a JavaScript Proxy. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the any of the input arguments is NULL. \n + * {@link JSVM_OBJECT_EXPECTED} if target or handler is not Javascript Object. \n + * {@link JSVM_PENDING_EXCEPTION} if an exception occurs. \n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_CreateProxy(JSVM_Env env, + JSVM_Value target, + JSVM_Value handler, + JSVM_Value* result); + +/** + * @brief This API checks if the value passed in is a Proxy. + * + * @param env The environment that the API is invoked under. + * @param value The JavaScript value to check. + * @param isProxy Whether the given value is Proxy. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the any of the input arguments is NULL. \n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_IsProxy(JSVM_Env env, + JSVM_Value value, + bool* isProxy); + +/** + * @brief This API gets target from proxy. + * + * @param env The environment that the API is invoked under. + * @param value JSVM_Value representing JavaScript Proxy whose target to return. + * @param result Target of the given proxy. + * @return Returns JSVM functions result code. + * {@link JSVM_OK } if the API succeeded. \n + * {@link JSVM_INVALID_ARG } if the any of the input arguments is NULL. \n + * {@link JSVM_INVALID_TYPE} if value is not a Javascript Proxy. \n + * + * @since 16 + */ +JSVM_EXTERN JSVM_Status OH_JSVM_ProxyGetTarget(JSVM_Env env, + JSVM_Value value, + JSVM_Value* result); + /** * @brief This API open a new VM scope for the VM instance. * @@ -1412,7 +1467,7 @@ JSVM_EXTERN JSVM_Status OH_JSVM_CreateStringUtf8(JSVM_Env env, * Returns {@link JSVM_INVALID_ARG } if one of env, str and copied is NULL.\n * @since 16 */ -JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringLatin1(JSVM_Env env, +JSVM_EXTERN JSVM_Status OH_JSVM_CreateExternalStringLatin1(JSVM_Env env, char* str, size_t length, JSVM_Finalize finalizeCallback, @@ -1439,7 +1494,7 @@ JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringLatin1(JSVM_Env env, * @since 16 */ -JSVM_Status JSVM_CDECL OH_JSVM_CreateExternalStringUtf16(JSVM_Env env, +JSVM_EXTERN JSVM_Status OH_JSVM_CreateExternalStringUtf16(JSVM_Env env, char16_t* str, size_t length, JSVM_Finalize finalizeCallback, @@ -1913,19 +1968,19 @@ JSVM_EXTERN JSVM_Status OH_JSVM_Instanceof(JSVM_Env env, /** -* @brief Add VM GC Callback. -* -* @param vm The environment that the API is invoked under. -* @param triggerTime The timing of GC callback trigger. -* @param handler When Trigger gc, the callback function will be called. -* @param gcType The type of gc. -* @param userData The native pointer data. -* @return Returns JSVM funtions result code. -* {@link JSVM_OK } if the function executed successfully.\n -* {@link JSVM_INVALID_ARG } if the vm or the handler is NULL or the handler has been added before.\n -* -* @since 16 -*/ + * @brief Add VM GC Callback. + * + * @param vm The environment that the API is invoked under. + * @param triggerTime The timing of GC callback trigger. + * @param handler When Trigger gc, the callback function will be called. + * @param gcType The type of gc. + * @param userData The native pointer data. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if the vm or the handler is NULL or the handler has been added before.\n + * + * @since 16 + */ JSVM_EXTERN JSVM_Status OH_JSVM_AddHandlerForGC(JSVM_VM vm, JSVM_CBTriggerTimeForGC triggerTime, JSVM_HandlerForGC handler, @@ -1933,19 +1988,19 @@ JSVM_EXTERN JSVM_Status OH_JSVM_AddHandlerForGC(JSVM_VM vm, void* userData); /** -* @brief Remove VM GC Callback. -* -* @param vm The environment that the API is invoked under. -* @param triggerTime The timing of GC callback trigger. -* @param handler When Trigger gc, the callback function will be called. -* @param userData The native pointer data. -* @return Returns JSVM funtions result code. -* {@link JSVM_OK } if the function executed successfully.\n -* {@link JSVM_INVALID_ARG } if the vm or the handler is NULL, or the handler has been removed, -* or the handler has never been added.\n -* -* @since 16 -*/ + * @brief Remove VM GC Callback. + * + * @param vm The environment that the API is invoked under. + * @param triggerTime The timing of GC callback trigger. + * @param handler When Trigger gc, the callback function will be called. + * @param userData The native pointer data. + * @return Returns JSVM funtions result code. + * {@link JSVM_OK } if the function executed successfully.\n + * {@link JSVM_INVALID_ARG } if the vm or the handler is NULL, or the handler has been removed, + * or the handler has never been added.\n + * + * @since 16 + */ JSVM_EXTERN JSVM_Status OH_JSVM_RemoveHandlerForGC(JSVM_VM vm, JSVM_CBTriggerTimeForGC triggerTime, JSVM_HandlerForGC handler, diff --git a/src/jsvm_types.h b/src/jsvm_types.h index 82900acd..f4a7e273 100644 --- a/src/jsvm_types.h +++ b/src/jsvm_types.h @@ -334,6 +334,10 @@ typedef enum { JSVM_NO_EXTERNAL_BUFFERS_ALLOWED, /** cannot run +js status. */ JSVM_CANNOT_RUN_JS, + /** invalid input type status. + * @since 16 + */ + JSVM_INVALID_TYPE } JSVM_Status; /** @@ -913,7 +917,7 @@ typedef enum { /** * @brief Function pointer type of GC callback. - * + * * @param vm The VM instance that the JSVM-API call is invoked under. * @param gcType The gc type. * @param flags The GC callback flags. @@ -969,7 +973,6 @@ typedef struct { bool boolean; } content; } JSVM_DefineClassOptions; -/** @} */ /** * @brief Microtask policies of JSVM. @@ -1007,5 +1010,6 @@ typedef enum { /** Tracing more detailed interface invoking of WASM, such as background compilation and wrappers. */ JSVM_TRACE_WASM_DETAILED } JSVM_TraceCategory; +/** @} */ #endif /* ARK_RUNTIME_JSVM_JSVM_TYPE_H */ -- Gitee