From ff18682485239722e7e380068686c20ff2105924 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Mon, 19 Jan 2026 18:56:54 -0600 Subject: [PATCH] rm for ... in --- bench.ce | 9 +- benchmarks/fib.ce | 4 +- config.ce | 4 +- graph.ce | 12 +- install.ce | 4 +- internal/engine.cm | 10 +- internal/shop.cm | 12 +- link.ce | 4 +- link.cm | 35 ++- list.ce | 3 +- package.cm | 21 +- resolve.ce | 10 +- source/quickjs-opcode.h | 3 - source/quickjs.c | 456 +--------------------------------------- test.ce | 4 +- tests/suite.cm | 48 ----- verify.ce | 8 +- 17 files changed, 72 insertions(+), 575 deletions(-) diff --git a/bench.ce b/bench.ce index 94b3e532..0b930202 100644 --- a/bench.ce +++ b/bench.ce @@ -441,11 +441,10 @@ function run_benchmarks(package_name, specific_bench) { if (is_function(bench_mod)) { benches.push({name: 'main', fn: bench_mod}) } else if (is_object(bench_mod)) { - for (var k in bench_mod) { - if (is_function(bench_mod[k])) { + arrfor(array(bench_mod), function(k) { + if (is_function(bench_mod[k])) benches.push({name: k, fn: bench_mod[k]}) - } - } + }) } if (length(benches) > 0) { @@ -474,7 +473,7 @@ function run_benchmarks(package_name, specific_bench) { file_result.benchmarks.push(error_result) pkg_result.total++ } - } + }) } } catch (e) { log.console(` Error loading ${f}: ${e}`) diff --git a/benchmarks/fib.ce b/benchmarks/fib.ce index 625549ca..98f92c54 100644 --- a/benchmarks/fib.ce +++ b/benchmarks/fib.ce @@ -7,9 +7,9 @@ function fib(n) { var now = time.number() var arr = [1,2,3,4,5] -for (var i in arr) { +arrfor(arr, function(i) { log.console(fib(28)) -} +}) log.console(`elapsed: ${time.number()-now}`) diff --git a/config.ce b/config.ce index 86ea1ce0..e62b86b4 100644 --- a/config.ce +++ b/config.ce @@ -84,7 +84,7 @@ function format_value(val) { // Print configuration tree recursively function print_config(obj, prefix = '') { - for (var key in obj) { + arrfor(array(obj), function(key) { var val = obj[key] var full_key = prefix ? prefix + '.' + key : key @@ -92,7 +92,7 @@ function print_config(obj, prefix = '') { print_config(val, full_key) else log.console(full_key + ' = ' + format_value(val)) - } + }) } // Main command handling diff --git a/graph.ce b/graph.ce index 0efd1042..8c111c26 100644 --- a/graph.ce +++ b/graph.ce @@ -94,12 +94,12 @@ function gather_graph(locator, visited) { try { var deps = pkg.dependencies(locator) if (deps) { - for (var alias in deps) { + arrfor(array(deps), function(alias) { var dep_locator = deps[alias] add_node(dep_locator) edges.push({ from: locator, to: dep_locator, alias: alias }) gather_graph(dep_locator, visited) - } + }) } } catch (e) { // Package might not have dependencies @@ -193,7 +193,7 @@ if (format == 'tree') { log.console("") // Node definitions - for (var id in nodes) { + arrfor(array(nodes), function(id) { var node = nodes[id] var label = id if (node.commit) label += "\\n@" + node.commit @@ -204,7 +204,7 @@ if (format == 'tree') { // Safe node ID for dot var safe_id = replace(id, /[^a-zA-Z0-9]/g, '_') log.console(' ' + safe_id + ' [' + attrs + '];') - } + }) log.console("") @@ -224,9 +224,9 @@ if (format == 'tree') { edges: [] } - for (var id in nodes) { + arrfor(array(nodes), function(id) { output.nodes.push(nodes[id]) - } + }) output.edges = edges diff --git a/install.ce b/install.ce index 4f364f6a..fbf6ff59 100644 --- a/install.ce +++ b/install.ce @@ -119,10 +119,10 @@ function gather_packages(pkg_locator) { var deps = pkg.dependencies(pkg_locator) if (deps) { - for (var alias in deps) { + arrfor(array(deps), function(alias) { var dep_locator = deps[alias] gather_packages(dep_locator) - } + }) } } catch (e) { // Package might not have dependencies or cell.toml issue diff --git a/internal/engine.cm b/internal/engine.cm index 0f72c725..c9c8af82 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -53,6 +53,8 @@ globalThis.ends_with = function(str, suffix) { return search(str, suffix, -length(suffix)) != null } + + var js = use_embed('js') var fd = use_embed('fd') @@ -410,7 +412,9 @@ function handle_host(e) { break case "disconnect": peer_queue.delete(e.peer) - for (var id in peers) if (peers[id] == e.peer) delete peers[id] + arrfor(array(peers), function(id) { + if (peers[id] == e.peer) delete peers[id] + }) log.system('portal got disconnect from ' + e.peer.address + ":" + e.peer.port) break case "receive": @@ -425,11 +429,11 @@ function handle_host(e) { obj[ACTORDATA].address = e.peer.address obj[ACTORDATA].port = e.peer.port } - for (var key in obj) { + arrfor(array(obj), function(key) { if (object.has(obj, key)) { populate_actor_addresses(obj[key]) } - } + }) } if (data.data) populate_actor_addresses(data.data) turn(data) diff --git a/internal/shop.cm b/internal/shop.cm index bb8b0f72..6ef206af 100644 --- a/internal/shop.cm +++ b/internal/shop.cm @@ -1,4 +1,5 @@ var toml = use('toml') + var json = use('json') var fd = use('fd') var http = use('http') @@ -7,6 +8,7 @@ var time = use('time') var js = use('js') var crypto = use('crypto') var blob = use('blob') + var pkg_tools = use('package') var os = use('os') var link = use('link') @@ -559,14 +561,14 @@ Shop.open_package_dylib = function(pkg) { var content = text(fd.slurp(toml_path)) var cfg = toml.decode(content) if (cfg.dependencies) { - for (var alias in cfg.dependencies) { + arrfor(array(cfg.dependencies), function(alias) { var dep_pkg = cfg.dependencies[alias] try { Shop.open_package_dylib(dep_pkg) } catch (dep_e) { // Dependency dylib load failed, continue with others } - } + }) } } catch (e) { // Error reading toml, continue @@ -1194,12 +1196,14 @@ Shop.module_reload = function(path, package) { var old = use_cache[cache_key] var newmod = get_module(path, package) - for (var i in newmod) + arrfor(array(newmod), function(i) { old[i] = newmod[i] + }) - for (var i in old) + arrfor(array(old), function(i) { if (!(i in newmod)) old[i] = null + }) } function get_package_scripts(package) diff --git a/link.ce b/link.ce index 257e78ac..0b1335d4 100644 --- a/link.ce +++ b/link.ce @@ -35,10 +35,10 @@ var cmd = args[0] if (cmd == 'list') { var links = link.load() var count = 0 - for (var k in links) { + arrfor(array(links), function(k) { log.console(k + " -> " + links[k]) count++ - } + }) if (count == 0) log.console("No links.") } else if (cmd == 'sync') { diff --git a/link.cm b/link.cm index 51daf2f1..6778d23b 100644 --- a/link.cm +++ b/link.cm @@ -120,12 +120,12 @@ Link.add = function(canonical, target, shop) { var content = text(fd.slurp(toml_path)) var cfg = toml.decode(content) if (cfg.dependencies) { - for (var alias in cfg.dependencies) { + arrfor(array(cfg.dependencies), function(alias) { var dep_locator = cfg.dependencies[alias] // Skip local dependencies that don't exist if (starts_with(dep_locator, '/') && !fd.is_dir(dep_locator)) { log.console(" Skipping missing local dependency: " + dep_locator) - continue + return } // Install the dependency if not already in shop try { @@ -135,7 +135,7 @@ Link.add = function(canonical, target, shop) { log.console(` Warning: Could not install dependency ${dep_locator}: ${e.message}`) log.error(e) } - } + }) } } catch (e) { log.console(` Warning: Could not read dependencies from ${toml_path}`) @@ -166,12 +166,12 @@ Link.remove = function(canonical) { Link.clear = function() { // Remove all symlinks first var links = Link.load() - for (var canonical in links) { + arrfor(array(links), function(canonical) { var target_dir = get_package_abs_dir(canonical) if (fd.is_link(target_dir)) { fd.unlink(target_dir) } - } + }) Link.save({}) log.console("Cleared all links") @@ -216,18 +216,18 @@ Link.sync_all = function(shop) { var count = 0 var errors = [] - for (var canonical in links) { + arrfor(array(links), function(canonical) { var target = links[canonical] try { // Validate target exists var link_target = resolve_link_target(target) if (!fd.is_dir(link_target)) { errors.push(canonical + ': target ' + link_target + ' does not exist') - continue + return } if (!fd.is_file(link_target + '/cell.toml')) { errors.push(canonical + ': target ' + link_target + ' is not a valid package') - continue + return } Link.sync_one(canonical, target, shop) @@ -238,11 +238,11 @@ Link.sync_all = function(shop) { var content = text(fd.slurp(toml_path)) var cfg = toml.decode(content) if (cfg.dependencies) { - for (var alias in cfg.dependencies) { + arrfor(array(cfg.dependencies), function(alias) { var dep_locator = cfg.dependencies[alias] // Skip local dependencies that don't exist if (starts_with(dep_locator, '/') && !fd.is_dir(dep_locator)) { - continue + return } // Install the dependency if not already in shop try { @@ -251,7 +251,7 @@ Link.sync_all = function(shop) { } catch (e) { // Silently continue - dependency may already be installed } - } + }) } } catch (e) { // Could not read dependencies - continue anyway @@ -261,7 +261,7 @@ Link.sync_all = function(shop) { } catch (e) { errors.push(canonical + ': ' + e.message) } - } + }) return { synced: count, errors: errors } } @@ -282,12 +282,11 @@ Link.get_target = function(canonical) { // Returns null if no package links to this target Link.get_origin = function(target) { var links = Link.load() - for (var origin in links) { - if (links[origin] == target) { - return origin - } - } - return null + var found = null + arrfor(array(links), function(origin) { + if (links[origin] == target) found = origin + }) + return found } return Link diff --git a/list.ce b/list.ce index fdac8a01..1326c25a 100644 --- a/list.ce +++ b/list.ce @@ -58,8 +58,7 @@ function print_deps(ctx, indent) { return } - var aliases = [] - for (var k in deps) aliases.push(k) + var aliases = array(deps) aliases = sort(aliases) if (length(aliases) == 0) { diff --git a/package.cm b/package.cm index ba91c1ef..b0e3cb04 100644 --- a/package.cm +++ b/package.cm @@ -1,5 +1,4 @@ var package = {} - var fd = use('fd') var toml = use('toml') var json = use('json') @@ -76,11 +75,11 @@ package.find_alias = function(name, locator) var config = package.load_config(name) if (!config.dependencies) return null - for (var alias in config.dependencies) - if (config.dependencies[alias] == locator) - return alias - - return null + var found = null + arrfor(array(config.dependencies), function(alias) { + if (config.dependencies[alias] == locator) found = alias + }) + return found } package.alias_to_package = function(name, alias) @@ -177,13 +176,13 @@ package.gather_dependencies = function(name) var deps = package.dependencies(pkg_name) if (!deps) return - for (var alias in deps) { + arrfor(array(deps), function(alias) { var locator = deps[alias] if (!all_deps[locator]) { all_deps[locator] = true gather_recursive(locator) } - } + }) } gather_recursive(name) @@ -319,7 +318,7 @@ package.get_c_files = function(name, target, exclude_main) { // Select appropriate file from each group var result = [] - for (var key in groups) { + arrfor(array(groups), function(key) { var group = groups[key] var selected = null @@ -334,11 +333,11 @@ package.get_c_files = function(name, target, exclude_main) { // Skip main.c if requested if (exclude_main) { var basename = fd.basename(selected) - if (basename == 'main.c' || starts_with(basename, 'main_')) continue + if (basename == 'main.c' || starts_with(basename, 'main_')) return } result.push(selected) } - } + }) return result } diff --git a/resolve.ce b/resolve.ce index 7ef1bba1..891cda43 100644 --- a/resolve.ce +++ b/resolve.ce @@ -92,10 +92,10 @@ function gather_deps(locator, depth) { try { var deps = pkg.dependencies(locator) if (deps) { - for (var alias in deps) { + arrfor(array(deps), function(alias) { var dep_locator = deps[alias] gather_deps(dep_locator, depth + 1) - } + }) } } catch (e) { // Package might not have dependencies @@ -110,11 +110,7 @@ log.console("Target: " + target_triple) log.console("") // Sort by depth then alphabetically -var sorted = [] -for (var locator in all_deps) { - sorted.push({ locator: locator, depth: all_deps[locator].depth }) -} - +var sorted = array(array(all_deps), function(locator) { return { locator: locator, depth: all_deps[locator].depth } }) sorted = sort(sorted, "locator") sorted = sort(sorted, "depth") diff --git a/source/quickjs-opcode.h b/source/quickjs-opcode.h index 612fd9de..602266de 100644 --- a/source/quickjs-opcode.h +++ b/source/quickjs-opcode.h @@ -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) diff --git a/source/quickjs.c b/source/quickjs.c index 0c16de29..ec60a956 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -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) diff --git a/test.ce b/test.ce index 0e2ac28c..7da7b400 100644 --- a/test.ce +++ b/test.ce @@ -289,11 +289,11 @@ function run_tests(package_name, specific_test) { if (is_function(test_mod)) { tests.push({name: 'main', fn: test_mod}) } else if (is_object(test_mod)) { - for (var k in test_mod) { + arrfor(array(test_mod), function(k) { if (is_function(test_mod[k])) { tests.push({name: k, fn: test_mod[k]}) } - } + }) } if (length(tests) > 0) { diff --git a/tests/suite.cm b/tests/suite.cm index 4892097d..715ea4ee 100644 --- a/tests/suite.cm +++ b/tests/suite.cm @@ -781,24 +781,6 @@ return { if (sum != 25) throw "for loop continue failed" }, - test_for_in_array: function() { - var arr = [10, 20, 30] - var sum = 0 - for (var i in arr) { - sum = sum + arr[i] - } - if (sum != 60) throw "for in array failed" - }, - - test_for_in_object: function() { - var obj = {a: 1, b: 2, c: 3} - var sum = 0 - for (var key in obj) { - sum = sum + obj[key] - } - if (sum != 6) throw "for in object failed" - }, - test_nested_for_loops: function() { var sum = 0 for (var i = 0; i < 3; i = i + 1) { @@ -1658,18 +1640,6 @@ return { if (o[k] != 200) throw "same object should be same key" }, - test_object_key_not_in_for_in: function() { - var k = {} - var o = {a: 1, b: 2} - o[k] = 999 - var count = 0 - for (var key in o) { - count = count + 1 - if (key == k) throw "object key should not appear in for-in" - } - if (count != 2) throw "for-in should only see string keys" - }, - test_object_key_computed_property: function() { var k = {} var o = {} @@ -3462,24 +3432,6 @@ return { if (data.users[1].name != "Bob") throw "mixed nested access failed" }, - test_empty_object_iteration: function() { - var obj = {} - var count = 0 - for (var k in obj) { - count++ - } - if (count != 0) throw "empty object iteration failed" - }, - - test_empty_array_iteration: function() { - var arr = [] - var count = 0 - for (var i in arr) { - count++ - } - if (count != 0) throw "empty array iteration failed" - }, - test_object_with_null_value: function() { var obj = {a: null, b: 2} if (obj.a != null) throw "object null value failed" diff --git a/verify.ce b/verify.ce index 36e1b54d..b01002f6 100644 --- a/verify.ce +++ b/verify.ce @@ -166,26 +166,26 @@ function check_link_cycles() { return null } - for (var origin in links) { + arrfor(links, function(origin) { var cycle_start = follow_chain(origin, {}) if (cycle_start) { add_error("Link cycle detected starting from: " + origin) } - } + }) } // Check for dangling links function check_dangling_links() { var links = link.load() - for (var origin in links) { + arrfor(array(links), function(origin) { var target = links[origin] if (starts_with(target, '/')) { if (!fd.is_dir(target)) { add_error("Dangling link: " + origin + " -> " + target + " (target does not exist)") } } - } + }) } // Gather packages to verify