Skip to content

Commit d44bb29

Browse files
authored
Add JS_IsProxy, JS_GetProxyHandler and JS_GetProxyTarget (#939)
This commit also turns JS_IsArray into a simple predicate function. It no longer punches automatically through proxies because that can raise exceptions and is inconsistent with the other predicate functions. Fixes: #938
1 parent 6d4667e commit d44bb29

File tree

3 files changed

+95
-11
lines changed

3 files changed

+95
-11
lines changed

api-test.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,45 @@ static void raw_context_global_var(void)
147147
JS_FreeRuntime(rt);
148148
}
149149

150+
static void is_array(void)
151+
{
152+
JSRuntime *rt = JS_NewRuntime();
153+
JSContext *ctx = JS_NewContext(rt);
154+
{
155+
static const char code[] = "[]";
156+
JSValue ret = JS_Eval(ctx, code, strlen(code), "*", JS_EVAL_TYPE_GLOBAL);
157+
assert(!JS_IsException(ret));
158+
assert(JS_IsArray(ret));
159+
JS_FreeValue(ctx, ret);
160+
}
161+
{
162+
static const char code[] = "new Proxy([], {})";
163+
JSValue ret = JS_Eval(ctx, code, strlen(code), "*", JS_EVAL_TYPE_GLOBAL);
164+
assert(!JS_IsException(ret));
165+
assert(!JS_IsArray(ret));
166+
assert(JS_IsProxy(ret));
167+
JSValue handler = JS_GetProxyHandler(ctx, ret);
168+
JSValue target = JS_GetProxyTarget(ctx, ret);
169+
assert(!JS_IsException(handler));
170+
assert(!JS_IsException(target));
171+
assert(!JS_IsProxy(handler));
172+
assert(!JS_IsProxy(target));
173+
assert(JS_IsObject(handler));
174+
assert(JS_IsArray(target));
175+
JS_FreeValue(ctx, handler);
176+
JS_FreeValue(ctx, target);
177+
JS_FreeValue(ctx, ret);
178+
}
179+
JS_FreeContext(ctx);
180+
JS_FreeRuntime(rt);
181+
}
182+
150183
int main(void)
151184
{
152185
sync_call();
153186
async_call();
154187
async_call_stack_overflow();
155188
raw_context_global_var();
189+
is_array();
156190
return 0;
157191
}

quickjs.c

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12059,8 +12059,17 @@ static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValue val)
1205912059
}
1206012060
}
1206112061

