mirror of
https://github.com/openharmony/ace_napi.git
synced 2026-07-01 15:07:43 -04:00
a99921b5ca
Signed-off-by: zhongjianfei <zhongjianfei@huawei.com> Change-Id: Ic43ae2175fca333394f68d670ee60343cf63c278
862 lines
31 KiB
C++
862 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 "v8_native_engine.h"
|
|
|
|
#include <js_native_api.h>
|
|
|
|
#include "native_engine/native_property.h"
|
|
#include "native_value/v8_native_array.h"
|
|
#include "native_value/v8_native_array_buffer.h"
|
|
#include "native_value/v8_native_boolean.h"
|
|
#include "native_value/v8_native_data_view.h"
|
|
#include "native_value/v8_native_external.h"
|
|
#include "native_value/v8_native_function.h"
|
|
#include "native_value/v8_native_number.h"
|
|
#include "native_value/v8_native_object.h"
|
|
#include "native_value/v8_native_string.h"
|
|
#include "native_value/v8_native_typed_array.h"
|
|
#include "securec.h"
|
|
#include "utils/log.h"
|
|
#include "v8_native_deferred.h"
|
|
#include "v8_native_reference.h"
|
|
|
|
V8NativeEngine::V8NativeEngine(v8::Platform* platform, v8::Isolate* isolate,
|
|
v8::Persistent<v8::Context>& context, void* jsEngine)
|
|
: NativeEngine(jsEngine),
|
|
platform_(platform),
|
|
isolate_(isolate),
|
|
context_(isolate, context),
|
|
isolateScope_(isolate),
|
|
handleScope_(isolate_),
|
|
contextScope_(context.Get(isolate_)),
|
|
tryCatch_(isolate_)
|
|
{
|
|
v8::Local<v8::String> requireNapiName = v8::String::NewFromUtf8(isolate_, "requireNapi").ToLocalChecked();
|
|
v8::Local<v8::String> requireInternalName = v8::String::NewFromUtf8(isolate_, "requireInternal").ToLocalChecked();
|
|
|
|
v8::Local<v8::Value> requireData = v8::External::New(isolate_, (void*)this).As<v8::Value>();
|
|
|
|
v8::Local<v8::Function> requireNapi =
|
|
v8::Function::New(
|
|
context_.Get(isolate_),
|
|
[](const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|
V8NativeEngine* engine = (V8NativeEngine*)info.Data().As<v8::External>()->Value();
|
|
if (engine == nullptr) {
|
|
return;
|
|
}
|
|
v8::String::Utf8Value moduleName(info.GetIsolate(), info[0]);
|
|
NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
|
|
if (moduleManager == nullptr) {
|
|
return;
|
|
}
|
|
|
|
bool isAppModule = false;
|
|
if (info.Length() == 2) {
|
|
isAppModule = info[1]->ToBoolean(info.GetIsolate())->Value();
|
|
}
|
|
NativeModule* module = moduleManager->LoadNativeModule(*moduleName, nullptr, isAppModule);
|
|
|
|
if (module == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (module->jsCode != nullptr) {
|
|
HILOG_INFO("load js code");
|
|
NativeValue* script = engine->CreateString(module->jsCode, strlen(module->jsCode));
|
|
NativeValue* exportObject = engine->LoadModule(script, "testjsnapi.js");
|
|
if (exportObject == nullptr) {
|
|
HILOG_ERROR("load module failed");
|
|
return;
|
|
}
|
|
v8::Local<v8::Object> exports = *exportObject;
|
|
info.GetReturnValue().Set(exports);
|
|
HILOG_ERROR("load module succ");
|
|
} else if (module->registerCallback != nullptr) {
|
|
HILOG_INFO("load napi module");
|
|
NativeValue* exportObject = new V8NativeObject(engine);
|
|
if (exportObject == nullptr) {
|
|
return;
|
|
}
|
|
module->registerCallback(engine, exportObject);
|
|
v8::Local<v8::Object> exports = *exportObject;
|
|
info.GetReturnValue().Set(exports);
|
|
}
|
|
},
|
|
requireData, 1)
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::Function> requireInternal =
|
|
v8::Function::New(
|
|
context_.Get(isolate_),
|
|
[](const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|
V8NativeEngine* engine = (V8NativeEngine*)info.Data().As<v8::External>()->Value();
|
|
if (engine == nullptr) {
|
|
return;
|
|
}
|
|
v8::String::Utf8Value moduleName(info.GetIsolate(), info[0]);
|
|
NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
|
|
if (moduleManager == nullptr) {
|
|
return;
|
|
}
|
|
NativeModule* module = moduleManager->LoadNativeModule(*moduleName, nullptr, false, true);
|
|
if (module == nullptr) {
|
|
return;
|
|
}
|
|
NativeValue* exportObject = new V8NativeObject(engine);
|
|
if (exportObject == nullptr) {
|
|
return;
|
|
}
|
|
module->registerCallback(engine, exportObject);
|
|
v8::Local<v8::Object> exports = *exportObject;
|
|
info.GetReturnValue().Set(exports);
|
|
},
|
|
requireData, 1)
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::Object> global = context_.Get(isolate_)->Global();
|
|
|
|
global->Set(context_.Get(isolate_), requireNapiName, requireNapi).FromJust();
|
|
global->Set(context_.Get(isolate_), requireInternalName, requireInternal).FromJust();
|
|
}
|
|
|
|
V8NativeEngine::~V8NativeEngine() {}
|
|
|
|
v8::Local<v8::Object> V8NativeEngine::GetModuleFromName(
|
|
const std::string& moduleName, bool isAppModule, const std::string& id, const std::string& param,
|
|
const std::string& instanceName, void** instance)
|
|
{
|
|
HILOG_INFO("GetModuleFromName");
|
|
v8::Isolate* isolate = this->GetContext()->GetIsolate();
|
|
v8::HandleScope handleScope(isolate);
|
|
|
|
v8::Local<v8::Object> exports;
|
|
NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
|
|
NativeModule* module = moduleManager->LoadNativeModule(moduleName.c_str(), nullptr, isAppModule);
|
|
if (module != nullptr) {
|
|
NativeValue* idValue = new V8NativeString(this, id.c_str(), id.size());
|
|
NativeValue* paramValue = new V8NativeString(this, param.c_str(), param.size());
|
|
NativeValue* exportObject = new V8NativeObject(this);
|
|
|
|
NativePropertyDescriptor idProperty, paramProperty;
|
|
idProperty.utf8name = "id";
|
|
idProperty.value = idValue;
|
|
paramProperty.utf8name = "param";
|
|
paramProperty.value = paramValue;
|
|
V8NativeObject* exportObj = reinterpret_cast<V8NativeObject*>(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;
|
|
}
|
|
|
|
v8::Isolate* V8NativeEngine::GetIsolate()
|
|
{
|
|
return isolate_;
|
|
}
|
|
|
|
v8::Local<v8::Context> V8NativeEngine::GetContext()
|
|
{
|
|
return *reinterpret_cast<v8::Local<v8::Context>*>(const_cast<v8::Global<v8::Context>*>(&context_));
|
|
}
|
|
|
|
void V8NativeEngine::Loop(LoopMode mode, bool needSync)
|
|
{
|
|
NativeEngine::Loop(mode, needSync);
|
|
v8::platform::PumpMessageLoop(platform_, isolate_);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::GetGlobal()
|
|
{
|
|
v8::Local<v8::Object> value = context_.Get(isolate_)->Global();
|
|
return V8ValueToNativeValue(this, value);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateNull()
|
|
{
|
|
v8::Local<v8::Primitive> value = v8::Null(isolate_);
|
|
return new V8NativeValue(this, value);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateUndefined()
|
|
{
|
|
v8::Local<v8::Primitive> value = v8::Undefined(isolate_);
|
|
return new V8NativeValue(this, value);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateBoolean(bool value)
|
|
{
|
|
return new V8NativeBoolean(this, value);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateNumber(int32_t value)
|
|
{
|
|
return new V8NativeNumber(this, value);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateNumber(uint32_t value)
|
|
{
|
|
return new V8NativeNumber(this, value);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateNumber(int64_t value)
|
|
{
|
|
return new V8NativeNumber(this, value);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateNumber(double value)
|
|
{
|
|
return new V8NativeNumber(this, value);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateString(const char* value, size_t length)
|
|
{
|
|
return new V8NativeString(this, value, length);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateSymbol(NativeValue* value)
|
|
{
|
|
return new V8NativeValue(this, v8::Symbol::New(isolate_, *value));
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateExternal(void* value, NativeFinalize callback, void* hint)
|
|
{
|
|
return new V8NativeExternal(this, value, callback, hint);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateObject()
|
|
{
|
|
return new V8NativeObject(this);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateFunction(const char* name, size_t length, NativeCallback cb, void* value)
|
|
{
|
|
return new V8NativeFunction(this, name, length, cb, value);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateArray(size_t length)
|
|
{
|
|
return new V8NativeArray(this, length);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateArrayBuffer(void** value, size_t length)
|
|
{
|
|
return new V8NativeArrayBuffer(this, (uint8_t**)value, length);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateArrayBufferExternal(void* value, size_t length, NativeFinalize cb, void* hint)
|
|
{
|
|
return new V8NativeArrayBuffer(this, (uint8_t*)value, length, cb, hint);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateTypedArray(NativeTypedArrayType type,
|
|
NativeValue* value,
|
|
size_t length,
|
|
size_t offset)
|
|
{
|
|
v8::Local<v8::ArrayBuffer> buffer = *value;
|
|
v8::Local<v8::TypedArray> typedArray;
|
|
|
|
switch (type) {
|
|
case NATIVE_INT8_ARRAY:
|
|
typedArray = v8::Int8Array::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_UINT8_ARRAY:
|
|
typedArray = v8::Uint8Array::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_UINT8_CLAMPED_ARRAY:
|
|
typedArray = v8::Uint8ClampedArray::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_INT16_ARRAY:
|
|
typedArray = v8::Int16Array::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_UINT16_ARRAY:
|
|
typedArray = v8::Uint16Array::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_INT32_ARRAY:
|
|
typedArray = v8::Int32Array::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_UINT32_ARRAY:
|
|
typedArray = v8::Uint32Array::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_FLOAT32_ARRAY:
|
|
typedArray = v8::Float32Array::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_FLOAT64_ARRAY:
|
|
typedArray = v8::Float64Array::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_BIGINT64_ARRAY:
|
|
typedArray = v8::BigInt64Array::New(buffer, offset, length);
|
|
break;
|
|
case NATIVE_BIGUINT64_ARRAY:
|
|
typedArray = v8::BigUint64Array::New(buffer, offset, length);
|
|
break;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
return new V8NativeTypedArray(this, typedArray);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateDataView(NativeValue* value, size_t length, size_t offset)
|
|
{
|
|
return new V8NativeDataView(this, value, length, offset);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreatePromise(NativeDeferred** deferred)
|
|
{
|
|
auto v8Resolver = v8::Promise::Resolver::New(context_.Get(isolate_)).ToLocalChecked();
|
|
|
|
*deferred = new V8NativeDeferred(this, v8Resolver);
|
|
|
|
return new V8NativeValue(this, v8Resolver->GetPromise());
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateError(NativeValue* code, NativeValue* message)
|
|
{
|
|
v8::Local<v8::Value> errorObj = v8::Exception::Error(*message);
|
|
if (code) {
|
|
v8::Local<v8::Value> codeKey = v8::String::NewFromUtf8(isolate_, "code").ToLocalChecked();
|
|
errorObj.As<v8::Object>()->Set(context_.Get(isolate_), codeKey, *code).FromJust();
|
|
}
|
|
return V8ValueToNativeValue(this, errorObj);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CallFunction(NativeValue* thisVar,
|
|
NativeValue* function,
|
|
NativeValue* const* argv,
|
|
size_t argc)
|
|
{
|
|
if (function == nullptr) {
|
|
return nullptr;
|
|
}
|
|
v8::Local<v8::Value> v8recv = (thisVar != nullptr) ? *thisVar : v8::Undefined(isolate_);
|
|
v8::Local<v8::Function> v8func = *function;
|
|
v8::Local<v8::Value>* args = nullptr;
|
|
v8::Local<v8::Context> context = context_.Get(isolate_);
|
|
if (argc > 0) {
|
|
args = new v8::Local<v8::Value>[argc];
|
|
for (size_t i = 0; i < argc && args != nullptr; i++) {
|
|
if (argv[i] != nullptr) {
|
|
args[i] = *argv[i];
|
|
} else {
|
|
args[i] = v8::Undefined(isolate_);
|
|
}
|
|
}
|
|
}
|
|
v8::MaybeLocal<v8::Value> maybeValue = v8func->Call(context, v8recv, argc, args);
|
|
if (args != nullptr) {
|
|
delete []args;
|
|
}
|
|
v8::Local<v8::Value> result;
|
|
if (!maybeValue.ToLocal(&result)) {
|
|
return nullptr;
|
|
}
|
|
return V8ValueToNativeValue(this, result);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::RunScript(NativeValue* script)
|
|
{
|
|
v8::Local<v8::Value> v8Script = *script;
|
|
auto maybeScript = v8::Script::Compile(context_.Get(isolate_), v8Script.As<v8::String>());
|
|
auto localScript = maybeScript.ToLocalChecked();
|
|
auto scriptResult = localScript->Run(context_.Get(isolate_));
|
|
|
|
v8::Local<v8::Value> result;
|
|
if (!scriptResult.ToLocal(&result)) {
|
|
return nullptr;
|
|
}
|
|
|
|
return V8ValueToNativeValue(this, result);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::RunBufferScript(std::vector<uint8_t>& buffer)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
namespace {
|
|
v8::MaybeLocal<v8::String> ReadFile(v8::Isolate* isolate, const char* path)
|
|
{
|
|
std::ifstream file(path);
|
|
if (file.fail()) {
|
|
file.close();
|
|
return v8::MaybeLocal<v8::String>();
|
|
}
|
|
|
|
std::string fileContent;
|
|
fileContent.clear();
|
|
file.seekg(0, std::ios::end);
|
|
fileContent.reserve(static_cast<std::string::size_type>(file.tellg()));
|
|
file.seekg(0, std::ios::beg);
|
|
fileContent.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
|
file.close();
|
|
|
|
v8::MaybeLocal<v8::String> result = v8::String::NewFromUtf8(isolate, fileContent.c_str(),
|
|
v8::NewStringType::kNormal, fileContent.size());
|
|
return result;
|
|
}
|
|
|
|
v8::MaybeLocal<v8::Module> ModuleResolveCallback(v8::Local<v8::Context> context,
|
|
v8::Local<v8::String> specifier,
|
|
v8::Local<v8::Module> referrer)
|
|
{
|
|
v8::Isolate* isolate = context->GetIsolate();
|
|
int len = specifier->Length();
|
|
char *buffer = new char[len + 1];
|
|
specifier->WriteUtf8(isolate, buffer, len, nullptr,
|
|
v8::String::REPLACE_INVALID_UTF8 | v8::String::NO_NULL_TERMINATION);
|
|
auto maybeSourceCode = ReadFile(isolate, buffer);
|
|
v8::Local<v8::String> sourceCode;
|
|
if (!maybeSourceCode.ToLocal(&sourceCode)) {
|
|
v8::ScriptOrigin origin(v8::String::NewFromUtf8(isolate, "moduleloader.js").ToLocalChecked(),
|
|
v8::Local<v8::Integer>(), v8::Local<v8::Integer>(), v8::Local<v8::Boolean>(),
|
|
v8::Local<v8::Integer>(), v8::Local<v8::Value>(), v8::Local<v8::Boolean>(),
|
|
v8::Local<v8::Boolean>(), True(isolate));
|
|
v8::ScriptCompiler::Source source(specifier, origin);
|
|
delete[] buffer;
|
|
return v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
|
}
|
|
|
|
v8::ScriptOrigin origin(specifier, v8::Local<v8::Integer>(), v8::Local<v8::Integer>(), v8::Local<v8::Boolean>(),
|
|
v8::Local<v8::Integer>(), v8::Local<v8::Value>(), v8::Local<v8::Boolean>(),
|
|
v8::Local<v8::Boolean>(), True(isolate));
|
|
v8::ScriptCompiler::Source source(sourceCode, origin);
|
|
auto result = v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
|
delete[] buffer;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::LoadModule(NativeValue* str, const std::string& fileName)
|
|
{
|
|
v8::Local<v8::Value> value = *str;
|
|
auto source = value.As<v8::String>();
|
|
if (source.IsEmpty() || fileName.empty()) {
|
|
isolate_->ThrowException(
|
|
v8::String::NewFromUtf8(isolate_, "Invalid input parameter", v8::NewStringType::kNormal).ToLocalChecked());
|
|
return nullptr;
|
|
}
|
|
|
|
v8::ScriptOrigin origin(v8::String::NewFromUtf8(isolate_, fileName.c_str()).ToLocalChecked(),
|
|
v8::Local<v8::Integer>(), v8::Local<v8::Integer>(), v8::Local<v8::Boolean>(),
|
|
v8::Local<v8::Integer>(), v8::Local<v8::Value>(), v8::Local<v8::Boolean>(),
|
|
v8::Local<v8::Boolean>(), True(isolate_));
|
|
v8::ScriptCompiler::Source moduleSource(source, origin);
|
|
v8::Local<v8::Module> module = v8::ScriptCompiler::CompileModule(isolate_, &moduleSource).ToLocalChecked();
|
|
|
|
auto context = context_.Get(isolate_);
|
|
if (!module->InstantiateModule(context, ModuleResolveCallback).FromJust()) {
|
|
return nullptr;
|
|
}
|
|
auto maybeEvaluate = module->Evaluate(context);
|
|
v8::Local<v8::Value> evaluate;
|
|
if (!maybeEvaluate.ToLocal(&evaluate)) {
|
|
return nullptr;
|
|
}
|
|
|
|
v8::Local<v8::Value> moduleNameSpace = module->GetModuleNamespace();
|
|
v8::Local<v8::Object> nameSpaceObject = moduleNameSpace->ToObject(context).ToLocalChecked();
|
|
auto exportObj = nameSpaceObject->Get(context, v8::String::NewFromUtf8(isolate_, "default").ToLocalChecked());
|
|
v8::Local<v8::Value> result;
|
|
if (!exportObj.ToLocal(&result)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// can use return V8ValueToNativeValue(this, result) ?
|
|
return new V8NativeObject(this, result);
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::DefineClass(const char* name,
|
|
NativeCallback callback,
|
|
void* data,
|
|
const NativePropertyDescriptor* properties,
|
|
size_t length)
|
|
{
|
|
auto classConstructor = new V8NativeFunction(this, name, 0, callback, data);
|
|
if (classConstructor == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto classPrototype = new V8NativeObject(this);
|
|
if (classPrototype == nullptr) {
|
|
delete classConstructor;
|
|
return nullptr;
|
|
}
|
|
|
|
classConstructor->SetProperty("prototype", classPrototype);
|
|
|
|
for (size_t i = 0; i < length; i++) {
|
|
if (properties[i].attributes & NATIVE_STATIC) {
|
|
classConstructor->DefineProperty(properties[i]);
|
|
} else {
|
|
classPrototype->DefineProperty(properties[i]);
|
|
}
|
|
}
|
|
|
|
return classConstructor;
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::CreateInstance(NativeValue* constructor, NativeValue* const* argv, size_t argc)
|
|
{
|
|
v8::Local<v8::Object> value = *constructor;
|
|
v8::Local<v8::Value>* args = new v8::Local<v8::Value>[argc];
|
|
for (size_t i = 0; i < argc && args != nullptr; i++) {
|
|
args[i] = *argv[i];
|
|
}
|
|
|
|
v8::TryCatch tryCatch(isolate_);
|
|
v8::MaybeLocal<v8::Value> maybeInstance = value->CallAsConstructor(context_.Get(isolate_), argc, args);
|
|
delete[] args;
|
|
|
|
v8::Local<v8::Value> result;
|
|
if (maybeInstance.IsEmpty()) {
|
|
result = v8::Undefined(isolate_);
|
|
} else {
|
|
result = maybeInstance.ToLocalChecked();
|
|
}
|
|
|
|
return V8ValueToNativeValue(this, result);
|
|
}
|
|
|
|
NativeReference* V8NativeEngine::CreateReference(NativeValue* value, uint32_t initialRefcount)
|
|
{
|
|
return new V8NativeReference(this, value, initialRefcount, false);
|
|
}
|
|
|
|
bool V8NativeEngine::Throw(NativeValue* error)
|
|
{
|
|
isolate_->ThrowException(*error);
|
|
lastException_ = error;
|
|
return true;
|
|
}
|
|
|
|
bool V8NativeEngine::Throw(NativeErrorType type, const char* code, const char* message)
|
|
{
|
|
v8::Local<v8::Value> error;
|
|
|
|
switch (type) {
|
|
case NATIVE_COMMON_ERROR:
|
|
error = v8::Exception::Error(v8::String::NewFromUtf8(isolate_, message).ToLocalChecked());
|
|
break;
|
|
case NATIVE_TYPE_ERROR:
|
|
error = v8::Exception::TypeError(v8::String::NewFromUtf8(isolate_, message).ToLocalChecked());
|
|
break;
|
|
case NATIVE_RANGE_ERROR:
|
|
error = v8::Exception::RangeError(v8::String::NewFromUtf8(isolate_, message).ToLocalChecked());
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
if (code) {
|
|
v8::Local<v8::Value> codeKey = v8::String::NewFromUtf8(isolate_, "code").ToLocalChecked();
|
|
v8::Local<v8::Value> codeValue = v8::String::NewFromUtf8(isolate_, code).ToLocalChecked();
|
|
error.As<v8::Object>()->Set(context_.Get(isolate_), codeKey, codeValue).FromJust();
|
|
}
|
|
|
|
isolate_->ThrowException(error);
|
|
lastException_ = V8ValueToNativeValue(this, error);
|
|
return true;
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::V8ValueToNativeValue(V8NativeEngine* engine, v8::Local<v8::Value> value)
|
|
{
|
|
NativeValue* result = nullptr;
|
|
if (value->IsNull() || value->IsUndefined() || value->IsSymbol() || value->IsPromise()) {
|
|
result = new V8NativeValue(engine, value);
|
|
} else if (value->IsNumber()) {
|
|
result = new V8NativeNumber(engine, value);
|
|
} else if (value->IsString()) {
|
|
result = new V8NativeString(engine, value);
|
|
} else if (value->IsArray()) {
|
|
result = new V8NativeArray(engine, value);
|
|
} else if (value->IsFunction()) {
|
|
result = new V8NativeFunction(engine, value);
|
|
} else if (value->IsArrayBuffer()) {
|
|
result = new V8NativeArrayBuffer(engine, value);
|
|
} else if (value->IsDataView()) {
|
|
result = new V8NativeDataView(engine, value);
|
|
} else if (value->IsTypedArray()) {
|
|
result = new V8NativeTypedArray(engine, value);
|
|
} else if (value->IsExternal()) {
|
|
result = new V8NativeExternal(engine, value);
|
|
} else if (value->IsObject()) {
|
|
result = new V8NativeObject(engine, value);
|
|
} else if (value->IsBoolean()) {
|
|
result = new V8NativeBoolean(engine, value);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void* V8NativeEngine::CreateRuntime()
|
|
{
|
|
v8::Isolate::CreateParams createParams;
|
|
createParams.array_buffer_allocator = isolate_->GetArrayBufferAllocator();
|
|
v8::Isolate* isolate = v8::Isolate::New(createParams);
|
|
v8::HandleScope handleScope(isolate);
|
|
v8::Isolate::Scope isolateScope(isolate);
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
v8::Persistent<v8::Context> persistContext;
|
|
persistContext.Reset(isolate, context);
|
|
if (context.IsEmpty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
V8NativeEngine* v8Engine = new V8NativeEngine(platform_, isolate, persistContext, jsEngine_);
|
|
v8Engine->MarkAutoDispose();
|
|
return reinterpret_cast<void*>(v8Engine);
|
|
}
|
|
|
|
class Serializer {
|
|
public:
|
|
explicit Serializer(v8::Isolate* isolate) : isolate_(isolate), v8Serializer_(isolate, nullptr) {}
|
|
~Serializer() = default;
|
|
|
|
bool SerializeValue(v8::Local<v8::Value> value, v8::Local<v8::Value> transfer)
|
|
{
|
|
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
|
|
bool ok = false;
|
|
DCHECK(!data_);
|
|
data_.reset(new SerializationData);
|
|
|
|
// check transfer list is right
|
|
if (!CheckTransferReliability(transfer)) {
|
|
return false;
|
|
}
|
|
|
|
// serial value
|
|
v8Serializer_.WriteHeader();
|
|
if (!v8Serializer_.WriteValue(context, value).To(&ok)) {
|
|
data_.reset();
|
|
return false;
|
|
}
|
|
|
|
// releasing Data Control Rights
|
|
if (!DetachTransfer()) {
|
|
data_.reset();
|
|
return false;
|
|
}
|
|
|
|
std::pair<uint8_t*, size_t> pair = v8Serializer_.Release();
|
|
data_->data_.reset(pair.first);
|
|
data_->size_ = pair.second;
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<SerializationData> Release()
|
|
{
|
|
return std::move(data_);
|
|
}
|
|
|
|
private:
|
|
bool CheckTransferReliability(v8::Local<v8::Value> transfer)
|
|
{
|
|
if (transfer->IsUndefined()) {
|
|
return true;
|
|
}
|
|
if (!transfer->IsArray()) {
|
|
std::string msg = "Transfer list must be an Array or undefined";
|
|
isolate_->ThrowException(
|
|
v8::String::NewFromUtf8(isolate_, msg.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
|
|
return false;
|
|
}
|
|
|
|
v8::Local<v8::Array> transferArray = v8::Local<v8::Array>::Cast(transfer);
|
|
uint32_t length = transferArray->Length();
|
|
uint32_t arrayBufferIdx = 0;
|
|
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
|
|
for (uint32_t i = 0; i < length; ++i) {
|
|
v8::Local<v8::Value> element;
|
|
if (transferArray->Get(context, i).ToLocal(&element)) {
|
|
if (!element->IsArrayBuffer()) {
|
|
std::string msg = "Transfer array elements must be an ArrayBuffer";
|
|
isolate_->ThrowException(
|
|
v8::String::NewFromUtf8(isolate_, msg.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
|
|
return false;
|
|
}
|
|
|
|
v8::Local<v8::ArrayBuffer> arrayBuffer = v8::Local<v8::ArrayBuffer>::Cast(element);
|
|
auto iter = std::find(visitedTransfer_.begin(), visitedTransfer_.end(), arrayBuffer);
|
|
if (iter != visitedTransfer_.end()) {
|
|
std::string msg = "ArrayBuffer occurs in the transfer array more than once";
|
|
isolate_->ThrowException(
|
|
v8::String::NewFromUtf8(isolate_, msg.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
|
|
return false;
|
|
}
|
|
|
|
v8Serializer_.TransferArrayBuffer(arrayBufferIdx++, arrayBuffer);
|
|
visitedTransfer_.emplace_back(isolate_, arrayBuffer);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DetachTransfer()
|
|
{
|
|
for (const auto& item : visitedTransfer_) {
|
|
v8::Local<v8::ArrayBuffer> arrayBuffer = v8::Local<v8::ArrayBuffer>::New(isolate_, item);
|
|
if (!arrayBuffer->IsDetachable()) {
|
|
std::string msg = "ArrayBuffer could not be transferred";
|
|
isolate_->ThrowException(
|
|
v8::String::NewFromUtf8(isolate_, msg.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
|
|
return false;
|
|
}
|
|
|
|
auto backingStore = arrayBuffer->GetBackingStore();
|
|
data_->backingStores_.push_back(std::move(backingStore));
|
|
arrayBuffer->Detach();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
v8::Isolate* isolate_ {nullptr};
|
|
v8::ValueSerializer v8Serializer_;
|
|
std::unique_ptr<SerializationData> data_;
|
|
std::vector<v8::Global<v8::ArrayBuffer>> visitedTransfer_;
|
|
std::vector<std::unique_ptr<v8::BackingStore>> backingStores_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Serializer);
|
|
};
|
|
|
|
class Deserializer {
|
|
public:
|
|
explicit Deserializer(v8::Isolate* isolate, std::unique_ptr<SerializationData> data)
|
|
: isolate_(isolate), v8Deserializer_(isolate, data->GetData(), data->GetSize(), nullptr), data_(std::move(data))
|
|
{
|
|
v8Deserializer_.SetSupportsLegacyWireFormat(true);
|
|
}
|
|
~Deserializer() = default;
|
|
|
|
v8::MaybeLocal<v8::Value> DeserializeValue()
|
|
{
|
|
v8::Local<v8::Context> context = isolate_->GetCurrentContext();
|
|
bool readResult = false;
|
|
if (!v8Deserializer_.ReadHeader(context).To(&readResult)) {
|
|
return v8::MaybeLocal<v8::Value>();
|
|
}
|
|
|
|
uint32_t index = 0;
|
|
for (const auto& backingStore : data_->GetBackingStores()) {
|
|
v8::Local<v8::ArrayBuffer> arrayBuffer = v8::ArrayBuffer::New(isolate_, std::move(backingStore));
|
|
v8Deserializer_.TransferArrayBuffer(index++, arrayBuffer);
|
|
}
|
|
|
|
return v8Deserializer_.ReadValue(context);
|
|
}
|
|
|
|
private:
|
|
v8::Isolate* isolate_ {nullptr};
|
|
v8::ValueDeserializer v8Deserializer_;
|
|
std::unique_ptr<SerializationData> data_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Deserializer);
|
|
};
|
|
|
|
NativeValue* V8NativeEngine::Serialize(NativeEngine* context, NativeValue* value, NativeValue* transfer)
|
|
{
|
|
v8::Isolate* isolate = reinterpret_cast<V8NativeEngine*>(context)->GetIsolate();
|
|
v8::Local<v8::Value> v8Value = *value;
|
|
v8::Local<v8::Value> v8Transfer = *transfer;
|
|
Serializer serializer(isolate);
|
|
std::unique_ptr<SerializationData> data;
|
|
if (serializer.SerializeValue(v8Value, v8Transfer)) {
|
|
data = serializer.Release();
|
|
}
|
|
return reinterpret_cast<NativeValue*>(data.release());
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::Deserialize(NativeEngine* context, NativeValue* recorder)
|
|
{
|
|
v8::Isolate* isolate = reinterpret_cast<V8NativeEngine*>(context)->GetIsolate();
|
|
std::unique_ptr<SerializationData> data(reinterpret_cast<SerializationData*>(recorder));
|
|
Deserializer deserializer(isolate, std::move(data));
|
|
v8::MaybeLocal<v8::Value> result = deserializer.DeserializeValue();
|
|
return V8ValueToNativeValue(this, result.ToLocalChecked());
|
|
}
|
|
|
|
void V8NativeEngine::DeleteSerializationData(NativeValue* value) const
|
|
{
|
|
SerializationData* data = reinterpret_cast<SerializationData*>(value);
|
|
delete data;
|
|
}
|
|
|
|
void V8NativeEngine::SetPackagePath(const std::string& packagePath)
|
|
{
|
|
auto moduleManager = NativeModuleManager::GetInstance();
|
|
if (moduleManager) {
|
|
moduleManager->SetAppLibPath(packagePath.c_str());
|
|
}
|
|
}
|
|
|
|
// Extracts a C string from a V8 Utf8Value.
|
|
const char* ToCString(const v8::String::Utf8Value& value)
|
|
{
|
|
return *value ? *value : "<string conversion failed>";
|
|
}
|
|
|
|
ExceptionInfo* V8NativeEngine::GetExceptionForWorker() const
|
|
{
|
|
DCHECK(tryCatch_.HasCaught());
|
|
v8::HandleScope handle_scope(isolate_);
|
|
|
|
ExceptionInfo* exceptionInfo = new ExceptionInfo();
|
|
v8::String::Utf8Value exception(isolate_, tryCatch_.Exception());
|
|
const char* exceptionString = ToCString(exception);
|
|
char* exceptionMessage = new char[strlen(exceptionString) + 1] { 0 };
|
|
if (memcpy_s(exceptionMessage, strlen(exceptionString) + 1, exceptionString, strlen(exceptionString)) != EOK) {
|
|
HILOG_INFO("worker:: memcpy_s error");
|
|
delete exceptionInfo;
|
|
delete[] exceptionMessage;
|
|
return nullptr;
|
|
}
|
|
exceptionInfo->message_ = exceptionMessage;
|
|
|
|
v8::Local<v8::Context> context = context_.Get(isolate_);
|
|
v8::Context::Scope contextScope(context);
|
|
v8::Local<v8::Message> message = tryCatch_.Message();
|
|
if (!message.IsEmpty()) {
|
|
int32_t lineno = message->GetLineNumber(context).FromJust();
|
|
exceptionInfo->lineno_ = lineno;
|
|
|
|
int32_t colno = message->GetStartColumn(context).FromJust();
|
|
exceptionInfo->colno_ = colno;
|
|
}
|
|
return exceptionInfo;
|
|
}
|
|
|
|
NativeValue* V8NativeEngine::ValueToNativeValue(JSValueWrapper& value)
|
|
{
|
|
v8::Local<v8::Value> v8Value = value;
|
|
return V8ValueToNativeValue(this, v8Value);
|
|
}
|