Skip to content

Commit 2c25499

Browse files
committed
Add support for the "#foo in obj" syntax
This is the ergonomic brand checks for private fields proposal that is in the ES2022 spec. https://github.com/tc39/proposal-private-fields-in-in Requires a new bytecode that checks the scope of the `#foo` private name, and then only pushes the var corresponding to it to the stack.
1 parent bf03257 commit 2c25499

File tree

2 files changed

+44
-12
lines changed

2 files changed

+44
-12
lines changed

quickjs-opcode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase
277277
def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */
278278
def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
279279
def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
280+
def(scope_ref_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
280281
def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
281282
def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
282283
def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */

quickjs.c

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7357,7 +7357,9 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
73577357
return 0;
73587358
}
73597359

7360-
static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
7360+
/* return -1 if exception or TRUE/FALSE */
7361+
static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func,
7362+
BOOL should_throw_for_brand)
73617363
{
73627364
JSObject *p, *p1, *home_obj;
73637365
JSShapeProperty *prs;
@@ -7378,8 +7380,11 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
73787380
goto not_obj;
73797381
prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);
73807382
if (!prs) {
7381-
JS_ThrowTypeError(ctx, "expecting <brand> private field");
7382-
return -1;
7383+
if (should_throw_for_brand) {
7384+
JS_ThrowTypeError(ctx, "expecting <brand> private field");
7385+
return -1;
7386+
}
7387+
return FALSE;
73837388
}
73847389
brand = pr->u.value;
73857390
/* safety check */
@@ -7392,10 +7397,13 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
73927397
p = JS_VALUE_GET_OBJ(obj);
73937398
prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
73947399
if (!prs) {
7395-
JS_ThrowTypeError(ctx, "invalid brand on object");
7396-
return -1;
7400+
if (should_throw_for_brand) {
7401+
JS_ThrowTypeError(ctx, "invalid brand on object");
7402+
return -1;
7403+
}
7404+
return FALSE;
73977405
}
7398-
return 0;
7406+
return TRUE;
73997407
}
74007408

74017409
static uint32_t js_string_obj_get_length(JSContext *ctx,
@@ -14757,11 +14765,16 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
1475714765
JS_ThrowTypeError(ctx, "invalid 'in' operand");
1475814766
return -1;
1475914767
}
14760-
atom = JS_ValueToAtom(ctx, op1);
14761-
if (unlikely(atom == JS_ATOM_NULL))
14762-
return -1;
14763-
ret = JS_HasProperty(ctx, op2, atom);
14764-
JS_FreeAtom(ctx, atom);
14768+
if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) {
14769+
atom = JS_ValueToAtom(ctx, op1);
14770+
if (unlikely(atom == JS_ATOM_NULL))
14771+
return -1;
14772+
ret = JS_HasProperty(ctx, op2, atom);
14773+
JS_FreeAtom(ctx, atom);
14774+
} else {
14775+
/* This case can occur if the LHS is a private method name */
14776+
ret = JS_CheckBrand(ctx, op2, op1, FALSE);
14777+
}
1476514778
if (ret < 0)
1476614779
return -1;
1476714780
JS_FreeValue(ctx, op1);
@@ -16754,7 +16767,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
1675416767
}
1675516768
BREAK;
1675616769
CASE(OP_check_brand):
16757-
if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)
16770+
if (JS_CheckBrand(ctx, sp[-2], sp[-1], TRUE) < 0)
1675816771
goto exception;
1675916772
BREAK;
1676016773
CASE(OP_add_brand):
@@ -24472,6 +24485,19 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
2447224485
return -1;
2447324486
emit_op(s, OP_push_true);
2447424487
break;
24488+
case TOK_PRIVATE_NAME:
24489+
{
24490+
JSAtom name;
24491+
if (peek_token(s, FALSE) != TOK_IN)
24492+
return -1;
24493+
name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
24494+
if (next_token(s))
24495+
return -1;
24496+
emit_op(s, OP_scope_ref_private_field);
24497+
emit_u32(s, name);
24498+
emit_u16(s, s->cur_func->scope_level);
24499+
break;
24500+
}
2447524501
case TOK_IDENT:
2447624502
{
2447724503
JSAtom name;
@@ -30106,6 +30132,10 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
3010630132
return -1;
3010730133
assert(var_kind != JS_VAR_NORMAL);
3010830134
switch (op) {
30135+
/* this case is used for `in` operator LHS references */
30136+
case OP_scope_ref_private_field:
30137+
get_loc_or_ref(bc, is_ref, idx);
30138+
break;
3010930139
case OP_scope_get_private_field:
3011030140
case OP_scope_get_private_field2:
3011130141
switch(var_kind) {
@@ -30884,6 +30914,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
3088430914
JS_FreeAtom(ctx, var_name);
3088530915
}
3088630916
break;
30917+
case OP_scope_ref_private_field:
3088730918
case OP_scope_get_private_field:
3088830919
case OP_scope_get_private_field2:
3088930920
case OP_scope_put_private_field:

0 commit comments

Comments
 (0)