Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions test-app/app/src/main/assets/internal/ts_helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,4 +390,99 @@
},
});
}

const pendingUnhandledRejections = [];
const hasBeenNotifiedProperty = new WeakMap();
function emitPendingUnhandledRejections() {
while (pendingUnhandledRejections.length > 0) {
var promise = pendingUnhandledRejections.shift();
var reason = pendingUnhandledRejections.shift();
if (
hasBeenNotifiedProperty.get(promise) === false &&
globalThis.__onUncaughtError
) {
globalThis.__onUncaughtError(reason);
}
}
}

function unhandledPromise(promise, reason) {
pendingUnhandledRejections.push(promise, reason);
__ns__setTimeout(() => {
emitPendingUnhandledRejections();
}, 1);
}

function handledPromise(promise) {
const hasBeenNotified = hasBeenNotifiedProperty.get(promise);
if (hasBeenNotified != undefined) {
hasBeenNotifiedProperty.delete(promise);
}
}

function makeRejectionError(reason) {
const stringValue = Object.prototype.toString.call(reason);
if (
stringValue === "[object Error]" ||
reason instanceof Error ||
typeof reason === "object"
) {
reason.message = `(Unhandled promise rejection): ${reason.message}`;

if (!reason.stack) {
reason.stack = new Error("").stack;
}
return reason;
} else {
const error = new Error(reason, {
cause: reason,
});
error.message = `(Unhandled promise rejection): ${error.message}`;
return error;
}
}

if (globalThis.__engine === "V8") {
// Only report errors for promise rejections that go unhandled.
globalThis.onUnhandledPromiseRejectionTracker = (
event,
promise,
reason
) => {
if (event === globalThis.__promiseUnhandledEvent) {
hasBeenNotifiedProperty.set(promise, false);
const error = makeRejectionError(reason);
unhandledPromise(promise, error);
} else {
handledPromise(promise);
}
};
} else if (globalThis.__engine === "QuickJS") {
globalThis.onUnhandledPromiseRejectionTracker = (
promise,
reason,
isHandled
) => {
if (!isHandled) {
hasBeenNotifiedProperty.set(promise, false);
const error = makeRejectionError(reason);
// Preserve original stack trace.
if (!promise.then["[[stack]]"]) {
promise.then["[[stack]]"] = error.stack;
} else {
error.stack = promise.then["[[stack]]"];
}
unhandledPromise(promise, error);
} else {
handledPromise(promise);
}
};
} else if (globalThis.__engine === "Hermes") {
HermesInternal.enablePromiseRejectionTracker({
allRejections: true,
onUnhandled: function (id, error) {
globalThis.__onUncaughtError(makeRejectionError(error));
},
});
}
})();
2 changes: 1 addition & 1 deletion test-app/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ if (QUICKJS)
# quickjs
src/main/cpp/napi/quickjs/source/cutils.c
src/main/cpp/napi/quickjs/source/libregexp.c
src/main/cpp/napi/quickjs/source/libbf.c
src/main/cpp/napi/quickjs/source/libunicode.c
src/main/cpp/napi/quickjs/source/quickjs.c
src/main/cpp/napi/quickjs/source/dtoa.c
# napi
src/main/cpp/napi/quickjs/quickjs-api.c
src/main/cpp/napi/quickjs/jsr.cpp
Expand Down
157 changes: 70 additions & 87 deletions test-app/runtime/src/main/cpp/napi/quickjs/quickjs-api.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <limits.h>
#include <quickjs.h>
#include "js_native_api.h"
#include "libbf.h"
#include "quicks-runtime.h"

#ifdef __ANDROID__
Expand Down Expand Up @@ -1043,48 +1042,48 @@ bool JS_GetBigIntWords(JSContext *context, JSValue value, int *signBit, size_t *
return rev;
}

typedef struct JS_BigFloatExt {
JSRefCountHeader header;
bf_t num;
} JS_BigFloatExt;

bool JS_ToInt64WithBigInt(JSContext *context, JSValueConst value, int64_t *pres, bool *lossless) {
if (pres == NULL || lossless == NULL) {
return 0;
}

bool rev = false;
JSValue val = JS_DupValue(context, value);
JS_BigFloatExt *p = (JS_BigFloatExt *) JS_VALUE_GET_PTR(val);
if (p) {
int opFlag = bf_get_int64(pres, &p->num, 0);
if (lossless != NULL) {
*lossless = (opFlag == 0);
}
rev = true;
}
JS_FreeValue(context, val);
return rev;
}

