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(URIError, "URIError")
|
||||
DEF(InternalError, "InternalError")
|
||||
DEF(CallSite, "CallSite")
|
||||
/* private symbols */
|
||||
DEF(Private_brand, "<brand>")
|
||||
/* symbols */
|
||||
|
|
372
quickjs.c
372
quickjs.c
|
@ -168,6 +168,7 @@ enum {
|
|||
JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
|
||||
JS_CLASS_WEAK_REF,
|
||||
JS_CLASS_FINALIZATION_REGISTRY,
|
||||
JS_CLASS_CALL_SITE,
|
||||
|
||||
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
|
||||
};
|
||||
|
@ -245,6 +246,8 @@ struct JSRuntime {
|
|||
JSValue current_exception;
|
||||
/* true if inside an out of memory error, to avoid recursing */
|
||||
BOOL in_out_of_memory : 8;
|
||||
/* and likewise if inside Error.prepareStackTrace() */
|
||||
BOOL in_prepare_stack_trace : 8;
|
||||
|
||||
struct JSStackFrame *current_stack_frame;
|
||||
|
||||
|
@ -377,6 +380,8 @@ struct JSContext {
|
|||
JSValue regexp_ctor;
|
||||
JSValue promise_ctor;
|
||||
JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
|
||||
JSValue error_ctor;
|
||||
JSValue error_prepare_stack;
|
||||
JSValue iterator_proto;
|
||||
JSValue async_iterator_proto;
|
||||
JSValue array_proto_values;
|
||||
|
@ -933,6 +938,16 @@ struct JSObject {
|
|||
} u;
|
||||
/* 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 {
|
||||
__JS_ATOM_NULL = JS_ATOM_NULL,
|
||||
#define DEF(name, str) JS_ATOM_ ## name,
|
||||
|
@ -1229,6 +1244,11 @@ static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
|
|||
JSAtom atom, void *opaque);
|
||||
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_string_exotic_methods;
|
||||
static const JSClassExoticMethods js_proxy_exotic_methods;
|
||||
|
@ -2079,6 +2099,8 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
|
|||
ctx->array_ctor = JS_NULL;
|
||||
ctx->regexp_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);
|
||||
|
||||
JS_AddIntrinsicBasicObjects(ctx);
|
||||
|
@ -2195,6 +2217,8 @@ static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
|
|||
for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
|
||||
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++) {
|
||||
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++) {
|
||||
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++) {
|
||||
JS_FreeValue(ctx, ctx->class_proto[i]);
|
||||
}
|
||||
|
@ -6359,73 +6385,147 @@ static void build_backtrace(JSContext *ctx, JSValue error_obj,
|
|||
const char *filename, int line_num, int col_num,
|
||||
int backtrace_flags)
|
||||
{
|
||||
// TODO(saghul) Make this configurable with Error.stackTraceLimit
|
||||
static const int stack_limit = 10;
|
||||
|
||||
JSStackFrame *sf;
|
||||
JSValue str;
|
||||
JSValue stack, prepare, saved_exception;
|
||||
DynBuf dbuf;
|
||||
const char *func_name_str;
|
||||
const char *str1;
|
||||
JSObject *p;
|
||||
BOOL backtrace_barrier;
|
||||
int flags;
|
||||
JSFunctionBytecode *b;
|
||||
BOOL backtrace_barrier, has_prepare;
|
||||
JSRuntime *rt;
|
||||
JSCallSiteData csd[stack_limit];
|
||||
uint32_t i;
|
||||
|
||||
flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE;
|
||||
js_dbuf_init(ctx, &dbuf);
|
||||
if (filename) {
|
||||
dbuf_printf(&dbuf, " at %s", filename);
|
||||
if (line_num != -1)
|
||||
dbuf_printf(&dbuf, ":%d:%d", line_num, col_num);
|
||||
dbuf_putc(&dbuf, '\n');
|
||||
if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)
|
||||
goto done;
|
||||
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;
|
||||
}
|
||||
for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
|
||||
|
||||
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);
|
||||
if (filename) {
|
||||
i++;
|
||||
dbuf_printf(&dbuf, " at %s", filename);
|
||||
if (line_num != -1)
|
||||
dbuf_printf(&dbuf, ":%d:%d", line_num, col_num);
|
||||
dbuf_putc(&dbuf, '\n');
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
|
||||
continue;
|
||||
}
|
||||
func_name_str = get_func_name(ctx, sf->cur_func);
|
||||
if (!func_name_str || func_name_str[0] == '\0')
|
||||
str1 = "<anonymous>";
|
||||
else
|
||||
str1 = func_name_str;
|
||||
dbuf_printf(&dbuf, " at %s", str1);
|
||||
JS_FreeCString(ctx, func_name_str);
|
||||
|
||||
p = JS_VALUE_GET_OBJ(sf->cur_func);
|
||||
b = NULL;
|
||||
backtrace_barrier = FALSE;
|
||||
if (js_class_has_bytecode(p->class_id)) {
|
||||
JSFunctionBytecode *b;
|
||||
const char *atom_str;
|
||||
int line_num1, col_num1;
|
||||
|
||||
if (js_class_has_bytecode(p->class_id)) {
|
||||
b = p->u.func.function_bytecode;
|
||||
backtrace_barrier = b->backtrace_barrier;
|
||||
line_num1 = find_line_num(ctx, b,
|
||||
sf->cur_pc - b->byte_code_buf - 1,
|
||||
&col_num1);
|
||||
atom_str = JS_AtomToCString(ctx, b->filename);
|
||||
dbuf_printf(&dbuf, " (%s",
|
||||
atom_str ? atom_str : "<null>");
|
||||
JS_FreeCString(ctx, atom_str);
|
||||
if (line_num1 != -1)
|
||||
dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1);
|
||||
dbuf_putc(&dbuf, ')');
|
||||
} else {
|
||||
dbuf_printf(&dbuf, " (native)");
|
||||
}
|
||||
dbuf_putc(&dbuf, '\n');
|
||||
|
||||
if (has_prepare) {
|
||||
js_new_callsite_data(ctx, &csd[i], sf);
|
||||
} else {
|
||||
func_name_str = get_func_name(ctx, sf->cur_func);
|
||||
if (!func_name_str || func_name_str[0] == '\0')
|
||||
str1 = "<anonymous>";
|
||||
else
|
||||
str1 = func_name_str;
|
||||
dbuf_printf(&dbuf, " at %s", str1);
|
||||
JS_FreeCString(ctx, func_name_str);
|
||||
|
||||
if (b) {
|
||||
const char *atom_str;
|
||||
int line_num1, col_num1;
|
||||
|
||||
line_num1 = find_line_num(ctx, b,
|
||||
sf->cur_pc - b->byte_code_buf - 1,
|
||||
&col_num1);
|
||||
atom_str = JS_AtomToCString(ctx, b->filename);
|
||||
dbuf_printf(&dbuf, " (%s", atom_str ? atom_str : "<null>");
|
||||
JS_FreeCString(ctx, atom_str);
|
||||
if (line_num1 != -1)
|
||||
dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1);
|
||||
dbuf_putc(&dbuf, ')');
|
||||
} else {
|
||||
dbuf_printf(&dbuf, " (native)");
|
||||
}
|
||||
dbuf_putc(&dbuf, '\n');
|
||||
}
|
||||
i++;
|
||||
|
||||
/* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */
|
||||
if (backtrace_barrier)
|
||||
break;
|
||||
}
|
||||
done:
|
||||
dbuf_putc(&dbuf, '\0');
|
||||
if (dbuf_error(&dbuf))
|
||||
str = JS_NULL;
|
||||
else
|
||||
str = JS_NewString(ctx, (char *)dbuf.buf);
|
||||
dbuf_free(&dbuf);
|
||||
JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str, flags);
|
||||
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');
|
||||
if (dbuf_error(&dbuf))
|
||||
stack = JS_NULL;
|
||||
else
|
||||
stack = JS_NewString(ctx, (char *)dbuf.buf);
|
||||
dbuf_free(&dbuf);
|
||||
}
|
||||
|
||||
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 */
|
||||
|
@ -36078,6 +36178,30 @@ static const JSCFunctionListEntry js_error_proto_funcs[] = {
|
|||
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 */
|
||||
|
||||
/* used by C code. */
|
||||
|
@ -47655,10 +47779,11 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
|
|||
ctx->function_proto);
|
||||
|
||||
/* Error */
|
||||
obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,
|
||||
"Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
|
||||
JS_NewGlobalCConstructor2(ctx, obj1,
|
||||
ctx->error_ctor = JS_NewCFunctionMagic(ctx, js_error_constructor,
|
||||
"Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
|
||||
JS_NewGlobalCConstructor2(ctx, js_dup(ctx->error_ctor),
|
||||
"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. */
|
||||
JSCFunctionType ft = { .generic_magic = js_error_constructor };
|
||||
|
@ -47668,11 +47793,15 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
|
|||
n_args = 1 + (i == JS_AGGREGATE_ERROR);
|
||||
func_obj = JS_NewCFunction3(ctx, ft.generic,
|
||||
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],
|
||||
ctx->native_error_proto[i]);
|
||||
}
|
||||
|
||||
/* CallSite */
|
||||
_JS_AddIntrinsicCallSite(ctx);
|
||||
|
||||
/* Iterator prototype */
|
||||
ctx->iterator_proto = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
|
||||
|
@ -51507,3 +51636,150 @@ uint32_t add_ic_slot1(JSContext *ctx, JSInlineCache *ic, JSAtom atom)
|
|||
end:
|
||||
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");
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if (arguments.length == 1)
|
||||
expected = true;
|
||||
|
@ -801,3 +828,4 @@ test_generator();
|
|||
test_proxy_is_array();
|
||||
test_exception_source_pos();
|
||||
test_function_source_pos();
|
||||
test_exception_prepare_stack();
|
||||
|
|
Loading…
Reference in a new issue