diff --git a/bundle.json b/bundle.json index 3145e934ba473262a7acf0dd4923ef94d5c492d2..465457429cd1cefeeb49e5225a7d893583f2922a 100644 --- a/bundle.json +++ b/bundle.json @@ -71,7 +71,8 @@ "header_base": "//foundation/arkui/napi/interfaces/inner_api", "header_files": [ "napi/native_common.h", - "napi/native_node_api.h" + "napi/native_node_api.h", + "napi/native_node_hybrid_api.h" ] }, "name": "//foundation/arkui/napi:ace_napi" diff --git a/interfaces/inner_api/napi/native_node_api.h b/interfaces/inner_api/napi/native_node_api.h index 03762ed9385d9d8c3cef0b83ed1986eef32be43e..92b92cfa5be10a3f74ba33dfdce133c7b2c12443 100644 --- a/interfaces/inner_api/napi/native_node_api.h +++ b/interfaces/inner_api/napi/native_node_api.h @@ -30,7 +30,6 @@ typedef void (*NapiNativeFinalize)(napi_env env, void* data, void* hint); typedef void* (*NapiDetachCallback)(napi_env env, void* nativeObject, void* hint); // hint: detach params typedef napi_value (*NapiAttachCallback)(napi_env env, void* nativeObject, void* hint); // hint: attach params typedef bool (*napi_module_validate_callback)(const char* moduleName); -typedef napi_value (*proxy_object_attach_cb)(napi_env env, void* data); typedef struct napi_fast_native_scope__* napi_fast_native_scope; typedef struct napi_module_with_js { @@ -68,10 +67,6 @@ typedef enum { using NapiAppStateCallback = void (*)(int state, int64_t timestamp); -NAPI_EXTERN napi_status napi_load_module_with_info_hybrid(napi_env env, - const char* path, - const char* module_info, - napi_value* result); NAPI_EXTERN napi_status napi_create_limit_runtime(napi_env env, napi_env* result_env); NAPI_EXTERN void napi_module_with_js_register(napi_module_with_js* mod); NAPI_EXTERN napi_status napi_is_callable(napi_env env, napi_value value, bool* result); diff --git a/interfaces/inner_api/napi/native_node_hybrid_api.h b/interfaces/inner_api/napi/native_node_hybrid_api.h new file mode 100644 index 0000000000000000000000000000000000000000..e4b0168cf9e1c74883fcccf62f3f50c657d71c56 --- /dev/null +++ b/interfaces/inner_api/napi/native_node_hybrid_api.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FOUNDATION_ACE_NAPI_INTERFACES_KITS_NAPI_NATIVE_NODE_HYBRID_API_H +#define FOUNDATION_ACE_NAPI_INTERFACES_KITS_NAPI_NATIVE_NODE_HYBRID_API_H + +#include +#include +#include +#include + +#include "js_native_api.h" +#include "native_common.h" +#include "node_api.h" + +typedef napi_value (*proxy_object_attach_cb)(napi_env env, void* data); + +typedef enum { + NAPI_APP_STATE_FOREGROUND = 0, + NAPI_APP_STATE_BACKGROUND = 1, + NAPI_APP_STATE_SENSITIVE_START = 2, + NAPI_APP_STATE_SENSITIVE_END = 3, + NAPI_APP_STATE_COLD_START_FINISHED = 4, +} NapiAppState; + +typedef enum { + NAPI_DIRECTION_INVALID = 0, + NAPI_DIRECTION_DYNAMIC_TO_STATIC = 1, // JS object references the STS object + NAPI_DIRECTION_STATIC_TO_DYNAMIC = 2, // STS object references the JS object + NAPI_DIRECTION_HYBRID = 3, // STS object and the JS object references each other +} NapiXRefDirection; + +using NapiAppStateCallback = void (*)(int state, int64_t timestamp); + +NAPI_EXTERN napi_status napi_load_module_with_info_hybrid(napi_env env, + const char* path, + const char* module_info, + napi_value* result); + +#ifdef PANDA_JS_ETS_HYBRID_MODE +// XGC specific internal API +NAPI_EXTERN napi_status napi_xref_wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + NapiXRefDirection ref_direction, + napi_ref* result); + +NAPI_EXTERN napi_status napi_xref_unwrap(napi_env env, napi_value js_object, void** result); + +NAPI_EXTERN napi_status napi_mark_from_object(napi_env env, napi_ref ref); + +NAPI_EXTERN napi_status napi_create_xref(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref* result); + +NAPI_EXTERN napi_status napi_vm_handshake(napi_env env, void* inputIface, void** outputIface); + +NAPI_EXTERN napi_status napi_mark_attach_with_xref(napi_env env, + napi_value js_object, + void* attach_data, + proxy_object_attach_cb attach_cb); + +NAPI_EXTERN napi_status napi_mark_from_object_for_cmc(napi_env env, napi_ref ref, + std::function &visitor); + +NAPI_EXTERN napi_status napi_is_alive_object(napi_env env, napi_ref ref, bool* result); +NAPI_EXTERN napi_status napi_is_contain_object(napi_env env, napi_ref ref, bool* result); +NAPI_EXTERN napi_status napi_is_xref_type(napi_env env, napi_value js_object, bool* result); +NAPI_EXTERN napi_status napi_get_ets_implements(napi_env env, napi_value value, napi_value* result); +NAPI_EXTERN napi_status napi_serialize_hybrid(napi_env env, + napi_value object, + napi_value transfer_list, + napi_value clone_list, + void** result); +NAPI_EXTERN napi_status napi_deserialize_hybrid(napi_env env, void* buffer, napi_value* object); + + +NAPI_EXTERN napi_status napi_wrap_with_xref(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + proxy_object_attach_cb proxy_cb, + napi_ref* result); +#endif // PANDA_JS_ETS_HYBRID_MODE +NAPI_EXTERN napi_status napi_register_appstate_callback(napi_env env, NapiAppStateCallback callback); + +#endif /* FOUNDATION_ACE_NAPI_INTERFACES_KITS_NAPI_NATIVE_NODE_HYBRID_API_H */ \ No newline at end of file diff --git a/module_manager/native_module_manager.h b/module_manager/native_module_manager.h index 6ac3f8819967476d16062426f198911c55236eab..8e9c94d05d922657c890ab13d1a954e37de239ab 100644 --- a/module_manager/native_module_manager.h +++ b/module_manager/native_module_manager.h @@ -28,6 +28,7 @@ #include "module_load_checker.h" #include "utils/macros.h" #include "interfaces/inner_api/napi/native_node_api.h" +#include "interfaces/inner_api/napi/native_node_hybrid_api.h" #ifdef WINDOWS_PLATFORM #include diff --git a/napi.gni b/napi.gni index 2872c4dcb9dcc90328bf523a57de79b61c9ba011..8d8aa02b8bf614ee9af4084dfd71b49cbbf74a80 100755 --- a/napi.gni +++ b/napi.gni @@ -33,6 +33,7 @@ napi_sources = [ "native_engine/native_engine.cpp", "native_engine/native_event.cpp", "native_engine/native_node_api.cpp", + "native_engine/native_node_hybrid_api.cpp", "native_engine/native_safe_async_work.cpp", "native_engine/native_sendable.cpp", "native_engine/worker_manager.cpp", diff --git a/native_engine/native_api.cpp b/native_engine/native_api.cpp index 8408b9c0563081c2b3af29f1f08e30f51a971b11..0e314b834839a132fe5397044c85a07a84630035 100644 --- a/native_engine/native_api.cpp +++ b/native_engine/native_api.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Copyright (c) 2021-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -1980,57 +1980,6 @@ NAPI_EXTERN napi_status napi_wrap_enhance(napi_env env, return GET_RETURN_STATUS(env); } -NAPI_EXTERN napi_status napi_xref_wrap(napi_env env, - napi_value js_object, - void* native_object, - napi_finalize finalize_cb, - NapiXRefDirection ref_direction, - napi_ref* result) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); - CHECK_ARG(env, native_object); - CHECK_ARG(env, finalize_cb); - - auto nativeValue = LocalValueFromJsValue(js_object); - auto callback = reinterpret_cast(finalize_cb); - auto engine = reinterpret_cast(env); - auto vm = engine->GetEcmaVm(); - panda::JsiFastNativeScope fastNativeScope(vm); - CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); - size_t nativeBindingSize = 0; - auto reference = reinterpret_cast(result); - Local key = panda::StringRef::GetProxyNapiWrapperString(vm); - NativeReference* ref = nullptr; - Local object = JSValueRef::Undefined(vm); - switch (ref_direction) { - case NapiXRefDirection::NAPI_DIRECTION_DYNAMIC_TO_STATIC: - object = panda::ObjectRef::NewJSXRefObject(vm); - ref = engine->CreateXRefReference(js_object, 1, false, callback, native_object); - break; - case NapiXRefDirection::NAPI_DIRECTION_STATIC_TO_DYNAMIC: - object = panda::ObjectRef::New(vm); - ref = engine->CreateXRefReference(js_object, 1, false, callback, native_object); - break; - case NapiXRefDirection::NAPI_DIRECTION_HYBRID: - // Hybrid object may only exist in cross-language inherence case. - // To be aborted in the future according to the specification - HILOG_ERROR("[napi_xref_wrap] napi_direction_hybrid is not supported now!"); - return napi_set_last_error(env, napi_invalid_arg); - default: - HILOG_ERROR("[napi_xref_wrap] invalid ref_direction!"); - return napi_set_last_error(env, napi_invalid_arg); - } - if (reference != nullptr) { - *reference = ref; - } - object->SetNativePointerFieldCount(vm, 1); - object->SetNativePointerField(vm, 0, ref, nullptr, nullptr, nativeBindingSize); - PropertyAttribute attr(object, true, false, true); - nativeObject->DefineProperty(vm, key, attr); - return GET_RETURN_STATUS(env); -} - // Ensure thread safety! Async finalizer will be called on the async thread. NAPI_EXTERN napi_status napi_wrap_async_finalizer(napi_env env, napi_value js_object, @@ -2135,28 +2084,6 @@ NAPI_EXTERN napi_status napi_unwrap(napi_env env, napi_value js_object, void** r return GET_RETURN_STATUS(env); } -NAPI_EXTERN napi_status napi_xref_unwrap(napi_env env, napi_value js_object, void** result) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); - CHECK_ARG(env, result); - - auto nativeValue = LocalValueFromJsValue(js_object); - auto vm = reinterpret_cast(env)->GetEcmaVm(); - panda::JsiFastNativeScope fastNativeScope(vm); - CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); - Local key = panda::StringRef::GetProxyNapiWrapperString(vm); - Local val = nativeObject->Get(vm, key); - *result = nullptr; - if (val->IsObject(vm)) { - Local ext(val); - auto ref = reinterpret_cast(ext->GetNativePointerField(vm, 0)); - *result = ref != nullptr ? ref->GetData() : nullptr; - } - - return GET_RETURN_STATUS(env); -} - NAPI_EXTERN napi_status napi_remove_wrap(napi_env env, napi_value js_object, void** result) { NAPI_PREAMBLE(env); @@ -3292,17 +3219,6 @@ NAPI_EXTERN napi_status napi_load_module_with_info(napi_env env, return GET_RETURN_STATUS(env); } -NAPI_EXTERN napi_status napi_load_module_with_info_hybrid(napi_env env, - const char* path, - const char* module_info, - napi_value* result) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, result); - auto engine = reinterpret_cast(env); - *result = engine->NapiLoadModuleWithInfoForHybridApp(path, module_info); - return GET_RETURN_STATUS(env); -} // Memory management NAPI_INNER_EXTERN napi_status napi_adjust_external_memory( napi_env env, int64_t change_in_bytes, int64_t* adjusted_value) @@ -4609,240 +4525,4 @@ NAPI_EXTERN napi_status napi_load_module_with_module_request(napi_env env, const } return GET_RETURN_STATUS(env); -} - -NAPI_EXTERN napi_status napi_register_appstate_callback(napi_env env, NapiAppStateCallback callback) -{ - CHECK_ENV(env); - CHECK_ARG(env, callback); - - auto* engine = reinterpret_cast(env); - engine->RegisterAppStateCallback(callback); - - return napi_clear_last_error(env); -} - -NAPI_EXTERN napi_status napi_remove_cleanup_finalizer(napi_env env, void (*fun)(void* arg), void* arg) -{ - CHECK_ENV(env); - CHECK_ARG(env, fun); - CROSS_THREAD_CHECK(env); - - auto engine = reinterpret_cast(env); - engine->RemoveCleanupFinalizer(fun, arg); - - return napi_clear_last_error(env); -} - -NAPI_EXTERN napi_status napi_create_ark_context(napi_env env, napi_env* newEnv) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, newEnv); - - auto nativeEngine = reinterpret_cast(env); - auto vm = nativeEngine->GetEcmaVm(); - // cannot to utilize a created env to generate a new env - if (!nativeEngine->IsMainEnvContext()) { - HILOG_ERROR("env cannot be generate by using a created env"); - return napi_set_last_error(env, napi_invalid_arg); - } - - // worker and taskpool will support multi-context later - if (!nativeEngine->IsMainThread()) { - HILOG_FATAL("multi-context feature only support main thread"); - return napi_set_last_error(env, napi_invalid_arg); - } - - auto context = panda::JSNApi::CreateContext(vm); - if (context.IsEmpty() || !context->IsJsGlobalEnv(vm)) { - HILOG_ERROR("Failed to create ark context"); - return napi_set_last_error(env, napi_generic_failure); - } - ArkNativeEngine* newEngine = ArkNativeEngine::New(nativeEngine, const_cast(vm), context); - if (newEngine == nullptr) { - HILOG_ERROR("Failed to create ark context engine"); - return napi_set_last_error(env, napi_generic_failure); - } - *newEnv = reinterpret_cast(newEngine); - - return GET_RETURN_STATUS(env); -} - -NAPI_EXTERN napi_status napi_switch_ark_context(napi_env env) -{ - NAPI_PREAMBLE(env); - auto nativeEngine = reinterpret_cast(env); - // worker and taskpool will support multi-context later - if (!nativeEngine->IsMainThread()) { - HILOG_FATAL("multi-context feature only support main thread"); - return napi_set_last_error(env, napi_invalid_arg); - } - - napi_status status = nativeEngine->SwitchContext(); - if (status != napi_ok) { - return napi_set_last_error(env, status); - } else { - HILOG_DEBUG("switch context successfully"); - } - return GET_RETURN_STATUS(env); -} - -NAPI_EXTERN napi_status napi_destroy_ark_context(napi_env env) -{ - CHECK_ENV(env); - - auto nativeEngine = reinterpret_cast(env); - const EcmaVM* vm = nativeEngine->GetEcmaVm(); - // The destructor of `TryCatch` may update `engine->lastException_`. - // If the engine is released before the `TryCatch` object is destroyed, - // it can lead to a use-after-free (UAF) vulnerability. - // Use equivalent logic instead of `TryCatch` to avoid this memory safety issue. - if (!nativeEngine->lastException_.IsEmpty() || panda::JSNApi::HasPendingException(vm)) { - nativeEngine->lastException_ = panda::JSNApi::GetAndClearUncaughtException(vm); - return napi_set_last_error(env, napi_pending_exception); - } - - // worker and taskpool will support multi-context later - if (!nativeEngine->IsMainThread()) { - HILOG_FATAL("multi-context feature only support main thread"); - return napi_set_last_error(env, napi_invalid_arg); - } - - // Do not use `nativeEngine` or `env` after this if status is napi_ok - napi_status status = nativeEngine->DestroyContext(); - if (status != napi_ok) { - return napi_set_last_error(env, status); - } - return napi_ok; -} - -#ifdef PANDA_JS_ETS_HYBRID_MODE -NAPI_EXTERN napi_status napi_vm_handshake(napi_env env, - [[maybe_unused]] void* inputIface, - [[maybe_unused]] void** outputIface) -{ - CHECK_ENV(env); - CHECK_ARG(env, inputIface); - CHECK_ARG(env, outputIface); - - auto vm = reinterpret_cast(env)->GetEcmaVm(); - panda::HandshakeHelper::DoHandshake(const_cast(vm), inputIface, outputIface); - - return napi_clear_last_error(env); -} - -NAPI_EXTERN napi_status napi_mark_from_object_for_cmc(napi_env env, napi_ref ref, - std::function &visitor) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, ref); - ArkNativeReference* reference = reinterpret_cast(ref); - reference->MarkFromObject(visitor); - return napi_clear_last_error(env); -} - -NAPI_EXTERN napi_status napi_mark_from_object(napi_env env, napi_ref ref) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, ref); - ArkNativeReference* reference = reinterpret_cast(ref); - reference->MarkFromObject(); - return napi_clear_last_error(env); -} - -NAPI_EXTERN napi_status napi_is_alive_object(napi_env env, napi_ref ref, bool* result) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, ref); - ArkNativeReference* reference = reinterpret_cast(ref); - *result = reference->IsObjectAlive(); - return napi_clear_last_error(env); -} - -NAPI_EXTERN napi_status napi_is_contain_object(napi_env env, napi_ref ref, bool* result) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, ref); - ArkNativeReference* reference = reinterpret_cast(ref); - *result = reference->IsValidHeapObject(); - return napi_clear_last_error(env); -} - -NAPI_EXTERN napi_status napi_create_xref(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref* result) -{ - CHECK_ENV(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - auto engine = reinterpret_cast(env); - auto ref = engine->CreateXRefReference(value, initial_refcount, false, nullptr, nullptr); - *result = reinterpret_cast(ref); - return napi_clear_last_error(env); -} - -NAPI_EXTERN napi_status napi_wrap_with_xref(napi_env env, - napi_value js_object, - void* native_object, - napi_finalize finalize_cb, - proxy_object_attach_cb proxy_cb, - napi_ref* result) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); - CHECK_ARG(env, native_object); - CHECK_ARG(env, finalize_cb); - CHECK_ARG(env, result); - - auto nativeValue = LocalValueFromJsValue(js_object); - auto callback = reinterpret_cast(finalize_cb); - auto engine = reinterpret_cast(env); - auto vm = engine->GetEcmaVm(); - panda::JsiFastNativeScope fastNativeScope(vm); - CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); - size_t nativeBindingSize = 0; - auto reference = reinterpret_cast(result); - Local key = panda::StringRef::GetProxyNapiWrapperString(vm); - NativeReference* ref = nullptr; - Local object = panda::ObjectRef::NewJSXRefObject(vm); - // Create strong reference now, will update to weak reference after interop support - ref = engine->CreateXRefReference(js_object, 1, false, callback, native_object); - *reference = ref; - panda::JSNApi::XRefBindingInfo* data = panda::JSNApi::XRefBindingInfo::CreateNewInstance(); - if (data == nullptr) { - HILOG_ERROR("data is nullptr"); - return napi_set_last_error(env, napi_invalid_arg); - } - data->attachXRefFunc = reinterpret_cast(proxy_cb); - data->attachXRefData = native_object; - object->SetNativePointerFieldCount(vm, 1); - object->SetNativePointerField(vm, 0, ref, - [](void* env, void* data, void* info) { - panda::JSNApi::XRefBindingInfo* externalInfo = reinterpret_cast(info); - delete externalInfo; - }, - reinterpret_cast(data), nativeBindingSize); - PropertyAttribute attr(object, true, false, true); - nativeObject->DefineProperty(vm, key, attr); - return GET_RETURN_STATUS(env); -} - -NAPI_EXTERN napi_status napi_is_xref_type(napi_env env, napi_value js_object, bool* result) -{ - *result = false; - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); - CHECK_ARG(env, result); - - auto nativeValue = LocalValueFromJsValue(js_object); - auto engine = reinterpret_cast(env); - auto vm = engine->GetEcmaVm(); - panda::JsiFastNativeScope fastNativeScope(vm); - CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); - Local key = panda::StringRef::GetProxyNapiWrapperString(vm); - - if (nativeObject->Has(vm, key)) { - *result = true; - } - return GET_RETURN_STATUS(env); -} -#endif // PANDA_JS_ETS_HYBRID_MODE +} \ No newline at end of file diff --git a/native_engine/native_node_api.cpp b/native_engine/native_node_api.cpp index 293cf09d032570afd3fa99da96f2c3eced4b0e7a..87049ff47a8becef6ead1c08d4cbde3d749a0816 100644 --- a/native_engine/native_node_api.cpp +++ b/native_engine/native_node_api.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,12 +16,6 @@ #include "native_api_internal.h" #include "native_engine/native_async_hook_context.h" #include "native_engine/native_utils.h" -#include "native_engine/impl/ark/ark_native_engine.h" -#ifdef PANDA_JS_ETS_HYBRID_MODE -#include "ecmascript/napi/include/jsnapi.h" -#include "ecmascript/napi/include/jsnapi_expo.h" -#include "native_engine/impl/ark/ark_native_reference.h" -#endif // PANDA_JS_ETS_HYBRID_MODE using panda::Local; using panda::StringRef; @@ -639,105 +633,4 @@ NAPI_EXTERN napi_status napi_make_callback(napi_env env, *result = reinterpret_cast(callBackRst); } return GET_RETURN_STATUS(env); -} - -NAPI_EXTERN napi_status napi_set_module_validate_callback(napi_module_validate_callback check_callback) -{ - CHECK_ENV(check_callback); - - if (ArkNativeEngine::SetModuleValidateCallback(check_callback)) { - return napi_ok; - } - return napi_generic_failure; -} -NAPI_EXTERN napi_status napi_get_ets_implements(napi_env env, napi_value value, napi_value* result) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, value); - CHECK_ARG(env, result); - - auto nativeValue = LocalValueFromJsValue(value); - auto engine = reinterpret_cast(env); - auto vm = engine->GetEcmaVm(); - Local implementsValue = panda::JSNApi::GetImplements(vm, nativeValue); - *result = JsValueFromLocalValue(implementsValue); - return GET_RETURN_STATUS(env); -} - -#ifdef PANDA_JS_ETS_HYBRID_MODE -NAPI_EXTERN napi_status napi_serialize_hybrid(napi_env env, - napi_value object, - napi_value transfer_list, - napi_value clone_list, - void** result) -{ - CHECK_ENV(env); - CHECK_ARG(env, object); - CHECK_ARG(env, transfer_list); - CHECK_ARG(env, clone_list); - CHECK_ARG(env, result); - - auto vm = reinterpret_cast(env)->GetEcmaVm(); - auto nativeValue = LocalValueFromJsValue(object); - auto transferList = LocalValueFromJsValue(transfer_list); - RETURN_STATUS_IF_FALSE(env, transferList->IsUndefined() || transferList->IsJSArray(vm), napi_invalid_arg); - auto cloneList = LocalValueFromJsValue(clone_list); - RETURN_STATUS_IF_FALSE(env, cloneList->IsUndefined() || cloneList->IsJSArray(vm), napi_invalid_arg); - *result = panda::JSNApi::InterOpSerializeValue(vm, nativeValue, transferList, cloneList, false, false); - - return napi_clear_last_error(env); -} - -NAPI_EXTERN napi_status napi_deserialize_hybrid(napi_env env, void* buffer, napi_value* object) -{ - CHECK_ENV(env); - CHECK_ARG(env, buffer); - CHECK_ARG(env, object); - - auto engine = reinterpret_cast(env); - auto vm = engine->GetEcmaVm(); - Local res = panda::JSNApi::InterOpDeserializeValue(vm, buffer, reinterpret_cast(engine)); - *object = JsValueFromLocalValue(res); - - return napi_clear_last_error(env); -} - - -NAPI_EXTERN napi_status napi_mark_attach_with_xref(napi_env env, - napi_value js_object, - void *attach_data, - proxy_object_attach_cb attach_cb) -{ - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); - CHECK_ARG(env, attach_data); - CHECK_ARG(env, attach_cb); - - auto nativeValue = LocalValueFromJsValue(js_object); - auto engine = reinterpret_cast(env); - auto vm = engine->GetEcmaVm(); - panda::JsiFastNativeScope fastNativeScope(vm); - CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); - size_t nativeBindingSize = 0; - Local key = panda::StringRef::GetProxyNapiWrapperString(vm); - Local object = panda::ObjectRef::NewJSXRefObject(vm); - // Create strong reference now, will update to weak reference after interop support - panda::JSNApi::XRefBindingInfo* data = panda::JSNApi::XRefBindingInfo::CreateNewInstance(); - if (data == nullptr) { - HILOG_ERROR("data is nullptr"); - return napi_set_last_error(env, napi_invalid_arg); - } - data->attachXRefFunc = reinterpret_cast(attach_cb); - data->attachXRefData = attach_data; - object->SetNativePointerFieldCount(vm, 1); - object->SetNativePointerField(vm, 0, nullptr, - [](void* env, void* data, void* info) { - panda::JSNApi::XRefBindingInfo* externalInfo = reinterpret_cast(info); - delete externalInfo; - }, - reinterpret_cast(data), nativeBindingSize); - panda::PropertyAttribute attr(object, true, false, true); - nativeObject->DefineProperty(vm, key, attr); - return GET_RETURN_STATUS(env); -} -#endif // PANDA_JS_ETS_HYBRID_MODE \ No newline at end of file +} \ No newline at end of file diff --git a/native_engine/native_node_hybrid_api.cpp b/native_engine/native_node_hybrid_api.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72c03db12752a086706a77f7234f16b3a2b63d17 --- /dev/null +++ b/native_engine/native_node_hybrid_api.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef NAPI_EXPERIMENTAL +#define NAPI_EXPERIMENTAL +#endif + +#ifdef ENABLE_CONTAINER_SCOPE +#include "core/common/container_scope.h" +#endif + +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/napi/include/jsnapi_expo.h" +#include "native_api_internal.h" +#include "native_engine/impl/ark/ark_native_reference.h" +#include "native_engine/native_create_env.h" +#include "native_engine/native_utils.h" +#include "native_engine/worker_manager.h" +#include "securec.h" + +using panda::ObjectRef; + +NAPI_EXTERN napi_status napi_xref_wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + NapiXRefDirection ref_direction, + napi_ref* result) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, js_object); + CHECK_ARG(env, native_object); + CHECK_ARG(env, finalize_cb); + + auto nativeValue = LocalValueFromJsValue(js_object); + auto callback = reinterpret_cast(finalize_cb); + auto engine = reinterpret_cast(env); + auto vm = engine->GetEcmaVm(); + panda::JsiFastNativeScope fastNativeScope(vm); + CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); + size_t nativeBindingSize = 0; + auto reference = reinterpret_cast(result); + Local key = panda::StringRef::GetProxyNapiWrapperString(vm); + NativeReference* ref = nullptr; + Local object = JSValueRef::Undefined(vm); + switch (ref_direction) { + case NapiXRefDirection::NAPI_DIRECTION_DYNAMIC_TO_STATIC: + object = panda::ObjectRef::NewJSXRefObject(vm); + ref = engine->CreateXRefReference(js_object, 1, false, callback, native_object); + break; + case NapiXRefDirection::NAPI_DIRECTION_STATIC_TO_DYNAMIC: + object = panda::ObjectRef::New(vm); + ref = engine->CreateXRefReference(js_object, 1, false, callback, native_object); + break; + case NapiXRefDirection::NAPI_DIRECTION_HYBRID: + // Hybrid object may only exist in cross-language inherence case. + // To be aborted in the future according to the specification + HILOG_ERROR("[napi_xref_wrap] napi_direction_hybrid is not supported now!"); + return napi_set_last_error(env, napi_invalid_arg); + default: + HILOG_ERROR("[napi_xref_wrap] invalid ref_direction!"); + return napi_set_last_error(env, napi_invalid_arg); + } + if (reference != nullptr) { + *reference = ref; + } + object->SetNativePointerFieldCount(vm, 1); + object->SetNativePointerField(vm, 0, ref, nullptr, nullptr, nativeBindingSize); + PropertyAttribute attr(object, true, false, true); + nativeObject->DefineProperty(vm, key, attr); + return GET_RETURN_STATUS(env); +} + +NAPI_EXTERN napi_status napi_xref_unwrap(napi_env env, napi_value js_object, void** result) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, js_object); + CHECK_ARG(env, result); + + auto nativeValue = LocalValueFromJsValue(js_object); + auto vm = reinterpret_cast(env)->GetEcmaVm(); + panda::JsiFastNativeScope fastNativeScope(vm); + CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); + Local key = panda::StringRef::GetProxyNapiWrapperString(vm); + Local val = nativeObject->Get(vm, key); + *result = nullptr; + if (val->IsObject(vm)) { + Local ext(val); + auto ref = reinterpret_cast(ext->GetNativePointerField(vm, 0)); + *result = ref != nullptr ? ref->GetData() : nullptr; + } + + return GET_RETURN_STATUS(env); +} + +NAPI_EXTERN napi_status napi_mark_from_object(napi_env env, napi_ref ref) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, ref); + ArkNativeReference* reference = reinterpret_cast(ref); + reference->MarkFromObject(); + return napi_clear_last_error(env); +} + +NAPI_EXTERN napi_status napi_create_xref(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref* result) +{ + CHECK_ENV(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + auto engine = reinterpret_cast(env); + auto ref = engine->CreateXRefReference(value, initial_refcount, false, nullptr, nullptr); + *result = reinterpret_cast(ref); + return napi_clear_last_error(env); +} + +NAPI_EXTERN napi_status napi_get_ets_implements(napi_env env, napi_value value, napi_value* result) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + CHECK_ARG(env, result); + + auto nativeValue = LocalValueFromJsValue(value); + auto engine = reinterpret_cast(env); + auto vm = engine->GetEcmaVm(); + Local implementsValue = panda::JSNApi::GetImplements(vm, nativeValue); + *result = JsValueFromLocalValue(implementsValue); + return GET_RETURN_STATUS(env); +} + +NAPI_EXTERN napi_status napi_register_appstate_callback(napi_env env, NapiAppStateCallback callback) +{ + CHECK_ENV(env); + CHECK_ARG(env, callback); + + auto* engine = reinterpret_cast(env); + engine->RegisterAppStateCallback(callback); + + return napi_clear_last_error(env); +} + +NAPI_EXTERN napi_status napi_load_module_with_info_hybrid(napi_env env, + const char* path, + const char* module_info, + napi_value* result) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, result); + auto engine = reinterpret_cast(env); + *result = engine->NapiLoadModuleWithInfoForHybridApp(path, module_info); + return GET_RETURN_STATUS(env); +} + +#ifdef PANDA_JS_ETS_HYBRID_MODE +NAPI_EXTERN napi_status napi_vm_handshake(napi_env env, + [[maybe_unused]] void* inputIface, + [[maybe_unused]] void** outputIface) +{ + CHECK_ENV(env); + CHECK_ARG(env, inputIface); + CHECK_ARG(env, outputIface); + + auto vm = reinterpret_cast(env)->GetEcmaVm(); + panda::HandshakeHelper::DoHandshake(const_cast(vm), inputIface, outputIface); + + return napi_clear_last_error(env); +} + +NAPI_EXTERN napi_status napi_mark_from_object_for_cmc(napi_env env, + napi_ref ref, + std::function& visitor) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, ref); + ArkNativeReference* reference = reinterpret_cast(ref); + reference->MarkFromObject(visitor); + return napi_clear_last_error(env); +} + +NAPI_EXTERN napi_status napi_is_alive_object(napi_env env, napi_ref ref, bool* result) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, ref); + ArkNativeReference* reference = reinterpret_cast(ref); + *result = reference->IsObjectAlive(); + return napi_clear_last_error(env); +} + +NAPI_EXTERN napi_status napi_is_contain_object(napi_env env, napi_ref ref, bool* result) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, ref); + ArkNativeReference* reference = reinterpret_cast(ref); + *result = reference->IsValidHeapObject(); + return napi_clear_last_error(env); +} + +NAPI_EXTERN napi_status napi_is_xref_type(napi_env env, napi_value js_object, bool* result) +{ + *result = false; + NAPI_PREAMBLE(env); + CHECK_ARG(env, js_object); + CHECK_ARG(env, result); + + auto nativeValue = LocalValueFromJsValue(js_object); + auto engine = reinterpret_cast(env); + auto vm = engine->GetEcmaVm(); + CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); + Local key = panda::StringRef::GetProxyNapiWrapperString(vm); + + if (nativeObject->Has(vm, key)) { + *result = true; + } + return GET_RETURN_STATUS(env); +} + +NAPI_EXTERN napi_status napi_mark_attach_with_xref(napi_env env, + napi_value js_object, + void* attach_data, + proxy_object_attach_cb attach_cb) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, js_object); + CHECK_ARG(env, attach_data); + CHECK_ARG(env, attach_cb); + + auto nativeValue = LocalValueFromJsValue(js_object); + auto engine = reinterpret_cast(env); + auto vm = engine->GetEcmaVm(); + panda::JsiFastNativeScope fastNativeScope(vm); + CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); + size_t nativeBindingSize = 0; + Local key = panda::StringRef::GetProxyNapiWrapperString(vm); + Local object = panda::ObjectRef::NewJSXRefObject(vm); + // Create strong reference now, will update to weak reference after interop support + panda::JSNApi::XRefBindingInfo* data = panda::JSNApi::XRefBindingInfo::CreateNewInstance(); + if (data == nullptr) { + HILOG_ERROR("data is nullptr"); + return napi_set_last_error(env, napi_invalid_arg); + } + data->attachXRefFunc = reinterpret_cast(attach_cb); + data->attachXRefData = attach_data; + object->SetNativePointerFieldCount(vm, 1); + object->SetNativePointerField( + vm, 0, nullptr, + [](void* env, void* data, void* info) { + panda::JSNApi::XRefBindingInfo* externalInfo = reinterpret_cast(info); + delete externalInfo; + }, + reinterpret_cast(data), nativeBindingSize); + PropertyAttribute attr(object, true, false, true); + nativeObject->DefineProperty(vm, key, attr); + return GET_RETURN_STATUS(env); +} + +NAPI_EXTERN napi_status +napi_serialize_hybrid(napi_env env, napi_value object, napi_value transfer_list, napi_value clone_list, void** result) +{ + CHECK_ENV(env); + CHECK_ARG(env, object); + CHECK_ARG(env, transfer_list); + CHECK_ARG(env, clone_list); + CHECK_ARG(env, result); + + auto vm = reinterpret_cast(env)->GetEcmaVm(); + auto nativeValue = LocalValueFromJsValue(object); + auto transferList = LocalValueFromJsValue(transfer_list); + RETURN_STATUS_IF_FALSE(env, transferList->IsUndefined() || transferList->IsJSArray(vm), napi_invalid_arg); + auto cloneList = LocalValueFromJsValue(clone_list); + RETURN_STATUS_IF_FALSE(env, cloneList->IsUndefined() || cloneList->IsJSArray(vm), napi_invalid_arg); + *result = panda::JSNApi::InterOpSerializeValue(vm, nativeValue, transferList, cloneList, false, false); + + return napi_clear_last_error(env); +} + +NAPI_EXTERN napi_status napi_deserialize_hybrid(napi_env env, void* buffer, napi_value* object) +{ + CHECK_ENV(env); + CHECK_ARG(env, buffer); + CHECK_ARG(env, object); + + auto engine = reinterpret_cast(env); + auto vm = engine->GetEcmaVm(); + Local res = panda::JSNApi::InterOpDeserializeValue(vm, buffer, reinterpret_cast(engine)); + *object = JsValueFromLocalValue(res); + + return napi_clear_last_error(env); +} + +NAPI_EXTERN napi_status napi_wrap_with_xref(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + proxy_object_attach_cb proxy_cb, + napi_ref* result) +{ + NAPI_PREAMBLE(env); + CHECK_ARG(env, js_object); + CHECK_ARG(env, native_object); + CHECK_ARG(env, finalize_cb); + CHECK_ARG(env, result); + + auto nativeValue = LocalValueFromJsValue(js_object); + auto callback = reinterpret_cast(finalize_cb); + auto engine = reinterpret_cast(env); + auto vm = engine->GetEcmaVm(); + panda::JsiFastNativeScope fastNativeScope(vm); + CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject); + size_t nativeBindingSize = 0; + auto reference = reinterpret_cast(result); + Local key = panda::StringRef::GetProxyNapiWrapperString(vm); + NativeReference* ref = nullptr; + Local object = panda::ObjectRef::NewJSXRefObject(vm); + // Create strong reference now, will update to weak reference after interop support + ref = engine->CreateXRefReference(js_object, 1, false, callback, native_object); + *reference = ref; + panda::JSNApi::XRefBindingInfo* data = panda::JSNApi::XRefBindingInfo::CreateNewInstance(); + if (data == nullptr) { + HILOG_ERROR("data is nullptr"); + return napi_set_last_error(env, napi_invalid_arg); + } + data->attachXRefFunc = reinterpret_cast(proxy_cb); + data->attachXRefData = native_object; + object->SetNativePointerFieldCount(vm, 1); + object->SetNativePointerField( + vm, 0, ref, + [](void* env, void* data, void* info) { + panda::JSNApi::XRefBindingInfo* externalInfo = reinterpret_cast(info); + delete externalInfo; + }, + reinterpret_cast(data), nativeBindingSize); + PropertyAttribute attr(object, true, false, true); + nativeObject->DefineProperty(vm, key, attr); + return GET_RETURN_STATUS(env); +} +#endif // PANDA_JS_ETS_HYBRID_MODE diff --git a/native_engine/native_value.h b/native_engine/native_value.h index 4f26c83983424ca6acf9e4bb1aab7f0c6babca40..750152cd9273392a83233a61a258da28f61ff0be 100644 --- a/native_engine/native_value.h +++ b/native_engine/native_value.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Huawei Device Co., Ltd. + * Copyright (c) 2021-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at diff --git a/test/unittest/test_napi.cpp b/test/unittest/test_napi.cpp index 142a6439a6b8b030d1144190f7d9fcf3bb9c7fac..7597ca2de30b7647e9944116066f35b3382ab064 100644 --- a/test/unittest/test_napi.cpp +++ b/test/unittest/test_napi.cpp @@ -28,6 +28,7 @@ #include "ecmascript/napi/include/jsnapi_expo.h" #include "napi/native_common.h" #include "napi/native_node_api.h" +#include "napi/native_node_hybrid_api.h" #include "native_create_env.h" #include "native_utils.h" #include "reference_manager/native_reference_manager.h"