bool JS_ToUInt64WithBigInt(JSContext *context, JSValueConst value, uint64_t *pres, bool *lossless) {
if (pres == NULL || lossless == NULL) {
return false;
}

bool rev = false;
JSValue val = JS_DupValue(context, value);
JS_BigFloatExt *p = (JS_BigFloatExt *) JS_VALUE_GET_PTR(val);
if (p) {
int opFlag = bf_get_uint64(pres, &p->num);
if (lossless != NULL) {
*lossless = (opFlag == 0);
}
rev = true;
}
JS_FreeValue(context, val);
return rev;
}
//typedef struct JS_BigFloatExt {
// JSRefCountHeader header;
// bf_t num;
//} JS_BigFloatExt;
//
//bool JS_ToInt64WithBigInt(JSContext *context, JSValueConst value, int64_t *pres, bool *lossless) {
// if (pres == NULL || lossless == NULL) {
// return 0;
// }
//
// bool rev = false;
// JSValue val = JS_DupValue(context, value);
// JS_BigFloatExt *p = (JS_BigFloatExt *) JS_VALUE_GET_PTR(val);
// if (p) {
// int opFlag = bf_get_int64(pres, &p->num, 0);
// if (lossless != NULL) {
// *lossless = (opFlag == 0);
// }
// rev = true;
// }
// JS_FreeValue(context, val);
// return rev;
//}
//
//bool JS_ToUInt64WithBigInt(JSContext *context, JSValueConst value, uint64_t *pres, bool *lossless) {
// if (pres == NULL || lossless == NULL) {
// return false;
// }
//
// bool rev = false;
// JSValue val = JS_DupValue(context, value);
// JS_BigFloatExt *p = (JS_BigFloatExt *) JS_VALUE_GET_PTR(val);
// if (p) {
// int opFlag = bf_get_uint64(pres, &p->num);
// if (lossless != NULL) {
// *lossless = (opFlag == 0);
// }
// rev = true;
// }
// JS_FreeValue(context, val);
// return rev;
//}

