1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "v8_native_engine.h"
17
18 #include <js_native_api.h>
19
20 #include "native_engine/native_property.h"
21 #include "native_value/v8_native_array.h"
22 #include "native_value/v8_native_array_buffer.h"
23 #include "native_value/v8_native_boolean.h"
24 #include "native_value/v8_native_data_view.h"
25 #include "native_value/v8_native_external.h"
26 #include "native_value/v8_native_function.h"
27 #include "native_value/v8_native_number.h"
28 #include "native_value/v8_native_object.h"
29 #include "native_value/v8_native_string.h"
30 #include "native_value/v8_native_typed_array.h"
31 #include "securec.h"
32 #include "utils/log.h"
33 #include "v8_native_deferred.h"
34 #include "v8_native_reference.h"
35
36
37 static thread_local V8NativeEngine* g_env = nullptr;
38
V8NativeEngine(v8::Platform * platform,v8::Isolate * isolate,v8::Persistent<v8::Context> & context,void * jsEngine)39 V8NativeEngine::V8NativeEngine(v8::Platform* platform, v8::Isolate* isolate,
40 v8::Persistent<v8::Context>& context, void* jsEngine)
41 : NativeEngine(jsEngine),
42 platform_(platform),
43 isolate_(isolate),
44 context_(isolate, context),
45 isolateScope_(isolate),
46 handleScope_(isolate_),
47 contextScope_(context.Get(isolate_)),
48 tryCatch_(isolate_)
49 {
50 g_env = this;
51 v8::Local<v8::String> requireNapiName = v8::String::NewFromUtf8(isolate_, "requireNapi").ToLocalChecked();
52 v8::Local<v8::String> requireInternalName = v8::String::NewFromUtf8(isolate_, "requireInternal").ToLocalChecked();
53
54 v8::Local<v8::Value> requireData = v8::External::New(isolate_, (void*)this).As<v8::Value>();
55
56 v8::Local<v8::Function> requireNapi =
57 v8::Function::New(
58 context_.Get(isolate_),
59 [](const v8::FunctionCallbackInfo<v8::Value>& info) {
60 v8::Isolate::Scope isolateScope(info.GetIsolate());
61 v8::HandleScope handleScope(info.GetIsolate());
62
63 V8NativeEngine* engine = (V8NativeEngine*)info.Data().As<v8::External>()->Value();
64 if (engine == nullptr) {
65 return;
66 }
67 v8::String::Utf8Value moduleName(info.GetIsolate(), info[0]);
68 NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
69 if (moduleManager == nullptr) {
70 return;
71 }
72
73 bool isAppModule = false;
74 if (info.Length() == 2) {
75 isAppModule = info[1]->ToBoolean(info.GetIsolate())->Value();
76 }
77 NativeModule* module = moduleManager->LoadNativeModule(*moduleName, nullptr, isAppModule);
78
79 if (module == nullptr) {
80 return;
81 }
82
83 if (module->jsCode != nullptr) {
84 HILOG_INFO("load js code");
85 NativeValue* script = engine->CreateString(module->jsCode, module->jsCodeLen);
86 NativeValue* exportObject = engine->LoadModule(script, "jsnapi.js");
87 if (exportObject == nullptr) {
88 HILOG_ERROR("load module failed");
89 return;
90 }
91 v8::Local<v8::Object> exports = *exportObject;
92 info.GetReturnValue().Set(exports);
93 HILOG_ERROR("load module succ");
94 } else if (module->registerCallback != nullptr) {
95 HILOG_INFO("load napi module");
96 NativeValue* exportObject = new V8NativeObject(engine);
97 if (exportObject == nullptr) {
98 return;
99 }
100 module->registerCallback(engine, exportObject);
101 v8::Local<v8::Object> exports = *exportObject;
102 info.GetReturnValue().Set(exports);
103 }
104 },
105 requireData, 1)
106 .ToLocalChecked();
107
108 v8::Local<v8::Function> requireInternal =
109 v8::Function::New(
110 context_.Get(isolate_),
111 [](const v8::FunctionCallbackInfo<v8::Value>& info) {
112 v8::Isolate::Scope isolateScope(info.GetIsolate());
113 v8::HandleScope handleScope(info.GetIsolate());
114
115 V8NativeEngine* engine = (V8NativeEngine*)info.Data().As<v8::External>()->Value();
116 if (engine == nullptr) {
117 return;
118 }
119 v8::String::Utf8Value moduleName(info.GetIsolate(), info[0]);
120 NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
121 if (moduleManager == nullptr) {
122 return;
123 }
124 NativeModule* module = moduleManager->LoadNativeModule(*moduleName, nullptr, false, true);
125 if (module == nullptr) {
126 return;
127 }
128 NativeValue* exportObject = new V8NativeObject(engine);
129 if (exportObject == nullptr) {
130 return;
131 }
132 module->registerCallback(engine, exportObject);
133 v8::Local<v8::Object> exports = *exportObject;
134 info.GetReturnValue().Set(exports);
135 },
136 requireData, 1)
137 .ToLocalChecked();
138
139 v8::Local<v8::Object> global = context_.Get(isolate_)->Global();
140
141 global->Set(context_.Get(isolate_), requireNapiName, requireNapi).FromJust();
142 global->Set(context_.Get(isolate_), requireInternalName, requireInternal).FromJust();
143 // need to call init of base class.
144 Init();
145 }
146
~V8NativeEngine()147 V8NativeEngine::~V8NativeEngine()
148 {
149 if (promiseRejectCallbackRef_ != nullptr) {
150 delete promiseRejectCallbackRef_;
151 }
152 if (checkCallbackRef_ != nullptr) {
153 delete checkCallbackRef_;
154 }
155 // need to call deinit before base class.
156 Deinit();
157 }
158
GetModuleFromName(const std::string & moduleName,bool isAppModule,const std::string & id,const std::string & param,const std::string & instanceName,void ** instance)159 v8::Local<v8::Object> V8NativeEngine::GetModuleFromName(
160 const std::string& moduleName, bool isAppModule, const std::string& id, const std::string& param,
161 const std::string& instanceName, void** instance)
162 {
163 v8::Isolate* isolate = this->GetContext()->GetIsolate();
164 v8::HandleScope handleScope(isolate);
165
166 v8::Local<v8::Object> exports;
167 NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
168 NativeModule* module = moduleManager->LoadNativeModule(moduleName.c_str(), nullptr, isAppModule);
169 if (module != nullptr) {
170 NativeValue* idValue = new V8NativeString(this, id.c_str(), id.size());
171 NativeValue* paramValue = new V8NativeString(this, param.c_str(), param.size());
172 NativeValue* exportObject = new V8NativeObject(this);
173
174 NativePropertyDescriptor idProperty, paramProperty;
175 idProperty.utf8name = "id";
176 idProperty.value = idValue;
177 paramProperty.utf8name = "param";
178 paramProperty.value = paramValue;
179 V8NativeObject* exportObj = reinterpret_cast<V8NativeObject*>(exportObject);
180 exportObj->DefineProperty(idProperty);
181 exportObj->DefineProperty(paramProperty);
182 module->registerCallback(this, exportObject);
183
184 napi_value nExport = reinterpret_cast<napi_value>(exportObject);
185 napi_value exportInstance = nullptr;
186 napi_status status = napi_get_named_property(
187 reinterpret_cast<napi_env>(this), nExport, instanceName.c_str(), &exportInstance);
188 if (status != napi_ok) {
189 HILOG_ERROR("GetModuleFromName napi_get_named_property status != napi_ok");
190 }
191
192 status = napi_unwrap(reinterpret_cast<napi_env>(this), exportInstance, reinterpret_cast<void**>(instance));
193 if (status != napi_ok) {
194 HILOG_ERROR("GetModuleFromName napi_unwrap status != napi_ok");
195 }
196
197 exports = *exportObject;
198 }
199 return exports;
200 }
201
LoadModuleByName(const std::string & moduleName,bool isAppModule,const std::string & param,const std::string & instanceName,void * instance)202 v8::Local<v8::Object> V8NativeEngine::LoadModuleByName(
203 const std::string& moduleName, bool isAppModule, const std::string& param,
204 const std::string& instanceName, void* instance)
205 {
206 v8::Isolate* isolate = this->GetContext()->GetIsolate();
207 v8::HandleScope handleScope(isolate);
208
209 v8::Local<v8::Object> exports;
210 NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
211 NativeModule* module = moduleManager->LoadNativeModule(moduleName.c_str(), nullptr, isAppModule);
212 if (module != nullptr) {
213 NativeValue* exportObject = new V8NativeObject(this);
214 V8NativeObject* exportObj = reinterpret_cast<V8NativeObject*>(exportObject);
215
216 NativePropertyDescriptor paramProperty, instanceProperty;
217
218 NativeValue* paramValue = new V8NativeString(this, param.c_str(), param.size());
219 paramProperty.utf8name = "param";
220 paramProperty.value = paramValue;
221
222 V8NativeObject* instanceValue = new V8NativeObject(this);
223 instanceValue->SetNativePointer(instance, nullptr, nullptr);
224 instanceProperty.utf8name = instanceName.c_str();
225 instanceProperty.value = instanceValue;
226
227 exportObj->DefineProperty(paramProperty);
228 exportObj->DefineProperty(instanceProperty);
229
230 module->registerCallback(this, exportObject);
231 exports = *exportObject;
232 }
233 return exports;
234 }
235
GetIsolate()236 v8::Isolate* V8NativeEngine::GetIsolate()
237 {
238 return isolate_;
239 }
240
GetContext()241 v8::Local<v8::Context> V8NativeEngine::GetContext()
242 {
243 return *reinterpret_cast<v8::Local<v8::Context>*>(const_cast<v8::Global<v8::Context>*>(&context_));
244 }
245
Loop(LoopMode mode,bool needSync)246 void V8NativeEngine::Loop(LoopMode mode, bool needSync)
247 {
248 NativeEngine::Loop(mode, needSync);
249 v8::platform::PumpMessageLoop(platform_, isolate_);
250 }
251
GetGlobal()252 NativeValue* V8NativeEngine::GetGlobal()
253 {
254 v8::Local<v8::Object> value = context_.Get(isolate_)->Global();
255 return V8ValueToNativeValue(this, value);
256 }
257
CreateNull()258 NativeValue* V8NativeEngine::CreateNull()
259 {
260 v8::Local<v8::Primitive> value = v8::Null(isolate_);
261 return new V8NativeValue(this, value);
262 }
263
CreateUndefined()264 NativeValue* V8NativeEngine::CreateUndefined()
265 {
266 v8::Local<v8::Primitive> value = v8::Undefined(isolate_);
267 return new V8NativeValue(this, value);
268 }
269
CreateBoolean(bool value)270 NativeValue* V8NativeEngine::CreateBoolean(bool value)
271 {
272 return new V8NativeBoolean(this, value);
273 }
274
CreateNumber(int32_t value)275 NativeValue* V8NativeEngine::CreateNumber(int32_t value)
276 {
277 return new V8NativeNumber(this, value);
278 }
279
CreateNumber(uint32_t value)280 NativeValue* V8NativeEngine::CreateNumber(uint32_t value)
281 {
282 return new V8NativeNumber(this, value);
283 }
284
CreateNumber(int64_t value)285 NativeValue* V8NativeEngine::CreateNumber(int64_t value)
286 {
287 return new V8NativeNumber(this, value);
288 }
289
CreateNumber(double value)290 NativeValue* V8NativeEngine::CreateNumber(double value)
291 {
292 return new V8NativeNumber(this, value);
293 }
294
CreateString(const char * value,size_t length)295 NativeValue* V8NativeEngine::CreateString(const char* value, size_t length)
296 {
297 return new V8NativeString(this, value, length);
298 }
299
CreateSymbol(NativeValue * value)300 NativeValue* V8NativeEngine::CreateSymbol(NativeValue* value)
301 {
302 return new V8NativeValue(this, v8::Symbol::New(isolate_, *value));
303 }
304
CreateExternal(void * value,NativeFinalize callback,void * hint,size_t nativeBindingSize)305 NativeValue* V8NativeEngine::CreateExternal(void* value, NativeFinalize callback, void* hint,
306 [[maybe_unused]] size_t nativeBindingSize)
307 {
308 return new V8NativeExternal(this, value, callback, hint);
309 }
310
CreateObject()311 NativeValue* V8NativeEngine::CreateObject()
312 {
313 return new V8NativeObject(this);
314 }
315
CreateNativeBindingObject(void * detach,void * attach)316 NativeValue* V8NativeEngine::CreateNativeBindingObject(void* detach, void* attach)
317 {
318 return nullptr;
319 }
320
CreateFunction(const char * name,size_t length,NativeCallback cb,void * value)321 NativeValue* V8NativeEngine::CreateFunction(const char* name, size_t length, NativeCallback cb, void* value)
322 {
323 return new V8NativeFunction(this, name, length, cb, value);
324 }
325
CreateArray(size_t length)326 NativeValue* V8NativeEngine::CreateArray(size_t length)
327 {
328 return new V8NativeArray(this, length);
329 }
330
CreateArrayBuffer(void ** value,size_t length)331 NativeValue* V8NativeEngine::CreateArrayBuffer(void** value, size_t length)
332 {
333 return new V8NativeArrayBuffer(this, (uint8_t**)value, length);
334 }
335
CreateArrayBufferExternal(void * value,size_t length,NativeFinalize cb,void * hint)336 NativeValue* V8NativeEngine::CreateArrayBufferExternal(void* value, size_t length, NativeFinalize cb, void* hint)
337 {
338 return new V8NativeArrayBuffer(this, (uint8_t*)value, length, cb, hint);
339 }
340
CreateTypedArray(NativeTypedArrayType type,NativeValue * value,size_t length,size_t offset)341 NativeValue* V8NativeEngine::CreateTypedArray(NativeTypedArrayType type,
342 NativeValue* value,
343 size_t length,
344 size_t offset)
345 {
346 v8::Local<v8::ArrayBuffer> buffer = *value;
347 v8::Local<v8::TypedArray> typedArray;
348
349 switch (type) {
350 case NATIVE_INT8_ARRAY:
351 typedArray = v8::Int8Array::New(buffer, offset, length);
352 break;
353 case NATIVE_UINT8_ARRAY:
354 typedArray = v8::Uint8Array::New(buffer, offset, length);
355 break;
356 case NATIVE_UINT8_CLAMPED_ARRAY:
357 typedArray = v8::Uint8ClampedArray::New(buffer, offset, length);
358 break;
359 case NATIVE_INT16_ARRAY:
360 typedArray = v8::Int16Array::New(buffer, offset, length);
361 break;
362 case NATIVE_UINT16_ARRAY:
363 typedArray = v8::Uint16Array::New(buffer, offset, length);
364 break;
365 case NATIVE_INT32_ARRAY:
366 typedArray = v8::Int32Array::New(buffer, offset, length);
367 break;
368 case NATIVE_UINT32_ARRAY:
369 typedArray = v8::Uint32Array::New(buffer, offset, length);
370 break;
371 case NATIVE_FLOAT32_ARRAY:
372 typedArray = v8::Float32Array::New(buffer, offset, length);
373 break;
374 case NATIVE_FLOAT64_ARRAY:
375 typedArray = v8::Float64Array::New(buffer, offset, length);
376 break;
377 case NATIVE_BIGINT64_ARRAY:
378 typedArray = v8::BigInt64Array::New(buffer, offset, length);
379 break;
380 case NATIVE_BIGUINT64_ARRAY:
381 typedArray = v8::BigUint64Array::New(buffer, offset, length);
382 break;
383 default:
384 return nullptr;
385 }
386 return new V8NativeTypedArray(this, typedArray);
387 }
388
389 struct CompleteWrapData {
390 NativeAsyncExecuteCallback execute_ = nullptr;
391 NativeAsyncCompleteCallback complete_ = nullptr;
392 void* data_ = nullptr;
393 v8::Isolate* isolate_ = nullptr;
394 };
395
ExecuteWrap(NativeEngine * engine,void * data)396 void V8NativeEngine::ExecuteWrap(NativeEngine* engine, void* data)
397 {
398 CompleteWrapData* wrapData = (CompleteWrapData*)data;
399 wrapData->execute_(engine, wrapData->data_);
400 }
401
CompleteWrap(NativeEngine * engine,int status,void * data)402 void V8NativeEngine::CompleteWrap(NativeEngine* engine, int status, void* data)
403 {
404 CompleteWrapData* wrapData = (CompleteWrapData*)data;
405 v8::Isolate::Scope isolateScope(wrapData->isolate_);
406 v8::HandleScope handleScope(wrapData->isolate_);
407 wrapData->complete_(engine, status, wrapData->data_);
408 delete wrapData;
409 }
410
CreateAsyncWork(NativeValue * asyncResource,NativeValue * asyncResourceName,NativeAsyncExecuteCallback execute,NativeAsyncCompleteCallback complete,void * data)411 NativeAsyncWork* V8NativeEngine::CreateAsyncWork(NativeValue* asyncResource, NativeValue* asyncResourceName,
412 NativeAsyncExecuteCallback execute, NativeAsyncCompleteCallback complete, void* data)
413 {
414 CompleteWrapData* wrapData = new CompleteWrapData();
415 if (wrapData == nullptr) {
416 HILOG_ERROR("create wrap data failed");
417 return nullptr;
418 }
419 wrapData->execute_ = execute;
420 wrapData->complete_ = complete;
421 wrapData->data_ = data;
422 wrapData->isolate_ = GetIsolate();
423
424 return NativeEngine::CreateAsyncWork(asyncResource, asyncResourceName, ExecuteWrap, CompleteWrap, (void*)wrapData);
425 }
426
CreateDataView(NativeValue * value,size_t length,size_t offset)427 NativeValue* V8NativeEngine::CreateDataView(NativeValue* value, size_t length, size_t offset)
428 {
429 return new V8NativeDataView(this, value, length, offset);
430 }
431
CreatePromise(NativeDeferred ** deferred)432 NativeValue* V8NativeEngine::CreatePromise(NativeDeferred** deferred)
433 {
434 auto v8Resolver = v8::Promise::Resolver::New(context_.Get(isolate_)).ToLocalChecked();
435
436 *deferred = new V8NativeDeferred(this, v8Resolver);
437
438 return new V8NativeValue(this, v8Resolver->GetPromise());
439 }
440
CreateError(NativeValue * code,NativeValue * message)441 NativeValue* V8NativeEngine::CreateError(NativeValue* code, NativeValue* message)
442 {
443 v8::Local<v8::Value> errorObj = v8::Exception::Error(*message);
444 if (code) {
445 v8::Local<v8::Value> codeKey = v8::String::NewFromUtf8(isolate_, "code").ToLocalChecked();
446 errorObj.As<v8::Object>()->Set(context_.Get(isolate_), codeKey, *code).FromJust();
447 }
448 return V8ValueToNativeValue(this, errorObj);
449 }
450
CallFunction(NativeValue * thisVar,NativeValue * function,NativeValue * const * argv,size_t argc)451 NativeValue* V8NativeEngine::CallFunction(NativeValue* thisVar,
452 NativeValue* function,
453 NativeValue* const* argv,
454 size_t argc)
455 {
456 if (function == nullptr) {
457 return nullptr;
458 }
459 v8::Local<v8::Value> v8recv = (thisVar != nullptr) ? *thisVar : v8::Undefined(isolate_);
460 v8::Local<v8::Function> v8func = *function;
461 v8::Local<v8::Value>* args = nullptr;
462 v8::Local<v8::Context> context = context_.Get(isolate_);
463 if (argc > 0) {
464 args = new v8::Local<v8::Value>[argc];
465 for (size_t i = 0; i < argc && args != nullptr; i++) {
466 if (argv[i] != nullptr) {
467 args[i] = *argv[i];
468 } else {
469 args[i] = v8::Undefined(isolate_);
470 }
471 }
472 }
473 v8::MaybeLocal<v8::Value> maybeValue = v8func->Call(context, v8recv, argc, args);
474 if (args != nullptr) {
475 delete []args;
476 }
477 v8::Local<v8::Value> result;
478 if (!maybeValue.ToLocal(&result)) {
479 return nullptr;
480 }
481 return V8ValueToNativeValue(this, result);
482 }
483
RunScript(NativeValue * script)484 NativeValue* V8NativeEngine::RunScript(NativeValue* script)
485 {
486 v8::Local<v8::Value> v8Script = *script;
487 auto maybeScript = v8::Script::Compile(context_.Get(isolate_), v8Script.As<v8::String>());
488 auto localScript = maybeScript.ToLocalChecked();
489 auto scriptResult = localScript->Run(context_.Get(isolate_));
490
491 v8::Local<v8::Value> result;
492 if (!scriptResult.ToLocal(&result)) {
493 return nullptr;
494 }
495
496 return V8ValueToNativeValue(this, result);
497 }
498
RunBufferScript(std::vector<uint8_t> & buffer)499 NativeValue* V8NativeEngine::RunBufferScript(std::vector<uint8_t>& buffer)
500 {
501 NativeValue* script = CreateString(reinterpret_cast<char*>(buffer.data()), buffer.size());
502 return RunScript(script);
503 }
504
RunActor(std::vector<uint8_t> & buffer,const char * descriptor)505 NativeValue* V8NativeEngine::RunActor(std::vector<uint8_t>& buffer, const char *descriptor)
506 {
507 return RunBufferScript(buffer);
508 }
509
510 namespace {
ReadFile(v8::Isolate * isolate,const char * path)511 v8::MaybeLocal<v8::String> ReadFile(v8::Isolate* isolate, const char* path)
512 {
513 std::ifstream file(path);
514 if (file.fail()) {
515 file.close();
516 return v8::MaybeLocal<v8::String>();
517 }
518
519 std::string fileContent;
520 fileContent.clear();
521 file.seekg(0, std::ios::end);
522 fileContent.reserve(static_cast<std::string::size_type>(file.tellg()));
523 file.seekg(0, std::ios::beg);
524 fileContent.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
525 file.close();
526
527 v8::MaybeLocal<v8::String> result = v8::String::NewFromUtf8(isolate, fileContent.c_str(),
528 v8::NewStringType::kNormal, fileContent.size());
529 return result;
530 }
531
ModuleResolveCallback(v8::Local<v8::Context> context,v8::Local<v8::String> specifier,v8::Local<v8::Module> referrer)532 v8::MaybeLocal<v8::Module> ModuleResolveCallback(v8::Local<v8::Context> context,
533 v8::Local<v8::String> specifier,
534 v8::Local<v8::Module> referrer)
535 {
536 v8::Isolate* isolate = context->GetIsolate();
537 int len = specifier->Length();
538 char *buffer = new char[len + 1];
539 specifier->WriteUtf8(isolate, buffer, len, nullptr,
540 v8::String::REPLACE_INVALID_UTF8 | v8::String::NO_NULL_TERMINATION);
541 auto maybeSourceCode = ReadFile(isolate, buffer);
542 v8::Local<v8::String> sourceCode;
543 if (!maybeSourceCode.ToLocal(&sourceCode)) {
544 v8::ScriptOrigin origin(v8::String::NewFromUtf8(isolate, "moduleloader.js").ToLocalChecked(),
545 v8::Local<v8::Integer>(), v8::Local<v8::Integer>(), v8::Local<v8::Boolean>(),
546 v8::Local<v8::Integer>(), v8::Local<v8::Value>(), v8::Local<v8::Boolean>(),
547 v8::Local<v8::Boolean>(), True(isolate));
548 v8::ScriptCompiler::Source source(specifier, origin);
549 delete[] buffer;
550 return v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
551 }
552
553 v8::ScriptOrigin origin(specifier, v8::Local<v8::Integer>(), v8::Local<v8::Integer>(), v8::Local<v8::Boolean>(),
554 v8::Local<v8::Integer>(), v8::Local<v8::Value>(), v8::Local<v8::Boolean>(),
555 v8::Local<v8::Boolean>(), True(isolate));
556 v8::ScriptCompiler::Source source(sourceCode, origin);
557 auto result = v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
558 delete[] buffer;
559 return result;
560 }
561 }
562
LoadModule(NativeValue * str,const std::string & fileName)563 NativeValue* V8NativeEngine::LoadModule(NativeValue* str, const std::string& fileName)
564 {
565 v8::Local<v8::Value> value = *str;
566 auto source = value.As<v8::String>();
567 if (source.IsEmpty() || fileName.empty()) {
568 isolate_->ThrowException(
569 v8::String::NewFromUtf8(isolate_, "Invalid input parameter", v8::NewStringType::kNormal).ToLocalChecked());
570 return nullptr;
571 }
572
573 v8::ScriptOrigin origin(v8::String::NewFromUtf8(isolate_, fileName.c_str()).ToLocalChecked(),
574 v8::Local<v8::Integer>(), v8::Local<v8::Integer>(), v8::Local<v8::Boolean>(),
575 v8::Local<v8::Integer>(), v8::Local<v8::Value>(), v8::Local<v8::Boolean>(),
576 v8::Local<v8::Boolean>(), True(isolate_));
577 v8::ScriptCompiler::Source moduleSource(source, origin);
578 v8::Local<v8::Module> module = v8::ScriptCompiler::CompileModule(isolate_, &moduleSource).ToLocalChecked();
579
580 auto context = context_.Get(isolate_);
581 if (!module->InstantiateModule(context, ModuleResolveCallback).FromJust()) {
582 return nullptr;
583 }
584 auto maybeEvaluate = module->Evaluate(context);
585 v8::Local<v8::Value> evaluate;
586 if (!maybeEvaluate.ToLocal(&evaluate)) {
587 return nullptr;
588 }
589
590 v8::Local<v8::Value> moduleNameSpace = module->GetModuleNamespace();
591 v8::Local<v8::Object> nameSpaceObject = moduleNameSpace->ToObject(context).ToLocalChecked();
592 auto exportObj = nameSpaceObject->Get(context, v8::String::NewFromUtf8(isolate_, "default").ToLocalChecked());
593 v8::Local<v8::Value> result;
594 if (!exportObj.ToLocal(&result)) {
595 return nullptr;
596 }
597
598 // can use return V8ValueToNativeValue(this, result) ?
599 return new V8NativeObject(this, result);
600 }
601
DefineClass(const char * name,NativeCallback callback,void * data,const NativePropertyDescriptor * properties,size_t length)602 NativeValue* V8NativeEngine::DefineClass(const char* name,
603 NativeCallback callback,
604 void* data,
605 const NativePropertyDescriptor* properties,
606 size_t length)
607 {
608 auto classConstructor = new V8NativeFunction(this, name, 0, callback, data);
609 if (classConstructor == nullptr) {
610 return nullptr;
611 }
612
613 auto classPrototype = new V8NativeObject(this);
614 if (classPrototype == nullptr) {
615 delete classConstructor;
616 return nullptr;
617 }
618
619 classConstructor->SetProperty("prototype", classPrototype);
620
621 for (size_t i = 0; i < length; i++) {
622 if (properties[i].attributes & NATIVE_STATIC) {
623 classConstructor->DefineProperty(properties[i]);
624 } else {
625 classPrototype->DefineProperty(properties[i]);
626 }
627 }
628
629 return classConstructor;
630 }
631
CreateInstance(NativeValue * constructor,NativeValue * const * argv,size_t argc)632 NativeValue* V8NativeEngine::CreateInstance(NativeValue* constructor, NativeValue* const* argv, size_t argc)
633 {
634 v8::Local<v8::Object> value = *constructor;
635 v8::Local<v8::Value>* args = new v8::Local<v8::Value>[argc];
636 for (size_t i = 0; i < argc && args != nullptr; i++) {
637 args[i] = *argv[i];
638 }
639
640 v8::TryCatch tryCatch(isolate_);
641 v8::MaybeLocal<v8::Value> maybeInstance = value->CallAsConstructor(context_.Get(isolate_), argc, args);
642 delete[] args;
643
644 v8::Local<v8::Value> result;
645 if (maybeInstance.IsEmpty()) {
646 result = v8::Undefined(isolate_);
647 } else {
648 result = maybeInstance.ToLocalChecked();
649 }
650
651 return V8ValueToNativeValue(this, result);
652 }
653
CreateReference(NativeValue * value,uint32_t initialRefcount)654 NativeReference* V8NativeEngine::CreateReference(NativeValue* value, uint32_t initialRefcount)
655 {
656 return new V8NativeReference(this, value, initialRefcount, false);
657 }
658
Throw(NativeValue * error)659 bool V8NativeEngine::Throw(NativeValue* error)
660 {
661 isolate_->ThrowException(*error);
662 lastException_ = error;
663 return true;
664 }
665
Throw(NativeErrorType type,const char * code,const char * message)666 bool V8NativeEngine::Throw(NativeErrorType type, const char* code, const char* message)
667 {
668 v8::Local<v8::Value> error;
669
670 switch (type) {
671 case NATIVE_COMMON_ERROR:
672 error = v8::Exception::Error(v8::String::NewFromUtf8(isolate_, message).ToLocalChecked());
673 break;
674 case NATIVE_TYPE_ERROR:
675 error = v8::Exception::TypeError(v8::String::NewFromUtf8(isolate_, message).ToLocalChecked());
676 break;
677 case NATIVE_RANGE_ERROR:
678 error = v8::Exception::RangeError(v8::String::NewFromUtf8(isolate_, message).ToLocalChecked());
679 break;
680 default:
681 return false;
682 }
683 if (code) {
684 v8::Local<v8::Value> codeKey = v8::String::NewFromUtf8(isolate_, "code").ToLocalChecked();
685 v8::Local<v8::Value> codeValue = v8::String::NewFromUtf8(isolate_, code).ToLocalChecked();
686 error.As<v8::Object>()->Set(context_.Get(isolate_), codeKey, codeValue).FromJust();
687 }
688
689 isolate_->ThrowException(error);
690 lastException_ = V8ValueToNativeValue(this, error);
691 return true;
692 }
693
V8ValueToNativeValue(V8NativeEngine * engine,v8::Local<v8::Value> value)694 NativeValue* V8NativeEngine::V8ValueToNativeValue(V8NativeEngine* engine, v8::Local<v8::Value> value)
695 {
696 NativeValue* result = nullptr;
697 if (value->IsNull() || value->IsUndefined() || value->IsSymbol() || value->IsPromise()) {
698 result = new V8NativeValue(engine, value);
699 } else if (value->IsNumber()) {
700 result = new V8NativeNumber(engine, value);
701 } else if (value->IsString()) {
702 result = new V8NativeString(engine, value);
703 } else if (value->IsArray()) {
704 result = new V8NativeArray(engine, value);
705 } else if (value->IsFunction()) {
706 result = new V8NativeFunction(engine, value);
707 } else if (value->IsArrayBuffer()) {
708 result = new V8NativeArrayBuffer(engine, value);
709 } else if (value->IsDataView()) {
710 result = new V8NativeDataView(engine, value);
711 } else if (value->IsTypedArray()) {
712 result = new V8NativeTypedArray(engine, value);
713 } else if (value->IsExternal()) {
714 result = new V8NativeExternal(engine, value);
715 } else if (value->IsObject()) {
716 result = new V8NativeObject(engine, value);
717 } else if (value->IsBoolean()) {
718 result = new V8NativeBoolean(engine, value);
719 }
720 return result;
721 }
722
CreateRuntime()723 void* V8NativeEngine::CreateRuntime()
724 {
725 v8::Isolate::CreateParams createParams;
726 createParams.array_buffer_allocator = isolate_->GetArrayBufferAllocator();
727 v8::Isolate* isolate = v8::Isolate::New(createParams);
728 v8::Persistent<v8::Context> persistContext;
729 {
730 v8::HandleScope handleScope(isolate);
731 v8::Isolate::Scope isolateScope(isolate);
732 v8::Local<v8::Context> context = v8::Context::New(isolate);
733 if (context.IsEmpty()) {
734 return nullptr;
735 }
736 persistContext.Reset(isolate, context);
737 }
738
739 V8NativeEngine* v8Engine = new V8NativeEngine(platform_, isolate, persistContext, jsEngine_);
740
741 // init callback
742 v8Engine->SetInitWorkerFunc(initWorkerFunc_);
743 v8Engine->SetGetAssetFunc(getAssetFunc_);
744 v8Engine->SetOffWorkerFunc(offWorkerFunc_);
745 v8Engine->SetWorkerAsyncWorkFunc(nativeAsyncExecuteCallback_, nativeAsyncCompleteCallback_);
746
747 v8Engine->MarkAutoDispose();
748 auto cleanEnv = [isolate]() {
749 if (isolate != nullptr) {
750 HILOG_INFO("worker:: cleanEnv is called");
751 isolate->Dispose();
752 }
753 };
754 v8Engine->SetCleanEnv(cleanEnv);
755 return static_cast<void*>(v8Engine);
756 }
757
758 class Serializer {
759 public:
Serializer(v8::Isolate * isolate)760 explicit Serializer(v8::Isolate* isolate) : isolate_(isolate), v8Serializer_(isolate, nullptr) {}
761 ~Serializer() = default;
762
SerializeValue(v8::Local<v8::Value> value,v8::Local<v8::Value> transfer)763 bool SerializeValue(v8::Local<v8::Value> value, v8::Local<v8::Value> transfer)
764 {
765 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
766 bool ok = false;
767 DCHECK(!data_);
768 data_.reset(new SerializationData);
769
770 // check transfer list is right
771 if (!CheckTransferReliability(transfer)) {
772 return false;
773 }
774
775 // serial value
776 v8Serializer_.WriteHeader();
777 if (!v8Serializer_.WriteValue(context, value).To(&ok)) {
778 data_.reset();
779 return false;
780 }
781
782 // releasing Data Control Rights
783 if (!DetachTransfer()) {
784 data_.reset();
785 return false;
786 }
787
788 std::pair<uint8_t*, size_t> pair = v8Serializer_.Release();
789 data_->data_.reset(pair.first);
790 data_->size_ = pair.second;
791 return true;
792 }
793
Release()794 std::unique_ptr<SerializationData> Release()
795 {
796 return std::move(data_);
797 }
798
799 private:
CheckTransferReliability(v8::Local<v8::Value> transfer)800 bool CheckTransferReliability(v8::Local<v8::Value> transfer)
801 {
802 if (transfer->IsUndefined()) {
803 return true;
804 }
805 if (!transfer->IsArray()) {
806 std::string msg = "Transfer list must be an Array or undefined";
807 isolate_->ThrowException(
808 v8::String::NewFromUtf8(isolate_, msg.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
809 return false;
810 }
811
812 v8::Local<v8::Array> transferArray = v8::Local<v8::Array>::Cast(transfer);
813 uint32_t length = transferArray->Length();
814 uint32_t arrayBufferIdx = 0;
815 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
816 for (uint32_t i = 0; i < length; ++i) {
817 v8::Local<v8::Value> element;
818 if (transferArray->Get(context, i).ToLocal(&element)) {
819 if (!element->IsArrayBuffer()) {
820 std::string msg = "Transfer array elements must be an ArrayBuffer";
821 isolate_->ThrowException(
822 v8::String::NewFromUtf8(isolate_, msg.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
823 return false;
824 }
825
826 v8::Local<v8::ArrayBuffer> arrayBuffer = v8::Local<v8::ArrayBuffer>::Cast(element);
827 auto iter = std::find(visitedTransfer_.begin(), visitedTransfer_.end(), arrayBuffer);
828 if (iter != visitedTransfer_.end()) {
829 std::string msg = "ArrayBuffer occurs in the transfer array more than once";
830 isolate_->ThrowException(
831 v8::String::NewFromUtf8(isolate_, msg.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
832 return false;
833 }
834
835 v8Serializer_.TransferArrayBuffer(arrayBufferIdx++, arrayBuffer);
836 visitedTransfer_.emplace_back(isolate_, arrayBuffer);
837 } else {
838 return false;
839 }
840 }
841 return true;
842 }
843
DetachTransfer()844 bool DetachTransfer()
845 {
846 for (const auto& item : visitedTransfer_) {
847 v8::Local<v8::ArrayBuffer> arrayBuffer = v8::Local<v8::ArrayBuffer>::New(isolate_, item);
848 if (!arrayBuffer->IsDetachable()) {
849 std::string msg = "ArrayBuffer could not be transferred";
850 isolate_->ThrowException(
851 v8::String::NewFromUtf8(isolate_, msg.c_str(), v8::NewStringType::kNormal).ToLocalChecked());
852 return false;
853 }
854
855 auto backingStore = arrayBuffer->GetBackingStore();
856 data_->backingStores_.push_back(std::move(backingStore));
857 arrayBuffer->Detach();
858 }
859
860 return true;
861 }
862
863 v8::Isolate* isolate_ {nullptr};
864 v8::ValueSerializer v8Serializer_;
865 std::unique_ptr<SerializationData> data_;
866 std::vector<v8::Global<v8::ArrayBuffer>> visitedTransfer_;
867 std::vector<std::unique_ptr<v8::BackingStore>> backingStores_;
868
869 DISALLOW_COPY_AND_ASSIGN(Serializer);
870 };
871
872 class Deserializer {
873 public:
Deserializer(v8::Isolate * isolate,std::unique_ptr<SerializationData> data)874 explicit Deserializer(v8::Isolate* isolate, std::unique_ptr<SerializationData> data)
875 : isolate_(isolate), v8Deserializer_(isolate, data->GetData(), data->GetSize(), nullptr), data_(std::move(data))
876 {
877 v8Deserializer_.SetSupportsLegacyWireFormat(true);
878 }
879 ~Deserializer() = default;
880
DeserializeValue()881 v8::MaybeLocal<v8::Value> DeserializeValue()
882 {
883 v8::Local<v8::Context> context = isolate_->GetCurrentContext();
884 bool readResult = false;
885 if (!v8Deserializer_.ReadHeader(context).To(&readResult)) {
886 return v8::MaybeLocal<v8::Value>();
887 }
888
889 uint32_t index = 0;
890 for (const auto& backingStore : data_->GetBackingStores()) {
891 v8::Local<v8::ArrayBuffer> arrayBuffer = v8::ArrayBuffer::New(isolate_, std::move(backingStore));
892 v8Deserializer_.TransferArrayBuffer(index++, arrayBuffer);
893 }
894
895 return v8Deserializer_.ReadValue(context);
896 }
897
898 private:
899 v8::Isolate* isolate_ {nullptr};
900 v8::ValueDeserializer v8Deserializer_;
901 std::unique_ptr<SerializationData> data_;
902
903 DISALLOW_COPY_AND_ASSIGN(Deserializer);
904 };
905
Serialize(NativeEngine * context,NativeValue * value,NativeValue * transfer)906 NativeValue* V8NativeEngine::Serialize(NativeEngine* context, NativeValue* value, NativeValue* transfer)
907 {
908 v8::Isolate* isolate = reinterpret_cast<V8NativeEngine*>(context)->GetIsolate();
909 v8::Local<v8::Value> v8Value = *value;
910 v8::Local<v8::Value> v8Transfer = *transfer;
911 Serializer serializer(isolate);
912 std::unique_ptr<SerializationData> data;
913 if (serializer.SerializeValue(v8Value, v8Transfer)) {
914 data = serializer.Release();
915 }
916 return reinterpret_cast<NativeValue*>(data.release());
917 }
918
Deserialize(NativeEngine * context,NativeValue * recorder)919 NativeValue* V8NativeEngine::Deserialize(NativeEngine* context, NativeValue* recorder)
920 {
921 v8::Isolate* isolate = reinterpret_cast<V8NativeEngine*>(context)->GetIsolate();
922 std::unique_ptr<SerializationData> data(reinterpret_cast<SerializationData*>(recorder));
923 Deserializer deserializer(isolate, std::move(data));
924 v8::MaybeLocal<v8::Value> result = deserializer.DeserializeValue();
925 return V8ValueToNativeValue(this, result.ToLocalChecked());
926 }
927
DeleteSerializationData(NativeValue * value) const928 void V8NativeEngine::DeleteSerializationData(NativeValue* value) const
929 {
930 SerializationData* data = reinterpret_cast<SerializationData*>(value);
931 delete data;
932 }
933
SetPackagePath(const std::vector<std::string> & packagePath)934 void V8NativeEngine::SetPackagePath(const std::vector<std::string>& packagePath)
935 {
936 auto moduleManager = NativeModuleManager::GetInstance();
937 if (moduleManager && !packagePath.empty()) {
938 moduleManager->SetAppLibPath(packagePath);
939 }
940 }
941
942 // Extracts a C string from a V8 Utf8Value.
ToCString(const v8::String::Utf8Value & value)943 const char* ToCString(const v8::String::Utf8Value& value)
944 {
945 return *value ? *value : "<string conversion failed>";
946 }
947
GetExceptionForWorker() const948 ExceptionInfo* V8NativeEngine::GetExceptionForWorker() const
949 {
950 DCHECK(tryCatch_.HasCaught());
951 v8::HandleScope handle_scope(isolate_);
952
953 ExceptionInfo* exceptionInfo = new ExceptionInfo();
954 v8::String::Utf8Value exception(isolate_, tryCatch_.Exception());
955 const char* exceptionString = ToCString(exception);
956 char* exceptionMessage = new char[strlen(exceptionString) + 1] { 0 };
957 if (memcpy_s(exceptionMessage, strlen(exceptionString) + 1, exceptionString, strlen(exceptionString)) != EOK) {
958 HILOG_INFO("worker:: memcpy_s error");
959 delete exceptionInfo;
960 delete[] exceptionMessage;
961 return nullptr;
962 }
963 exceptionInfo->message_ = exceptionMessage;
964
965 v8::Local<v8::Context> context = context_.Get(isolate_);
966 v8::Context::Scope contextScope(context);
967 v8::Local<v8::Message> message = tryCatch_.Message();
968 if (!message.IsEmpty()) {
969 int32_t lineno = message->GetLineNumber(context).FromJust();
970 exceptionInfo->lineno_ = lineno;
971
972 int32_t colno = message->GetStartColumn(context).FromJust();
973 exceptionInfo->colno_ = colno;
974 }
975 return exceptionInfo;
976 }
977
ValueToNativeValue(JSValueWrapper & value)978 NativeValue* V8NativeEngine::ValueToNativeValue(JSValueWrapper& value)
979 {
980 v8::Local<v8::Value> v8Value = value;
981 return V8ValueToNativeValue(this, v8Value);
982 }
983
SetPromiseRejectCallback(NativeReference * rejectCallbackRef,NativeReference * checkCallbackRef)984 void V8NativeEngine::SetPromiseRejectCallback(NativeReference* rejectCallbackRef, NativeReference* checkCallbackRef)
985 {
986 if (rejectCallbackRef == nullptr || checkCallbackRef == nullptr) {
987 HILOG_ERROR("rejectCallbackRef or checkCallbackRef is nullptr");
988 return;
989 }
990 promiseRejectCallbackRef_ = rejectCallbackRef;
991 checkCallbackRef_ = checkCallbackRef;
992 isolate_->SetPromiseRejectCallback(PromiseRejectCallback);
993 }
994
995
PromiseRejectCallback(v8::PromiseRejectMessage message)996 void V8NativeEngine::PromiseRejectCallback(v8::PromiseRejectMessage message)
997 {
998 v8::Local<v8::Promise> promise = message.GetPromise();
999 v8::PromiseRejectEvent event = message.GetEvent();
1000 v8::Isolate* isolate = promise->GetIsolate();
1001 v8::Local<v8::Value> reason = message.GetValue();
1002 if (reason.IsEmpty()) {
1003 reason = v8::Undefined(isolate);
1004 }
1005 V8NativeEngine* engine = g_env;
1006 if (engine == nullptr) {
1007 HILOG_ERROR("engine is nullptr");
1008 return;
1009 }
1010 v8::Local<v8::Function> promiseRejectCallback = *(engine->promiseRejectCallbackRef_->Get());
1011
1012 if (promiseRejectCallback.IsEmpty()) {
1013 HILOG_ERROR("promiseRejectCallback is empty");
1014 return;
1015 }
1016 v8::Local<v8::Value> type = v8::Number::New(isolate, event);
1017 v8::Local<v8::Value> promiseValue(promise);
1018 v8::Local<v8::Context> context = engine->context_.Get(isolate);
1019 v8::Local<v8::Value> args[] = {type, promiseValue, reason};
1020
1021 size_t size = sizeof(args) / sizeof(args[0]);
1022 bool succ = promiseRejectCallback->Call(context, v8::Undefined(isolate), size, args).IsEmpty();
1023 if (succ) {
1024 HILOG_ERROR("error : call function promiseRejectCallback is failed");
1025 }
1026 if (event == v8::kPromiseRejectWithNoHandler) {
1027 v8::Local<v8::Function> cb = *(engine->checkCallbackRef_->Get());
1028 isolate->EnqueueMicrotask(cb);
1029 }
1030 }
1031