Implement Error.prepareStackTrace support
Based on V8's API: https://v8.dev/docs/stack-trace-api.
Bits picked from Frida: 78fd25fed8
Closes: https://github.com/quickjs-ng/quickjs/issues/134
This commit is contained in:
parent
471e821570
commit
555d837334
3 changed files with 353 additions and 48 deletions
|
@ -236,6 +236,7 @@ DEF(SyntaxError, "SyntaxError")
|
||||||
DEF(TypeError, "TypeError")
|
DEF(TypeError, "TypeError")
|
||||||
DEF(URIError, "URIError")
|
DEF(URIError, "URIError")
|
||||||
DEF(InternalError, "InternalError")
|
DEF(InternalError, "InternalError")
|
||||||
|
DEF(CallSite, "CallSite")
|
||||||
/* private symbols */
|
/* private symbols */
|
||||||
DEF(Private_brand, "<brand>")
|
DEF(Private_brand, "<brand>")
|
||||||
/* symbols */
|
/* symbols */
|
||||||
|
|
318
quickjs.c
318
quickjs.c
|
@ -168,6 +168,7 @@ enum {
|
||||||
JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
|
JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
|
||||||
JS_CLASS_WEAK_REF,
|
JS_CLASS_WEAK_REF,
|
||||||
JS_CLASS_FINALIZATION_REGISTRY,
|
JS_CLASS_FINALIZATION_REGISTRY,
|
||||||
|
JS_CLASS_CALL_SITE,
|
||||||
|
|
||||||
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
|
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
|
||||||
};
|
};
|
||||||
|
@ -245,6 +246,8 @@ struct JSRuntime {
|
||||||
JSValue current_exception;
|
JSValue current_exception;
|
||||||
/* true if inside an out of memory error, to avoid recursing */
|
/* true if inside an out of memory error, to avoid recursing */
|
||||||
BOOL in_out_of_memory : 8;
|
BOOL in_out_of_memory : 8;
|
||||||
|
/* and likewise if inside Error.prepareStackTrace() */
|
||||||
|
BOOL in_prepare_stack_trace : 8;
|
||||||
|
|
||||||
struct JSStackFrame *current_stack_frame;
|
struct JSStackFrame *current_stack_frame;
|
||||||
|
|
||||||
|
@ -377,6 +380,8 @@ struct JSContext {
|
||||||
JSValue regexp_ctor;
|
JSValue regexp_ctor;
|
||||||
JSValue promise_ctor;
|
JSValue promise_ctor;
|
||||||
JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
|
JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
|
||||||
|
JSValue error_ctor;
|
||||||
|
JSValue error_prepare_stack;
|
||||||
JSValue iterator_proto;
|
JSValue iterator_proto;
|
||||||
JSValue async_iterator_proto;
|
JSValue async_iterator_proto;
|
||||||
JSValue array_proto_values;
|
JSValue array_proto_values;
|
||||||
|
@ -933,6 +938,16 @@ struct JSObject {
|
||||||
} u;
|
} u;
|
||||||
/* byte sizes: 40/48/72 */
|
/* byte sizes: 40/48/72 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct JSCallSiteData {
|
||||||
|
JSValue filename;
|
||||||
|
JSValue func;
|
||||||
|
JSValue func_name;
|
||||||
|
BOOL native;
|
||||||
|
int line_num;
|
||||||
|
int col_num;
|
||||||
|
} JSCallSiteData;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
__JS_ATOM_NULL = JS_ATOM_NULL,
|
__JS_ATOM_NULL = JS_ATOM_NULL,
|
||||||
#define DEF(name, str) JS_ATOM_ ## name,
|
#define DEF(name, str) JS_ATOM_ ## name,
|
||||||
|
@ -1229,6 +1244,11 @@ static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
|
||||||
JSAtom atom, void *opaque);
|
JSAtom atom, void *opaque);
|
||||||
void JS_SetUncatchableError(JSContext *ctx, JSValue val, BOOL flag);
|
void JS_SetUncatchableError(JSContext *ctx, JSValue val, BOOL flag);
|
||||||
|
|
||||||
|
static JSValue js_new_callsite(JSContext *ctx, JSCallSiteData *csd);
|
||||||
|
static void js_new_callsite_data(JSContext *ctx, JSCallSiteData *csd, JSStackFrame *sf);
|
||||||
|
static void js_new_callsite_data2(JSContext *ctx, JSCallSiteData *csd, const char *filename, int line_num, int col_num);
|
||||||
|
static void _JS_AddIntrinsicCallSite(JSContext *ctx);
|
||||||
|
|
||||||
static const JSClassExoticMethods js_arguments_exotic_methods;
|
static const JSClassExoticMethods js_arguments_exotic_methods;
|
||||||
static const JSClassExoticMethods js_string_exotic_methods;
|
static const JSClassExoticMethods js_string_exotic_methods;
|
||||||
static const JSClassExoticMethods js_proxy_exotic_methods;
|
static const JSClassExoticMethods js_proxy_exotic_methods;
|
||||||
|
@ -2079,6 +2099,8 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
|
||||||
ctx->array_ctor = JS_NULL;
|
ctx->array_ctor = JS_NULL;
|
||||||
ctx->regexp_ctor = JS_NULL;
|
ctx->regexp_ctor = JS_NULL;
|
||||||
ctx->promise_ctor = JS_NULL;
|
ctx->promise_ctor = JS_NULL;
|
||||||
|
ctx->error_ctor = JS_NULL;
|
||||||
|
ctx->error_prepare_stack = JS_UNDEFINED;
|
||||||
init_list_head(&ctx->loaded_modules);
|
init_list_head(&ctx->loaded_modules);
|
||||||
|
|
||||||
JS_AddIntrinsicBasicObjects(ctx);
|
JS_AddIntrinsicBasicObjects(ctx);
|
||||||
|
@ -2195,6 +2217,8 @@ static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
|
||||||
for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
|
for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
|
||||||
JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
|
JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
|
||||||
}
|
}
|
||||||
|
JS_MarkValue(rt, ctx->error_ctor, mark_func);
|
||||||
|
JS_MarkValue(rt, ctx->error_prepare_stack, mark_func);
|
||||||
for(i = 0; i < rt->class_count; i++) {
|
for(i = 0; i < rt->class_count; i++) {
|
||||||
JS_MarkValue(rt, ctx->class_proto[i], mark_func);
|
JS_MarkValue(rt, ctx->class_proto[i], mark_func);
|
||||||
}
|
}
|
||||||
|
@ -2258,6 +2282,8 @@ void JS_FreeContext(JSContext *ctx)
|
||||||
for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
|
for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
|
||||||
JS_FreeValue(ctx, ctx->native_error_proto[i]);
|
JS_FreeValue(ctx, ctx->native_error_proto[i]);
|
||||||
}
|
}
|
||||||
|
JS_FreeValue(ctx, ctx->error_ctor);
|
||||||
|
JS_FreeValue(ctx, ctx->error_prepare_stack);
|
||||||
for(i = 0; i < rt->class_count; i++) {
|
for(i = 0; i < rt->class_count; i++) {
|
||||||
JS_FreeValue(ctx, ctx->class_proto[i]);
|
JS_FreeValue(ctx, ctx->class_proto[i]);
|
||||||
}
|
}
|
||||||
|
@ -6359,30 +6385,68 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj,
|
||||||
const char *filename, int line_num, int col_num,
|
const char *filename, int line_num, int col_num,
|
||||||
int backtrace_flags)
|
int backtrace_flags)
|
||||||
{
|
{
|
||||||
|
// TODO(saghul) Make this configurable with Error.stackTraceLimit
|
||||||
|
static const int stack_limit = 10;
|
||||||
|
|
||||||
JSStackFrame *sf;
|
JSStackFrame *sf;
|
||||||
JSValue str;
|
JSValue stack, prepare, saved_exception;
|
||||||
DynBuf dbuf;
|
DynBuf dbuf;
|
||||||
const char *func_name_str;
|
const char *func_name_str;
|
||||||
const char *str1;
|
const char *str1;
|
||||||
JSObject *p;
|
JSObject *p;
|
||||||
BOOL backtrace_barrier;
|
JSFunctionBytecode *b;
|
||||||
int flags;
|
BOOL backtrace_barrier, has_prepare;
|
||||||
|
JSRuntime *rt;
|
||||||
|
JSCallSiteData csd[stack_limit];
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE;
|
rt = ctx->rt;
|
||||||
|
has_prepare = FALSE;
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
if (!rt->in_prepare_stack_trace && !JS_IsNull(ctx->error_ctor)) {
|
||||||
|
prepare = js_dup(ctx->error_prepare_stack);
|
||||||
|
has_prepare = JS_IsFunction(ctx, prepare);
|
||||||
|
rt->in_prepare_stack_trace = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_prepare) {
|
||||||
|
saved_exception = rt->current_exception;
|
||||||
|
rt->current_exception = JS_NULL;
|
||||||
|
if (filename)
|
||||||
|
js_new_callsite_data2(ctx, &csd[i++], filename, line_num, col_num);
|
||||||
|
} else {
|
||||||
js_dbuf_init(ctx, &dbuf);
|
js_dbuf_init(ctx, &dbuf);
|
||||||
if (filename) {
|
if (filename) {
|
||||||
|
i++;
|
||||||
dbuf_printf(&dbuf, " at %s", filename);
|
dbuf_printf(&dbuf, " at %s", filename);
|
||||||
if (line_num != -1)
|
if (line_num != -1)
|
||||||
dbuf_printf(&dbuf, ":%d:%d", line_num, col_num);
|
dbuf_printf(&dbuf, ":%d:%d", line_num, col_num);
|
||||||
dbuf_putc(&dbuf, '\n');
|
dbuf_putc(&dbuf, '\n');
|
||||||
if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
|
}
|
||||||
|
|
||||||
|
if (filename && (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL))
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
for (sf = rt->current_stack_frame; sf != NULL && i < stack_limit; sf = sf->prev_frame) {
|
||||||
if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
|
if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
|
||||||
backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
|
backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p = JS_VALUE_GET_OBJ(sf->cur_func);
|
||||||
|
b = NULL;
|
||||||
|
backtrace_barrier = FALSE;
|
||||||
|
|
||||||
|
if (js_class_has_bytecode(p->class_id)) {
|
||||||
|
b = p->u.func.function_bytecode;
|
||||||
|
backtrace_barrier = b->backtrace_barrier;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_prepare) {
|
||||||
|
js_new_callsite_data(ctx, &csd[i], sf);
|
||||||
|
} else {
|
||||||
func_name_str = get_func_name(ctx, sf->cur_func);
|
func_name_str = get_func_name(ctx, sf->cur_func);
|
||||||
if (!func_name_str || func_name_str[0] == '\0')
|
if (!func_name_str || func_name_str[0] == '\0')
|
||||||
str1 = "<anonymous>";
|
str1 = "<anonymous>";
|
||||||
|
@ -6391,21 +6455,15 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj,
|
||||||
dbuf_printf(&dbuf, " at %s", str1);
|
dbuf_printf(&dbuf, " at %s", str1);
|
||||||
JS_FreeCString(ctx, func_name_str);
|
JS_FreeCString(ctx, func_name_str);
|
||||||
|
|
||||||
p = JS_VALUE_GET_OBJ(sf->cur_func);
|
if (b) {
|
||||||
backtrace_barrier = FALSE;
|
|
||||||
if (js_class_has_bytecode(p->class_id)) {
|
|
||||||
JSFunctionBytecode *b;
|
|
||||||
const char *atom_str;
|
const char *atom_str;
|
||||||
int line_num1, col_num1;
|
int line_num1, col_num1;
|
||||||
|
|
||||||
b = p->u.func.function_bytecode;
|
|
||||||
backtrace_barrier = b->backtrace_barrier;
|
|
||||||
line_num1 = find_line_num(ctx, b,
|
line_num1 = find_line_num(ctx, b,
|
||||||
sf->cur_pc - b->byte_code_buf - 1,
|
sf->cur_pc - b->byte_code_buf - 1,
|
||||||
&col_num1);
|
&col_num1);
|
||||||
atom_str = JS_AtomToCString(ctx, b->filename);
|
atom_str = JS_AtomToCString(ctx, b->filename);
|
||||||
dbuf_printf(&dbuf, " (%s",
|
dbuf_printf(&dbuf, " (%s", atom_str ? atom_str : "<null>");
|
||||||
atom_str ? atom_str : "<null>");
|
|
||||||
JS_FreeCString(ctx, atom_str);
|
JS_FreeCString(ctx, atom_str);
|
||||||
if (line_num1 != -1)
|
if (line_num1 != -1)
|
||||||
dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1);
|
dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1);
|
||||||
|
@ -6414,18 +6472,60 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj,
|
||||||
dbuf_printf(&dbuf, " (native)");
|
dbuf_printf(&dbuf, " (native)");
|
||||||
}
|
}
|
||||||
dbuf_putc(&dbuf, '\n');
|
dbuf_putc(&dbuf, '\n');
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
/* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */
|
/* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */
|
||||||
if (backtrace_barrier)
|
if (backtrace_barrier)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
if (has_prepare) {
|
||||||
|
int j = 0;
|
||||||
|
stack = JS_NewArray(ctx);
|
||||||
|
if (JS_IsException(stack)) {
|
||||||
|
stack = JS_NULL;
|
||||||
|
} else {
|
||||||
|
for (; j < i; j++) {
|
||||||
|
JSValue v = js_new_callsite(ctx, &csd[j]);
|
||||||
|
if (JS_IsException(v))
|
||||||
|
break;
|
||||||
|
if (JS_DefinePropertyValueUint32(ctx, stack, j, v, JS_PROP_C_W_E) < 0) {
|
||||||
|
JS_FreeValue(ctx, v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear the csd's we didn't use in case of error.
|
||||||
|
for (int k = j; k < i; k++) {
|
||||||
|
JS_FreeValue(ctx, csd[k].filename);
|
||||||
|
JS_FreeValue(ctx, csd[k].func);
|
||||||
|
JS_FreeValue(ctx, csd[k].func_name);
|
||||||
|
}
|
||||||
|
JSValue args[] = {
|
||||||
|
error_obj,
|
||||||
|
stack,
|
||||||
|
};
|
||||||
|
JSValue stack2 = JS_Call(ctx, prepare, ctx->error_ctor, countof(args), args);
|
||||||
|
JS_FreeValue(ctx, stack);
|
||||||
|
if (JS_IsException(stack2))
|
||||||
|
stack = JS_NULL;
|
||||||
|
else
|
||||||
|
stack = stack2;
|
||||||
|
JS_FreeValue(ctx, prepare);
|
||||||
|
JS_FreeValue(ctx, rt->current_exception);
|
||||||
|
rt->current_exception = saved_exception;
|
||||||
|
} else {
|
||||||
dbuf_putc(&dbuf, '\0');
|
dbuf_putc(&dbuf, '\0');
|
||||||
if (dbuf_error(&dbuf))
|
if (dbuf_error(&dbuf))
|
||||||
str = JS_NULL;
|
stack = JS_NULL;
|
||||||
else
|
else
|
||||||
str = JS_NewString(ctx, (char *)dbuf.buf);
|
stack = JS_NewString(ctx, (char *)dbuf.buf);
|
||||||
dbuf_free(&dbuf);
|
dbuf_free(&dbuf);
|
||||||
JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str, flags);
|
}
|
||||||
|
|
||||||
|
rt->in_prepare_stack_trace = FALSE;
|
||||||
|
JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, stack, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note: it is important that no exception is returned by this function */
|
/* Note: it is important that no exception is returned by this function */
|
||||||
|
@ -36078,6 +36178,30 @@ static const JSCFunctionListEntry js_error_proto_funcs[] = {
|
||||||
JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
|
JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static JSValue js_error_get_prepareStackTrace(JSContext *ctx, JSValue this_val)
|
||||||
|
{
|
||||||
|
JSValue val, ret;
|
||||||
|
|
||||||
|
val = JS_ToObject(ctx, this_val);
|
||||||
|
if (JS_IsException(val))
|
||||||
|
return val;
|
||||||
|
JS_FreeValue(ctx, val);
|
||||||
|
return js_dup(ctx->error_prepare_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_error_set_prepareStackTrace(JSContext *ctx, JSValue this_val, JSValue value)
|
||||||
|
{
|
||||||
|
if (JS_IsUndefined(this_val) || JS_IsNull(this_val))
|
||||||
|
return JS_ThrowTypeErrorNotAnObject(ctx);
|
||||||
|
JS_FreeValue(ctx, ctx->error_prepare_stack);
|
||||||
|
ctx->error_prepare_stack = js_dup(value);
|
||||||
|
return JS_UNDEFINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JSCFunctionListEntry js_error_funcs[] = {
|
||||||
|
JS_CGETSET_DEF("prepareStackTrace", js_error_get_prepareStackTrace, js_error_set_prepareStackTrace ),
|
||||||
|
};
|
||||||
|
|
||||||
/* AggregateError */
|
/* AggregateError */
|
||||||
|
|
||||||
/* used by C code. */
|
/* used by C code. */
|
||||||
|
@ -47655,10 +47779,11 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
|
||||||
ctx->function_proto);
|
ctx->function_proto);
|
||||||
|
|
||||||
/* Error */
|
/* Error */
|
||||||
obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,
|
ctx->error_ctor = JS_NewCFunctionMagic(ctx, js_error_constructor,
|
||||||
"Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
|
"Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
|
||||||
JS_NewGlobalCConstructor2(ctx, obj1,
|
JS_NewGlobalCConstructor2(ctx, js_dup(ctx->error_ctor),
|
||||||
"Error", ctx->class_proto[JS_CLASS_ERROR]);
|
"Error", ctx->class_proto[JS_CLASS_ERROR]);
|
||||||
|
JS_SetPropertyFunctionList(ctx, ctx->error_ctor, js_error_funcs, countof(js_error_funcs));
|
||||||
|
|
||||||
/* Used to squelch a -Wcast-function-type warning. */
|
/* Used to squelch a -Wcast-function-type warning. */
|
||||||
JSCFunctionType ft = { .generic_magic = js_error_constructor };
|
JSCFunctionType ft = { .generic_magic = js_error_constructor };
|
||||||
|
@ -47668,11 +47793,15 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
|
||||||
n_args = 1 + (i == JS_AGGREGATE_ERROR);
|
n_args = 1 + (i == JS_AGGREGATE_ERROR);
|
||||||
func_obj = JS_NewCFunction3(ctx, ft.generic,
|
func_obj = JS_NewCFunction3(ctx, ft.generic,
|
||||||
native_error_name[i], n_args,
|
native_error_name[i], n_args,
|
||||||
JS_CFUNC_constructor_or_func_magic, i, obj1);
|
JS_CFUNC_constructor_or_func_magic, i,
|
||||||
|
ctx->error_ctor);
|
||||||
JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],
|
JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],
|
||||||
ctx->native_error_proto[i]);
|
ctx->native_error_proto[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CallSite */
|
||||||
|
_JS_AddIntrinsicCallSite(ctx);
|
||||||
|
|
||||||
/* Iterator prototype */
|
/* Iterator prototype */
|
||||||
ctx->iterator_proto = JS_NewObject(ctx);
|
ctx->iterator_proto = JS_NewObject(ctx);
|
||||||
JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
|
JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
|
||||||
|
@ -51507,3 +51636,150 @@ uint32_t add_ic_slot1(JSContext *ctx, JSInlineCache *ic, JSAtom atom)
|
||||||
end:
|
end:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CallSite */
|
||||||
|
|
||||||
|
static void js_callsite_finalizer(JSRuntime *rt, JSValue val)
|
||||||
|
{
|
||||||
|
JSCallSiteData *csd = JS_GetOpaque(val, JS_CLASS_CALL_SITE);
|
||||||
|
if (csd) {
|
||||||
|
JS_FreeValueRT(rt, csd->filename);
|
||||||
|
JS_FreeValueRT(rt, csd->func);
|
||||||
|
JS_FreeValueRT(rt, csd->func_name);
|
||||||
|
js_free_rt(rt, csd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void js_callsite_mark(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func)
|
||||||
|
{
|
||||||
|
JSCallSiteData *csd = JS_GetOpaque(val, JS_CLASS_CALL_SITE);
|
||||||
|
if (csd) {
|
||||||
|
JS_MarkValue(rt, csd->filename, mark_func);
|
||||||
|
JS_MarkValue(rt, csd->func, mark_func);
|
||||||
|
JS_MarkValue(rt, csd->func_name, mark_func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_new_callsite(JSContext *ctx, JSCallSiteData *csd) {
|
||||||
|
JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_CALL_SITE);
|
||||||
|
if (JS_IsException(obj))
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
|
||||||
|
JSCallSiteData *csd1 = js_malloc(ctx, sizeof(*csd));
|
||||||
|
if (!csd1) {
|
||||||
|
JS_FreeValue(ctx, obj);
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(csd1, csd, sizeof(*csd));
|
||||||
|
|
||||||
|
JS_SetOpaque(obj, csd1);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void js_new_callsite_data(JSContext *ctx, JSCallSiteData *csd, JSStackFrame *sf)
|
||||||
|
{
|
||||||
|
const char *func_name_str;
|
||||||
|
JSObject *p;
|
||||||
|
|
||||||
|
csd->func = js_dup(sf->cur_func);
|
||||||
|
func_name_str = get_func_name(ctx, sf->cur_func);
|
||||||
|
if (!func_name_str || func_name_str[0] == '\0')
|
||||||
|
csd->func_name = JS_NULL;
|
||||||
|
else
|
||||||
|
csd->func_name = JS_NewString(ctx, func_name_str);
|
||||||
|
JS_FreeCString(ctx, func_name_str);
|
||||||
|
if (JS_IsException(csd->func_name))
|
||||||
|
csd->func_name = JS_NULL;
|
||||||
|
|
||||||
|
p = JS_VALUE_GET_OBJ(sf->cur_func);
|
||||||
|
if (js_class_has_bytecode(p->class_id)) {
|
||||||
|
JSFunctionBytecode *b = p->u.func.function_bytecode;
|
||||||
|
int line_num1, col_num1;
|
||||||
|
line_num1 = find_line_num(ctx, b,
|
||||||
|
sf->cur_pc - b->byte_code_buf - 1,
|
||||||
|
&col_num1);
|
||||||
|
csd->native = FALSE;
|
||||||
|
csd->line_num = line_num1;
|
||||||
|
csd->col_num = col_num1;
|
||||||
|
csd->filename = JS_AtomToString(ctx, b->filename);
|
||||||
|
if (JS_IsException(csd->filename)) {
|
||||||
|
csd->filename = JS_NULL;
|
||||||
|
JS_FreeValue(ctx, JS_GetException(ctx)); // Clear exception.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
csd->native = TRUE;
|
||||||
|
csd->line_num = -1;
|
||||||
|
csd->col_num = -1;
|
||||||
|
csd->filename = JS_NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void js_new_callsite_data2(JSContext *ctx, JSCallSiteData *csd, const char *filename, int line_num, int col_num)
|
||||||
|
{
|
||||||
|
csd->func = JS_NULL;
|
||||||
|
csd->func_name = JS_NULL;
|
||||||
|
csd->native = FALSE;
|
||||||
|
csd->line_num = line_num;
|
||||||
|
csd->col_num = col_num;
|
||||||
|
csd->filename = JS_NewString(ctx, filename);
|
||||||
|
if (JS_IsException(csd->filename)) {
|
||||||
|
csd->filename = JS_NULL;
|
||||||
|
JS_FreeValue(ctx, JS_GetException(ctx)); // Clear exception.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_callsite_getfield(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic)
|
||||||
|
{
|
||||||
|
JSCallSiteData *csd = JS_GetOpaque2(ctx, this_val, JS_CLASS_CALL_SITE);
|
||||||
|
if (!csd)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
JSValue *field = (void *)((char *)csd + magic);
|
||||||
|
return js_dup(*field);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_callsite_isnative(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
|
||||||
|
{
|
||||||
|
JSCallSiteData *csd = JS_GetOpaque2(ctx, this_val, JS_CLASS_CALL_SITE);
|
||||||
|
if (!csd)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
return JS_NewBool(ctx, csd->native);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue js_callsite_getnumber(JSContext *ctx, JSValue this_val, int argc, JSValue *argv, int magic)
|
||||||
|
{
|
||||||
|
JSCallSiteData *csd = JS_GetOpaque2(ctx, this_val, JS_CLASS_CALL_SITE);
|
||||||
|
if (!csd)
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
int *field = (void *)((char *)csd + magic);
|
||||||
|
return JS_NewInt32(ctx, *field);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const JSCFunctionListEntry js_callsite_proto_funcs[] = {
|
||||||
|
JS_CFUNC_DEF("isNative", 0, js_callsite_isnative),
|
||||||
|
JS_CFUNC_MAGIC_DEF("getFileName", 0, js_callsite_getfield, offsetof(JSCallSiteData, filename)),
|
||||||
|
JS_CFUNC_MAGIC_DEF("getFunction", 0, js_callsite_getfield, offsetof(JSCallSiteData, func)),
|
||||||
|
JS_CFUNC_MAGIC_DEF("getFunctionName", 0, js_callsite_getfield, offsetof(JSCallSiteData, func_name)),
|
||||||
|
JS_CFUNC_MAGIC_DEF("getColumnNumber", 0, js_callsite_getnumber, offsetof(JSCallSiteData, col_num)),
|
||||||
|
JS_CFUNC_MAGIC_DEF("getLineNumber", 0, js_callsite_getnumber, offsetof(JSCallSiteData, line_num)),
|
||||||
|
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "CallSite", JS_PROP_CONFIGURABLE ),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const JSClassShortDef js_callsite_class_def[] = {
|
||||||
|
{ JS_ATOM_CallSite, js_callsite_finalizer, js_callsite_mark }, /* JS_CLASS_CALL_SITE */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void _JS_AddIntrinsicCallSite(JSContext *ctx)
|
||||||
|
{
|
||||||
|
JSRuntime *rt = ctx->rt;
|
||||||
|
|
||||||
|
if (!JS_IsRegisteredClass(rt, JS_CLASS_CALL_SITE)) {
|
||||||
|
init_class_range(rt, js_callsite_class_def, JS_CLASS_CALL_SITE,
|
||||||
|
countof(js_callsite_class_def));
|
||||||
|
}
|
||||||
|
ctx->class_proto[JS_CLASS_CALL_SITE] = JS_NewObject(ctx);
|
||||||
|
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_CALL_SITE],
|
||||||
|
js_callsite_proto_funcs,
|
||||||
|
countof(js_callsite_proto_funcs));
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,33 @@ function test_function_source_pos() // line 18, column 1
|
||||||
assert(`${f.lineNumber}:${f.columnNumber}`, "1:1");
|
assert(`${f.lineNumber}:${f.columnNumber}`, "1:1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep this at the top; it tests source positions.
|
||||||
|
function test_exception_prepare_stack()
|
||||||
|
{
|
||||||
|
var e;
|
||||||
|
|
||||||
|
Error.prepareStackTrace = (_, frames) => {
|
||||||
|
// Just return the array to check.
|
||||||
|
return frames;
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
throw new Error(""); // line 38, column 19
|
||||||
|
} catch(_e) {
|
||||||
|
e = _e;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(e.stack.length === 2);
|
||||||
|
const f = e.stack[0];
|
||||||
|
assert(f.getFunctionName() === 'test_exception_prepare_stack');
|
||||||
|
assert(f.getFileName() === 'tests/test_builtin.js');
|
||||||
|
assert(f.getLineNumber() === 38);
|
||||||
|
assert(f.getColumnNumber() === 19);
|
||||||
|
assert(!f.isNative());
|
||||||
|
|
||||||
|
Error.prepareStackTrace = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function assert(actual, expected, message) {
|
function assert(actual, expected, message) {
|
||||||
if (arguments.length == 1)
|
if (arguments.length == 1)
|
||||||
expected = true;
|
expected = true;
|
||||||
|
@ -801,3 +828,4 @@ test_generator();
|
||||||
test_proxy_is_array();
|
test_proxy_is_array();
|
||||||
test_exception_source_pos();
|
test_exception_source_pos();
|
||||||
test_function_source_pos();
|
test_function_source_pos();
|
||||||
|
test_exception_prepare_stack();
|
||||||
|
|
Loading…
Reference in a new issue