napi_status napi_create_bigint_int64(napi_env env, int64_t value, napi_value *result) {
CHECK_ARG(env)
Expand Down Expand Up @@ -1508,7 +1507,7 @@ napi_status napi_get_array_length(napi_env env,

JSValue jsValue = *((JSValue *) value);

if (!JS_IsArray(env->context, jsValue))
if (!JS_IsArray(jsValue))
return napi_set_last_error(env, napi_array_expected, NULL, 0, NULL);

int64_t length = 0;
Expand Down Expand Up @@ -1706,18 +1705,6 @@ napi_status napi_get_typedarray_info(napi_env env,
return napi_clear_last_error(env);
}

bool JS_IsDataView(JSContext *context, JSValue value) {
bool result = false;
JSValue constructor = JS_GetPropertyStr(context, value, "constructor");
JSValue name = JS_GetPropertyStr(context, constructor, "name");
const char *cName = JS_ToCString(context, name);
result = !strcmp("DataView", cName ? cName : "");
JS_FreeCString(context, cName);
JS_FreeValue(context, name);
JS_FreeValue(context, constructor);
return result;
}

napi_status napi_get_dataview_info(napi_env env,
napi_value dataview,
size_t *byte_length,
Expand All @@ -1729,7 +1716,7 @@ napi_status napi_get_dataview_info(napi_env env,

JSValue value = *((JSValue *) dataview);

if (!JS_IsDataView(env->context, value)) {
if (!JS_IsDataView(value)) {
return napi_set_last_error(env, napi_invalid_arg, NULL, 0, NULL);
}

Expand Down Expand Up @@ -1835,11 +1822,11 @@ napi_status napi_get_value_bigint_int64(napi_env env,
CHECK_ARG(value)
CHECK_ARG(result)

if (!JS_IsBigInt(env->context, *(JSValue *) value)) {
if (!JS_IsBigInt(*(JSValue *) value)) {
return napi_set_last_error(env, napi_bigint_expected, NULL, 0, NULL);
}

JS_ToInt64WithBigInt(env->context, *(JSValue *) value, result, lossless);
JS_ToBigInt64(env->context, result,*(JSValue *) value);

return napi_clear_last_error(env);
}
Expand All @@ -1852,11 +1839,11 @@ napi_status napi_get_value_bigint_uint64(napi_env env,
CHECK_ARG(value)
CHECK_ARG(result)

if (!JS_IsBigInt(env->context, *(JSValue *) value)) {
if (!JS_IsBigInt(*(JSValue *) value)) {
return napi_set_last_error(env, napi_bigint_expected, NULL, 0, NULL);
}

JS_ToUInt64WithBigInt(env->context, *(JSValue *) value, result, lossless);
JS_ToBigUint64(env->context, result, *(JSValue *) value);

return napi_clear_last_error(env);
}
Expand All @@ -1874,7 +1861,7 @@ napi_status napi_get_value_bigint_words(napi_env env,

JSValue jsValue = *(JSValue *) value;

if (!JS_IsBigInt(env->context, jsValue)) {
if (!JS_IsBigInt(jsValue)) {
return napi_set_last_error(env, napi_bigint_expected, NULL, 0, NULL);
}

Expand Down Expand Up @@ -2271,7 +2258,7 @@ napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype *result)
*result = napi_string;
} else if (JS_IsSymbol(jsValue)) {
*result = napi_symbol;
} else if (JS_IsBigInt(env->context, jsValue)) {
} else if (JS_IsBigInt(jsValue)) {
*result = napi_bigint;
} else if (JS_IsFunction(env->context, jsValue)) {
*result = napi_function;
Expand Down Expand Up @@ -2319,7 +2306,7 @@ napi_status napi_is_array(napi_env env, napi_value value, bool *result) {
CHECK_ARG(result)

JSValue jsValue = *((JSValue *) value);
int status = JS_IsArray(env->context, jsValue);
int status = JS_IsArray(jsValue);
RETURN_STATUS_IF_FALSE(status != -1, napi_pending_exception);
*result = status;

Expand Down Expand Up @@ -2388,7 +2375,7 @@ napi_status napi_is_error(napi_env env, napi_value value, bool *result) {
CHECK_ARG(value)
CHECK_ARG(result)

int status = JS_IsError(env->context, *((JSValue *) value));
int status = JS_IsError(*((JSValue *) value));
*result = status;
return napi_clear_last_error(env);
}
Expand All @@ -2411,7 +2398,7 @@ napi_status napi_is_dataview(napi_env env, napi_value value, bool *result) {
CHECK_ARG(value)
CHECK_ARG(result)

int status = JS_IsDataView(env->context, *((JSValue *) value));
int status = JS_IsDataView(*((JSValue *) value));
*result = status;

return napi_clear_last_error(env);
Expand Down Expand Up @@ -4001,23 +3988,6 @@ napi_status qjs_create_runtime(napi_runtime *runtime) {
return napi_ok;
}

static void JS_AfterGCCallback(JSRuntime *rt) {
napi_env env = (napi_env) JS_GetRuntimeOpaque(rt);
if (env->gcAfter != NULL) {
env->gcAfter->finalizeCallback(env, env->gcAfter->data, env->gcAfter->finalizeHint);
}
}

static int JS_BeforeGCCallback(JSRuntime *rt) {
napi_env env = (napi_env) JS_GetRuntimeOpaque(rt);
bool hint = true;
if (env->gcAfter != NULL) {
env->gcAfter->finalizeCallback(env, env->gcAfter->data, &hint);
}

return hint;
}

static JSValue JSRunGCCallback(JSContext *ctx, JSValue
this_val,
int argc, JSValue
Expand Down Expand Up @@ -4047,6 +4017,21 @@ JSEngineCallback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *
return JS_UNDEFINED;
}

void JSR_PromiseRejectionTracker(JSContext *ctx, JSValue promise,
JSValue reason,
bool is_handled, void *opaque) {
JSValue global = JS_GetGlobalObject(ctx);
JSValue onUnhandledRejection = JS_GetPropertyStr(ctx, global, "onUnhandledPromiseRejectionTracker");
if (JS_IsFunction(ctx, onUnhandledRejection)) {
JSValue isHandled = JS_NewBool(ctx, is_handled);
JSValue argv[3] = {promise, reason, isHandled};
JS_Call(ctx, onUnhandledRejection, global, 3, argv);
JS_FreeValue(ctx, isHandled);
}
JS_FreeValue(ctx, onUnhandledRejection);
JS_FreeValue(ctx, global);
}

napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) {
assert(env && runtime);

Expand All @@ -4064,10 +4049,6 @@ napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) {

JS_SetRuntimeOpaque(runtime->runtime, *env);

JS_SetGCAfterCallback(runtime->runtime, JS_AfterGCCallback);

JS_SetGCBeforeCallback(runtime->runtime, JS_BeforeGCCallback);

// Create runtime atoms
(*env)->atoms.napi_external = JS_NewAtom(context, "napi_external");
(*env)->atoms.registerFinalizer = JS_NewAtom(context, "register");
Expand Down Expand Up @@ -4106,6 +4087,8 @@ napi_status qjs_create_napi_env(napi_env *env, napi_runtime runtime) {
JSValue EngineCallback = JS_NewCFunction(context, JSEngineCallback, NULL, 0);
JS_SetPropertyStr(context, globalValue, "directFunction", EngineCallback);

JS_SetHostPromiseRejectionTracker(runtime->runtime, JSR_PromiseRejectionTracker, *env);

(*env)->instanceData = NULL;
(*env)->isThrowNull = false;
(*env)->gcBefore = NULL;
Expand Down
Loading
Loading