mirror of
https://github.com/openharmony/ability_ability_runtime.git
synced 2026-08-24 12:43:16 -04:00
4a0be31cb9
Signed-off-by: RuiChen_01 <chenrui193@huawei.com>
550 lines
18 KiB
C++
550 lines
18 KiB
C++
/*
|
|
* Copyright (c) 2021-2024 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 "js_runtime_utils.h"
|
|
|
|
#include "hilog_tag_wrapper.h"
|
|
#include "js_runtime.h"
|
|
#include "napi/native_api.h"
|
|
|
|
namespace OHOS {
|
|
namespace AbilityRuntime {
|
|
namespace {
|
|
std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
|
|
std::unique_ptr<NapiAsyncTask::ExecuteCallback>&& execute,
|
|
std::unique_ptr<NapiAsyncTask::CompleteCallback>&& complete,
|
|
napi_value* result)
|
|
{
|
|
napi_valuetype type = napi_undefined;
|
|
napi_typeof(env, lastParam, &type);
|
|
if (lastParam == nullptr || type != napi_function) {
|
|
napi_deferred nativeDeferred = nullptr;
|
|
napi_create_promise(env, &nativeDeferred, result);
|
|
return std::make_unique<NapiAsyncTask>(nativeDeferred, std::move(execute), std::move(complete));
|
|
} else {
|
|
napi_get_undefined(env, result);
|
|
napi_ref callbackRef = nullptr;
|
|
napi_create_reference(env, lastParam, 1, &callbackRef);
|
|
return std::make_unique<NapiAsyncTask>(callbackRef, std::move(execute), std::move(complete));
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
// Help Functions
|
|
napi_value CreateJsError(napi_env env, int32_t errCode, const std::string& message)
|
|
{
|
|
HandleEscape handleEscape(env);
|
|
napi_value result = nullptr;
|
|
napi_create_error(env, CreateJsValue(env, errCode), CreateJsValue(env, message), &result);
|
|
return handleEscape.Escape(result);
|
|
}
|
|
|
|
void BindNativeFunction(napi_env env, napi_value object, const char* name,
|
|
const char* moduleName, napi_callback func)
|
|
{
|
|
HandleScope handleScope(env);
|
|
std::string fullName(moduleName);
|
|
fullName += ".";
|
|
fullName += name;
|
|
napi_value result = nullptr;
|
|
napi_create_function(env, fullName.c_str(), fullName.length(), func, nullptr, &result);
|
|
napi_set_named_property(env, object, name, result);
|
|
}
|
|
|
|
void BindNativeProperty(napi_env env, napi_value object, const char* name, napi_callback getter)
|
|
{
|
|
napi_property_descriptor properties[1];
|
|
properties[0].utf8name = name;
|
|
properties[0].name = nullptr;
|
|
properties[0].method = nullptr;
|
|
properties[0].getter = getter;
|
|
properties[0].setter = nullptr;
|
|
properties[0].value = nullptr;
|
|
properties[0].attributes = napi_default;
|
|
properties[0].data = nullptr;
|
|
napi_define_properties(env, object, 1, properties);
|
|
}
|
|
|
|
void* GetNativePointerFromCallbackInfo(napi_env env, napi_callback_info info, const char* name)
|
|
{
|
|
size_t argcAsync = ARGC_MAX_COUNT;
|
|
napi_value args[ARGC_MAX_COUNT] = {nullptr};
|
|
napi_value thisVar = nullptr;
|
|
NAPI_CALL_NO_THROW(napi_get_cb_info(env, info, &argcAsync, args, &thisVar, nullptr), nullptr);
|
|
if (name != nullptr) {
|
|
napi_get_named_property(env, thisVar, name, &thisVar);
|
|
}
|
|
void* result = nullptr;
|
|
NAPI_CALL_NO_THROW(napi_unwrap(env, thisVar, &result), nullptr);
|
|
return result;
|
|
}
|
|
|
|
void* GetCbInfoFromCallbackInfo(napi_env env, napi_callback_info info, size_t* argc, napi_value* argv)
|
|
{
|
|
napi_value thisVar = nullptr;
|
|
NAPI_CALL_NO_THROW(napi_get_cb_info(env, info, argc, argv, &thisVar, nullptr), nullptr);
|
|
void* result = nullptr;
|
|
NAPI_CALL_NO_THROW(napi_unwrap(env, thisVar, &result), nullptr);
|
|
return result;
|
|
}
|
|
|
|
void* GetNapiCallbackInfoAndThis(napi_env env, napi_callback_info info, NapiCallbackInfo& napiInfo, const char* name)
|
|
{
|
|
NAPI_CALL_NO_THROW(napi_get_cb_info(
|
|
env, info, &napiInfo.argc, napiInfo.argv, &napiInfo.thisVar, nullptr), nullptr);
|
|
napi_value value = napiInfo.thisVar;
|
|
if (name != nullptr) {
|
|
napi_get_named_property(env, value, name, &value);
|
|
}
|
|
void* result = nullptr;
|
|
NAPI_CALL_NO_THROW(napi_unwrap(env, value, &result), nullptr);
|
|
return result;
|
|
}
|
|
|
|
void SetNamedNativePointer(napi_env env, napi_value object, const char* name, void* ptr, napi_finalize func)
|
|
{
|
|
HandleScope handleScope(env);
|
|
napi_value objValue = nullptr;
|
|
napi_create_object(env, &objValue);
|
|
napi_wrap(env, objValue, ptr, func, nullptr, nullptr);
|
|
napi_set_named_property(env, object, name, objValue);
|
|
}
|
|
|
|
void* GetNamedNativePointer(napi_env env, napi_value object, const char* name)
|
|
{
|
|
napi_value proValue = nullptr;
|
|
napi_get_named_property(env, object, name, &proValue);
|
|
void* result = nullptr;
|
|
napi_unwrap(env, proValue, &result);
|
|
return result;
|
|
}
|
|
|
|
bool CheckTypeForNapiValue(napi_env env, napi_value param, napi_valuetype expectType)
|
|
{
|
|
napi_valuetype valueType = napi_undefined;
|
|
if (napi_typeof(env, param, &valueType) != napi_ok) {
|
|
return false;
|
|
}
|
|
return valueType == expectType;
|
|
}
|
|
|
|
// Handle Scope
|
|
HandleScope::HandleScope(JsRuntime& jsRuntime)
|
|
{
|
|
env_ = (napi_env)jsRuntime.GetNativeEnginePointer();
|
|
napi_open_handle_scope(env_, &scope_);
|
|
}
|
|
|
|
HandleScope::HandleScope(napi_env env)
|
|
{
|
|
env_ = env;
|
|
napi_open_handle_scope(env_, &scope_);
|
|
}
|
|
|
|
HandleScope::~HandleScope()
|
|
{
|
|
napi_close_handle_scope(env_, scope_);
|
|
}
|
|
|
|
// Handle Escape
|
|
HandleEscape::HandleEscape(JsRuntime& jsRuntime)
|
|
{
|
|
env_ = (napi_env)jsRuntime.GetNativeEnginePointer();
|
|
napi_open_escapable_handle_scope(env_, &scope_);
|
|
}
|
|
|
|
HandleEscape::HandleEscape(napi_env env)
|
|
{
|
|
env_ = env;
|
|
napi_open_escapable_handle_scope(env_, &scope_);
|
|
}
|
|
|
|
HandleEscape::~HandleEscape()
|
|
{
|
|
napi_close_escapable_handle_scope(env_, scope_);
|
|
}
|
|
|
|
napi_value HandleEscape::Escape(napi_value value)
|
|
{
|
|
napi_value result = nullptr;
|
|
napi_escape_handle(env_, scope_, value, &result);
|
|
return result;
|
|
}
|
|
|
|
// Async Task
|
|
NapiAsyncTask::NapiAsyncTask(napi_deferred deferred, std::unique_ptr<NapiAsyncTask::ExecuteCallback>&& execute,
|
|
std::unique_ptr<NapiAsyncTask::CompleteCallback>&& complete)
|
|
: deferred_(deferred), execute_(std::move(execute)), complete_(std::move(complete))
|
|
{}
|
|
|
|
NapiAsyncTask::NapiAsyncTask(napi_ref callbackRef, std::unique_ptr<NapiAsyncTask::ExecuteCallback>&& execute,
|
|
std::unique_ptr<NapiAsyncTask::CompleteCallback>&& complete)
|
|
: callbackRef_(callbackRef), execute_(std::move(execute)), complete_(std::move(complete))
|
|
{}
|
|
|
|
NapiAsyncTask::~NapiAsyncTask()
|
|
{
|
|
if (work_ && env_) {
|
|
napi_delete_async_work(env_, work_);
|
|
work_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::Schedule(const std::string &name, napi_env env, std::unique_ptr<NapiAsyncTask>&& task)
|
|
{
|
|
if (task && task->Start(name, env)) {
|
|
task.release();
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::ScheduleHighQos(const std::string &name, napi_env env, std::unique_ptr<NapiAsyncTask>&& task)
|
|
{
|
|
if (task && task->StartHighQos(name, env)) {
|
|
task.release();
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::ScheduleLowQos(const std::string &name, napi_env env, std::unique_ptr<NapiAsyncTask>&& task)
|
|
{
|
|
if (task && task->StartLowQos(name, env)) {
|
|
task.release();
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::ScheduleWithDefaultQos(const std::string &name, napi_env env, std::unique_ptr<NapiAsyncTask>&& task)
|
|
{
|
|
if (task && task->StartWithDefaultQos(name, env)) {
|
|
task.release();
|
|
}
|
|
}
|
|
|
|
bool NapiAsyncTask::StartWithDefaultQos(const std::string &name, napi_env env)
|
|
{
|
|
if (work_) {
|
|
napi_delete_async_work(env, work_);
|
|
work_ = nullptr;
|
|
}
|
|
if (env == nullptr) {
|
|
return false;
|
|
}
|
|
if (execute_ == nullptr) {
|
|
return SendNapiEvent(env, napi_eprio_high);
|
|
}
|
|
NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
|
|
work_ = reinterpret_cast<napi_async_work>(engine->CreateAsyncWork(name,
|
|
reinterpret_cast<NativeAsyncExecuteCallback>(Execute),
|
|
reinterpret_cast<NativeAsyncCompleteCallback>(Complete), this));
|
|
napi_queue_async_work_with_qos(env, work_, napi_qos_default);
|
|
return true;
|
|
}
|
|
|
|
void NapiAsyncTask::Resolve(napi_env env, napi_value value)
|
|
{
|
|
TAG_LOGD(AAFwkTag::JSRUNTIME, "called");
|
|
if (deferred_) {
|
|
napi_resolve_deferred(env, deferred_, value);
|
|
deferred_ = nullptr;
|
|
}
|
|
if (callbackRef_) {
|
|
HandleScope handleScope(env);
|
|
napi_value argv[] = {
|
|
CreateJsError(env, 0),
|
|
value,
|
|
};
|
|
napi_value func = nullptr;
|
|
napi_get_reference_value(env, callbackRef_, &func);
|
|
napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
|
|
napi_delete_reference(env, callbackRef_);
|
|
callbackRef_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::ResolveWithNoError(napi_env env, napi_value value)
|
|
{
|
|
TAG_LOGD(AAFwkTag::JSRUNTIME, "called");
|
|
if (deferred_) {
|
|
napi_resolve_deferred(env, deferred_, value);
|
|
deferred_ = nullptr;
|
|
}
|
|
if (callbackRef_) {
|
|
HandleScope handleScope(env);
|
|
napi_value argv[] = {
|
|
CreateJsNull(env),
|
|
value,
|
|
};
|
|
napi_value func = nullptr;
|
|
napi_get_reference_value(env, callbackRef_, &func);
|
|
napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
|
|
napi_delete_reference(env, callbackRef_);
|
|
callbackRef_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::Reject(napi_env env, napi_value error)
|
|
{
|
|
HandleScope handleScope(env);
|
|
if (deferred_) {
|
|
napi_reject_deferred(env, deferred_, error);
|
|
deferred_ = nullptr;
|
|
}
|
|
if (callbackRef_) {
|
|
napi_value argv[] = {
|
|
error,
|
|
CreateJsUndefined(env),
|
|
};
|
|
napi_value func = nullptr;
|
|
napi_get_reference_value(env, callbackRef_, &func);
|
|
napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
|
|
napi_delete_reference(env, callbackRef_);
|
|
callbackRef_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::ResolveWithCustomize(napi_env env, napi_value error, napi_value value)
|
|
{
|
|
TAG_LOGD(AAFwkTag::JSRUNTIME, "called");
|
|
if (deferred_) {
|
|
napi_resolve_deferred(env, deferred_, value);
|
|
deferred_ = nullptr;
|
|
}
|
|
if (callbackRef_) {
|
|
napi_value argv[] = {
|
|
error,
|
|
value,
|
|
};
|
|
napi_value func = nullptr;
|
|
napi_get_reference_value(env, callbackRef_, &func);
|
|
napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
|
|
napi_delete_reference(env, callbackRef_);
|
|
callbackRef_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::RejectWithCustomize(napi_env env, napi_value error, napi_value value)
|
|
{
|
|
TAG_LOGD(AAFwkTag::JSRUNTIME, "called");
|
|
if (deferred_) {
|
|
napi_reject_deferred(env, deferred_, error);
|
|
deferred_ = nullptr;
|
|
}
|
|
if (callbackRef_) {
|
|
napi_value argv[] = {
|
|
error,
|
|
value,
|
|
};
|
|
napi_value func = nullptr;
|
|
napi_get_reference_value(env, callbackRef_, &func);
|
|
napi_call_function(env, CreateJsUndefined(env), func, ArraySize(argv), argv, nullptr);
|
|
napi_delete_reference(env, callbackRef_);
|
|
callbackRef_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::Execute(napi_env env, void* data)
|
|
{
|
|
if (data == nullptr) {
|
|
return;
|
|
}
|
|
auto me = static_cast<NapiAsyncTask*>(data);
|
|
if (me->execute_ && *(me->execute_)) {
|
|
(*me->execute_)();
|
|
}
|
|
}
|
|
|
|
void NapiAsyncTask::Complete(napi_env env, napi_status status, void* data)
|
|
{
|
|
TAG_LOGD(AAFwkTag::JSRUNTIME, "called");
|
|
if (data == nullptr) {
|
|
return;
|
|
}
|
|
std::unique_ptr<NapiAsyncTask> me(static_cast<NapiAsyncTask*>(data));
|
|
if (me->complete_ && *(me->complete_)) {
|
|
HandleScope handleScope(env);
|
|
(*me->complete_)(env, *me, static_cast<int32_t>(status));
|
|
}
|
|
}
|
|
|
|
bool NapiAsyncTask::Start(const std::string &name, napi_env env)
|
|
{
|
|
if (work_) {
|
|
napi_delete_async_work(env, work_);
|
|
work_ = nullptr;
|
|
}
|
|
if (env == nullptr) {
|
|
return false;
|
|
}
|
|
if (execute_ == nullptr) {
|
|
return SendNapiEvent(env, napi_eprio_high);
|
|
}
|
|
env_ = env;
|
|
NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
|
|
work_ = reinterpret_cast<napi_async_work>(engine->CreateAsyncWork(name,
|
|
reinterpret_cast<NativeAsyncExecuteCallback>(Execute),
|
|
reinterpret_cast<NativeAsyncCompleteCallback>(Complete), this));
|
|
napi_queue_async_work(env, work_);
|
|
return true;
|
|
}
|
|
|
|
bool NapiAsyncTask::StartHighQos(const std::string &name, napi_env env)
|
|
{
|
|
if (work_) {
|
|
napi_delete_async_work(env, work_);
|
|
work_ = nullptr;
|
|
}
|
|
if (env == nullptr) {
|
|
return false;
|
|
}
|
|
if (execute_ == nullptr) {
|
|
return SendNapiEvent(env, napi_eprio_immediate);
|
|
}
|
|
NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
|
|
work_ = reinterpret_cast<napi_async_work>(engine->CreateAsyncWork(name,
|
|
reinterpret_cast<NativeAsyncExecuteCallback>(Execute),
|
|
reinterpret_cast<NativeAsyncCompleteCallback>(Complete), this));
|
|
napi_queue_async_work_with_qos(env, work_, napi_qos_user_initiated);
|
|
return true;
|
|
}
|
|
|
|
bool NapiAsyncTask::StartLowQos(const std::string &name, napi_env env)
|
|
{
|
|
if (work_) {
|
|
napi_delete_async_work(env, work_);
|
|
work_ = nullptr;
|
|
}
|
|
if (env == nullptr) {
|
|
return false;
|
|
}
|
|
if (execute_ == nullptr) {
|
|
return SendNapiEvent(env, napi_eprio_low);
|
|
}
|
|
NativeEngine* engine = reinterpret_cast<NativeEngine*>(env);
|
|
work_ = reinterpret_cast<napi_async_work>(engine->CreateAsyncWork(name,
|
|
reinterpret_cast<NativeAsyncExecuteCallback>(Execute),
|
|
reinterpret_cast<NativeAsyncCompleteCallback>(Complete), this));
|
|
napi_queue_async_work_with_qos(env, work_, napi_qos_utility);
|
|
return true;
|
|
}
|
|
|
|
bool NapiAsyncTask::SendNapiEvent(napi_env env, napi_event_priority eventPriority)
|
|
{
|
|
if (napi_send_event(
|
|
env,
|
|
[env, this]() {
|
|
napi_status status = napi_ok;
|
|
Complete(env, status, this);
|
|
},
|
|
eventPriority) != napi_ok) {
|
|
TAG_LOGE(AAFwkTag::JSRUNTIME, "SendNapiEvent failed");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
|
|
NapiAsyncTask::ExecuteCallback&& execute, NapiAsyncTask::CompleteCallback&& complete, napi_value* result)
|
|
{
|
|
return CreateAsyncTaskWithLastParam(env, lastParam,
|
|
std::make_unique<NapiAsyncTask::ExecuteCallback>(std::move(execute)),
|
|
std::make_unique<NapiAsyncTask::CompleteCallback>(std::move(complete)), result);
|
|
}
|
|
|
|
std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
|
|
NapiAsyncTask::ExecuteCallback&& execute, nullptr_t, napi_value* result)
|
|
{
|
|
return CreateAsyncTaskWithLastParam(
|
|
env, lastParam, std::make_unique<NapiAsyncTask::ExecuteCallback>(std::move(execute)), nullptr, result);
|
|
}
|
|
|
|
std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
|
|
nullptr_t, NapiAsyncTask::CompleteCallback&& complete, napi_value* result)
|
|
{
|
|
return CreateAsyncTaskWithLastParam(
|
|
env, lastParam, nullptr, std::make_unique<NapiAsyncTask::CompleteCallback>(std::move(complete)), result);
|
|
}
|
|
|
|
std::unique_ptr<NapiAsyncTask> CreateAsyncTaskWithLastParam(napi_env env, napi_value lastParam,
|
|
nullptr_t, nullptr_t, napi_value* result)
|
|
{
|
|
return CreateAsyncTaskWithLastParam(env, lastParam, std::unique_ptr<NapiAsyncTask::ExecuteCallback>(),
|
|
std::unique_ptr<NapiAsyncTask::CompleteCallback>(), result);
|
|
}
|
|
|
|
std::unique_ptr<NapiAsyncTask> CreateEmptyAsyncTask(napi_env env, napi_value lastParam, napi_value* result)
|
|
{
|
|
napi_valuetype type = napi_undefined;
|
|
napi_typeof(env, lastParam, &type);
|
|
if (lastParam == nullptr || type != napi_function) {
|
|
napi_deferred nativeDeferred = nullptr;
|
|
napi_create_promise(env, &nativeDeferred, result);
|
|
return std::make_unique<NapiAsyncTask>(nativeDeferred, std::unique_ptr<NapiAsyncTask::ExecuteCallback>(),
|
|
std::unique_ptr<NapiAsyncTask::CompleteCallback>());
|
|
} else {
|
|
napi_get_undefined(env, result);
|
|
napi_ref callbackRef = nullptr;
|
|
napi_create_reference(env, lastParam, 1, &callbackRef);
|
|
return std::make_unique<NapiAsyncTask>(callbackRef, std::unique_ptr<NapiAsyncTask::ExecuteCallback>(),
|
|
std::unique_ptr<NapiAsyncTask::CompleteCallback>());
|
|
}
|
|
}
|
|
|
|
void ReleaseNativeReference(napi_env env, NativeReference* ref)
|
|
{
|
|
if (ref == nullptr) {
|
|
TAG_LOGD(AAFwkTag::JSRUNTIME, "null ref");
|
|
return;
|
|
}
|
|
uv_work_t *work = new (std::nothrow) uv_work_t;
|
|
if (work == nullptr) {
|
|
TAG_LOGE(AAFwkTag::JSRUNTIME, "null work");
|
|
delete ref;
|
|
return;
|
|
}
|
|
work->data = reinterpret_cast<void *>(ref);
|
|
if (napi_send_event(env,
|
|
[work]() {
|
|
if (work == nullptr) {
|
|
TAG_LOGE(AAFwkTag::JSRUNTIME, "null uv work");
|
|
return;
|
|
}
|
|
auto tempWork = const_cast<uv_work_t*>(work);
|
|
if (tempWork == nullptr) {
|
|
TAG_LOGE(AAFwkTag::JSRUNTIME, "null uv tempWork");
|
|
return;
|
|
}
|
|
if (tempWork->data == nullptr) {
|
|
TAG_LOGE(AAFwkTag::JSRUNTIME, "null data");
|
|
delete tempWork;
|
|
tempWork = nullptr;
|
|
return;
|
|
}
|
|
NativeReference *refPtr = reinterpret_cast<NativeReference *>(work->data);
|
|
delete refPtr;
|
|
refPtr = nullptr;
|
|
delete tempWork;
|
|
tempWork = nullptr;
|
|
},
|
|
napi_eprio_low) == napi_ok) {
|
|
return;
|
|
}
|
|
TAG_LOGE(AAFwkTag::JSRUNTIME, "napi_send_event failed");
|
|
delete ref;
|
|
if (work != nullptr) {
|
|
delete work;
|
|
work = nullptr;
|
|
}
|
|
}
|
|
} // namespace AbilityRuntime
|
|
} // namespace OHOS
|