added 'in' operator for private fields
This commit is contained in:
parent
16057644f3
commit
3ab1c2b314
5 changed files with 166 additions and 77 deletions
2
TODO
2
TODO
|
@ -63,5 +63,5 @@ Optimization ideas:
|
|||
Test262o: 0/11262 errors, 463 excluded
|
||||
Test262o commit: 7da91bceb9ce7613f87db47ddd1292a2dda58b42 (es5-tests branch)
|
||||
|
||||
Result: 16/76909 errors, 1497 excluded, 8136 skipped
|
||||
Result: 18/76947 errors, 1497 excluded, 8117 skipped
|
||||
Test262 commit: 6cbb6da9473c56d95358d8e679c5a6d2b4574efb
|
||||
|
|
|
@ -256,6 +256,7 @@ DEF( and, 1, 2, 1, none)
|
|||
DEF( xor, 1, 2, 1, none)
|
||||
DEF( or, 1, 2, 1, none)
|
||||
DEF(is_undefined_or_null, 1, 1, 1, none)
|
||||
DEF( private_in, 1, 2, 1, none)
|
||||
#ifdef CONFIG_BIGNUM
|
||||
DEF( mul_pow10, 1, 2, 1, none)
|
||||
DEF( math_mod, 1, 2, 1, none)
|
||||
|
@ -280,7 +281,7 @@ def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in pha
|
|||
def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
|
||||
def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
|
||||
def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
|
||||
|
||||
def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */
|
||||
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
|
||||
|
||||
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
|
||||
|
|
224
quickjs.c
224
quickjs.c
|
@ -7351,6 +7351,8 @@ static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* add a private brand field to 'home_obj' if not already present and
|
||||
if obj is != null add a private brand to it */
|
||||
static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
|
||||
{
|
||||
JSObject *p, *p1;
|
||||
|
@ -7366,10 +7368,10 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
|
|||
p = JS_VALUE_GET_OBJ(home_obj);
|
||||
prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
|
||||
if (!prs) {
|
||||
/* if the brand is not present, add it */
|
||||
brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
|
||||
if (JS_IsException(brand))
|
||||
return -1;
|
||||
/* if the brand is not present, add it */
|
||||
pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
|
||||
if (!pr) {
|
||||
JS_FreeValue(ctx, brand);
|
||||
|
@ -7381,26 +7383,27 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
|
|||
}
|
||||
brand_atom = js_symbol_to_atom(ctx, brand);
|
||||
|
||||
if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
|
||||
JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
if (JS_IsObject(obj)) {
|
||||
p1 = JS_VALUE_GET_OBJ(obj);
|
||||
prs = find_own_property(&pr, p1, brand_atom);
|
||||
if (unlikely(prs)) {
|
||||
JS_FreeAtom(ctx, brand_atom);
|
||||
JS_ThrowTypeError(ctx, "private method is already present");
|
||||
return -1;
|
||||
}
|
||||
pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
|
||||
JS_FreeAtom(ctx, brand_atom);
|
||||
return -1;
|
||||
}
|
||||
p1 = JS_VALUE_GET_OBJ(obj);
|
||||
prs = find_own_property(&pr, p1, brand_atom);
|
||||
if (unlikely(prs)) {
|
||||
if (!pr)
|
||||
return -1;
|
||||
pr->u.value = JS_UNDEFINED;
|
||||
} else {
|
||||
JS_FreeAtom(ctx, brand_atom);
|
||||
JS_ThrowTypeError(ctx, "private method is already present");
|
||||
return -1;
|
||||
}
|
||||
pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
|
||||
JS_FreeAtom(ctx, brand_atom);
|
||||
if (!pr)
|
||||
return -1;
|
||||
pr->u.value = JS_UNDEFINED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return a boolean telling if the brand of the home object of 'func'
|
||||
is present on 'obj' or -1 in case of exception */
|
||||
static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
|
||||
{
|
||||
JSObject *p, *p1, *home_obj;
|
||||
|
@ -7409,11 +7412,8 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
|
|||
JSValueConst brand;
|
||||
|
||||
/* get the home object of 'func' */
|
||||
if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {
|
||||
not_obj:
|
||||
JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT))
|
||||
goto not_obj;
|
||||
p1 = JS_VALUE_GET_OBJ(func);
|
||||
if (!js_class_has_bytecode(p1->class_id))
|
||||
goto not_obj;
|
||||
|
@ -7431,15 +7431,14 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
|
|||
goto not_obj;
|
||||
|
||||
/* get the brand array of 'obj' */
|
||||
if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
|
||||
goto not_obj;
|
||||
p = JS_VALUE_GET_OBJ(obj);
|
||||
prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
|
||||
if (!prs) {
|
||||
JS_ThrowTypeError(ctx, "invalid brand on object");
|
||||
if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
|
||||
not_obj:
|
||||
JS_ThrowTypeErrorNotAnObject(ctx);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
p = JS_VALUE_GET_OBJ(obj);
|
||||
prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
|
||||
return (prs != NULL);
|
||||
}
|
||||
|
||||
static uint32_t js_string_obj_get_length(JSContext *ctx,
|
||||
|
@ -14498,6 +14497,43 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp)
|
||||
{
|
||||
JSValue op1, op2;
|
||||
int ret;
|
||||
|
||||
op1 = sp[-2]; /* object */
|
||||
op2 = sp[-1]; /* field name or method function */
|
||||
|
||||
if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) {
|
||||
JS_ThrowTypeError(ctx, "invalid 'in' operand");
|
||||
return -1;
|
||||
}
|
||||
if (JS_IsObject(op2)) {
|
||||
/* method: use the brand */
|
||||
ret = JS_CheckBrand(ctx, op1, op2);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
} else {
|
||||
JSAtom atom;
|
||||
JSObject *p;
|
||||
JSShapeProperty *prs;
|
||||
JSProperty *pr;
|
||||
/* field */
|
||||
atom = JS_ValueToAtom(ctx, op2);
|
||||
if (unlikely(atom == JS_ATOM_NULL))
|
||||
return -1;
|
||||
p = JS_VALUE_GET_OBJ(op1);
|
||||
prs = find_own_property(&pr, p, atom);
|
||||
JS_FreeAtom(ctx, atom);
|
||||
ret = (prs != NULL);
|
||||
}
|
||||
JS_FreeValue(ctx, op1);
|
||||
JS_FreeValue(ctx, op2);
|
||||
sp[-2] = JS_NewBool(ctx, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
|
||||
JSAtom atom)
|
||||
{
|
||||
|
@ -16500,8 +16536,15 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
|||
}
|
||||
BREAK;
|
||||
CASE(OP_check_brand):
|
||||
if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)
|
||||
goto exception;
|
||||
{
|
||||
int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]);
|
||||
if (ret < 0)
|
||||
goto exception;
|
||||
if (!ret) {
|
||||
JS_ThrowTypeError(ctx, "invalid brand on object");
|
||||
goto exception;
|
||||
}
|
||||
}
|
||||
BREAK;
|
||||
CASE(OP_add_brand):
|
||||
if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
|
||||
|
@ -18156,6 +18199,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
|
|||
goto exception;
|
||||
sp--;
|
||||
BREAK;
|
||||
CASE(OP_private_in):
|
||||
if (js_operator_private_in(ctx, sp))
|
||||
goto exception;
|
||||
sp--;
|
||||
BREAK;
|
||||
CASE(OP_instanceof):
|
||||
if (js_operator_instanceof(ctx, sp))
|
||||
goto exception;
|
||||
|
@ -22642,8 +22690,9 @@ static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)
|
|||
typedef struct {
|
||||
JSFunctionDef *fields_init_fd;
|
||||
int computed_fields_count;
|
||||
BOOL has_brand;
|
||||
BOOL need_brand;
|
||||
int brand_push_pos;
|
||||
BOOL is_static;
|
||||
} ClassFieldsDef;
|
||||
|
||||
static __exception int emit_class_init_start(JSParseState *s,
|
||||
|
@ -22657,41 +22706,27 @@ static __exception int emit_class_init_start(JSParseState *s,
|
|||
|
||||
s->cur_func = cf->fields_init_fd;
|
||||
|
||||
/* XXX: would be better to add the code only if needed, maybe in a
|
||||
later pass */
|
||||
emit_op(s, OP_push_false); /* will be patched later */
|
||||
cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
|
||||
label_add_brand = emit_goto(s, OP_if_false, -1);
|
||||
if (!cf->is_static) {
|
||||
/* add the brand to the newly created instance */
|
||||
/* XXX: would be better to add the code only if needed, maybe in a
|
||||
later pass */
|
||||
emit_op(s, OP_push_false); /* will be patched later */
|
||||
cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
|
||||
label_add_brand = emit_goto(s, OP_if_false, -1);
|
||||
|
||||
emit_op(s, OP_scope_get_var);
|
||||
emit_atom(s, JS_ATOM_this);
|
||||
emit_u16(s, 0);
|
||||
emit_op(s, OP_scope_get_var);
|
||||
emit_atom(s, JS_ATOM_this);
|
||||
emit_u16(s, 0);
|
||||
|
||||
emit_op(s, OP_scope_get_var);
|
||||
emit_atom(s, JS_ATOM_home_object);
|
||||
emit_u16(s, 0);
|
||||
emit_op(s, OP_scope_get_var);
|
||||
emit_atom(s, JS_ATOM_home_object);
|
||||
emit_u16(s, 0);
|
||||
|
||||
emit_op(s, OP_add_brand);
|
||||
emit_op(s, OP_add_brand);
|
||||
|
||||
emit_label(s, label_add_brand);
|
||||
|
||||
s->cur_func = s->cur_func->parent;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf)
|
||||
{
|
||||
if (!cf->has_brand) {
|
||||
/* define the brand field in 'this' of the initializer */
|
||||
if (!cf->fields_init_fd) {
|
||||
if (emit_class_init_start(s, cf))
|
||||
return -1;
|
||||
}
|
||||
/* patch the start of the function to enable the OP_add_brand code */
|
||||
cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
|
||||
|
||||
cf->has_brand = TRUE;
|
||||
emit_label(s, label_add_brand);
|
||||
}
|
||||
s->cur_func = s->cur_func->parent;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -22798,7 +22833,8 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
|
|||
ClassFieldsDef *cf = &class_fields[i];
|
||||
cf->fields_init_fd = NULL;
|
||||
cf->computed_fields_count = 0;
|
||||
cf->has_brand = FALSE;
|
||||
cf->need_brand = FALSE;
|
||||
cf->is_static = i;
|
||||
}
|
||||
|
||||
ctor_fd = NULL;
|
||||
|
@ -22905,8 +22941,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
|
|||
JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0)
|
||||
goto fail;
|
||||
}
|
||||
if (add_brand(s, &class_fields[is_static]) < 0)
|
||||
goto fail;
|
||||
class_fields[is_static].need_brand = TRUE;
|
||||
}
|
||||
|
||||
if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
|
||||
|
@ -23053,8 +23088,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
|
|||
func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
|
||||
}
|
||||
if (is_private) {
|
||||
if (add_brand(s, &class_fields[is_static]) < 0)
|
||||
goto fail;
|
||||
class_fields[is_static].need_brand = TRUE;
|
||||
}
|
||||
if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd))
|
||||
goto fail;
|
||||
|
@ -23120,12 +23154,29 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
|
|||
if (next_token(s))
|
||||
goto fail;
|
||||
|
||||
/* store the function to initialize the fields to that it can be
|
||||
referenced by the constructor */
|
||||
{
|
||||
ClassFieldsDef *cf = &class_fields[0];
|
||||
int var_idx;
|
||||
|
||||
if (cf->need_brand) {
|
||||
/* add a private brand to the prototype */
|
||||
emit_op(s, OP_dup);
|
||||
emit_op(s, OP_null);
|
||||
emit_op(s, OP_swap);
|
||||
emit_op(s, OP_add_brand);
|
||||
|
||||
/* define the brand field in 'this' of the initializer */
|
||||
if (!cf->fields_init_fd) {
|
||||
if (emit_class_init_start(s, cf))
|
||||
goto fail;
|
||||
}
|
||||
/* patch the start of the function to enable the
|
||||
OP_add_brand_instance code */
|
||||
cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
|
||||
}
|
||||
|
||||
/* store the function to initialize the fields to that it can be
|
||||
referenced by the constructor */
|
||||
var_idx = define_var(s, fd, JS_ATOM_class_fields_init,
|
||||
JS_VAR_DEF_CONST);
|
||||
if (var_idx < 0)
|
||||
|
@ -23143,6 +23194,13 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
|
|||
/* drop the prototype */
|
||||
emit_op(s, OP_drop);
|
||||
|
||||
if (class_fields[1].need_brand) {
|
||||
/* add a private brand to the class */
|
||||
emit_op(s, OP_dup);
|
||||
emit_op(s, OP_dup);
|
||||
emit_op(s, OP_add_brand);
|
||||
}
|
||||
|
||||
/* initialize the static fields */
|
||||
if (class_fields[1].fields_init_fd != NULL) {
|
||||
ClassFieldsDef *cf = &class_fields[1];
|
||||
|
@ -25013,9 +25071,32 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level,
|
|||
if (level == 0) {
|
||||
return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) |
|
||||
PF_POW_ALLOWED);
|
||||
} else if (s->token.val == TOK_PRIVATE_NAME &&
|
||||
(parse_flags & PF_IN_ACCEPTED) && level == 4 &&
|
||||
peek_token(s, FALSE) == TOK_IN) {
|
||||
JSAtom atom;
|
||||
|
||||
atom = JS_DupAtom(s->ctx, s->token.u.ident.atom);
|
||||
if (next_token(s))
|
||||
goto fail_private_in;
|
||||
if (s->token.val != TOK_IN)
|
||||
goto fail_private_in;
|
||||
if (next_token(s))
|
||||
goto fail_private_in;
|
||||
if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC)) {
|
||||
fail_private_in:
|
||||
JS_FreeAtom(s->ctx, atom);
|
||||
return -1;
|
||||
}
|
||||
emit_op(s, OP_scope_in_private_field);
|
||||
emit_atom(s, atom);
|
||||
emit_u16(s, s->cur_func->scope_level);
|
||||
JS_FreeAtom(s->ctx, atom);
|
||||
return 0;
|
||||
} else {
|
||||
if (js_parse_expr_binary(s, level - 1, parse_flags))
|
||||
return -1;
|
||||
}
|
||||
if (js_parse_expr_binary(s, level - 1, parse_flags))
|
||||
return -1;
|
||||
for(;;) {
|
||||
op = s->token.val;
|
||||
switch(level) {
|
||||
|
@ -30507,6 +30588,10 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
|
|||
abort();
|
||||
}
|
||||
break;
|
||||
case OP_scope_in_private_field:
|
||||
get_loc_or_ref(bc, is_ref, idx);
|
||||
dbuf_putc(bc, OP_private_in);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
@ -31213,6 +31298,7 @@ static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
|
|||
case OP_scope_get_private_field:
|
||||
case OP_scope_get_private_field2:
|
||||
case OP_scope_put_private_field:
|
||||
case OP_scope_in_private_field:
|
||||
{
|
||||
int ret;
|
||||
var_name = get_u32(bc_buf + pos + 1);
|
||||
|
|
|
@ -76,7 +76,7 @@ caller
|
|||
change-array-by-copy
|
||||
class
|
||||
class-fields-private
|
||||
class-fields-private-in=skip
|
||||
class-fields-private-in
|
||||
class-fields-public
|
||||
class-methods-private
|
||||
class-static-block
|
||||
|
|
|
@ -11,6 +11,8 @@ test262/test/language/expressions/assignment/target-member-computed-reference-un
|
|||
test262/test/language/expressions/assignment/target-member-computed-reference-undefined.js:32: strict mode: Test262Error: Expected a DummyError but got a TypeError
|
||||
test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: TypeError: $DONE() not called
|
||||
test262/test/language/expressions/dynamic-import/usage-from-eval.js:26: strict mode: TypeError: $DONE() not called
|
||||
test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: unexpected error type: Test262: This statement should not be evaluated.
|
||||
test262/test/language/expressions/in/private-field-invalid-assignment-target.js:23: strict mode: unexpected error type: Test262: This statement should not be evaluated.
|
||||
test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:21: TypeError: cannot read property 'c' of undefined
|
||||
test262/test/language/expressions/optional-chaining/optional-call-preserves-this.js:15: strict mode: TypeError: cannot read property '_b' of undefined
|
||||
test262/test/language/global-code/script-decl-lex-var-declared-via-eval-sloppy.js:13: Test262Error: variable Expected a SyntaxError to be thrown but no exception was thrown at all
|
||||
|
|
Loading…
Reference in a new issue