Skip to content

Commit 86dec82

Browse files
committed
fix(android): preserve unified runtime behavior
1 parent 4def1b9 commit 86dec82

7 files changed

Lines changed: 121 additions & 17 deletions

File tree

NativeScript/napi/android/quickjs/quickjs-api.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4370,10 +4370,11 @@ napi_status qjs_execute_script(napi_env env,
43704370
JS_FreeCString(env->context, cScript);
43714371
js_exit(env);
43724372
if (JS_IsException(eval_result)) {
4373-
const char *exceptionMessage = JS_ToCString(env->context, eval_result);
4373+
JSValue exceptionValue = JS_GetException(env->context);
4374+
const char *exceptionMessage = JS_ToCString(env->context, exceptionValue);
43744375
napi_set_last_error(env, napi_cannot_run_js, exceptionMessage, 0, NULL);
43754376
JS_FreeCString(env->context, exceptionMessage);
4376-
JS_Throw(env->context, eval_result);
4377+
JS_Throw(env->context, exceptionValue);
43774378
return napi_cannot_run_js;
43784379
}
43794380

@@ -4432,4 +4433,4 @@ napi_status qjs_update_stack_top(napi_env env) {
44324433
CHECK_ARG(env)
44334434
JS_UpdateStackTop(env->runtime->runtime);
44344435
return napi_clear_last_error(env);
4435-
}
4436+
}

NativeScript/napi/common/native_api_util.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,24 @@ inline napi_status define_property(
341341
return napi_define_properties(env, object, 1, &desc);
342342
}
343343