12062+
bool JS_IsArray(JSValue val)
12063+
{
12064+
if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
12065+
JSObject *p = JS_VALUE_GET_OBJ(val);
12066+
return p->class_id == JS_CLASS_ARRAY;
12067+
}
12068+
return false;
12069+
}
12070+
1206212071
/* return -1 if exception (proxy case) or true/false */
12063-
int JS_IsArray(JSContext *ctx, JSValue val)
12072+
static int js_is_array(JSContext *ctx, JSValue val)
1206412073
{
1206512074
JSObject *p;
1206612075
if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
@@ -36880,7 +36889,7 @@ static JSValue js_object_toString(JSContext *ctx, JSValue this_val,
3688036889
obj = JS_ToObject(ctx, this_val);
3688136890
if (JS_IsException(obj))
3688236891
return obj;
36883-
is_array = JS_IsArray(ctx, obj);
36892+
is_array = js_is_array(ctx, obj);
3688436893
if (is_array < 0) {
3688536894
JS_FreeValue(ctx, obj);
3688636895
return JS_EXCEPTION;
@@ -38197,7 +38206,7 @@ static JSValue js_array_isArray(JSContext *ctx, JSValue this_val,
3819738206
int argc, JSValue *argv)
3819838207
{
3819938208
int ret;
38200-
ret = JS_IsArray(ctx, argv[0]);
38209+
ret = js_is_array(ctx, argv[0]);
3820138210
if (ret < 0)
3820238211
return JS_EXCEPTION;
3820338212
else
@@ -38217,7 +38226,7 @@ static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValue obj,
3821738226
int res;
3821838227
JSContext *realm;
3821938228

38220-
res = JS_IsArray(ctx, obj);
38229+
res = js_is_array(ctx, obj);
3822138230
if (res < 0)
3822238231
return JS_EXCEPTION;
3822338232
if (!res)
@@ -38274,7 +38283,7 @@ static int JS_isConcatSpreadable(JSContext *ctx, JSValue obj)
3827438283
return -1;
3827538284
if (!JS_IsUndefined(val))
3827638285
return JS_ToBoolFree(ctx, val);
38277-
return JS_IsArray(ctx, obj);
38286+
return js_is_array(ctx, obj);
3827838287
}
3827938288

3828038289
static JSValue js_array_at(JSContext *ctx, JSValue this_val,
@@ -39555,7 +39564,7 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValue target,
3955539564
return -1;
3955639565
}
3955739566
if (depth > 0) {
39558-
is_array = JS_IsArray(ctx, element);
39567+
is_array = js_is_array(ctx, element);
3955939568
if (is_array < 0)
3956039569
goto fail;
3956139570
if (is_array) {
@@ -45088,7 +45097,7 @@ static JSValue internalize_json_property(JSContext *ctx, JSValue holder,
4508845097
if (JS_IsException(val))
4508945098
return val;
4509045099
if (JS_IsObject(val)) {
45091-
is_array = JS_IsArray(ctx, val);
45100+
is_array = js_is_array(ctx, val);
4509245101
if (is_array < 0)
4509345102
goto fail;
4509445103
if (is_array) {
@@ -45299,7 +45308,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
4529945308
v = js_array_push(ctx, jsc->stack, 1, &val, 0);
4530045309
if (check_exception_free(ctx, v))
4530145310
goto exception;
45302-
ret = JS_IsArray(ctx, val);
45311+
ret = js_is_array(ctx, val);
4530345312
if (ret < 0)
4530445313
goto exception;
4530545314
if (ret) {
@@ -45447,7 +45456,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValue obj,
4544745456
if (JS_IsFunction(ctx, replacer)) {
4544845457
jsc->replacer_func = replacer;
4544945458
} else {
45450-
res = JS_IsArray(ctx, replacer);
45459+
res = js_is_array(ctx, replacer);
4545145460
if (res < 0)
4545245461
goto exception;
4545345462
if (res) {
@@ -46591,7 +46600,7 @@ static int js_proxy_isArray(JSContext *ctx, JSValue obj)
4659146600
JS_ThrowTypeErrorRevokedProxy(ctx);
4659246601
return -1;
4659346602
}
46594-
return JS_IsArray(ctx, s->target);
46603+
return js_is_array(ctx, s->target);
4659546604
}
4659646605

4659746606
static const JSClassExoticMethods js_proxy_exotic_methods = {
@@ -46708,6 +46717,39 @@ void JS_AddIntrinsicProxy(JSContext *ctx)
4670846717
obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
4670946718
}
4671046719

46720+
bool JS_IsProxy(JSValue val)
46721+
{
46722+
if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
46723+
JSObject *p = JS_VALUE_GET_OBJ(val);
46724+
return p->class_id == JS_CLASS_PROXY;
46725+
}
46726+
return false;
46727+
}
46728+
46729+
static JSValue js_get_proxy_field(JSContext *ctx, JSValue proxy, int offset)
46730+
{
46731+
if (JS_VALUE_GET_TAG(proxy) == JS_TAG_OBJECT) {
46732+
JSObject *p = JS_VALUE_GET_OBJ(proxy);
46733+
if (p->class_id == JS_CLASS_PROXY) {
46734+
JSProxyData *s = JS_GetOpaque(proxy, JS_CLASS_PROXY);
46735+
if (s->is_revoked)
46736+
return JS_ThrowTypeErrorRevokedProxy(ctx);
46737+
return js_dup(*(JSValue *)((char *)s + offset));
46738+
}
46739+
}
46740+
return JS_ThrowTypeError(ctx, "not a proxy");
46741+
}
46742+
46743+
JSValue JS_GetProxyTarget(JSContext *ctx, JSValue proxy)
46744+
{
46745+
return js_get_proxy_field(ctx, proxy, offsetof(JSProxyData, target));
46746+
}
46747+
46748+
JSValue JS_GetProxyHandler(JSContext *ctx, JSValue proxy)
46749+
{
46750+
return js_get_proxy_field(ctx, proxy, offsetof(JSProxyData, handler));
46751+
}
46752+
4671146753
/* Symbol */
4671246754

4671346755
static JSValue js_symbol_constructor(JSContext *ctx, JSValue new_target,

quickjs.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -748,7 +748,15 @@ JS_EXTERN JSValue JS_NewArray(JSContext *ctx);
748748
// takes ownership of the values
749749
JS_EXTERN JSValue JS_NewArrayFrom(JSContext *ctx, int count,
750750
const JSValue *values);
751-
JS_EXTERN int JS_IsArray(JSContext *ctx, JSValue val);
751+
// reader beware: JS_IsArray used to "punch" through proxies and check
752+
// if the target object is an array but it no longer does; use JS_IsProxy
753+
// and JS_GetProxyTarget instead, and remember that the target itself can
754+
// also be a proxy, ad infinitum
755+
JS_EXTERN bool JS_IsArray(JSValue val);
756+
757+
JS_EXTERN bool JS_IsProxy(JSValue val);
758+
JS_EXTERN JSValue JS_GetProxyTarget(JSContext *ctx, JSValue proxy);
759+
JS_EXTERN JSValue JS_GetProxyHandler(JSContext *ctx, JSValue proxy);
752760

753761
JS_EXTERN JSValue JS_NewDate(JSContext *ctx, double epoch_ms);
754762
JS_EXTERN bool JS_IsDate(JSValue v);

0 commit comments

Comments
 (0)