Files
ace_napi/native_engine/impl/quickjs/quickjs_native_engine.cpp
T
zhanghaibo 7413301b07 use jsCodeLen
Signed-off-by: sunfei <sunfei.sun@huawei.com>
Change-Id:  I0a80e7c6463a71668884386cb7c3466b1a3e8f78
2022-01-06 09:11:57 +08:00

887 lines
31 KiB
C++

/*
* Copyright (c) 2021 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 "quickjs_native_engine.h"
#include <js_native_api.h>
#include "native_engine/native_engine.h"
#include "native_engine/native_property.h"
#include "native_value/quickjs_native_array.h"
#include "native_value/quickjs_native_array_buffer.h"
#include "native_value/quickjs_native_big_int.h"
#include "native_value/quickjs_native_boolean.h"
#include "native_value/quickjs_native_buffer.h"
#include "native_value/quickjs_native_data_view.h"
#include "native_value/quickjs_native_date.h"
#include "native_value/quickjs_native_external.h"
#include "native_value/quickjs_native_function.h"
#include "native_value/quickjs_native_number.h"
#include "native_value/quickjs_native_object.h"
#include "native_value/quickjs_native_string.h"
#include "native_value/quickjs_native_typed_array.h"
#include "quickjs_native_deferred.h"
#include "quickjs_native_reference.h"
#include "securec.h"
#include "utils/assert.h"
#include "utils/log.h"
static const int JS_WRITE_OBJ = (1 << 2) | (1 << 3);
static const int JS_ATOM_MESSAGE = 51;
QuickJSNativeEngine::QuickJSNativeEngine(JSRuntime* runtime, JSContext* context, void* jsEngine)
: NativeEngine(jsEngine)
{
runtime_ = runtime;
context_ = context;
AddIntrinsicBaseClass(context_);
AddIntrinsicExternal(context_);
JSValue jsGlobal = JS_GetGlobalObject(context_);
JSValue jsNativeEngine = (JSValue)JS_MKPTR(JS_TAG_INT, this);
JSValue jsRequireInternal = JS_NewCFunctionData(
context_,
[](JSContext* ctx, JSValueConst thisVal, int argc, JSValueConst* argv, int magic,
JSValue* funcData) -> JSValue {
JSValue result = JS_UNDEFINED;
QuickJSNativeEngine* that = (QuickJSNativeEngine*)JS_VALUE_GET_PTR(funcData[0]);
const char* moduleName = JS_ToCString(that->GetContext(), argv[0]);
if (moduleName == nullptr || strlen(moduleName) == 0) {
HILOG_ERROR("moduleName is nullptr or length is 0");
return result;
}
NativeModuleManager* moduleManager = that->GetModuleManager();
NativeModule* module = moduleManager->LoadNativeModule(moduleName, nullptr, false, true);
if (module != nullptr && module->registerCallback != nullptr) {
NativeValue* value = new QuickJSNativeObject(that);
if (value != nullptr) {
module->registerCallback(that, value);
result = JS_DupValue(that->GetContext(), *value);
}
}
JS_FreeCString(that->GetContext(), moduleName);
return result;
},
0, 0, 1, &jsNativeEngine);
JSValue jsRequire = JS_NewCFunctionData(
context_,
[](JSContext* ctx, JSValueConst thisVal, int argc, JSValueConst* argv, int magic,
JSValue* funcData) -> JSValue {
JSValue result = JS_UNDEFINED;
QuickJSNativeEngine* that = (QuickJSNativeEngine*)JS_VALUE_GET_PTR(funcData[0]);
const char* moduleName = JS_ToCString(that->GetContext(), argv[0]);
if (moduleName == nullptr || strlen(moduleName) == 0) {
HILOG_ERROR("moduleName is nullptr or length is 0");
return result;
}
bool isAppModule = false;
if (argc == 2) {
int ret = JS_ToBool(that->GetContext(), argv[1]);
if (ret != -1) {
isAppModule = ret;
}
}
NativeModuleManager* moduleManager = that->GetModuleManager();
NativeModule* module = moduleManager->LoadNativeModule(moduleName, nullptr, isAppModule);
if (module != nullptr) {
if (module->jsCode != nullptr) {
HILOG_INFO("load js code");
NativeValue* script = that->CreateString(module->jsCode, module->jsCodeLen);
NativeValue* exportObject = that->LoadModule(script, "testjsnapi.js");
if (exportObject == nullptr) {
HILOG_ERROR("load module failed");
return result;
}
result = JS_DupValue(that->GetContext(), *exportObject);
HILOG_ERROR("load module succ");
} else if (module->registerCallback != nullptr) {
HILOG_INFO("load napi module");
NativeValue* value = new QuickJSNativeObject(that);
module->registerCallback(that, value);
result = JS_DupValue(that->GetContext(), *value);
} else {
HILOG_ERROR("init module failed");
}
}
JS_FreeCString(that->GetContext(), moduleName);
return result;
},
0, 0, 1, &jsNativeEngine);
JS_SetPropertyStr(context_, jsGlobal, "requireInternal", jsRequireInternal);
#if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
JS_SetPropertyStr(context_, jsGlobal, "requireNapi", jsRequire);
#else
JS_SetPropertyStr(context_, jsGlobal, "requireNapiPreview", jsRequire);
#endif
JS_FreeValue(context_, jsGlobal);
// need to call init of base class.
Init();
}
QuickJSNativeEngine::~QuickJSNativeEngine()
{
// need to call deinit before base class.
Deinit();
}
JSValue QuickJSNativeEngine::GetModuleFromName(
const std::string& moduleName, bool isAppModule, const std::string& id, const std::string& param,
const std::string& instanceName, void** instance)
{
JSValue exports = JS_UNDEFINED;
NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
NativeModule* module = moduleManager->LoadNativeModule(moduleName.c_str(), nullptr, isAppModule);
if (module != nullptr) {
NativeValue* idValue = new QuickJSNativeString(this, id.c_str(), id.size());
NativeValue* paramValue = new QuickJSNativeString(this, param.c_str(), param.size());
NativeValue* exportObject = new QuickJSNativeObject(this);
NativePropertyDescriptor idProperty, paramProperty;
idProperty.utf8name = "id";
idProperty.value = idValue;
paramProperty.utf8name = "param";
paramProperty.value = paramValue;
QuickJSNativeObject* exportObj = reinterpret_cast<QuickJSNativeObject*>(exportObject);
exportObj->DefineProperty(idProperty);
exportObj->DefineProperty(paramProperty);
module->registerCallback(this, exportObject);
napi_value nExport = reinterpret_cast<napi_value>(exportObject);
napi_value exportInstance = nullptr;
napi_status status =
napi_get_named_property(reinterpret_cast<napi_env>(this), nExport, instanceName.c_str(), &exportInstance);
if (status != napi_ok) {
HILOG_ERROR("GetModuleFromName napi_get_named_property status != napi_ok");
}
status = napi_unwrap(reinterpret_cast<napi_env>(this), exportInstance, reinterpret_cast<void**>(instance));
if (status != napi_ok) {
HILOG_ERROR("GetModuleFromName napi_unwrap status != napi_ok");
}
exports = *exportObject;
}
return exports;
}
JSValue QuickJSNativeEngine::LoadModuleByName(
const std::string& moduleName, bool isAppModule, const std::string& param,
const std::string& instanceName, void* instance)
{
JSValue exports = JS_UNDEFINED;
NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
NativeModule* module = moduleManager->LoadNativeModule(moduleName.c_str(), nullptr, isAppModule);
if (module != nullptr) {
NativeValue* exportObject = new QuickJSNativeObject(this);
QuickJSNativeObject* exportObj = reinterpret_cast<QuickJSNativeObject*>(exportObject);
NativePropertyDescriptor paramProperty, instanceProperty;
NativeValue* paramValue = new QuickJSNativeString(this, param.c_str(), param.size());
paramProperty.utf8name = "param";
paramProperty.value = paramValue;
auto instanceValue = new QuickJSNativeObject(this);
instanceValue->SetNativePointer(instance, nullptr, nullptr);
instanceProperty.utf8name = instanceName.c_str();
instanceProperty.value = instanceValue;
exportObj->DefineProperty(paramProperty);
exportObj->DefineProperty(instanceProperty);
module->registerCallback(this, exportObject);
exports = JS_DupValue(GetContext(), *exportObject);
}
return exports;
}
JSRuntime* QuickJSNativeEngine::GetRuntime()
{
return runtime_;
}
JSContext* QuickJSNativeEngine::GetContext()
{
return context_;
}
void QuickJSNativeEngine::Loop(LoopMode mode, bool needSync)
{
JSContext* context = nullptr;
NativeEngine::Loop(mode, needSync);
int err = JS_ExecutePendingJob(runtime_, &context);
if (err < 0) {
js_std_dump_error(context);
}
}
NativeValue* QuickJSNativeEngine::GetGlobal()
{
JSValue value = JS_GetGlobalObject(context_);
return new QuickJSNativeObject(this, value);
}
NativeValue* QuickJSNativeEngine::CreateNull()
{
return new QuickJSNativeValue(this, JS_NULL);
}
NativeValue* QuickJSNativeEngine::CreateUndefined()
{
return new QuickJSNativeValue(this, JS_UNDEFINED);
}
NativeValue* QuickJSNativeEngine::CreateBoolean(bool value)
{
return new QuickJSNativeBoolean(this, value);
}
NativeValue* QuickJSNativeEngine::CreateNumber(int32_t value)
{
return new QuickJSNativeNumber(this, value);
}
NativeValue* QuickJSNativeEngine::CreateNumber(uint32_t value)
{
return new QuickJSNativeNumber(this, value);
}
NativeValue* QuickJSNativeEngine::CreateNumber(int64_t value)
{
return new QuickJSNativeNumber(this, value);
}
NativeValue* QuickJSNativeEngine::CreateNumber(double value)
{
return new QuickJSNativeNumber(this, value);
}
NativeValue* QuickJSNativeEngine::CreateBigInt(int64_t value)
{
return new QuickJSNativeBigInt(this, value);
}
NativeValue* QuickJSNativeEngine::CreateBigInt(uint64_t value)
{
return new QuickJSNativeBigInt(this, value, true);
}
NativeValue* QuickJSNativeEngine::CreateString(const char* value, size_t length)
{
return new QuickJSNativeString(this, value, length);
}
NativeValue* QuickJSNativeEngine::CreateString16(const char16_t* value, size_t length)
{
return new QuickJSNativeString(this, value, length);
}
NativeValue* QuickJSNativeEngine::CreateSymbol(NativeValue* value)
{
JSValue symbol = { 0 };
JSValue global = JS_GetGlobalObject(context_);
JSValue symbolCotr = JS_GetPropertyStr(context_, global, "Symbol");
JSValue jsValue = *value;
symbol = JS_Call(context_, symbolCotr, global, 1, &jsValue);
JS_FreeValue(context_, symbolCotr);
JS_FreeValue(context_, global);
js_std_loop(context_);
return new QuickJSNativeValue(this, symbol);
}
NativeValue* QuickJSNativeEngine::CreateFunction(const char* name, size_t length, NativeCallback cb, void* value)
{
return new QuickJSNativeFunction(this, name, cb, value);
}
NativeValue* QuickJSNativeEngine::CreateExternal(void* value, NativeFinalize callback, void* hint)
{
return new QuickJSNativeExternal(this, value, callback, hint);
}
NativeValue* QuickJSNativeEngine::CreateObject()
{
return new QuickJSNativeObject(this);
}
NativeValue* QuickJSNativeEngine::CreateArrayBuffer(void** value, size_t length)
{
return new QuickJSNativeArrayBuffer(this, (uint8_t**)value, length);
}
NativeValue* QuickJSNativeEngine::CreateArrayBufferExternal(void* value, size_t length, NativeFinalize cb, void* hint)
{
return new QuickJSNativeArrayBuffer(this, (uint8_t*)value, length, cb, hint);
}
NativeValue* QuickJSNativeEngine::CreateArray(size_t length)
{
return new QuickJSNativeArray(this, length);
}
NativeValue* QuickJSNativeEngine::CreateDataView(NativeValue* value, size_t length, size_t offset)
{
return new QuickJSNativeDataView(this, value, length, offset);
}
NativeValue* QuickJSNativeEngine::CreateTypedArray(NativeTypedArrayType type,
NativeValue* value,
size_t length,
size_t offset)
{
return new QuickJSNativeTypedArray(this, type, value, length, offset);
}
NativeValue* QuickJSNativeEngine::CreatePromise(NativeDeferred** deferred)
{
JSValue promise = { 0 };
JSValue resolvingFuncs[2] = { 0 };
promise = JS_NewPromiseCapability(context_, resolvingFuncs);
*deferred = new QuickJSNativeDeferred(this, resolvingFuncs);
return new QuickJSNativeValue(this, promise);
}
NativeValue* QuickJSNativeEngine::CreateError(NativeValue* code, NativeValue* message)
{
JSValue error = JS_NewError(context_);
if (code) {
JS_SetPropertyStr(context_, error, "code", JS_DupValue(context_, *code));
}
if (message) {
JS_SetPropertyStr(context_, error, "message", JS_DupValue(context_, *message));
}
return new QuickJSNativeObject(this, error);
}
NativeValue* QuickJSNativeEngine::CreateInstance(NativeValue* constructor, NativeValue* const* argv, size_t argc)
{
JSValue result = JS_UNDEFINED;
JSValue* params = nullptr;
if (argc > 0) {
params = new JSValue[argc];
for (size_t i = 0; i < argc && params != nullptr; i++) {
params[i] = *argv[i];
}
}
result = JS_CallConstructor(context_, *constructor, argc, params);
if (params != nullptr) {
delete[] params;
}
return QuickJSNativeEngine::JSValueToNativeValue(this, result);
}
NativeReference* QuickJSNativeEngine::CreateReference(NativeValue* value, uint32_t initialRefcount,
NativeFinalize callback, void* data, void* hint)
{
return new QuickJSNativeReference(this, value, initialRefcount, callback, data, hint);
}
NativeValue* QuickJSNativeEngine::CallFunction(NativeValue* thisVar,
NativeValue* function,
NativeValue* const* argv,
size_t argc)
{
JSValue result = JS_UNDEFINED;
if (function == nullptr) {
return new QuickJSNativeValue(this, JS_UNDEFINED);
}
NativeScope* scope = scopeManager_->Open();
if (scope == nullptr) {
HILOG_ERROR("Open scope failed");
return new QuickJSNativeValue(this, JS_UNDEFINED);
}
JSValue* args = nullptr;
if (argc > 0) {
args = new JSValue[argc];
for (size_t i = 0; i < argc && args != nullptr; i++) {
if (argv[i] != nullptr) {
args[i] = *argv[i];
} else {
args[i] = JS_UNDEFINED;
}
}
}
result = JS_Call(context_, *function, (thisVar != nullptr) ? (JSValue)*thisVar : JS_UNDEFINED, argc, args);
js_std_loop(context_);
JS_DupValue(context_, result);
if (args != nullptr) {
delete[] args;
}
scopeManager_->Close(scope);
if (JS_IsError(context_, result) || JS_IsException(result)) {
return nullptr;
}
return JSValueToNativeValue(this, result);
}
NativeValue* QuickJSNativeEngine::RunScript(NativeValue* script)
{
JSValue result;
const char* cScript = JS_ToCString(context_, *script);
result = JS_Eval(context_, cScript, strlen(cScript), "<input>", JS_EVAL_TYPE_GLOBAL);
JS_FreeCString(context_, cScript);
if (JS_IsError(context_, result) || JS_IsException(result)) {
return nullptr;
}
js_std_loop(context_);
return JSValueToNativeValue(this, result);
}
void QuickJSNativeEngine::SetPackagePath(const std::string& packagePath)
{
auto moduleManager = NativeModuleManager::GetInstance();
if (moduleManager) {
moduleManager->SetAppLibPath(packagePath.c_str());
}
}
NativeValue* QuickJSNativeEngine::RunBufferScript(std::vector<uint8_t>& buffer)
{
JSValue result =
JS_Eval(context_, reinterpret_cast<char*>(buffer.data()), buffer.size(), "<input>", JS_EVAL_TYPE_GLOBAL);
if (JS_IsError(context_, result) || JS_IsException(result)) {
return nullptr;
}
js_std_loop(context_);
return JSValueToNativeValue(this, result);
}
NativeValue* QuickJSNativeEngine::LoadModule(NativeValue* str, const std::string& fileName)
{
if (str == nullptr || fileName.empty()) {
HILOG_ERROR("moduleName is nullptr or source code length is 0");
return nullptr;
}
JS_SetModuleLoaderFunc(runtime_, nullptr, js_module_loader, nullptr);
const char* moduleSource = JS_ToCString(context_, *str);
size_t len = strlen(moduleSource);
int flags = JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY;
JSValue moduleVal = JS_Eval(context_, moduleSource, len, fileName.c_str(), flags);
if (JS_IsException(moduleVal)) {
HILOG_ERROR("Eval source code exception");
JS_FreeCString(context_, moduleSource);
return nullptr;
}
JSValue evalRes = JS_EvalFunction(context_, moduleVal);
if (JS_IsException(evalRes)) {
HILOG_ERROR("An exception occurred during Eval module");
}
JSValue ns = JS_GetNameSpace(context_, moduleVal);
JSValue result = JS_GetPropertyStr(context_, ns, "default");
JS_FreeValue(context_, ns);
JS_FreeValue(context_, evalRes);
JS_FreeCString(context_, moduleSource);
js_std_loop(context_);
return JSValueToNativeValue(this, result);
}
NativeValue* QuickJSNativeEngine::DefineClass(const char* name,
NativeCallback callback,
void* data,
const NativePropertyDescriptor* properties,
size_t length)
{
NativeFunctionInfo* functionInfo = new NativeFunctionInfo();
if (functionInfo == nullptr) {
HILOG_ERROR("new NativeFunctionInfo failed");
return nullptr;
}
functionInfo->engine = this;
functionInfo->data = data;
functionInfo->callback = callback;
JSValue classConstructor = JS_NewCFunction2(
context_,
[](JSContext* ctx, JSValueConst newTarget, int argc, JSValueConst* argv) -> JSValue {
auto callbackInfo = new NativeCallbackInfo();
if (callbackInfo == nullptr) {
HILOG_ERROR("callbackInfo is nullptr");
return JS_UNDEFINED;
}
JSValue prototype = JS_GetPropertyStr(ctx, newTarget, "prototype");
JSValue classContext = JS_GetPropertyStr(ctx, newTarget, "_classContext");
auto functionInfo = (NativeFunctionInfo*)JS_ExternalToNativeObject(ctx, classContext);
if (functionInfo == nullptr) {
HILOG_ERROR("functionInfo is nullptr");
return JS_UNDEFINED;
}
QuickJSNativeEngine* engine = (QuickJSNativeEngine*)functionInfo->engine;
NativeScopeManager* scopeManager = engine->GetScopeManager();
if (scopeManager == nullptr) {
HILOG_ERROR("scopeManager is nullptr");
return JS_UNDEFINED;
}
NativeScope* scope = scopeManager->Open();
if (scope == nullptr) {
HILOG_ERROR("scope is nullptr");
return JS_UNDEFINED;
}
callbackInfo->argc = argc;
callbackInfo->argv = nullptr;
callbackInfo->function = JSValueToNativeValue(engine, JS_DupValue(ctx, newTarget));
callbackInfo->functionInfo = functionInfo;
callbackInfo->thisVar =
JSValueToNativeValue(engine, JS_NewObjectProtoClass(ctx, prototype, GetBaseClassID()));
if (callbackInfo->argc > 0) {
callbackInfo->argv = new NativeValue*[argc];
for (size_t i = 0; i < callbackInfo->argc && callbackInfo->argv != nullptr; i++) {
callbackInfo->argv[i] = JSValueToNativeValue(engine, JS_DupValue(ctx, argv[i]));
}
}
NativeValue* value = functionInfo->callback(engine, callbackInfo);
if (callbackInfo != nullptr) {
delete callbackInfo->argv;
}
JSValue result = JS_UNDEFINED;
if (value != nullptr) {
result = JS_DupValue(ctx, *value);
} else if (engine->IsExceptionPending()) {
NativeValue* error = engine->GetAndClearLastException();
if (error != nullptr) {
result = JS_DupValue(ctx, *error);
}
}
delete callbackInfo;
scopeManager->Close(scope);
return result;
},
name, 0, JS_CFUNC_constructor_or_func, 0);
JSValue proto = JS_NewObject(context_);
QuickJSNativeObject* nativeClass = new QuickJSNativeObject(this, JS_DupValue(context_, classConstructor));
if (nativeClass == nullptr) {
delete functionInfo;
return nullptr;
}
QuickJSNativeObject* nativeClassProto = new QuickJSNativeObject(this, proto);
if (nativeClassProto == nullptr) {
delete functionInfo;
delete nativeClass;
return nullptr;
}
for (size_t i = 0; i < length; i++) {
if (properties[i].attributes & NATIVE_STATIC) {
nativeClass->DefineProperty(properties[i]);
} else {
nativeClassProto->DefineProperty(properties[i]);
}
}
JS_DefinePropertyValueStr(context_, *nativeClass, "prototype", JS_DupValue(context_, *nativeClassProto), 0);
JSValue classContext = JS_NewExternal(
context_, functionInfo,
[](JSContext* ctx, void* data, void* hint) {
auto info = (NativeFunctionInfo*)data;
HILOG_INFO("_classContext Destroy");
if (info != nullptr) {
delete info;
}
},
nullptr);
JS_DefinePropertyValueStr(context_, *nativeClass, "_classContext", classContext, 0);
JS_DefinePropertyValueStr(context_, *nativeClassProto, "constructor", JS_DupValue(context_, *nativeClass),
JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
return nativeClass;
}
bool QuickJSNativeEngine::Throw(NativeValue* error)
{
JS_Throw(context_, *error);
this->lastException_ = error;
return true;
}
bool QuickJSNativeEngine::Throw(NativeErrorType type, const char* code, const char* message)
{
JSValue error;
switch (type) {
case NativeErrorType::NATIVE_TYPE_ERROR:
error = JS_ThrowTypeError(context_, "code: %s, message: %s\n", code, message);
break;
case NativeErrorType::NATIVE_RANGE_ERROR:
error = JS_ThrowRangeError(context_, "code: %s, message: %s\n", code, message);
break;
default:
error = JS_ThrowInternalError(context_, "code: %s, message: %s\n", code, message);
}
this->lastException_ = new QuickJSNativeValue(this, error);
return true;
}
NativeValue* QuickJSNativeEngine::JSValueToNativeValue(QuickJSNativeEngine* engine, JSValue value)
{
NativeValue* result = nullptr;
int tag = JS_VALUE_GET_NORM_TAG(value);
switch (tag) {
case JS_TAG_BIG_INT:
result = new QuickJSNativeBigInt(engine, value);
break;
case JS_TAG_BIG_FLOAT:
result = new QuickJSNativeObject(engine, value);
break;
case JS_TAG_SYMBOL:
result = new QuickJSNativeValue(engine, value);
break;
case JS_TAG_STRING:
result = new QuickJSNativeString(engine, value);
break;
case JS_TAG_OBJECT:
if (JS_IsArray(engine->GetContext(), value)) {
result = new QuickJSNativeArray(engine, value);
} else if (JS_IsError(engine->GetContext(), value)) {
result = new QuickJSNativeValue(engine, value);
} else if (JS_IsPromise(engine->GetContext(), value)) {
result = new QuickJSNativeValue(engine, value);
} else if (JS_IsArrayBuffer(engine->GetContext(), value)) {
result = new QuickJSNativeArrayBuffer(engine, value);
} else if (JS_IsBuffer(engine->GetContext(), value)) {
result = new QuickJSNativeBuffer(engine, value);
} else if (JS_IsDataView(engine->GetContext(), value)) {
result = new QuickJSNativeDataView(engine, value);
} else if (JS_IsTypedArray(engine->GetContext(), value)) {
result = new QuickJSNativeTypedArray(engine, value);
} else if (JS_IsExternal(engine->GetContext(), value)) {
result = new QuickJSNativeExternal(engine, value);
} else if (JS_IsFunction(engine->GetContext(), value)) {
result = new QuickJSNativeFunction(engine, value);
} else if (JS_IsDate(engine->GetContext(), value)) {
result = new QuickJSNativeDate(engine, value);
} else {
result = new QuickJSNativeObject(engine, value);
}
break;
case JS_TAG_BOOL:
result = new QuickJSNativeBoolean(engine, value);
break;
case JS_TAG_NULL:
case JS_TAG_UNDEFINED:
case JS_TAG_UNINITIALIZED:
case JS_TAG_CATCH_OFFSET:
case JS_TAG_EXCEPTION:
result = new QuickJSNativeValue(engine, value);
break;
case JS_TAG_INT:
case JS_TAG_FLOAT64:
result = new QuickJSNativeNumber(engine, value);
break;
default:
HILOG_DEBUG("JS_VALUE_GET_NORM_TAG %{public}d", tag);
}
return result;
}
void* QuickJSNativeEngine::CreateRuntime()
{
JSRuntime* runtime = JS_NewRuntime();
JSContext* context = JS_NewContext(runtime);
return reinterpret_cast<void*>(new QuickJSNativeEngine(runtime, context, this));
}
bool QuickJSNativeEngine::CheckTransferList(JSValue transferList)
{
if (JS_IsUndefined(transferList)) {
return true;
}
if (!JS_IsArray(context_, transferList)) {
JS_ThrowTypeError(context_, "postMessage second parameter not a list or undefined");
return false;
}
int64_t len = 0;
js_get_length64(context_, &len, transferList);
for (int64_t i = 0; i < len; i++) {
JSValue tmp = JS_GetPropertyInt64(context_, transferList, i);
if (!JS_IsException(tmp)) {
if (!JS_IsArrayBuffer(context_, tmp)) {
HILOG_ERROR("JS_ISArrayBuffer fail");
return false;
}
} else {
HILOG_ERROR("JS_GetPropertyInt64 fail");
return false;
}
}
return true;
}
bool QuickJSNativeEngine::DetachTransferList(JSValue transferList)
{
if (JS_IsUndefined(transferList)) {
return true;
}
int64_t len = 0;
js_get_length64(context_, &len, transferList);
for (int64_t i = 0; i < len; i++) {
JSValue tmp = JS_GetPropertyInt64(context_, transferList, i);
if (!JS_IsException(tmp)) {
JS_DetachArrayBuffer(context_, tmp);
} else {
return false;
}
}
return true;
}
NativeValue* QuickJSNativeEngine::Serialize(NativeEngine* context, NativeValue* value, NativeValue* transfer)
{
if (!CheckTransferList(*transfer)) {
return nullptr;
}
size_t dataLen;
uint8_t* data = JS_WriteObject(context_, &dataLen, *value, JS_WRITE_OBJ);
DetachTransferList(*transfer);
return reinterpret_cast<NativeValue*>(new SerializeData(dataLen, data));
}
NativeValue* QuickJSNativeEngine::Deserialize(NativeEngine* context, NativeValue* recorder)
{
auto data = reinterpret_cast<SerializeData*>(recorder);
JSValue result = JS_ReadObject(context_, data->GetData(), data->GetSize(), JS_WRITE_OBJ);
return JSValueToNativeValue(this, result);
}
void QuickJSNativeEngine::DeleteSerializationData(NativeValue* value) const
{
SerializeData* data = reinterpret_cast<SerializeData*>(value);
delete data;
}
ExceptionInfo* QuickJSNativeEngine::GetExceptionForWorker() const
{
JSValue exception = JS_GetCurrentException(runtime_);
ASSERT(JS_IsObject(exception));
JSValue msg;
ExceptionInfo* exceptionInfo = new ExceptionInfo();
msg = JS_GetProperty(context_, exception, JS_ATOM_MESSAGE);
ASSERT(JS_IsString(msg));
const char* exceptionStr = reinterpret_cast<char*>(JS_GetStringFromObject(msg));
if (exceptionStr == nullptr) {
delete exceptionInfo;
exceptionInfo = nullptr;
return nullptr;
}
const char* error = "Error: ";
int len = strlen(exceptionStr) + strlen(error) + 1;
if (len <= 0) {
return nullptr;
}
char* exceptionMessage = new char[len] { 0 };
if (memcpy_s(exceptionMessage, len, error, strlen(error)) != EOK) {
HILOG_INFO("worker:: memcpy_s error");
delete exceptionInfo;
delete[] exceptionMessage;
return nullptr;
}
if (memcpy_s(exceptionMessage + strlen(error), len, exceptionStr, strlen(exceptionStr)) != EOK) {
HILOG_INFO("worker:: memcpy_s error");
delete exceptionInfo;
delete[] exceptionMessage;
return nullptr;
}
exceptionInfo->message_ = exceptionMessage;
return exceptionInfo;
}
NativeValue* QuickJSNativeEngine::ValueToNativeValue(JSValueWrapper& value)
{
JSValue quickValue = value;
return JSValueToNativeValue(this, quickValue);
}
NativeValue* QuickJSNativeEngine::CreateBuffer(void** value, size_t length)
{
return new QuickJSNativeBuffer(this, (uint8_t**)value, length);
}
NativeValue* QuickJSNativeEngine::CreateBufferCopy(void** value, size_t length, const void* data)
{
return new QuickJSNativeBuffer(this, (uint8_t**)value, length, data);
}
NativeValue* QuickJSNativeEngine::CreateBufferExternal(void* value, size_t length, NativeFinalize cb, void* hint)
{
return new QuickJSNativeBuffer(this, (uint8_t*)value, length, cb, hint);
}
NativeValue* QuickJSNativeEngine::CreateDate(double time)
{
JSValue value = JS_StrictDate(context_, time);
return new QuickJSNativeDate(this, value);
}
NativeValue* QuickJSNativeEngine::CreateBigWords(int sign_bit, size_t word_count, const uint64_t* words)
{
JSValue value = JS_CreateBigIntWords(context_, sign_bit, word_count, words);
return new QuickJSNativeBigInt(this, value);
}
bool QuickJSNativeEngine::TriggerFatalException(NativeValue* error)
{
return false;
}
bool QuickJSNativeEngine::AdjustExternalMemory(int64_t ChangeInBytes, int64_t* AdjustedValue)
{
HILOG_INFO("L2: napi_adjust_external_memory not supported!");
return true;
}
void QuickJSNativeEngine::SetPromiseRejectCallback(NativeReference* rejectCallbackRef,
NativeReference* checkCallbackRef) {}