rm for ... in

This commit is contained in:
2026-01-19 18:56:54 -06:00
parent 9b3891c126
commit ff18682485
17 changed files with 72 additions and 575 deletions

View File

@@ -179,9 +179,6 @@ DEF( make_arg_ref, 7, 0, 2, atom_u16)
DEF(make_var_ref_ref, 7, 0, 2, atom_u16)
DEF( make_var_ref, 5, 0, 2, atom)
DEF( for_in_start, 1, 1, 1, none)
DEF( for_in_next, 1, 1, 3, none)
/* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none)
DEF( plus, 1, 1, 1, none)

View File

@@ -168,7 +168,6 @@ enum {
JS_CLASS_BYTECODE_FUNCTION, /* u.func */
JS_CLASS_BOUND_FUNCTION, /* u.bound_function */
JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */
JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */
JS_CLASS_REGEXP, /* u.regexp */
JS_CLASS_FINALIZATION_REGISTRY,
JS_CLASS_BLOB, /* u.opaque (blob *) */
@@ -682,15 +681,6 @@ typedef struct JSBoundFunction {
JSValue argv[0];
} JSBoundFunction;
typedef struct JSForInIterator {
JSValue obj;
uint32_t idx;
uint32_t atom_count;
uint8_t in_prototype_chain;
uint8_t is_array;
JSPropertyEnum *tab_atom; /* is_array = FALSE */
} JSForInIterator;
/* Used by js_object_keys and related functions */
typedef enum JSIteratorKindEnum {
JS_ITERATOR_KIND_KEY,
@@ -823,7 +813,6 @@ struct JSObject {
void *opaque;
struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
struct JSFunctionBytecode *function_bytecode;
JSVarRef **var_refs;
@@ -949,9 +938,6 @@ static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);
static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);
static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func);
static void js_regexp_finalizer(JSRuntime *rt, JSValue val);
#define HINT_STRING 0
@@ -1240,7 +1226,6 @@ static JSClassShortDef const js_std_class_def[] = {
{ JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
{ JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
{ JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
{ JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */
{ JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */
};
@@ -5316,30 +5301,6 @@ static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
JS_MarkValue(rt, bf->argv[i], mark_func);
}
static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JSForInIterator *it = p->u.for_in_iterator;
int i;
JS_FreeValueRT(rt, it->obj);
if (!it->is_array) {
for(i = 0; i < it->atom_count; i++) {
JS_FreeAtomRT(rt, it->tab_atom[i].atom);
}
js_free_rt(rt, it->tab_atom);
}
js_free_rt(rt, it);
}
static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func)
{
JSObject *p = JS_VALUE_GET_OBJ(val);
JSForInIterator *it = p->u.for_in_iterator;
JS_MarkValue(rt, it->obj, mark_func);
}
static void free_object(JSRuntime *rt, JSObject *p)
{
int i;
@@ -5955,16 +5916,6 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
}
}
break;
case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */
{
JSForInIterator *it = p->u.for_in_iterator;
if (it) {
compute_value_size(it->obj, hp);
s->memory_used_count += 1;
s->memory_used_size += sizeof(*it);
}
}
break;
case JS_CLASS_REGEXP: /* u.regexp */
compute_jsstring_size(p->u.regexp.pattern, hp);
compute_jsstring_size(p->u.regexp.bytecode, hp);
@@ -11534,236 +11485,6 @@ static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *
return val;
}
static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
{
JSObject *p, *p1;
JSPropertyEnum *tab_atom;
int i;
JSValue enum_obj;
JSForInIterator *it;
uint32_t tag, tab_atom_count;
tag = JS_VALUE_GET_TAG(obj);
if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL) {
obj = JS_ToObjectFree(ctx, obj);
}
it = js_malloc(ctx, sizeof(*it));
if (!it) {
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);
if (JS_IsException(enum_obj)) {
js_free(ctx, it);
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
it->is_array = FALSE;
it->obj = obj;
it->idx = 0;
it->tab_atom = NULL;
it->atom_count = 0;
it->in_prototype_chain = FALSE;
p1 = JS_VALUE_GET_OBJ(enum_obj);
p1->u.for_in_iterator = it;
if (tag == JS_TAG_NULL)
return enum_obj;
p = JS_VALUE_GET_OBJ(obj);
if (p->fast_array) {
JSShape *sh;
JSShapeProperty *prs;
/* check that there are no enumerable normal fields */
sh = p->shape;
for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
if (prs->flags & JS_PROP_ENUMERABLE)
goto normal_case;
}
/* for fast arrays, we only store the number of elements */
it->is_array = TRUE;
it->atom_count = p->u.array.count;
} else {
normal_case:
if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
JS_FreeValue(ctx, enum_obj);
return JS_EXCEPTION;
}
it->tab_atom = tab_atom;
it->atom_count = tab_atom_count;
}
return enum_obj;
}
/* obj -> enum_obj */
static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
{
sp[-1] = build_for_in_iterator(ctx, sp[-1]);
if (JS_IsException(sp[-1]))
return -1;
return 0;
}
/* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */
static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx,
JSValueConst enum_obj)
{
JSObject *p;
JSForInIterator *it;
JSPropertyEnum *tab_atom;
uint32_t tab_atom_count, i;
JSValue obj1;
p = JS_VALUE_GET_OBJ(enum_obj);
it = p->u.for_in_iterator;
/* check if there are enumerable properties in the prototype chain (fast path) */
obj1 = JS_DupValue(ctx, it->obj);
for(;;) {
obj1 = JS_GetPrototypeFree(ctx, obj1);
if (JS_IsNull(obj1))
break;
if (JS_IsException(obj1))
goto fail;
if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
JS_VALUE_GET_OBJ(obj1),
JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) {
JS_FreeValue(ctx, obj1);
goto fail;
}
JS_FreePropertyEnum(ctx, tab_atom, tab_atom_count);
if (tab_atom_count != 0) {
JS_FreeValue(ctx, obj1);
goto slow_path;
}
/* must check for timeout to avoid infinite loop */
if (js_poll_interrupts(ctx)) {
JS_FreeValue(ctx, obj1);
goto fail;
}
}
JS_FreeValue(ctx, obj1);
return 1;
slow_path:
/* add the visited properties, even if they are not enumerable */
if (it->is_array) {
if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
JS_VALUE_GET_OBJ(it->obj),
JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
goto fail;
}
it->is_array = FALSE;
it->tab_atom = tab_atom;
it->atom_count = tab_atom_count;
}
for(i = 0; i < it->atom_count; i++) {
if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0)
goto fail;
}
return 0;
fail:
return -1;
}
/* enum_obj -> enum_obj value done */
static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
{
JSValueConst enum_obj;
JSObject *p;
JSAtom prop;
JSForInIterator *it;
JSPropertyEnum *tab_atom;
uint32_t tab_atom_count;
int ret;
enum_obj = sp[-1];
/* fail safe */
if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)
goto done;
p = JS_VALUE_GET_OBJ(enum_obj);
if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)
goto done;
it = p->u.for_in_iterator;
for(;;) {
if (it->idx >= it->atom_count) {
if (JS_IsNull(it->obj) || JS_IsNull(it->obj))
goto done; /* not an object */
/* no more property in the current object: look in the prototype */
if (!it->in_prototype_chain) {
ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj);
if (ret < 0)
return -1;
if (ret)
goto done;
it->in_prototype_chain = TRUE;
}
it->obj = JS_GetPrototypeFree(ctx, it->obj);
if (JS_IsException(it->obj))
return -1;
if (JS_IsNull(it->obj))
goto done; /* no more prototype */
/* must check for timeout to avoid infinite loop */
if (js_poll_interrupts(ctx))
return -1;
if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
JS_VALUE_GET_OBJ(it->obj),
JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
return -1;
}
JS_FreePropertyEnum(ctx, it->tab_atom, it->atom_count);
it->tab_atom = tab_atom;
it->atom_count = tab_atom_count;
it->idx = 0;
} else {
if (it->is_array) {
prop = __JS_AtomFromUInt32(it->idx);
it->idx++;
} else {
BOOL is_enumerable;
prop = it->tab_atom[it->idx].atom;
is_enumerable = it->tab_atom[it->idx].is_enumerable;
it->idx++;
if (it->in_prototype_chain) {
/* slow case: we are in the prototype chain */
ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop);
if (ret < 0)
return ret;
if (ret)
continue; /* already visited */
/* add to the visited property list */
if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL,
JS_PROP_ENUMERABLE) < 0)
return -1;
}
if (!is_enumerable)
continue;
}
/* check if the property was deleted */
ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->obj), prop);
if (ret < 0)
return ret;
if (ret)
break;
}
}
/* return the property */
sp[0] = JS_AtomToValue(ctx, prop);
sp[1] = JS_FALSE;
return 0;
done:
/* return the end */
sp[0] = JS_NULL;
sp[1] = JS_TRUE;
return 0;
}
static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)
{
/* Try and handle fast arrays explicitly */
@@ -13820,17 +13541,6 @@ static JSValue JS_CallInternal_OLD(JSContext *caller_ctx, JSValueConst func_obj,
}
BREAK;
CASE(OP_for_in_start):
sf->cur_pc = pc;
if (js_for_in_start(ctx, sp))
goto exception;
BREAK;
CASE(OP_for_in_next):
sf->cur_pc = pc;
if (js_for_in_next(ctx, sp))
goto exception;
sp += 2;
BREAK;
CASE(OP_nip_catch):
{
JSValue ret_val;
@@ -20293,172 +20003,10 @@ static int is_let(JSParseState *s, int decl_mask)
return res;
}
/* for-in loop parsing (for-of is not supported) */
/* for-in and for-of loops are not supported */
static __exception int js_parse_for_in_of(JSParseState *s, int label_name)
{
JSContext *ctx = s->ctx;
JSFunctionDef *fd = s->cur_func;
JSAtom var_name;
BOOL has_initializer, has_destructuring;
int tok, tok1, opcode, scope, block_scope_level;
int label_next, label_expr, label_cont, label_body, label_break;
int pos_next, pos_expr;
BlockEnv break_entry;
has_initializer = FALSE;
has_destructuring = FALSE;
block_scope_level = fd->scope_level;
label_cont = new_label(s);
label_body = new_label(s);
label_break = new_label(s);
label_next = new_label(s);
/* create scope for the lexical variables declared in the enumeration
expressions. XXX: Not completely correct because of weird capturing
semantics in `for (i of o) a.push(function(){return i})` */
push_scope(s);
/* local for_in scope starts here so individual elements
can be closed in statement. */
push_break_entry(s->cur_func, &break_entry,
label_name, label_break, label_cont, 1);
break_entry.scope_level = block_scope_level;
label_expr = emit_goto(s, OP_goto, -1);
pos_next = s->cur_func->byte_code.size;
emit_label(s, label_next);
tok = s->token.val;
if (tok == TOK_VAR || tok == TOK_DEF) {
if (next_token(s))
return -1;
if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
if (s->token.val == '[' || s->token.val == '{') {
if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE, FALSE) < 0)
return -1;
has_destructuring = TRUE;
} else {
return js_parse_error(s, "variable name expected");
}
var_name = JS_ATOM_NULL;
} else {
var_name = JS_DupAtom(ctx, s->token.u.ident.atom);
if (next_token(s)) {
JS_FreeAtom(s->ctx, var_name);
return -1;
}
if (js_define_var(s, var_name, tok)) {
JS_FreeAtom(s->ctx, var_name);
return -1;
}
emit_op(s, (tok == TOK_DEF || tok == TOK_VAR) ?
OP_scope_put_var_init : OP_scope_put_var);
emit_atom(s, var_name);
emit_u16(s, fd->scope_level);
}
} else {
int skip_bits;
if ((s->token.val == '[' || s->token.val == '{')
&& ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {
if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE, FALSE) < 0)
return -1;
} else {
int lvalue_label;
if (js_parse_left_hand_side_expr(s))
return -1;
if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,
NULL, FALSE, TOK_FOR))
return -1;
put_lvalue(s, opcode, scope, var_name, lvalue_label,
PUT_LVALUE_NOKEEP_BOTTOM, FALSE);
}
var_name = JS_ATOM_NULL;
}
emit_goto(s, OP_goto, label_body);
pos_expr = s->cur_func->byte_code.size;
emit_label(s, label_expr);
if (s->token.val == '=') {
/* XXX: potential scoping issue if inside `with` statement */
has_initializer = TRUE;
/* parse and evaluate initializer prior to evaluating the
object (only used with "for in" with a non lexical variable
in non strict mode */
if (next_token(s) || js_parse_assign_expr2(s, 0)) {
JS_FreeAtom(ctx, var_name);
return -1;
}
if (var_name != JS_ATOM_NULL) {
emit_op(s, OP_scope_put_var);
emit_atom(s, var_name);
emit_u16(s, fd->scope_level);
}
}
JS_FreeAtom(ctx, var_name);
if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
return js_parse_error(s, "'for of' loops are not supported");
} else if (s->token.val == TOK_IN) {
if (has_initializer &&
(tok != TOK_VAR || has_destructuring)) {
return js_parse_error(s, "a declaration in the head of a for-in loop can't have an initializer");
}
} else {
return js_parse_error(s, "expected 'in' in for control expression");
}
if (next_token(s))
return -1;
if (js_parse_expr(s))
return -1;
/* close the scope after having evaluated the expression so that
the TDZ values are in the closures */
close_scopes(s, s->cur_func->scope_level, block_scope_level);
emit_op(s, OP_for_in_start);
/* on stack: enum_obj */
emit_goto(s, OP_goto, label_cont);
if (js_parse_expect(s, ')'))
return -1;
if (OPTIMIZE) {
/* move the `next` code here */
DynBuf *bc = &s->cur_func->byte_code;
int chunk_size = pos_expr - pos_next;
int offset = bc->size - pos_next;
int i;
dbuf_realloc(bc, bc->size + chunk_size);
dbuf_put(bc, bc->buf + pos_next, chunk_size);
memset(bc->buf + pos_next, OP_nop, chunk_size);
/* `next` part ends with a goto */
s->cur_func->last_opcode_pos = bc->size - 5;
/* relocate labels */
for (i = label_cont; i < s->cur_func->label_count; i++) {
LabelSlot *ls = &s->cur_func->label_slots[i];
if (ls->pos >= pos_next && ls->pos < pos_expr)
ls->pos += offset;
}
}
emit_label(s, label_body);
if (js_parse_statement(s))
return -1;
close_scopes(s, s->cur_func->scope_level, block_scope_level);
emit_label(s, label_cont);
emit_op(s, OP_for_in_next);
/* on stack: enum_obj value bool */
emit_goto(s, OP_if_false, label_next);
/* drop the undefined value from for_in_next */
emit_op(s, OP_drop);
emit_label(s, label_break);
emit_op(s, OP_drop);
pop_break_entry(s->cur_func);
pop_scope(s);
return 0;
return js_parse_error(s, "'for in' and 'for of' loops are not supported");
}
static void set_eval_ret_undefined(JSParseState *s)