344+
inline napi_status define_property_value(
345+
napi_env env, napi_value object, const char* propertyName,
346+
napi_value value = nullptr,
347+
napi_property_attributes attributes = napi_default_jsproperty,
348+
void* data = nullptr) {
349+
return napi_util::define_property(env, object, propertyName, value, nullptr,
350+
nullptr, data, attributes);
351+
}
352+
353+
inline napi_status define_property_get_set(
354+
napi_env env, napi_value object, const char* propertyName,
355+
napi_callback getter, napi_callback setter,
356+
napi_property_attributes attributes = napi_default_jsproperty,
357+
void* data = nullptr) {
358+
return napi_util::define_property(env, object, propertyName, nullptr, getter,
359+
setter, data, attributes);
360+
}
361+
344362
inline void setPrototypeOf(napi_env env, napi_value object,
345363
napi_value prototype) {
346364
napi_value global, global_object, set_proto;

NativeScript/napi/quickjs/quickjs-api.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4464,10 +4464,11 @@ napi_status qjs_execute_script(napi_env env, napi_value script,
44644464
JS_FreeCString(env->context, cScript);
44654465
js_exit(env);
44664466
if (JS_IsException(eval_result)) {
4467-
const char* exceptionMessage = JS_ToCString(env->context, eval_result);
4467+
JSValue exceptionValue = JS_GetException(env->context);
4468+
const char* exceptionMessage = JS_ToCString(env->context, exceptionValue);
44684469
napi_set_last_error(env, napi_cannot_run_js, exceptionMessage, 0, NULL);
44694470
JS_FreeCString(env->context, exceptionMessage);
4470-
JS_Throw(env->context, eval_result);
4471+
JS_Throw(env->context, exceptionValue);
44714472
return napi_cannot_run_js;
44724473
}
44734474

NativeScript/runtime/android/Runtime.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,33 @@ void Runtime::Init(JNIEnv *_env, jstring filesPath, jstring nativeLibsDir,
205205
js_get_runtime_version(env, &engine);
206206
napi_set_named_property(env, global, "__engine", engine);
207207

208+
const char* engineVariant = "UNKNOWN";
209+
#if defined(__SHERMES__)
210+
engineVariant = "SHERMES";
211+
#elif defined(NS_HERMES_LEGACY_NAPI)
212+
engineVariant = "HERMES";
213+
#elif defined(__HERMES__)
214+
engineVariant = "HERMES";
215+
#elif defined(__JSC__)
216+
engineVariant = "JSC";
217+
#elif defined(__V8_13__)
218+
engineVariant = "V8-13";
219+
#elif defined(__V8_11__)
220+
engineVariant = "V8-11";
221+
#elif defined(__V8_10__)
222+
engineVariant = "V8-10";
223+
#elif defined(__V8__)
224+
engineVariant = "V8";
225+
#elif defined(__PRIMJS__)
226+
engineVariant = "PRIMJS";
227+
#elif defined(__QJS_NG__)
228+
engineVariant = "QUICKJS_NG";
229+
#elif defined(__QJS__)
230+
engineVariant = "QUICKJS";
231+
#endif
232+
napi_value engineVariantValue;
233+
napi_create_string_utf8(env, engineVariant, NAPI_AUTO_LENGTH, &engineVariantValue);
234+
napi_set_named_property(env, global, "__engineVariant", engineVariantValue);
208235

209236
napi_util::napi_set_function(env, global, "__time", CallbackHandlers::TimeCallback);
210237
napi_util::napi_set_function(env, global, "__releaseNativeCounterpart",

NativeScript/runtime/android/modules/module/ModuleInternal.cpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,31 @@
2222
using namespace tns;
2323
using namespace std;
2424

25+
namespace {
26+
void ThrowFallbackRequireError(napi_env env, const char* message) {
27+
bool pendingException = false;
28+
napi_is_exception_pending(env, &pendingException);
29+
if (pendingException) {
30+
napi_value ignored;
31+
napi_get_and_clear_last_exception(env, &ignored);
32+
}
33+
34+
napi_throw_error(env, nullptr, message);
35+
}
36+
37+
void ReThrowRequireError(napi_env env, NativeScriptException& exception) {
38+
try {
39+
exception.ReThrowToNapi(env);
40+
} catch (NativeScriptException&) {
41+
ThrowFallbackRequireError(env, "Error rethrowing NativeScript require exception.");
42+
} catch (std::exception& nestedException) {
43+
ThrowFallbackRequireError(env, nestedException.what());
44+
} catch (...) {
45+
ThrowFallbackRequireError(env, "Error rethrowing NativeScript require exception.");
46+
}
47+
}
48+
}
49+
2550
ModuleInternal::ModuleInternal()
2651
: m_env(nullptr), m_requireFunction(nullptr), m_requireFactoryFunction(nullptr) {
2752
}
@@ -131,15 +156,15 @@ napi_value ModuleInternal::RequireCallback(napi_env env, napi_callback_info info
131156
auto thiz = static_cast<ModuleInternal*>(data);
132157
return thiz->RequireCallbackImpl(env, info);
133158
} catch (NativeScriptException& e) {
134-
e.ReThrowToNapi(env);
159+
ReThrowRequireError(env, e);
135160
} catch (std::exception& e) {
136161
stringstream ss;
137162
ss << "Error: c++ exception: " << e.what() << endl;
138163
NativeScriptException nsEx(ss.str());
139-
nsEx.ReThrowToNapi(env);
164+
ReThrowRequireError(env, nsEx);
140165
} catch (...) {
141166
NativeScriptException nsEx(std::string("Error: c++ exception!"));
142-
nsEx.ReThrowToNapi(env);
167+
ReThrowRequireError(env, nsEx);
143168
}
144169

145170
return nullptr;
@@ -164,6 +189,9 @@ napi_value ModuleInternal::RequireCallbackImpl(napi_env env, napi_callback_info
164189
auto isData = false;
165190

166191
auto moduleObj = LoadImpl(env, moduleName, callingModuleDirName, isData);
192+
if (moduleObj == nullptr) {
193+
return nullptr;
194+
}
167195

168196
if (isData) {
169197
assert(!napi_util::is_null_or_undefined(env, moduleObj));
@@ -346,10 +374,10 @@ napi_value ModuleInternal::LoadModule(napi_env env, const std::string& modulePat
346374
if (status != napi_ok) {
347375
bool pendingException;
348376
napi_is_exception_pending(env, &pendingException);
349-
napi_value error = nullptr;
350377
if (pendingException) {
351-
napi_get_and_clear_last_exception(env, &error);
378+
return nullptr;
352379
}
380+
napi_value error = nullptr;
353381
if (error) {
354382
throw NativeScriptException(env, error, "Error running script " + modulePath);
355383
} else {
@@ -411,6 +439,9 @@ napi_value ModuleInternal::LoadModule(napi_env env, const std::string& modulePat
411439
bool pendingException;
412440
napi_is_exception_pending(env, &pendingException);
413441
if (status != napi_ok || pendingException) {
442+
if (pendingException) {
443+
return nullptr;
444+
}
414445
napi_value exception;
415446
napi_get_and_clear_last_exception(env, &exception);
416447
if (exception) {
@@ -490,4 +521,3 @@ jmethodID ModuleInternal::RESOLVE_PATH_METHOD_ID = nullptr;
490521
const char* ModuleInternal::MODULE_PROLOGUE = "(function(module, exports, require, __filename, __dirname){ ";
491522
const char* ModuleInternal::MODULE_EPILOGUE = "\n})";
492523
int ModuleInternal::MODULE_PROLOGUE_LENGTH = std::string(ModuleInternal::MODULE_PROLOGUE).length();
493-

platforms/android/test-app/runtime/src/main/java/com/tns/MessageType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ public class MessageType {
1111
public static int CloseWorker = 6;
1212
public static int BubbleUpException = 7;
1313
public static int TerminateAndCloseThread = 8;
14+
public static int WorkerReady = 9;
1415
}

platforms/android/test-app/runtime/src/main/java/com/tns/Runtime.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -481,24 +481,30 @@ public void run() {
481481
}
482482

483483
/*
484-
Send a message to the Main Thread to `shake hands`,
485-
Main Thread will cache the Worker Handler for later use
484+
Send a message to the Main Thread to `shake hands`,
485+
Main Thread will verify the Worker runtime is still alive.
486486
*/
487487
Message msg = Message.obtain();
488488
msg.arg1 = MessageType.Handshake;
489489
msg.arg2 = runtime.runtimeId;
490490

491491
runtime.mainThreadHandler.sendMessage(msg);
492-
runtime.runWorker(runtime.runtimeId, filePath);
493492

493+
runtime.runWorker(runtime.runtimeId, filePath);
494494
runtime.processPendingMessages();
495+
496+
Message readyMsg = Message.obtain();
497+
readyMsg.arg1 = MessageType.WorkerReady;
498+
readyMsg.arg2 = runtime.runtimeId;
499+
500+
runtime.mainThreadHandler.sendMessage(readyMsg);
495501
}
496502
}));
497503
}
498504
}
499505

500506
private void processPendingMessages() {
501-
Queue<Message> messages = Runtime.pendingWorkerMessages.get(this.getWorkerId());
507+
Queue<Message> messages = Runtime.pendingWorkerMessages.remove(this.getWorkerId());
502508
if (messages == null) {
503509
return;
504510
}
@@ -526,8 +532,8 @@ Calls the Worker (with id - workerId) object's onmessage implementation with arg
526532
WorkerObjectOnMessageCallback(Runtime.getCurrentRuntime().runtimeId, msg.arg2, msg.obj.toString());
527533
}
528534
/*
529-
Handle a 'Handshake' message sent from a new Worker,
530-
so that the Main may cache it and send messages to it later
535+
Handle a 'Handshake' message sent from a new Worker,
536+
so that the Main may verify that the Worker has not already terminated
531537
*/
532538
else if (msg.arg1 == MessageType.Handshake) {
533539
int senderRuntimeId = msg.arg2;
@@ -542,12 +548,31 @@ else if (msg.arg1 == MessageType.Handshake) {
542548

543549
return;
544550
}
551+
}
552+
/*
553+
Handle a 'WorkerReady' message sent after a Worker script has loaded,
554+
so that the Main may cache it and send messages to it later
555+
*/
556+
else if (msg.arg1 == MessageType.WorkerReady) {
557+
int senderRuntimeId = msg.arg2;
558+
Runtime workerRuntime = runtimeCache.get(senderRuntimeId);
559+
Runtime mainRuntime = Runtime.getCurrentRuntime();
560+
561+
// If worker has had its close/terminate called before the threads could shake hands
562+
if (workerRuntime == null) {
563+
if (mainRuntime.logger.isEnabled()) {
564+
mainRuntime.logger.write("Main thread couldn't mark worker (runtimeId: " + workerRuntime + ") as ready because it has been terminated!");
565+
}
566+
567+
return;
568+
}
545569

546570
/*
547571
Main thread now has a reference to the Worker's handler,
548572
so messaging between the two threads can begin
549573
*/
550574
mainRuntime.workerIdToHandler.put(workerRuntime.getWorkerId(), workerRuntime.getHandler());
575+
workerRuntime.processPendingMessages();
551576

552577
if (mainRuntime.logger.isEnabled()) {
553578
mainRuntime.logger.write("Worker thread (workerId:" + workerRuntime.getWorkerId() + ") shook hands with the main thread!");
@@ -1469,6 +1494,7 @@ public static void sendMessageFromMainToWorker(int workerId, String message) {
14691494
Message msg = Message.obtain();
14701495
msg.obj = message;
14711496
msg.arg1 = MessageType.MainToWorker;
1497+
msg.arg2 = workerId;
14721498

14731499
boolean hasKey = currentRuntime.workerIdToHandler.containsKey(workerId);
14741500
Handler workerHandler = currentRuntime.workerIdToHandler.get(workerId);

0 commit comments

Comments
 (0)