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 "quickjs_native_engine.h"
17
18 #include <js_native_api.h>
19
20 #include "native_engine/native_engine.h"
21 #include "native_engine/native_property.h"
22 #include "native_value/quickjs_native_array.h"
23 #include "native_value/quickjs_native_array_buffer.h"
24 #include "native_value/quickjs_native_big_int.h"
25 #include "native_value/quickjs_native_boolean.h"
26 #include "native_value/quickjs_native_buffer.h"
27 #include "native_value/quickjs_native_data_view.h"
28 #include "native_value/quickjs_native_date.h"
29 #include "native_value/quickjs_native_external.h"
30 #include "native_value/quickjs_native_function.h"
31 #include "native_value/quickjs_native_number.h"
32 #include "native_value/quickjs_native_object.h"
33 #include "native_value/quickjs_native_string.h"
34 #include "native_value/quickjs_native_typed_array.h"
35 #include "quickjs_native_deferred.h"
36 #include "quickjs_native_reference.h"
37 #include "securec.h"
38 #include "utils/assert.h"
39 #include "utils/log.h"
40 static const int JS_WRITE_OBJ = (1 << 2) | (1 << 3);
41 static const int JS_ATOM_MESSAGE = 51;
42
QuickJSNativeEngine(JSRuntime * runtime,JSContext * context,void * jsEngine)43 QuickJSNativeEngine::QuickJSNativeEngine(JSRuntime* runtime, JSContext* context, void* jsEngine)
44 : NativeEngine(jsEngine)
45 {
46 runtime_ = runtime;
47 context_ = context;
48
49 AddIntrinsicBaseClass(context_);
50 AddIntrinsicExternal(context_);
51
52 JSValue jsGlobal = JS_GetGlobalObject(context_);
53 JSValue jsNativeEngine = (JSValue)JS_MKPTR(JS_TAG_INT, this);
54 JSValue jsRequireInternal = JS_NewCFunctionData(
55 context_,
56 [](JSContext* ctx, JSValueConst thisVal, int argc, JSValueConst* argv, int magic,
57 JSValue* funcData) -> JSValue {
58 JSValue result = JS_UNDEFINED;
59
60 QuickJSNativeEngine* that = (QuickJSNativeEngine*)JS_VALUE_GET_PTR(funcData[0]);
61
62 const char* moduleName = JS_ToCString(that->GetContext(), argv[0]);
63
64 if (moduleName == nullptr || strlen(moduleName) == 0) {
65 HILOG_ERROR("moduleName is nullptr or length is 0");
66 return result;
67 }
68
69 NativeModuleManager* moduleManager = that->GetModuleManager();
70 NativeModule* module = moduleManager->LoadNativeModule(moduleName, nullptr, false, true);
71
72 if (module != nullptr && module->registerCallback != nullptr) {
73 NativeValue* value = new QuickJSNativeObject(that);
74 if (value != nullptr) {
75 module->registerCallback(that, value);
76 result = JS_DupValue(that->GetContext(), *value);
77 }
78 }
79 JS_FreeCString(that->GetContext(), moduleName);
80 return result;
81 },
82 0, 0, 1, &jsNativeEngine);
83
84 JSValue jsRequire = JS_NewCFunctionData(
85 context_,
86 [](JSContext* ctx, JSValueConst thisVal, int argc, JSValueConst* argv, int magic,
87 JSValue* funcData) -> JSValue {
88 JSValue result = JS_UNDEFINED;
89
90 QuickJSNativeEngine* that = (QuickJSNativeEngine*)JS_VALUE_GET_PTR(funcData[0]);
91 const char* moduleName = JS_ToCString(that->GetContext(), argv[0]);
92 if (moduleName == nullptr || strlen(moduleName) == 0) {
93 HILOG_ERROR("moduleName is nullptr or length is 0");
94 return result;
95 }
96
97 bool isAppModule = false;
98 if (argc == 2) {
99 int ret = JS_ToBool(that->GetContext(), argv[1]);
100 if (ret != -1) {
101 isAppModule = ret;
102 }
103 }
104 NativeModuleManager* moduleManager = that->GetModuleManager();
105 NativeModule* module = moduleManager->LoadNativeModule(moduleName, nullptr, isAppModule);
106
107 if (module != nullptr) {
108 if (module->jsCode != nullptr) {
109 HILOG_INFO("load js code");
110 NativeValue* script = that->CreateString(module->jsCode, module->jsCodeLen);
111 NativeValue* exportObject = that->LoadModule(script, "testjsnapi.js");
112 if (exportObject == nullptr) {
113 HILOG_ERROR("load module failed");
114 return result;
115 }
116 result = JS_DupValue(that->GetContext(), *exportObject);
117 HILOG_ERROR("load module succ");
118 } else if (module->registerCallback != nullptr) {
119 HILOG_INFO("load napi module");
120 NativeValue* value = new QuickJSNativeObject(that);
121 module->registerCallback(that, value);
122 result = JS_DupValue(that->GetContext(), *value);
123 } else {
124 HILOG_ERROR("init module failed");
125 }
126 }
127 JS_FreeCString(that->GetContext(), moduleName);
128 return result;
129 },
130 0, 0, 1, &jsNativeEngine);
131
132 JS_SetPropertyStr(context_, jsGlobal, "requireInternal", jsRequireInternal);
133 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
134 JS_SetPropertyStr(context_, jsGlobal, "requireNapi", jsRequire);
135 #else
136 JS_SetPropertyStr(context_, jsGlobal, "requireNapiPreview", jsRequire);
137 #endif
138 JS_FreeValue(context_, jsGlobal);
139 // need to call init of base class.
140 Init();
141 }
142
~QuickJSNativeEngine()143 QuickJSNativeEngine::~QuickJSNativeEngine()
144 {
145 // need to call deinit before base class.
146 Deinit();
147 }
148
GetModuleFromName(const std::string & moduleName,bool isAppModule,const std::string & id,const std::string & param,const std::string & instanceName,void ** instance)149 JSValue QuickJSNativeEngine::GetModuleFromName(
150 const std::string& moduleName, bool isAppModule, const std::string& id, const std::string& param,
151 const std::string& instanceName, void** instance)
152 {
153 JSValue exports = JS_UNDEFINED;
154 NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
155 NativeModule* module = moduleManager->LoadNativeModule(moduleName.c_str(), nullptr, isAppModule);
156 if (module != nullptr) {
157 NativeValue* idValue = new QuickJSNativeString(this, id.c_str(), id.size());
158 NativeValue* paramValue = new QuickJSNativeString(this, param.c_str(), param.size());
159 NativeValue* exportObject = new QuickJSNativeObject(this);
160
161 NativePropertyDescriptor idProperty, paramProperty;
162 idProperty.utf8name = "id";
163 idProperty.value = idValue;
164 paramProperty.utf8name = "param";
165 paramProperty.value = paramValue;
166 QuickJSNativeObject* exportObj = reinterpret_cast<QuickJSNativeObject*>(exportObject);
167 exportObj->DefineProperty(idProperty);
168 exportObj->DefineProperty(paramProperty);
169 module->registerCallback(this, exportObject);
170
171 napi_value nExport = reinterpret_cast<napi_value>(exportObject);
172 napi_value exportInstance = nullptr;
173 napi_status status =
174 napi_get_named_property(reinterpret_cast<napi_env>(this), nExport, instanceName.c_str(), &exportInstance);
175 if (status != napi_ok) {
176 HILOG_ERROR("GetModuleFromName napi_get_named_property status != napi_ok");
177 }
178
179 status = napi_unwrap(reinterpret_cast<napi_env>(this), exportInstance, reinterpret_cast<void**>(instance));
180 if (status != napi_ok) {
181 HILOG_ERROR("GetModuleFromName napi_unwrap status != napi_ok");
182 }
183
184 exports = *exportObject;
185 }
186 return exports;
187 }
188
LoadModuleByName(const std::string & moduleName,bool isAppModule,const std::string & param,const std::string & instanceName,void * instance)189 JSValue QuickJSNativeEngine::LoadModuleByName(
190 const std::string& moduleName, bool isAppModule, const std::string& param,
191 const std::string& instanceName, void* instance)
192 {
193 JSValue exports = JS_UNDEFINED;
194 NativeModuleManager* moduleManager = NativeModuleManager::GetInstance();
195 NativeModule* module = moduleManager->LoadNativeModule(moduleName.c_str(), nullptr, isAppModule);
196 if (module != nullptr) {
197 NativeValue* exportObject = new QuickJSNativeObject(this);
198 QuickJSNativeObject* exportObj = reinterpret_cast<QuickJSNativeObject*>(exportObject);
199
200 NativePropertyDescriptor paramProperty, instanceProperty;
201
202 NativeValue* paramValue = new QuickJSNativeString(this, param.c_str(), param.size());
203 paramProperty.utf8name = "param";
204 paramProperty.value = paramValue;
205
206 auto instanceValue = new QuickJSNativeObject(this);
207 instanceValue->SetNativePointer(instance, nullptr, nullptr);
208 instanceProperty.utf8name = instanceName.c_str();
209 instanceProperty.value = instanceValue;
210
211 exportObj->DefineProperty(paramProperty);
212 exportObj->DefineProperty(instanceProperty);
213
214 module->registerCallback(this, exportObject);
215 exports = JS_DupValue(GetContext(), *exportObject);
216 }
217 return exports;
218 }
219
GetRuntime()220 JSRuntime* QuickJSNativeEngine::GetRuntime()
221 {
222 return runtime_;
223 }
224
GetContext()225 JSContext* QuickJSNativeEngine::GetContext()
226 {
227 return context_;
228 }
229
Loop(LoopMode mode,bool needSync)230 void QuickJSNativeEngine::Loop(LoopMode mode, bool needSync)
231 {
232 JSContext* context = nullptr;
233 NativeEngine::Loop(mode, needSync);
234 int err = JS_ExecutePendingJob(runtime_, &context);
235 if (err < 0) {
236 js_std_dump_error(context);
237 }
238 }
239
GetGlobal()240 NativeValue* QuickJSNativeEngine::GetGlobal()
241 {
242 JSValue value = JS_GetGlobalObject(context_);
243 return new QuickJSNativeObject(this, value);
244 }
245
CreateNull()246 NativeValue* QuickJSNativeEngine::CreateNull()
247 {
248 return new QuickJSNativeValue(this, JS_NULL);
249 }
250
CreateUndefined()251 NativeValue* QuickJSNativeEngine::CreateUndefined()
252 {
253 return new QuickJSNativeValue(this, JS_UNDEFINED);
254 }
255
CreateBoolean(bool value)256 NativeValue* QuickJSNativeEngine::CreateBoolean(bool value)
257 {
258 return new QuickJSNativeBoolean(this, value);
259 }
260
CreateNumber(int32_t value)261 NativeValue* QuickJSNativeEngine::CreateNumber(int32_t value)
262 {
263 return new QuickJSNativeNumber(this, value);
264 }
265
CreateNumber(uint32_t value)266 NativeValue* QuickJSNativeEngine::CreateNumber(uint32_t value)
267 {
268 return new QuickJSNativeNumber(this, value);
269 }
270
CreateNumber(int64_t value)271 NativeValue* QuickJSNativeEngine::CreateNumber(int64_t value)
272 {
273 return new QuickJSNativeNumber(this, value);
274 }
275
CreateNumber(double value)276 NativeValue* QuickJSNativeEngine::CreateNumber(double value)
277 {
278 return new QuickJSNativeNumber(this, value);
279 }
280
CreateBigInt(int64_t value)281 NativeValue* QuickJSNativeEngine::CreateBigInt(int64_t value)
282 {
283 return new QuickJSNativeBigInt(this, value);
284 }
285
CreateBigInt(uint64_t value)286 NativeValue* QuickJSNativeEngine::CreateBigInt(uint64_t value)
287 {
288 return new QuickJSNativeBigInt(this, value, true);
289 }
290
CreateString(const char * value,size_t length)291 NativeValue* QuickJSNativeEngine::CreateString(const char* value, size_t length)
292 {
293 return new QuickJSNativeString(this, value, length);
294 }
295
CreateString16(const char16_t * value,size_t length)296 NativeValue* QuickJSNativeEngine::CreateString16(const char16_t* value, size_t length)
297 {
298 return new QuickJSNativeString(this, value, length);
299 }
300
CreateSymbol(NativeValue * value)301 NativeValue* QuickJSNativeEngine::CreateSymbol(NativeValue* value)
302 {
303 JSValue symbol = { 0 };
304
305 JSValue global = JS_GetGlobalObject(context_);
306 JSValue symbolCotr = JS_GetPropertyStr(context_, global, "Symbol");
307
308 JSValue jsValue = *value;
309
310 symbol = JS_Call(context_, symbolCotr, global, 1, &jsValue);
311
312 JS_FreeValue(context_, symbolCotr);
313 JS_FreeValue(context_, global);
314 js_std_loop(context_);
315
316 return new QuickJSNativeValue(this, symbol);
317 }
318
CreateFunction(const char * name,size_t length,NativeCallback cb,void * value)319 NativeValue* QuickJSNativeEngine::CreateFunction(const char* name, size_t length, NativeCallback cb, void* value)
320 {
321 return new QuickJSNativeFunction(this, name, cb, value);
322 }
323
CreateExternal(void * value,NativeFinalize callback,void * hint)324 NativeValue* QuickJSNativeEngine::CreateExternal(void* value, NativeFinalize callback, void* hint)
325 {
326 return new QuickJSNativeExternal(this, value, callback, hint);
327 }
328
CreateObject()329 NativeValue* QuickJSNativeEngine::CreateObject()
330 {
331 return new QuickJSNativeObject(this);
332 }
333
CreateArrayBuffer(void ** value,size_t length)334 NativeValue* QuickJSNativeEngine::CreateArrayBuffer(void** value, size_t length)
335 {
336 return new QuickJSNativeArrayBuffer(this, (uint8_t**)value, length);
337 }
338
CreateArrayBufferExternal(void * value,size_t length,NativeFinalize cb,void * hint)339 NativeValue* QuickJSNativeEngine::CreateArrayBufferExternal(void* value, size_t length, NativeFinalize cb, void* hint)
340 {
341 return new QuickJSNativeArrayBuffer(this, (uint8_t*)value, length, cb, hint);
342 }
343
CreateArray(size_t length)344 NativeValue* QuickJSNativeEngine::CreateArray(size_t length)
345 {
346 return new QuickJSNativeArray(this, length);
347 }
348
CreateDataView(NativeValue * value,size_t length,size_t offset)349 NativeValue* QuickJSNativeEngine::CreateDataView(NativeValue* value, size_t length, size_t offset)
350 {
351 return new QuickJSNativeDataView(this, value, length, offset);
352 }
353
CreateTypedArray(NativeTypedArrayType type,NativeValue * value,size_t length,size_t offset)354 NativeValue* QuickJSNativeEngine::CreateTypedArray(NativeTypedArrayType type,
355 NativeValue* value,
356 size_t length,
357 size_t offset)
358 {
359 return new QuickJSNativeTypedArray(this, type, value, length, offset);
360 }
361
CreatePromise(NativeDeferred ** deferred)362 NativeValue* QuickJSNativeEngine::CreatePromise(NativeDeferred** deferred)
363 {
364 JSValue promise = { 0 };
365 JSValue resolvingFuncs[2] = { 0 };
366 promise = JS_NewPromiseCapability(context_, resolvingFuncs);
367 *deferred = new QuickJSNativeDeferred(this, resolvingFuncs);
368 return new QuickJSNativeValue(this, promise);
369 }
370
CreateError(NativeValue * code,NativeValue * message)371 NativeValue* QuickJSNativeEngine::CreateError(NativeValue* code, NativeValue* message)
372 {
373 JSValue error = JS_NewError(context_);
374 if (code) {
375 JS_SetPropertyStr(context_, error, "code", JS_DupValue(context_, *code));
376 }
377 if (message) {
378 JS_SetPropertyStr(context_, error, "message", JS_DupValue(context_, *message));
379 }
380
381 return new QuickJSNativeObject(this, error);
382 }
383
CreateInstance(NativeValue * constructor,NativeValue * const * argv,size_t argc)384 NativeValue* QuickJSNativeEngine::CreateInstance(NativeValue* constructor, NativeValue* const* argv, size_t argc)
385 {
386 JSValue result = JS_UNDEFINED;
387 JSValue* params = nullptr;
388 if (argc > 0) {
389 params = new JSValue[argc];
390 for (size_t i = 0; i < argc && params != nullptr; i++) {
391 params[i] = *argv[i];
392 }
393 }
394 result = JS_CallConstructor(context_, *constructor, argc, params);
395 if (params != nullptr) {
396 delete[] params;
397 }
398 return QuickJSNativeEngine::JSValueToNativeValue(this, result);
399 }
400
CreateReference(NativeValue * value,uint32_t initialRefcount,NativeFinalize callback,void * data,void * hint)401 NativeReference* QuickJSNativeEngine::CreateReference(NativeValue* value, uint32_t initialRefcount,
402 NativeFinalize callback, void* data, void* hint)
403 {
404 return new QuickJSNativeReference(this, value, initialRefcount, callback, data, hint);
405 }
406
CallFunction(NativeValue * thisVar,NativeValue * function,NativeValue * const * argv,size_t argc)407 NativeValue* QuickJSNativeEngine::CallFunction(NativeValue* thisVar,
408 NativeValue* function,
409 NativeValue* const* argv,
410 size_t argc)
411 {
412 JSValue result = JS_UNDEFINED;
413
414 if (function == nullptr) {
415 return new QuickJSNativeValue(this, JS_UNDEFINED);
416 }
417
418 NativeScope* scope = scopeManager_->Open();
419 if (scope == nullptr) {
420 HILOG_ERROR("Open scope failed");
421 return new QuickJSNativeValue(this, JS_UNDEFINED);
422 }
423
424 JSValue* args = nullptr;
425 if (argc > 0) {
426 args = new JSValue[argc];
427 for (size_t i = 0; i < argc && args != nullptr; i++) {
428 if (argv[i] != nullptr) {
429 args[i] = *argv[i];
430 } else {
431 args[i] = JS_UNDEFINED;
432 }
433 }
434 }
435
436 result = JS_Call(context_, *function, (thisVar != nullptr) ? (JSValue)*thisVar : JS_UNDEFINED, argc, args);
437 js_std_loop(context_);
438 JS_DupValue(context_, result);
439
440 if (args != nullptr) {
441 delete[] args;
442 }
443
444 scopeManager_->Close(scope);
445
446 if (JS_IsError(context_, result) || JS_IsException(result)) {
447 return nullptr;
448 }
449
450 return JSValueToNativeValue(this, result);
451 }
452
RunScript(NativeValue * script)453 NativeValue* QuickJSNativeEngine::RunScript(NativeValue* script)
454 {
455 JSValue result;
456 const char* cScript = JS_ToCString(context_, *script);
457 result = JS_Eval(context_, cScript, strlen(cScript), "<input>", JS_EVAL_TYPE_GLOBAL);
458 JS_FreeCString(context_, cScript);
459 if (JS_IsError(context_, result) || JS_IsException(result)) {
460 return nullptr;
461 }
462 js_std_loop(context_);
463
464 return JSValueToNativeValue(this, result);
465 }
466
SetPackagePath(const std::string & packagePath)467 void QuickJSNativeEngine::SetPackagePath(const std::string& packagePath)
468 {
469 auto moduleManager = NativeModuleManager::GetInstance();
470 if (moduleManager && !packagePath.empty()) {
471 moduleManager->SetAppLibPath(packagePath.c_str());
472 }
473 }
474
RunBufferScript(std::vector<uint8_t> & buffer)475 NativeValue* QuickJSNativeEngine::RunBufferScript(std::vector<uint8_t>& buffer)
476 {
477 JSValue result =
478 JS_Eval(context_, reinterpret_cast<char*>(buffer.data()), buffer.size(), "<input>", JS_EVAL_TYPE_GLOBAL);
479 if (JS_IsError(context_, result) || JS_IsException(result)) {
480 return nullptr;
481 }
482 js_std_loop(context_);
483 return JSValueToNativeValue(this, result);
484 }
485
RunActor(std::vector<uint8_t> & buffer,const char * descriptor)486 NativeValue* QuickJSNativeEngine::RunActor(std::vector<uint8_t>& buffer, const char *descriptor)
487 {
488 return RunBufferScript(buffer);
489 }
490
LoadModule(NativeValue * str,const std::string & fileName)491 NativeValue* QuickJSNativeEngine::LoadModule(NativeValue* str, const std::string& fileName)
492 {
493 if (str == nullptr || fileName.empty()) {
494 HILOG_ERROR("moduleName is nullptr or source code length is 0");
495 return nullptr;
496 }
497
498 JS_SetModuleLoaderFunc(runtime_, nullptr, js_module_loader, nullptr);
499 const char* moduleSource = JS_ToCString(context_, *str);
500 size_t len = strlen(moduleSource);
501 int flags = JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY;
502 JSValue moduleVal = JS_Eval(context_, moduleSource, len, fileName.c_str(), flags);
503 if (JS_IsException(moduleVal)) {
504 HILOG_ERROR("Eval source code exception");
505 JS_FreeCString(context_, moduleSource);
506 return nullptr;
507 }
508
509 JSValue evalRes = JS_EvalFunction(context_, moduleVal);
510 if (JS_IsException(evalRes)) {
511 HILOG_ERROR("An exception occurred during Eval module");
512 }
513
514 JSValue ns = JS_GetNameSpace(context_, moduleVal);
515 JSValue result = JS_GetPropertyStr(context_, ns, "default");
516 JS_FreeValue(context_, ns);
517 JS_FreeValue(context_, evalRes);
518 JS_FreeCString(context_, moduleSource);
519 js_std_loop(context_);
520 return JSValueToNativeValue(this, result);
521 }
522
DefineClass(const char * name,NativeCallback callback,void * data,const NativePropertyDescriptor * properties,size_t length)523 NativeValue* QuickJSNativeEngine::DefineClass(const char* name,
524 NativeCallback callback,
525 void* data,
526 const NativePropertyDescriptor* properties,
527 size_t length)
528 {
529 NativeFunctionInfo* functionInfo = new NativeFunctionInfo();
530 if (functionInfo == nullptr) {
531 HILOG_ERROR("new NativeFunctionInfo failed");
532 return nullptr;
533 }
534 functionInfo->engine = this;
535 functionInfo->data = data;
536 functionInfo->callback = callback;
537
538 JSValue classConstructor = JS_NewCFunction2(
539 context_,
540 [](JSContext* ctx, JSValueConst newTarget, int argc, JSValueConst* argv) -> JSValue {
541 auto callbackInfo = new NativeCallbackInfo();
542 if (callbackInfo == nullptr) {
543 HILOG_ERROR("callbackInfo is nullptr");
544 return JS_UNDEFINED;
545 }
546 JSValue prototype = JS_GetPropertyStr(ctx, newTarget, "prototype");
547 JSValue classContext = JS_GetPropertyStr(ctx, newTarget, "_classContext");
548
549 auto functionInfo = (NativeFunctionInfo*)JS_ExternalToNativeObject(ctx, classContext);
550 if (functionInfo == nullptr) {
551 HILOG_ERROR("functionInfo is nullptr");
552 return JS_UNDEFINED;
553 }
554
555 QuickJSNativeEngine* engine = (QuickJSNativeEngine*)functionInfo->engine;
556 NativeScopeManager* scopeManager = engine->GetScopeManager();
557 if (scopeManager == nullptr) {
558 HILOG_ERROR("scopeManager is nullptr");
559 return JS_UNDEFINED;
560 }
561
562 NativeScope* scope = scopeManager->Open();
563 if (scope == nullptr) {
564 HILOG_ERROR("scope is nullptr");
565 delete callbackInfo;
566 return JS_UNDEFINED;
567 }
568
569 callbackInfo->argc = argc;
570 callbackInfo->argv = nullptr;
571 callbackInfo->function = JSValueToNativeValue(engine, JS_DupValue(ctx, newTarget));
572 callbackInfo->functionInfo = functionInfo;
573 callbackInfo->thisVar =
574 JSValueToNativeValue(engine, JS_NewObjectProtoClass(ctx, prototype, GetBaseClassID()));
575
576 if (callbackInfo->argc > 0) {
577 callbackInfo->argv = new NativeValue*[argc];
578 for (size_t i = 0; i < callbackInfo->argc && callbackInfo->argv != nullptr; i++) {
579 callbackInfo->argv[i] = JSValueToNativeValue(engine, JS_DupValue(ctx, argv[i]));
580 }
581 }
582
583 NativeValue* value = functionInfo->callback(engine, callbackInfo);
584
585 if (callbackInfo != nullptr) {
586 delete []callbackInfo->argv;
587 }
588
589 JSValue result = JS_UNDEFINED;
590 if (value != nullptr) {
591 result = JS_DupValue(ctx, *value);
592 } else if (engine->IsExceptionPending()) {
593 NativeValue* error = engine->GetAndClearLastException();
594 if (error != nullptr) {
595 result = JS_DupValue(ctx, *error);
596 }
597 }
598
599 delete callbackInfo;
600 scopeManager->Close(scope);
601
602 return result;
603 },
604 name, 0, JS_CFUNC_constructor_or_func, 0);
605 JSValue proto = JS_NewObject(context_);
606
607 QuickJSNativeObject* nativeClass = new QuickJSNativeObject(this, JS_DupValue(context_, classConstructor));
608 if (nativeClass == nullptr) {
609 delete functionInfo;
610 return nullptr;
611 }
612
613 QuickJSNativeObject* nativeClassProto = new QuickJSNativeObject(this, proto);
614 if (nativeClassProto == nullptr) {
615 delete functionInfo;
616 delete nativeClass;
617 return nullptr;
618 }
619
620 for (size_t i = 0; i < length; i++) {
621 if (properties[i].attributes & NATIVE_STATIC) {
622 nativeClass->DefineProperty(properties[i]);
623 } else {
624 nativeClassProto->DefineProperty(properties[i]);
625 }
626 }
627
628 JS_DefinePropertyValueStr(context_, *nativeClass, "prototype", JS_DupValue(context_, *nativeClassProto), 0);
629
630 JSValue classContext = JS_NewExternal(
631 context_, functionInfo,
632 [](JSContext* ctx, void* data, void* hint) {
633 auto info = (NativeFunctionInfo*)data;
634 HILOG_INFO("_classContext Destroy");
635 if (info != nullptr) {
636 delete info;
637 }
638 },
639 nullptr);
640 JS_DefinePropertyValueStr(context_, *nativeClass, "_classContext", classContext, 0);
641
642 JS_DefinePropertyValueStr(context_, *nativeClassProto, "constructor", JS_DupValue(context_, *nativeClass),
643 JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
644
645 return nativeClass;
646 }
647
Throw(NativeValue * error)648 bool QuickJSNativeEngine::Throw(NativeValue* error)
649 {
650 JS_Throw(context_, *error);
651 this->lastException_ = error;
652 return true;
653 }
654
Throw(NativeErrorType type,const char * code,const char * message)655 bool QuickJSNativeEngine::Throw(NativeErrorType type, const char* code, const char* message)
656 {
657 JSValue error;
658 switch (type) {
659 case NativeErrorType::NATIVE_TYPE_ERROR:
660 error = JS_ThrowTypeError(context_, "code: %s, message: %s\n", code, message);
661 break;
662 case NativeErrorType::NATIVE_RANGE_ERROR:
663 error = JS_ThrowRangeError(context_, "code: %s, message: %s\n", code, message);
664 break;
665 default:
666 error = JS_ThrowInternalError(context_, "code: %s, message: %s\n", code, message);
667 }
668 this->lastException_ = new QuickJSNativeValue(this, error);
669 return true;
670 }
671
JSValueToNativeValue(QuickJSNativeEngine * engine,JSValue value)672 NativeValue* QuickJSNativeEngine::JSValueToNativeValue(QuickJSNativeEngine* engine, JSValue value)
673 {
674 NativeValue* result = nullptr;
675 int tag = JS_VALUE_GET_NORM_TAG(value);
676 switch (tag) {
677 case JS_TAG_BIG_INT:
678 result = new QuickJSNativeBigInt(engine, value);
679 break;
680 case JS_TAG_BIG_FLOAT:
681 result = new QuickJSNativeObject(engine, value);
682 break;
683 case JS_TAG_SYMBOL:
684 result = new QuickJSNativeValue(engine, value);
685 break;
686 case JS_TAG_STRING:
687 result = new QuickJSNativeString(engine, value);
688 break;
689 case JS_TAG_OBJECT:
690 if (JS_IsArray(engine->GetContext(), value)) {
691 result = new QuickJSNativeArray(engine, value);
692 } else if (JS_IsError(engine->GetContext(), value)) {
693 result = new QuickJSNativeValue(engine, value);
694 } else if (JS_IsPromise(engine->GetContext(), value)) {
695 result = new QuickJSNativeValue(engine, value);
696 } else if (JS_IsArrayBuffer(engine->GetContext(), value)) {
697 result = new QuickJSNativeArrayBuffer(engine, value);
698 } else if (JS_IsBuffer(engine->GetContext(), value)) {
699 result = new QuickJSNativeBuffer(engine, value);
700 } else if (JS_IsDataView(engine->GetContext(), value)) {
701 result = new QuickJSNativeDataView(engine, value);
702 } else if (JS_IsTypedArray(engine->GetContext(), value)) {
703 result = new QuickJSNativeTypedArray(engine, value);
704 } else if (JS_IsExternal(engine->GetContext(), value)) {
705 result = new QuickJSNativeExternal(engine, value);
706 } else if (JS_IsFunction(engine->GetContext(), value)) {
707 result = new QuickJSNativeFunction(engine, value);
708 } else if (JS_IsDate(engine->GetContext(), value)) {
709 result = new QuickJSNativeDate(engine, value);
710 } else {
711 result = new QuickJSNativeObject(engine, value);
712 }
713 break;
714 case JS_TAG_BOOL:
715 result = new QuickJSNativeBoolean(engine, value);
716 break;
717 case JS_TAG_NULL:
718 case JS_TAG_UNDEFINED:
719 case JS_TAG_UNINITIALIZED:
720 case JS_TAG_CATCH_OFFSET:
721 case JS_TAG_EXCEPTION:
722 result = new QuickJSNativeValue(engine, value);
723 break;
724 case JS_TAG_INT:
725 case JS_TAG_FLOAT64:
726 result = new QuickJSNativeNumber(engine, value);
727 break;
728 default:
729 HILOG_DEBUG("JS_VALUE_GET_NORM_TAG %{public}d", tag);
730 }
731 return result;
732 }
733
CreateRuntimeFunc(NativeEngine * engine,void * jsEngine)734 NativeEngine* QuickJSNativeEngine::CreateRuntimeFunc(NativeEngine* engine, void* jsEngine)
735 {
736 JSRuntime* runtime = JS_NewRuntime();
737 JSContext* context = JS_NewContext(runtime);
738
739 QuickJSNativeEngine *qjsEngine = new QuickJSNativeEngine(runtime, context, jsEngine);
740
741 // init callback
742 qjsEngine->RegisterWorkerFunction(engine);
743
744 auto cleanEnv = [runtime, context]() {
745 if (context) {
746 JS_FreeContext(context);
747 }
748 if (runtime) {
749 JS_FreeRuntime(runtime);
750 }
751 };
752 qjsEngine->SetCleanEnv(cleanEnv);
753
754 return qjsEngine;
755 }
756
CreateRuntime()757 void* QuickJSNativeEngine::CreateRuntime()
758 {
759 return QuickJSNativeEngine::CreateRuntimeFunc(this, jsEngine_);
760 }
761
CheckTransferList(JSValue transferList)762 bool QuickJSNativeEngine::CheckTransferList(JSValue transferList)
763 {
764 if (JS_IsUndefined(transferList)) {
765 return true;
766 }
767 if (!JS_IsArray(context_, transferList)) {
768 JS_ThrowTypeError(context_, "postMessage second parameter not a list or undefined");
769 return false;
770 }
771 int64_t len = 0;
772 js_get_length64(context_, &len, transferList);
773 for (int64_t i = 0; i < len; i++) {
774 JSValue tmp = JS_GetPropertyInt64(context_, transferList, i);
775 if (!JS_IsException(tmp)) {
776 if (!JS_IsArrayBuffer(context_, tmp)) {
777 HILOG_ERROR("JS_ISArrayBuffer fail");
778 return false;
779 }
780 } else {
781 HILOG_ERROR("JS_GetPropertyInt64 fail");
782 return false;
783 }
784 }
785 return true;
786 }
787
DetachTransferList(JSValue transferList)788 bool QuickJSNativeEngine::DetachTransferList(JSValue transferList)
789 {
790 if (JS_IsUndefined(transferList)) {
791 return true;
792 }
793 int64_t len = 0;
794 js_get_length64(context_, &len, transferList);
795 for (int64_t i = 0; i < len; i++) {
796 JSValue tmp = JS_GetPropertyInt64(context_, transferList, i);
797 if (!JS_IsException(tmp)) {
798 JS_DetachArrayBuffer(context_, tmp);
799 } else {
800 return false;
801 }
802 }
803 return true;
804 }
805
Serialize(NativeEngine * context,NativeValue * value,NativeValue * transfer)806 NativeValue* QuickJSNativeEngine::Serialize(NativeEngine* context, NativeValue* value, NativeValue* transfer)
807 {
808 if (!CheckTransferList(*transfer)) {
809 return nullptr;
810 }
811 size_t dataLen;
812 uint8_t* data = JS_WriteObject(context_, &dataLen, *value, JS_WRITE_OBJ);
813 DetachTransferList(*transfer);
814 return reinterpret_cast<NativeValue*>(new SerializeData(dataLen, data));
815 }
816
Deserialize(NativeEngine * context,NativeValue * recorder)817 NativeValue* QuickJSNativeEngine::Deserialize(NativeEngine* context, NativeValue* recorder)
818 {
819 auto data = reinterpret_cast<SerializeData*>(recorder);
820 JSValue result = JS_ReadObject(context_, data->GetData(), data->GetSize(), JS_WRITE_OBJ);
821 return JSValueToNativeValue(this, result);
822 }
823
DeleteSerializationData(NativeValue * value) const824 void QuickJSNativeEngine::DeleteSerializationData(NativeValue* value) const
825 {
826 SerializeData* data = reinterpret_cast<SerializeData*>(value);
827 delete data;
828 }
829
GetExceptionForWorker() const830 ExceptionInfo* QuickJSNativeEngine::GetExceptionForWorker() const
831 {
832 JSValue exception = JS_GetCurrentException(runtime_);
833 ASSERT(JS_IsObject(exception));
834 JSValue msg;
835 ExceptionInfo* exceptionInfo = new ExceptionInfo();
836 msg = JS_GetProperty(context_, exception, JS_ATOM_MESSAGE);
837 ASSERT(JS_IsString(msg));
838 const char* exceptionStr = reinterpret_cast<char*>(JS_GetStringFromObject(msg));
839 if (exceptionStr == nullptr) {
840 delete exceptionInfo;
841 exceptionInfo = nullptr;
842 return nullptr;
843 }
844 const char* error = "Error: ";
845 int len = strlen(exceptionStr) + strlen(error) + 1;
846 if (len <= 0) {
847 return nullptr;
848 }
849 char* exceptionMessage = new char[len] { 0 };
850 if (memcpy_s(exceptionMessage, len, error, strlen(error)) != EOK) {
851 HILOG_INFO("worker:: memcpy_s error");
852 delete exceptionInfo;
853 delete[] exceptionMessage;
854 return nullptr;
855 }
856 if (memcpy_s(exceptionMessage + strlen(error), len, exceptionStr, strlen(exceptionStr)) != EOK) {
857 HILOG_INFO("worker:: memcpy_s error");
858 delete exceptionInfo;
859 delete[] exceptionMessage;
860 return nullptr;
861 }
862 exceptionInfo->message_ = exceptionMessage;
863 return exceptionInfo;
864 }
865
ValueToNativeValue(JSValueWrapper & value)866 NativeValue* QuickJSNativeEngine::ValueToNativeValue(JSValueWrapper& value)
867 {
868 JSValue quickValue = value;
869 return JSValueToNativeValue(this, quickValue);
870 }
871
CreateBuffer(void ** value,size_t length)872 NativeValue* QuickJSNativeEngine::CreateBuffer(void** value, size_t length)
873 {
874 return new QuickJSNativeBuffer(this, (uint8_t**)value, length);
875 }
876
CreateBufferCopy(void ** value,size_t length,const void * data)877 NativeValue* QuickJSNativeEngine::CreateBufferCopy(void** value, size_t length, const void* data)
878 {
879 return new QuickJSNativeBuffer(this, (uint8_t**)value, length, data);
880 }
881
CreateBufferExternal(void * value,size_t length,NativeFinalize cb,void * hint)882 NativeValue* QuickJSNativeEngine::CreateBufferExternal(void* value, size_t length, NativeFinalize cb, void* hint)
883 {
884 return new QuickJSNativeBuffer(this, (uint8_t*)value, length, cb, hint);
885 }
886
CreateDate(double time)887 NativeValue* QuickJSNativeEngine::CreateDate(double time)
888 {
889 JSValue value = JS_StrictDate(context_, time);
890
891 return new QuickJSNativeDate(this, value);
892 }
893
CreateBigWords(int sign_bit,size_t word_count,const uint64_t * words)894 NativeValue* QuickJSNativeEngine::CreateBigWords(int sign_bit, size_t word_count, const uint64_t* words)
895 {
896 JSValue value = JS_CreateBigIntWords(context_, sign_bit, word_count, words);
897
898 return new QuickJSNativeBigInt(this, value);
899 }
900
TriggerFatalException(NativeValue * error)901 bool QuickJSNativeEngine::TriggerFatalException(NativeValue* error)
902 {
903 return false;
904 }
905
AdjustExternalMemory(int64_t ChangeInBytes,int64_t * AdjustedValue)906 bool QuickJSNativeEngine::AdjustExternalMemory(int64_t ChangeInBytes, int64_t* AdjustedValue)
907 {
908 HILOG_INFO("L2: napi_adjust_external_memory not supported!");
909 return true;
910 }
911
SetPromiseRejectCallback(NativeReference * rejectCallbackRef,NativeReference * checkCallbackRef)912 void QuickJSNativeEngine::SetPromiseRejectCallback(NativeReference* rejectCallbackRef,
913 NativeReference* checkCallbackRef) {}
914