* 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
*
* 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.
*/
#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"
using panda::Local;
using panda::StringRef;
using panda::ObjectRef;
static constexpr int32_t MAX_THREAD_SAFE_COUNT = 128;
static constexpr size_t SMALL_STRING_SIZE = 16;
static constexpr size_t INT_ARG_2 = 2;
struct WrapperData {
void* data;
napi_type_tag type_tag;
napi_finalize finalize_cb;
};
NAPI_EXTERN void napi_module_register(napi_module* mod)
{
if (mod == nullptr) {
HILOG_ERROR("mod is nullptr");
return;
}
NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
NativeModule module;
module.version = mod->nm_version;
module.fileName = mod->nm_filename;
module.name = mod->nm_modname;
module.flags = mod->nm_flags;
module.registerCallback = (RegisterCallback)mod->nm_register_func;
moduleManager->Register(&module);
}
NAPI_EXTERN void napi_module_with_js_register(napi_module_with_js* mod)
{
if (mod == nullptr) {
HILOG_ERROR("mod is nullptr");
return;
}
NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
NativeModule module;
module.version = mod->nm_version;
module.fileName = mod->nm_filename;
module.name = mod->nm_modname;
module.registerCallback = (RegisterCallback)mod->nm_register_func;
module.getJSCode = (GetJSCodeCallback)mod->nm_get_js_code;
module.getABCCode = (GetJSCodeCallback)mod->nm_get_abc_code;
moduleManager->Register(&module);
}
NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location,
size_t location_len,
const char* message,
size_t message_len)
{
(void)location_len;
(void)message_len;
HILOG_FATAL("FATAL ERROR: %{public}s %{public}s\n", location, message);
}
NAPI_EXTERN napi_status napi_create_limit_runtime(napi_env env, napi_env* result_env)
{
CHECK_ENV(env);
CHECK_ARG(env, result_env);
auto engine = reinterpret_cast<NativeEngine*>(env);
if (!engine->IsMainEnvContext()) {
HILOG_FATAL("multi-context does not support this interface");
}
auto result = engine->CreateRuntime(true);
RETURN_STATUS_IF_FALSE(env, result != nullptr, napi_generic_failure);
*result_env = reinterpret_cast<napi_env>(result);
auto hostVM = engine->GetEcmaVmCritical();
auto workerVM = reinterpret_cast<NativeEngine*>(result)->GetEcmaVm();
JSNApi::RegisterUncatchableErrorHandler(const_cast<EcmaVM*>(workerVM),
JSNApi::GetUncatchableErrorHandler(hostVM));
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, err);
auto exceptionValue = LocalValueFromJsValue(err);
auto ecmaVm = reinterpret_cast<NativeEngine*>(env)->GetEcmaVm();
RETURN_STATUS_IF_FALSE(env, exceptionValue->IsError(ecmaVm), napi_invalid_arg);
auto engine = reinterpret_cast<NativeEngine*>(env);
engine->TriggerFatalException(exceptionValue);
return napi_ok;
}
NAPI_EXTERN napi_status napi_create_async_work(napi_env env,
napi_value async_resource,
napi_value async_resource_name,
napi_async_execute_callback execute,
napi_async_complete_callback complete,
void* data,
napi_async_work* result)
{
CHECK_ENV(env);
CHECK_ARG(env, async_resource_name);
CHECK_ARG(env, execute);
CHECK_ARG(env, complete);
CHECK_ARG(env, result);
SWITCH_CONTEXT(env);
auto ecmaVm = engine->GetEcmaVm();
auto asyncResource = LocalValueFromJsValue(async_resource);
auto asyncResourceName = LocalValueFromJsValue(async_resource_name);
auto asyncExecute = reinterpret_cast<NativeAsyncExecuteCallback>(execute);
auto asyncComplete = reinterpret_cast<NativeAsyncCompleteCallback>(complete);
(void)asyncResource;
(void)asyncResourceName;
char name[64] = {0};
if (!(asyncResourceName->IsNull() || asyncResourceName->IsUndefined())) {
panda::Local<panda::StringRef> nativeString(asyncResourceName);
int copied = nativeString->WriteUtf8(ecmaVm, name, 63, true) - 1;
name[copied] = '\0';
}
auto asyncWork = new NativeAsyncWork(engine, asyncExecute, asyncComplete, name, data);
*result = reinterpret_cast<napi_async_work>(asyncWork);
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_delete_async_work(napi_env env, napi_async_work work)
{
CHECK_ENV(env);
CHECK_ARG(env, work);
auto asyncWork = reinterpret_cast<NativeAsyncWork*>(work);
delete asyncWork;
asyncWork = nullptr;
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_queue_async_work(napi_env env, napi_async_work work)
{
CHECK_ENV(env);
CHECK_ARG(env, work);
auto asyncWork = reinterpret_cast<NativeAsyncWork*>(work);
asyncWork->Queue(reinterpret_cast<NativeEngine*>(env));
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env, napi_async_work work)
{
CHECK_ENV(env);
CHECK_ARG(env, work);
auto asyncWork = reinterpret_cast<NativeAsyncWork*>(work);
asyncWork->Cancel(reinterpret_cast<NativeEngine*>(env));
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_get_node_version(napi_env env, const napi_node_version** version)
{
(void)version;
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, struct uv_loop_s** loop)
{
CHECK_ENV(env);
CHECK_ARG(env, loop);
auto engine = reinterpret_cast<NativeEngine*>(env);
if (!NativeEngine::IsAlive(engine)) {
HILOG_ERROR("napi_env has been destroyed!");
return napi_status::napi_generic_failure;
}
if (!engine->IsMainEnvContext()) {
HILOG_ERROR("multi-context does not support obtain uv looper");
return napi_set_last_error(env, napi_invalid_arg);
}
*loop = engine->GetUVLoop();
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env, void (*fun)(void* arg), void* arg)
{
CHECK_ENV(env);
CHECK_ARG(env, fun);
WEAK_CROSS_THREAD_CHECK(env);
auto engine = reinterpret_cast<NativeEngine*>(env);
engine->AddCleanupHook(fun, arg);
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env, void (*fun)(void* arg), void* arg)
{
CHECK_ENV(env);
CHECK_ARG(env, fun);
WEAK_CROSS_THREAD_CHECK(env);
auto engine = reinterpret_cast<NativeEngine*>(env);
engine->RemoveCleanupHook(fun, arg);
return napi_clear_last_error(env);
}
using CleanupHook = void (*)(void* arg);
using AsyncCleanupHook = void (*)(void* arg, void (*)(void*), void*);
struct AsyncCleanupHookInfo final {
napi_env env_;
AsyncCleanupHook fun_;
void* arg_;
bool started_ = false;
std::shared_ptr<AsyncCleanupHookInfo> self_;
};
struct ACHHandle final {
std::shared_ptr<AsyncCleanupHookInfo> info_;
};
struct DeleteACHHandle {
void operator()(ACHHandle* handle) const
{
delete handle;
};
};
using AsyncCleanupHookHandle = std::unique_ptr<ACHHandle, DeleteACHHandle>;
static void FinishAsyncCleanupHook(void* arg)
{
HILOG_INFO("%{public}s, start.", __func__);
AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
std::shared_ptr<AsyncCleanupHookInfo> keep_alive = info->self_;
auto engine = reinterpret_cast<NativeEngine*>(info->env_);
engine->DecreaseWaitingRequestCounter();
info->self_.reset();
HILOG_INFO("%{public}s, end.", __func__);
}
static void RunAsyncCleanupHook(void* arg)
{
HILOG_INFO("%{public}s, start.", __func__);
AsyncCleanupHookInfo* info = static_cast<AsyncCleanupHookInfo*>(arg);
auto engine = reinterpret_cast<NativeEngine*>(info->env_);
engine->IncreaseWaitingRequestCounter();
info->started_ = true;
info->fun_(info->arg_, FinishAsyncCleanupHook, info);
HILOG_INFO("%{public}s, end.", __func__);
}
static AsyncCleanupHookHandle AddEnvironmentCleanupHook(napi_env env, AsyncCleanupHook fun, void* arg)
{
HILOG_INFO("%{public}s, start.", __func__);
auto info = std::make_shared<AsyncCleanupHookInfo>();
info->env_ = env;
info->fun_ = fun;
info->arg_ = arg;
info->self_ = info;
auto engine = reinterpret_cast<NativeEngine*>(env);
engine->AddCleanupHook(RunAsyncCleanupHook, info.get());
HILOG_INFO("%{public}s, end.", __func__);
return AsyncCleanupHookHandle(new ACHHandle { info });
}
static void RemoveEnvironmentCleanupHook(AsyncCleanupHookHandle handle)
{
if (handle->info_->started_) {
return;
}
handle->info_->self_.reset();
auto engine = reinterpret_cast<NativeEngine*>(handle->info_->env_);
engine->RemoveCleanupHook(RunAsyncCleanupHook, handle->info_.get());
}
struct napi_async_cleanup_hook_handle__ {
napi_async_cleanup_hook_handle__(napi_env env, napi_async_cleanup_hook user_hook, void* user_data)
: env_(env), user_hook_(user_hook), user_data_(user_data)
{
handle_ = AddEnvironmentCleanupHook(env, Hook, this);
}
~napi_async_cleanup_hook_handle__()
{
RemoveEnvironmentCleanupHook(std::move(handle_));
if (done_cb_ != nullptr) {
done_cb_(done_data_);
}
}
static void Hook(void* data, void (*done_cb)(void*), void* done_data)
{
auto handle = static_cast<napi_async_cleanup_hook_handle__*>(data);
handle->done_cb_ = done_cb;
handle->done_data_ = done_data;
handle->user_hook_(handle, handle->user_data_);
}
AsyncCleanupHookHandle handle_;
napi_env env_ = nullptr;
napi_async_cleanup_hook user_hook_ = nullptr;
void* user_data_ = nullptr;
void (*done_cb_)(void*) = nullptr;
void* done_data_ = nullptr;
};
NAPI_EXTERN napi_status napi_add_async_cleanup_hook(
napi_env env, napi_async_cleanup_hook hook, void* arg, napi_async_cleanup_hook_handle* remove_handle)
{
CHECK_ENV(env);
CHECK_ARG(env, hook);
CROSS_THREAD_CHECK(env);
napi_async_cleanup_hook_handle__* handle = new napi_async_cleanup_hook_handle__(env, hook, arg);
if (remove_handle != nullptr)
*remove_handle = handle;
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status napi_remove_async_cleanup_hook(napi_async_cleanup_hook_handle remove_handle)
{
if (remove_handle == nullptr) {
return napi_invalid_arg;
}
delete remove_handle;
return napi_ok;
}
NAPI_EXTERN napi_status napi_create_threadsafe_function(napi_env env, napi_value func, napi_value async_resource,
napi_value async_resource_name, size_t max_queue_size, size_t initial_thread_count, void* thread_finalize_data,
napi_finalize thread_finalize_cb, void* context, napi_threadsafe_function_call_js call_js_cb,
napi_threadsafe_function* result)
{
CHECK_ENV(env);
CHECK_ARG(env, async_resource_name);
RETURN_STATUS_IF_FALSE(
env, initial_thread_count > 0 && initial_thread_count <= MAX_THREAD_SAFE_COUNT, napi_invalid_arg);
CHECK_ARG(env, result);
if (func == nullptr) {
CHECK_ARG(env, call_js_cb);
}
SWITCH_CONTEXT(env);
auto finalizeCallback = reinterpret_cast<NativeFinalize>(thread_finalize_cb);
auto callJsCallback = reinterpret_cast<NativeThreadSafeFunctionCallJs>(call_js_cb);
auto safeAsyncWork = engine->CreateSafeAsyncWork(func, async_resource, async_resource_name, max_queue_size,
initial_thread_count, thread_finalize_data, finalizeCallback, context, callJsCallback);
CHECK_ENV(safeAsyncWork);
auto ret = safeAsyncWork->Init();
if (ret) {
*result = reinterpret_cast<napi_threadsafe_function>(safeAsyncWork);
} else {
return napi_status::napi_generic_failure;
}
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_call_threadsafe_function(
napi_threadsafe_function func, void* data, napi_threadsafe_function_call_mode is_blocking)
{
CHECK_ENV(func);
auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
auto callMode = static_cast<NativeThreadSafeFunctionCallMode>(is_blocking);
napi_status status = napi_status::napi_ok;
auto code = safeAsyncWork->Send(data, callMode);
switch (code) {
case SafeAsyncCode::SAFE_ASYNC_OK:
status = napi_status::napi_ok;
break;
case SafeAsyncCode::SAFE_ASYNC_QUEUE_FULL:
status = napi_status::napi_queue_full;
break;
case SafeAsyncCode::SAFE_ASYNC_INVALID_ARGS:
status = napi_status::napi_invalid_arg;
break;
case SafeAsyncCode::SAFE_ASYNC_CLOSED:
status = napi_status::napi_closing;
break;
case SafeAsyncCode::SAFE_ASYNC_FAILED:
status = napi_status::napi_generic_failure;
break;
default:
HILOG_FATAL("this branch is unreachable, code is %{public}d", code);
break;
}
return status;
}
NAPI_EXTERN napi_status napi_acquire_threadsafe_function(napi_threadsafe_function func)
{
CHECK_ENV(func);
auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
auto ret = safeAsyncWork->Acquire();
if (ret != SafeAsyncCode::SAFE_ASYNC_OK) {
return napi_status::napi_generic_failure;
}
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_release_threadsafe_function(
napi_threadsafe_function func, napi_threadsafe_function_release_mode mode)
{
CHECK_ENV(func);
auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
auto releaseMode = static_cast<NativeThreadSafeFunctionReleaseMode>(mode);
auto ret = safeAsyncWork->Release(releaseMode);
if (ret != SafeAsyncCode::SAFE_ASYNC_OK) {
return napi_status::napi_generic_failure;
}
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_get_threadsafe_function_context(napi_threadsafe_function func, void** result)
{
CHECK_ENV(func);
CHECK_ENV(result);
auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
*result = safeAsyncWork->GetContext();
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func)
{
CHECK_ENV(env);
CHECK_ARG(env, func);
auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
auto ret = safeAsyncWork->Ref();
if (!ret) {
return napi_status::napi_generic_failure;
}
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_unref_threadsafe_function(napi_env env, napi_threadsafe_function func)
{
CHECK_ENV(env);
CHECK_ARG(env, func);
auto safeAsyncWork = reinterpret_cast<NativeSafeAsyncWork*>(func);
auto ret = safeAsyncWork->Unref();
if (!ret) {
return napi_status::napi_generic_failure;
}
return napi_status::napi_ok;
}
NAPI_EXTERN napi_status napi_async_init(
napi_env env, napi_value async_resource, napi_value async_resource_name, napi_async_context* result)
{
CHECK_ENV(env);
CHECK_ARG(env, async_resource_name);
CHECK_ARG(env, result);
SWITCH_CONTEXT(env);
auto ecmaVm = reinterpret_cast<NativeEngine*>(env)->GetEcmaVm();
panda::Local<panda::ObjectRef> resource;
bool isExternalResource;
if (async_resource != nullptr) {
auto nativeValue = LocalValueFromJsValue(async_resource);
resource = nativeValue->ToObject(ecmaVm);
isExternalResource = true;
} else {
resource = panda::ObjectRef::New(ecmaVm);
isExternalResource = false;
}
auto nativeValue = LocalValueFromJsValue(async_resource_name);
auto resourceName = nativeValue->ToString(ecmaVm);
auto asyncContext = new NativeAsyncHookContext(reinterpret_cast<NativeEngine*>(env),
resource,
resourceName,
isExternalResource);
*result = reinterpret_cast<napi_async_context>(asyncContext);
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status napi_async_destroy(napi_env env, napi_async_context async_context)
{
CHECK_ENV(env);
CHECK_ARG(env, async_context);
NativeAsyncHookContext* nativeAsyncContext = reinterpret_cast<NativeAsyncHookContext*>(async_context);
delete nativeAsyncContext;
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status napi_open_callback_scope(
napi_env env, napi_value, napi_async_context async_context_handle, napi_callback_scope* result)
{
CHECK_ENV(env);
CHECK_ARG(env, result);
NativeAsyncHookContext* nodeAsyncContext = reinterpret_cast<NativeAsyncHookContext*>(async_context_handle);
*result = reinterpret_cast<napi_callback_scope>(nodeAsyncContext->OpenCallbackScope());
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, napi_callback_scope scope)
{
CHECK_ENV(env);
CHECK_ARG(env, scope);
auto ret = NativeAsyncHookContext::CloseCallbackScope(reinterpret_cast<NativeEngine*>(env),
reinterpret_cast<NativeCallbackScope*>(scope));
if (ret == CALLBACK_SCOPE_MISMATCH) {
return napi_callback_scope_mismatch;
} else if (ret != CALLBACK_SCOPE_OK) {
return napi_invalid_arg;
}
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status napi_set_instance_data(
napi_env env, void* data, napi_finalize finalize_cb, void* finalize_hint)
{
CHECK_ENV(env);
CROSS_THREAD_CHECK(env);
auto engine = reinterpret_cast<NativeEngine*>(env);
auto callback = reinterpret_cast<NativeFinalize>(finalize_cb);
engine->SetInstanceData(data, callback, finalize_hint);
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status napi_get_instance_data(napi_env env, void** data)
{
CHECK_ENV(env);
CHECK_ARG(env, data);
CROSS_THREAD_CHECK(env);
auto engine = reinterpret_cast<NativeEngine*>(env);
engine->GetInstanceData(data);
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status node_api_get_module_file_name(napi_env env, const char** result)
{
CHECK_ENV(env);
CHECK_ARG(env, result);
auto engine = reinterpret_cast<NativeEngine*>(env);
*result = engine->GetModuleFileName();
HILOG_INFO("%{public}s, napi called fileName : %{public}s", __func__, *result);
return napi_clear_last_error(env);
}
NAPI_EXTERN napi_status napi_make_callback(napi_env env,
napi_async_context async_context,
napi_value recv,
napi_value func,
size_t argc,
const napi_value* argv,
napi_value* result)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, func);
CHECK_ARG(env, recv);
if (argc > 0) {
CHECK_ARG(env, argv);
}
SWITCH_CONTEXT(env);
auto vm = engine->GetEcmaVm();
RETURN_STATUS_IF_FALSE(env, reinterpret_cast<panda::JSValueRef *>(recv)->IsObject(vm), napi_object_expected);
RETURN_STATUS_IF_FALSE(env, reinterpret_cast<panda::JSValueRef *>(func)->IsFunction(vm), napi_function_expected);
panda::JSValueRef* Obj = reinterpret_cast<panda::JSValueRef *>(recv);
panda::FunctionRef* funRef = reinterpret_cast<panda::FunctionRef *>(func);
panda::JSValueRef* callBackRst;
if (async_context == nullptr) {
callBackRst = MakeCallback(engine, funRef, Obj,
argc, reinterpret_cast<panda::JSValueRef* const*>(argv), nullptr);
} else {
NativeAsyncHookContext* nativeAsyncContext = reinterpret_cast<NativeAsyncHookContext*>(async_context);
callBackRst = nativeAsyncContext->MakeCallback(funRef, Obj,
reinterpret_cast<panda::JSValueRef* const*>(argv), argc);
}
if (tryCatch.HasCaught()) {
HILOG_ERROR("print exception info: ");
panda::JSNApi::PrintExceptionInfo(vm);
return napi_set_last_error(env, napi_pending_exception);
}
if (result) {
*result = reinterpret_cast<napi_value>(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_create_string_utf8_with_replacement(napi_env env,
const char* str,
size_t length,
napi_value* result)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, str);
CHECK_ARG(env, result);
auto vm = reinterpret_cast<NativeEngine*>(env)->GetEcmaVm();
if (length < SMALL_STRING_SIZE) {
Local<panda::StringRef> object = panda::StringRef::NewFromUtf8WithoutStringTableReplacement(vm, str, length);
*result = JsValueFromLocalValue(object);
} else {
Local<panda::StringRef> object = panda::StringRef::NewFromUtf8Replacement(
vm, str, (length == NAPI_AUTO_LENGTH) ? strlen(str) : length);
*result = JsValueFromLocalValue(object);
}
return GET_RETURN_STATUS(env);
}
NAPI_EXTERN napi_status napi_wrap_s(napi_env env,
napi_value js_object,
void* native_object,
napi_finalize finalize_cb,
void* finalize_hint,
const napi_type_tag* type_tag,
napi_ref* result)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, js_object);
CHECK_ARG(env, native_object);
CHECK_ARG(env, finalize_cb);
CHECK_ARG(env, type_tag);
auto nativeValue = LocalValueFromJsValue(js_object);
auto callback = reinterpret_cast<NapiNativeFinalize>(finalize_cb);
SWITCH_CONTEXT(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<NativeReference**>(result);
Local<panda::StringRef> key = panda::StringRef::GetNapiWrapperString(vm);
if (nativeObject->Has(vm, key)) {
HILOG_ERROR("napi_wrap_s: current js_object has been wrapped.");
return napi_set_last_error(env, napi_invalid_arg);
}
Local<panda::ObjectRef> object = panda::ObjectRef::New(vm);
NativeReference* ref = nullptr;
if (reference != nullptr) {
ref = engine->CreateReference(js_object, 1, false, callback, native_object, finalize_hint);
*reference = ref;
} else {
ref = engine->CreateReference(js_object, 0, true, callback, native_object, finalize_hint);
}
object->SetNativePointerFieldCount(vm, INT_ARG_2);
object->SetNativePointerField(vm, 0, ref, nullptr, nullptr, nativeBindingSize);
object->SetNativePointerField(vm, 1, const_cast<napi_type_tag*>(type_tag), nullptr, nullptr, nativeBindingSize);
panda::PropertyAttribute attr(object, true, false, true);
nativeObject->DefineProperty(vm, key, attr);
return GET_RETURN_STATUS(env);
}
NAPI_EXTERN napi_status napi_unwrap_s(napi_env env, napi_value js_object, const napi_type_tag* type_tag, void** result)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, js_object);
CHECK_ARG(env, type_tag);
CHECK_ARG(env, result);
auto nativeValue = LocalValueFromJsValue(js_object);
SWITCH_CONTEXT(env);
auto vm = reinterpret_cast<NativeEngine*>(env)->GetEcmaVm();
panda::JsiFastNativeScope fastNativeScope(vm);
CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject);
Local<panda::StringRef> key = panda::StringRef::GetNapiWrapperString(vm);
Local<panda::JSValueRef> val = nativeObject->Get(vm, key);
*result = nullptr;
if (val->IsObjectWithoutSwitchState(vm)) {
Local<panda::ObjectRef> ext(val);
size_t fieldCount = ext->GetNativePointerFieldCount(vm);
if (fieldCount != INT_ARG_2) {
HILOG_ERROR("napi_unwrap_s: expected 2 native fields");
return napi_set_last_error(env, napi_invalid_arg);
}
napi_type_tag* tag = static_cast<napi_type_tag*>(ext->GetNativePointerField(vm, 1));
if (tag == nullptr) {
HILOG_ERROR("napi_unwrap_s: tag is nullptr");
return napi_set_last_error(env, napi_generic_failure);
} else {
if (tag->lower != type_tag->lower ||tag->upper != type_tag->upper) {
HILOG_ERROR("napi_unwrap_s: type tag mismatch");
return napi_set_last_error(env, napi_invalid_arg);
}
auto ref = reinterpret_cast<NativeReference*>(ext->GetNativePointerField(vm, 0));
*result = ref != nullptr ? ref->GetData() : nullptr;
}
}
return GET_RETURN_STATUS(env);
}
NAPI_EXTERN napi_status napi_wrap_enhance_s(napi_env env,
napi_value js_object,
void* native_object,
napi_finalize finalize_cb,
bool async_finalizer,
void* finalize_hint,
size_t native_binding_size,
const napi_type_tag* type_tag,
napi_ref* result)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, js_object);
CHECK_ARG(env, native_object);
CHECK_ARG(env, type_tag);
auto nativeValue = LocalValueFromJsValue(js_object);
auto callback = reinterpret_cast<NapiNativeFinalize>(finalize_cb);
SWITCH_CONTEXT(env);
auto vm = engine->GetEcmaVm();
panda::JsiFastNativeScope fastNativeScope(vm);
CHECK_AND_CONVERT_TO_OBJECT(env, vm, nativeValue, nativeObject);
auto reference = reinterpret_cast<NativeReference**>(result);
Local<panda::StringRef> key = panda::StringRef::GetNapiWrapperString(vm);
if (nativeObject->Has(vm, key)) {
HILOG_ERROR("napi_wrap_enhance_s: current js_object has been wrapped.");
return napi_set_last_error(env, napi_invalid_arg);
}
Local<panda::ObjectRef> object = panda::ObjectRef::New(vm);
NativeReference* ref = nullptr;
if (!async_finalizer) {
if (reference != nullptr) {
ref = engine->CreateReference(js_object, 1, false, callback, native_object, finalize_hint,
native_binding_size);
*reference = ref;
} else {
ref = engine->CreateReference(js_object, 0, true, callback, native_object, finalize_hint,
native_binding_size);
}
} else {
if (reference != nullptr) {
ref = engine->CreateAsyncReference(js_object, 1, false, callback, native_object, finalize_hint);
*reference = ref;
} else {
ref = engine->CreateAsyncReference(js_object, 0, true, callback, native_object, finalize_hint);
}
}
object->SetNativePointerFieldCount(vm, INT_ARG_2);
object->SetNativePointerField(vm, 0, ref, nullptr, nullptr, native_binding_size);
object->SetNativePointerField(vm, 1, const_cast<napi_type_tag*>(type_tag), nullptr, nullptr, native_binding_size);
panda::PropertyAttribute attr(object, true, false, true);
nativeObject->DefineProperty(vm, key, attr);
return GET_RETURN_STATUS(env);
}
NAPI_EXTERN napi_status napi_wrap_sendable_s(napi_env env,
napi_value js_object,
void* native_object,
napi_finalize finalize_cb,
void* finalize_hint,
const napi_type_tag* type_tag)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, js_object);
CHECK_ARG(env, native_object);
CHECK_ARG(env, type_tag);
auto engine = reinterpret_cast<NativeEngine*>(env);
if (!engine->IsMainEnvContext()) {
HILOG_ERROR("multi-context does not support sendable feature");
return napi_set_last_error(env, napi_invalid_arg);
}
auto nativeValue = LocalValueFromJsValue(js_object);
auto callback = reinterpret_cast<panda::NativePointerCallback>(finalize_cb);
auto vm = engine->GetEcmaVm();
panda::JsiFastNativeScope fastNativeScope(vm);
RETURN_STATUS_IF_FALSE(env, nativeValue->IsSendableObject(vm), napi_object_expected);
Local<ObjectRef> nativeObject(nativeValue);
nativeObject->SetNativePointerFieldCount(vm, INT_ARG_2);
nativeObject->SetNativePointerField(vm, 0, native_object, callback, finalize_hint);
nativeObject->SetNativePointerField(vm, 1, const_cast<napi_type_tag*>(type_tag), nullptr, nullptr);
return GET_RETURN_STATUS(env);
}
NAPI_EXTERN napi_status napi_unwrap_sendable_s(napi_env env,
napi_value js_object,
const napi_type_tag* type_tag,
void** result)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, js_object);
CHECK_ARG(env, result);
CHECK_ARG(env, type_tag);
auto engine = reinterpret_cast<NativeEngine*>(env);
if (!engine->IsMainEnvContext()) {
HILOG_ERROR("multi-context does not support sendable feature");
return napi_set_last_error(env, napi_invalid_arg);
}
auto nativeValue = LocalValueFromJsValue(js_object);
auto vm = engine->GetEcmaVm();
panda::JsiFastNativeScope fastNativeScope(vm);
RETURN_STATUS_IF_FALSE(env, nativeValue->IsSendableObject(vm), napi_object_expected);
Local<ObjectRef> nativeObject(nativeValue);
size_t fieldCount = nativeObject->GetNativePointerFieldCount(vm);
if (fieldCount != INT_ARG_2) {
HILOG_ERROR("napi_unwrap_sendable_s: expected 2 native fields");
return napi_set_last_error(env, napi_invalid_arg);
}
napi_type_tag* tag = reinterpret_cast<napi_type_tag*>(nativeObject->GetNativePointerField(vm, 1));
if (tag == nullptr) {
HILOG_ERROR("napi_unwrap_sendable_s: tag is nullptr");
return napi_set_last_error(env, napi_generic_failure);
} else {
if (tag->lower != type_tag->lower || tag->upper != type_tag->upper) {
HILOG_ERROR("napi_unwrap_sendable_s: type tag mismatch");
return napi_set_last_error(env, napi_invalid_arg);
}
void* val = nativeObject->GetNativePointerField(vm, 0);
*result = val;
}
return GET_RETURN_STATUS(env);
}
NAPI_EXTERN napi_status napi_create_external_s(napi_env env,
void* data,
napi_finalize finalize_cb,
void* finalize_hint,
const napi_type_tag* type_tag,
napi_value* result)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, type_tag);
CHECK_ARG(env, result);
auto engine = reinterpret_cast<NativeEngine*>(env);
auto vm = engine->GetEcmaVm();
WrapperData *wrapperData = new WrapperData{ data, *type_tag, finalize_cb };
auto wrapperCallback = [](napi_env env, void* data, void* hint) {
WrapperData *wrapperData = reinterpret_cast<WrapperData*>(data);
if (wrapperData) {
if (wrapperData->finalize_cb) {
wrapperData->finalize_cb(static_cast<napi_env>(env), wrapperData->data, hint);
}
HILOG_DEBUG("Delete wrapperData.");
delete wrapperData;
}
};
Local<panda::NativePointerRef> object = panda::NativePointerRef::NewWrapperData(vm,
wrapperData, nullptr, nullptr, 0);
*result = JsValueFromLocalValue(object);
engine->CreateReference(*result, 0, true, wrapperCallback, wrapperData, finalize_hint);
return GET_RETURN_STATUS(env);
}
NAPI_EXTERN napi_status napi_get_value_external_s(napi_env env,
napi_value value,
const napi_type_tag* type_tag,
void** result)
{
NAPI_PREAMBLE(env);
CHECK_ARG(env, value);
CHECK_ARG(env, type_tag);
CHECK_ARG(env, result);
auto nativeValue = LocalValueFromJsValue(value);
bool isNativePointer = false;
bool isWrapperData = false;
auto vm = reinterpret_cast<NativeEngine*>(env)->GetEcmaVm();
void* ret = nativeValue->GetNativePointerWrapperDataValue(vm, isNativePointer, isWrapperData);
RETURN_STATUS_IF_FALSE(env, isNativePointer&&isWrapperData, napi_invalid_arg);
WrapperData *wrapperData = reinterpret_cast<WrapperData*>(ret);
if (wrapperData->type_tag.lower != type_tag->lower ||
wrapperData->type_tag.upper != type_tag->upper) {
HILOG_ERROR("napi_get_value_external_s: type tag mismatch");
return napi_set_last_error(env, napi_invalid_arg);
}
*result = wrapperData->data;
return GET_RETURN_STATUS(env);
}