diff --git a/quickjs.c b/quickjs.c index 11caae0e3..e06941d1a 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1863,7 +1863,7 @@ typedef struct JSClassShortDef { static JSClassShortDef const js_std_class_def[] = { { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ - { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ + { JS_ATOM_Error, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_ERROR */ { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ @@ -5829,6 +5829,7 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->u.array.u.ptr = NULL; p->u.array.count = 0; break; + case JS_CLASS_ERROR: case JS_CLASS_NUMBER: case JS_CLASS_STRING: case JS_CLASS_BOOLEAN: @@ -7286,6 +7287,7 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) } } break; + case JS_CLASS_ERROR: /* u.object_data */ case JS_CLASS_NUMBER: /* u.object_data */ case JS_CLASS_STRING: /* u.object_data */ case JS_CLASS_BOOLEAN: /* u.object_data */ @@ -7749,7 +7751,23 @@ static bool can_add_backtrace(JSValueConst obj) if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) return false; p = JS_VALUE_GET_OBJ(obj); - if (p->class_id != JS_CLASS_ERROR && p->class_id != JS_CLASS_DOM_EXCEPTION) + if (p->class_id != JS_CLASS_DOM_EXCEPTION) + return false; + if (find_own_property1(p, JS_ATOM_stack)) + return false; + return true; +} + +/* Note: it is important that no exception is returned by this function */ +static bool can_store_error_stack(JSValueConst obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return false; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ERROR) + return false; + if (!JS_IsUndefined(p->u.object_data)) return false; if (find_own_property1(p, JS_ATOM_stack)) return false; @@ -7952,7 +7970,18 @@ static void build_backtrace(JSContext *ctx, JSValueConst error_val, if (JS_IsUndefined(ctx->error_back_trace)) ctx->error_back_trace = js_dup(stack); - if (has_filter_func || can_add_backtrace(error_obj)) { + if (has_filter_func) { + /* Error.captureStackTrace(target, ...): install an own data property + on the (possibly non-Error) target, shadowing the accessor */ + JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, stack, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } else if (can_store_error_stack(error_obj)) { + /* genuine Error instance: store as the [[ErrorData]] stack value */ + p = JS_VALUE_GET_OBJ(error_obj); + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = stack; + } else if (can_add_backtrace(error_obj)) { + /* DOMException and the like keep an own "stack" data property */ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, stack, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); } else { @@ -17536,14 +17565,7 @@ static void print_func_name(JSFunctionBytecode *b); static bool needs_backtrace(JSValue exc) { - JSObject *p; - - if (JS_VALUE_GET_TAG(exc) != JS_TAG_OBJECT) - return false; - p = JS_VALUE_GET_OBJ(exc); - if (p->class_id != JS_CLASS_ERROR) - return false; - return !find_own_property1(p, JS_ATOM_stack); + return can_store_error_stack(exc) || can_add_backtrace(exc); } /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ @@ -42005,10 +42027,50 @@ static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val, return JS_ConcatString(ctx, name, msg); } +static JSValue js_error_get_stack(JSContext *ctx, JSValueConst this_val) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + p = JS_VALUE_GET_OBJ(this_val); + if (p->class_id != JS_CLASS_ERROR) + return JS_UNDEFINED; + return js_dup(p->u.object_data); +} + +static JSValue js_error_set_stack(JSContext *ctx, JSValueConst this_val, + JSValueConst value) +{ + int ret, flags; + + if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) + return JS_ThrowTypeErrorNotAnObject(ctx); + if (!JS_IsString(value)) + return JS_ThrowTypeError(ctx, "Error.prototype.stack setter expects a string"); + if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_ERROR])) + return JS_ThrowTypeError(ctx, "Error.prototype.stack setter called on the home object"); + ret = JS_GetOwnPropertyFlagsInternal(ctx, &flags, JS_VALUE_GET_OBJ(this_val), + JS_ATOM_stack); + if (ret < 0) + return JS_EXCEPTION; + if (ret == 0) { + if (JS_DefinePropertyValue(ctx, this_val, JS_ATOM_stack, js_dup(value), + JS_PROP_C_W_E | JS_PROP_THROW) < 0) + return JS_EXCEPTION; + } else { + if (JS_SetPropertyInternal2(ctx, this_val, JS_ATOM_stack, js_dup(value), + this_val, JS_PROP_THROW) < 0) + return JS_EXCEPTION; + } + return JS_UNDEFINED; +} + static const JSCFunctionListEntry js_error_proto_funcs[] = { JS_CFUNC_DEF("toString", 0, js_error_toString ), JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ), + JS_CGETSET_DEF("stack", js_error_get_stack, js_error_set_stack ), }; static JSValue js_error_isError(JSContext *ctx, JSValueConst this_val, diff --git a/test262.conf b/test262.conf index 9b328c538..6cae96544 100644 --- a/test262.conf +++ b/test262.conf @@ -98,7 +98,7 @@ destructuring-assignment destructuring-binding dynamic-import error-cause -error-stack-accessor=skip +error-stack-accessor Error.isError explicit-resource-management exponentiation