From 97be5a32af9c942765250bbae30c7d792815e5e3 Mon Sep 17 00:00:00 2001 From: Charlie Gordon Date: Thu, 9 May 2024 14:14:50 +0200 Subject: [PATCH] Add `js_resolve_proxy` (#293) - simplify `JS_IsArray` for proxy chains - remove `js_proxy_isArray` --- quickjs.c | 55 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/quickjs.c b/quickjs.c index 2ef43b7..f000ff7 100644 --- a/quickjs.c +++ b/quickjs.c @@ -1175,9 +1175,10 @@ static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, JSValueConst proto_val, BOOL throw_flag); + +static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception); static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj); static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj); -static int js_proxy_isArray(JSContext *ctx, JSValueConst obj); static int JS_CreateProperty(JSContext *ctx, JSObject *p, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, @@ -12109,15 +12110,14 @@ static __maybe_unused void JS_PrintValue(JSContext *ctx, } /* return -1 if exception (proxy case) or TRUE/FALSE */ +// TODO: should take flags to make proxy resolution and exceptions optional int JS_IsArray(JSContext *ctx, JSValueConst val) { - JSObject *p; + if (js_resolve_proxy(ctx, &val, TRUE)) + return -1; if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { - p = JS_VALUE_GET_OBJ(val); - if (unlikely(p->class_id == JS_CLASS_PROXY)) - return js_proxy_isArray(ctx, val); - else - return p->class_id == JS_CLASS_ARRAY; + JSObject *p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ARRAY; } else { return FALSE; } @@ -46713,20 +46713,35 @@ static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, return ret; } -static int js_proxy_isArray(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); - if (!s) - return FALSE; - if (js_check_stack_overflow(ctx->rt, 0)) { - JS_ThrowStackOverflow(ctx); - return -1; +/* `js_resolve_proxy`: resolve the proxy chain + `*pval` is updated with to ultimate proxy target + `throw_exception` controls whether exceptions are thown or not + - return -1 in case of error + - otherwise return 0 + */ +static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, BOOL throw_exception) { + int depth = 0; + JSObject *p; + JSProxyData *s; + + while (JS_VALUE_GET_TAG(*pval) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(*pval); + if (p->class_id != JS_CLASS_PROXY) + break; + if (depth++ > 1000) { + if (throw_exception) + JS_ThrowStackOverflow(ctx); + return -1; + } + s = p->u.opaque; + if (s->is_revoked) { + if (throw_exception) + JS_ThrowTypeErrorRevokedProxy(ctx); + return -1; + } + *pval = s->target; } - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - return -1; - } - return JS_IsArray(ctx, s->target); + return 0; } static const JSClassExoticMethods js_proxy_exotic_methods = {