diff --git a/fd.c b/fd.c index 832cdfb1..252610f3 100644 --- a/fd.c +++ b/fd.c @@ -504,7 +504,7 @@ JSC_SCALL(fd_readdir, ret = JS_NewArray(js); do { if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue; - JS_ArrayPush(js, ret,JS_NewString(js, ffd.cFileName)); + JS_ArrayPush(js, &ret, JS_NewString(js, ffd.cFileName)); } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); } @@ -516,7 +516,7 @@ JSC_SCALL(fd_readdir, ret = JS_NewArray(js); while ((dir = readdir(d)) != NULL) { if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue; - JS_ArrayPush(js, ret, JS_NewString(js, dir->d_name)); + JS_ArrayPush(js, &ret, JS_NewString(js, dir->d_name)); } closedir(d); } else { @@ -565,18 +565,22 @@ JSC_CCALL(fd_slurpwrite, if (!str) return JS_EXCEPTION; int fd = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { + ret = JS_ThrowInternalError(js, "open failed for %s: %s", str, strerror(errno)); JS_FreeCString(js, str); - return JS_ThrowInternalError(js, "open failed for %s: %s", str, strerror(errno)); + return ret; } - + ssize_t written = write(fd, data, len); close(fd); + if (written != (ssize_t)len) { + ret = JS_ThrowInternalError(js, "write failed for %s: %s", str, strerror(errno)); + JS_FreeCString(js, str); + return ret; + } + JS_FreeCString(js, str); - if (written != (ssize_t)len) - return JS_ThrowInternalError(js, "write failed for %s: %s", str, strerror(errno)); - return JS_NULL; ) @@ -664,17 +668,21 @@ JSC_CCALL(fd_realpath, #ifdef _WIN32 char resolved[PATH_MAX]; DWORD len = GetFullPathNameA(path, PATH_MAX, resolved, NULL); - JS_FreeCString(js, path); if (len == 0 || len >= PATH_MAX) { - return JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno)); + JSValue err = JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno)); + JS_FreeCString(js, path); + return err; } + JS_FreeCString(js, path); return JS_NewString(js, resolved); #else char *resolved = realpath(path, NULL); - JS_FreeCString(js, path); if (!resolved) { - return JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno)); + JSValue err = JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno)); + JS_FreeCString(js, path); + return err; } + JS_FreeCString(js, path); JSValue result = JS_NewString(js, resolved); free(resolved); return result; diff --git a/fd.cm b/fd.cm index 01a618fc..cc0ffbf2 100644 --- a/fd.cm +++ b/fd.cm @@ -1,4 +1,4 @@ -var fd = this +var fd = native var wildstar = use('wildstar') function last_pos(str, sep) { diff --git a/internal/bootstrap.cm b/internal/bootstrap.cm index fabcea7b..09fb543f 100644 --- a/internal/bootstrap.cm +++ b/internal/bootstrap.cm @@ -242,7 +242,7 @@ var script = null var ast = null if (args != null) { - // CLI mode — run script directly + // CLI mode — parse args program = args[0] _j = 1 while (_j < length(args)) { @@ -250,11 +250,11 @@ if (args != null) { _j = _j + 1 } + // Resolve script file: try .cm then .ce in CWD then core_path script_file = program if (!ends_with(script_file, '.ce') && !ends_with(script_file, '.cm')) script_file = program + '.cm' - // Search CWD then core_path, trying .cm then .ce if (!fd.is_file(script_file)) script_file = core_path + '/' + program + '.cm' if (!fd.is_file(script_file)) @@ -262,9 +262,20 @@ if (args != null) { if (!fd.is_file(script_file)) script_file = core_path + '/' + program + '.ce' - script = text(fd.slurp(script_file)) - ast = analyze(script, script_file) - run_ast(program, ast, {use: use_fn, args: user_args, json: json}) + if (ends_with(script_file, '.ce')) { + // Actor script — delegate to engine + load_engine({ + os: os, actorsym: actorsym, + init: {program: program, arg: user_args}, + core_path: core_path, shop_path: shop_path, json: json, + analyze: analyze, run_ast_fn: run_ast + }) + } else { + // Module script — run directly + script = text(fd.slurp(script_file)) + ast = analyze(script, script_file) + run_ast(program, ast, {use: use_fn, args: user_args, json: json}) + } } else { // Actor spawn mode — load engine.cm with full actor env load_engine({ diff --git a/internal/bootstrap.mach b/internal/bootstrap.mach index 0a18f9cd..7276d31a 100644 Binary files a/internal/bootstrap.mach and b/internal/bootstrap.mach differ diff --git a/internal/engine.cm b/internal/engine.cm index 39d1ee97..011fdbbc 100644 --- a/internal/engine.cm +++ b/internal/engine.cm @@ -56,9 +56,13 @@ var packages_path = shop_path ? shop_path + '/packages' : null var use_cache = {} use_cache['core/os'] = os +// Extra env properties added as engine initializes (log, runtime fns, etc.) +var core_extras = {} + // Load a core module from the file system function use_core(path) { var cache_key = 'core/' + path + var env = null if (use_cache[cache_key]) return use_cache[cache_key] @@ -67,10 +71,15 @@ function use_core(path) { var script = null var ast = null + // Build env: merge core_extras, include C embed as 'native' if available + env = {use: use_core} + arrfor(array(core_extras), function(k) { env[k] = core_extras[k] }) + if (sym) env.native = sym + // Check for pre-compiled .mach file first var mach_path = core_path + '/' + path + '.mach' if (fd.is_file(mach_path)) { - result = mach_load(fd.slurp(mach_path), {use: use_core}) + result = mach_load(fd.slurp(mach_path), env) use_cache[cache_key] = result return result } @@ -80,7 +89,7 @@ function use_core(path) { if (fd.is_file(file_path)) { script = text(fd.slurp(file_path)) ast = analyze(script, file_path) - result = run_ast_fn('core:' + path, ast, {use: use_core}) + result = run_ast_fn('core:' + path, ast, env) use_cache[cache_key] = result return result } @@ -90,6 +99,9 @@ function use_core(path) { return sym } +// Load full modules via use_core (extends C embeds with .cm additions, and caches) +fd = use_core('fd') +use_core('js') var blob = use_core('blob') function actor() { @@ -109,23 +121,7 @@ var REPLYTIMEOUT = 60 // seconds before replies are ignored function caller_data(depth) { - var _depth = depth == null ? 0 : depth - var file = "nofile" - var line = 0 - var md = null - var m = null - - var caller = array(Error().stack, "\n")[1+_depth] - if (caller) { - md = extract(caller, /\((.*)\:/) - m = md ? md[1] : "SCRIPT" - if (m) file = m - md = extract(caller, /\:(\d*)\)/) - m = md ? md[1] : 0 - if (m) line = m - } - - return {file,line} + return {file: "nofile", line: 0} } function console_rec(line, file, msg) { @@ -140,9 +136,7 @@ function log(name, args) { if (name == 'console') { os.print(console_rec(caller.line, caller.file, msg)) } else if (name == 'error') { - if (msg == null) msg = Error() - if (is_proto(msg, Error)) - msg = msg.name + ": " + msg.message + "\n" + msg.stack + if (msg == null) msg = "error" os.print(console_rec(caller.line, caller.file, msg)) } else if (name == 'system') { msg = "[SYSTEM] " + msg @@ -166,7 +160,7 @@ function actor_die(err) if (overling) { if (err) { // with an err, this is a forceful disrupt - reason = (is_proto(err, Error)) ? err.stack : err + reason = err report_to_overling({type:'disrupt', reason}) } else report_to_overling({type:'stop'}) @@ -241,6 +235,9 @@ var runtime_env = { sequence: sequence } +// Make runtime functions available to modules loaded via use_core +arrfor(array(runtime_env), function(k) { core_extras[k] = runtime_env[k] }) + // Pass to os for shop to access os.runtime_env = runtime_env @@ -300,8 +297,8 @@ $_.time_limit = function(requestor, seconds) callback(val, reason) }, value) } disruption { - cancel(Error('requestor failed')) - callback(null, Error('requestor failed')) + cancel('requestor failed') + callback(null, 'requestor failed') } do_request() @@ -821,6 +818,7 @@ function enet_check() actor_mod.setname(_cell.args.program) var prog = _cell.args.program +if (ends_with(prog, '.ce')) prog = text(prog, 0, -3) var package = use_core('package') @@ -863,7 +861,11 @@ $_.clock(_ => { } var pkg = file_info ? file_info.package : null - env.use = function(path) { return shop.use(path, pkg) } + env.use = function(path) { + var ck = 'core/' + path + if (use_cache[ck]) return use_cache[ck] + return shop.use(path, pkg) + } env.args = _cell.args.arg env.log = log diff --git a/internal/engine.mach b/internal/engine.mach index f5841e61..3ec759ec 100644 Binary files a/internal/engine.mach and b/internal/engine.mach differ diff --git a/internal/shop.cm b/internal/shop.cm index 722d246d..3c84d551 100644 --- a/internal/shop.cm +++ b/internal/shop.cm @@ -80,12 +80,12 @@ function get_packages_dir() { } // Get the core directory (in the global shop) +var core_package = 'core' + Shop.get_core_dir = function() { return get_packages_dir() + '/' + core_package } -var core_package = 'core' - // Get the links file path (in the global shop) function get_links_path() { return global_shop_path + '/link.toml' @@ -400,98 +400,54 @@ Shop.get_script_capabilities = function(path) { return Shop.script_inject_for(file_info) } +// Build the env object for a module, with runtime fns and $-prefixed capabilities. +// Matches engine.cm's approach: env properties become free variables in the module. function inject_env(inject) { - // Start with runtime functions from engine var env = {} var rt = my$_.os ? my$_.os.runtime_env : null if (rt) { arrfor(array(rt), function(k) { env[k] = rt[k] }) } - // Add capability injections + // Add capability injections with $ prefix var i = 0 var inj = null var key = null for (i = 0; i < length(inject); i++) { inj = inject[i] - key = trim(inj, '$') - if (key == 'fd') env[key] = fd - else env[key] = my$_[key] + key = inj + if (key && key[0] == '$') key = text(key, 1) + if (key == 'fd') env['$fd'] = fd + else env['$' + key] = my$_[key] } return env } -function inject_bindings_code(inject) { - var lines = [] - - // Runtime function bindings - var runtime_fns = ['logical', 'some', 'every', 'starts_with', 'ends_with', - 'actor', 'is_actor', 'log', 'send', - 'fallback', 'parallel', 'race', 'sequence'] - var i = 0 - var fn = null - var inj = null - var key = null - for (i = 0; i < length(runtime_fns); i++) { - fn = runtime_fns[i] - push(lines, `var ${fn} = env["${fn}"];`) - } - - // Capability bindings ($delay, $start, etc.) - for (i = 0; i < length(inject); i++) { - inj = inject[i] - key = trim(inj, '$') - push(lines, `var $${key} = env["${key}"];`) - } - return text(lines, '\n') -} - -// Build the use function for a specific package context -function make_use_fn_code(pkg_arg) { - return `function(path) { return globalThis.use(path, ${pkg_arg}); }` -} - -// for script forms, path is the canonical path of the module -var script_form = function(path, script, pkg, inject) { - var pkg_arg = pkg ? `'${pkg}'` : 'null' - var binds = inject_bindings_code(inject) - - var fn = `(function setup_module(args, use, env){ -def arg = args; -def PACKAGE = ${pkg_arg}; -${binds} -${script} -})` - return fn -} - -// Resolve module function, hashing it in the process -// path is the exact path to the script file +// Compile a module and return its bytecode blob. +// The bytecode is cached on disk by content hash. function resolve_mod_fn(path, pkg) { if (!fd.is_file(path)) { print(`path ${path} is not a file`); disrupt } - var file_info = Shop.file_info(path) - var file_pkg = file_info.package - var inject = Shop.script_inject_for(file_info) var content = text(fd.slurp(path)) - var script = script_form(path, content, file_pkg, inject) // Check cache for pre-compiled .mach blob - var cached = pull_from_cache(stone(blob(script))) + var cached = pull_from_cache(stone(blob(content))) + var ast = null + var ast_json = null + var compiled = null if (cached) { - return mach_load(cached) + return cached } // Compile via new pipeline - var ast = analyze(script, path) - var ast_json = shop_json.encode(ast) + ast = analyze(content, path) + ast_json = shop_json.encode(ast) // Cache compiled binary - var compiled = mach_compile_ast(path, ast_json) - put_into_cache(stone(blob(script)), compiled) + compiled = mach_compile_ast(path, ast_json) + put_into_cache(stone(blob(content)), compiled) - // Evaluate to get the function object - return mach_eval_ast(path, ast_json) + return compiled } // given a path and a package context @@ -854,29 +810,26 @@ function execute_module(info) var mod_resolve = info.mod_resolve var used = null - var context = null var file_info = null var inject = null var env = null var pkg = null - var use_fn = null if (mod_resolve.scope < 900) { - context = null - if (c_resolve.scope < 900) { - context = call_c_module(c_resolve) - } - - // Get file info to determine inject list + // Build env with runtime fns, capabilities, and use function file_info = Shop.file_info(mod_resolve.path) inject = Shop.script_inject_for(file_info) env = inject_env(inject) pkg = file_info.package - use_fn = make_use_fn(pkg) + env.use = make_use_fn(pkg) - // Call with signature: setup_module(args, use, env) - // args is null for module loading - used = call(mod_resolve.symbol, context, [null, use_fn, env]) + // Add C module as native context if available + if (c_resolve.scope < 900) { + env.native = call_c_module(c_resolve) + } + + // Load compiled bytecode with env + used = mach_load(mod_resolve.symbol, env) } else if (c_resolve.scope < 900) { // C only used = call_c_module(c_resolve) @@ -884,12 +837,8 @@ function execute_module(info) print(`Module ${info.path} could not be found`); disrupt } -// if (is_function(used)) -// throw Error('C module loader returned a function; did you forget to call it?') - if (!used) { print(`Module ${info} returned null`); disrupt } -// stone(used) return used } diff --git a/parse.cm b/parse.cm index addb6868..00bb5bf8 100644 --- a/parse.cm +++ b/parse.cm @@ -1661,6 +1661,8 @@ var parse = function(tokens, src, filename, tokenizer) { operand.level = -1 } } + } else if (operand != null) { + sem_check_assign_target(scope, operand) } return null } diff --git a/parse.mach b/parse.mach index 9e259f42..22336711 100644 Binary files a/parse.mach and b/parse.mach differ diff --git a/pronto.cm b/pronto.cm index 1695890e..e860d550 100644 --- a/pronto.cm +++ b/pronto.cm @@ -44,9 +44,10 @@ function fallback(requestor_array) { var cancelled = false function cancel(reason) { + var _c = null cancelled = true if (current_cancel) { - var _c = function() { current_cancel(reason) } disruption {} + _c = function() { current_cancel(reason) } disruption {} _c() current_cancel = null } @@ -293,9 +294,10 @@ function sequence(requestor_array) { var cancelled = false function cancel(reason) { + var _c = null cancelled = true if (current_cancel) { - var _c = function() { current_cancel(reason) } disruption {} + _c = function() { current_cancel(reason) } disruption {} _c() current_cancel = null } diff --git a/source/cell.c b/source/cell.c index 88426eab..d90cb3cc 100644 --- a/source/cell.c +++ b/source/cell.c @@ -462,7 +462,7 @@ int cell_init(int argc, char **argv) if (scheduler_actor_count() > 0) { actor_loop(); exit_handler(); - return exit_code; + exit(0); } /* No actors spawned — clean up CLI context */ diff --git a/source/mach.c b/source/mach.c index 606c1afc..f180843f 100644 --- a/source/mach.c +++ b/source/mach.c @@ -608,6 +608,11 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { int ki = mach_cpool_add_str(cs, prop_name); + /* If ki overflows 8-bit C field, load key into R(base+1) register */ + if (ki >= 0xFF) { + mach_emit(cs, MACH_ABx(MACH_LOADK, base + 1, ki)); + } + /* Compile args into R(base+2)..R(base+1+nargs) */ for (int i = 0; i < nargs; i++) { int arg_reg = mach_reserve_reg(cs); @@ -617,7 +622,7 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { mach_emit(cs, MACH_ABC(MACH_MOVE, arg_reg, r, 0)); } - mach_emit(cs, MACH_ABC(MACH_CALLMETHOD, base, nargs, ki)); + mach_emit(cs, MACH_ABC(MACH_CALLMETHOD, base, nargs, ki >= 0xFF ? 0xFF : ki)); mach_free_reg_to(cs, save_freereg); if (dest >= 0 && dest != base) mach_emit(cs, MACH_ABC(MACH_MOVE, dest, base, 0)); @@ -843,19 +848,95 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { int slot = mach_find_var(cs, name); if (slot >= 0) { if (is_postfix) { - /* Return old value, then increment */ mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0)); mach_emit(cs, MACH_ABC(inc_op, slot, slot, 0)); } else { - /* Increment, then return new value */ mach_emit(cs, MACH_ABC(inc_op, slot, slot, 0)); if (dest != slot) mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0)); } return dest; } + } else if (level > 0 && name) { + /* Closure variable */ + int save = cs->freereg; + MachCompState *target = cs; + for (int i = 0; i < level; i++) target = target->parent; + int slot = mach_find_var(target, name); + int val_r = mach_reserve_reg(cs); + mach_emit(cs, MACH_ABC(MACH_GETUP, val_r, level, slot)); + if (is_postfix) + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); + mach_emit(cs, MACH_ABC(inc_op, val_r, val_r, 0)); + if (!is_postfix) + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); + mach_emit(cs, MACH_ABC(MACH_SETUP, val_r, level, slot)); + mach_free_reg_to(cs, save); + return dest; } } + /* Property access: obj.prop++ */ + if (op_kind && strcmp(op_kind, ".") == 0) { + int save = cs->freereg; + cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "expression"); + if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "left"); + cJSON *prop = cJSON_GetObjectItemCaseSensitive(operand, "name"); + if (!prop) prop = cJSON_GetObjectItemCaseSensitive(operand, "right"); + const char *prop_name = NULL; + if (cJSON_IsString(prop)) prop_name = cJSON_GetStringValue(prop); + else if (prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "value")); + if (!prop_name && prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "name")); + if (!prop_name) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(operand, "value")); + if (prop_name) { + int obj_r = mach_compile_expr(cs, obj_expr, -1); + if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; + int ki = mach_cpool_add_str(cs, prop_name); + int val_r = mach_reserve_reg(cs); + if (ki < 256) { + mach_emit(cs, MACH_ABC(MACH_GETFIELD, val_r, obj_r, ki)); + } else { + int kr = mach_reserve_reg(cs); + mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); + mach_emit(cs, MACH_ABC(MACH_GETINDEX, val_r, obj_r, kr)); + } + if (is_postfix) + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); + mach_emit(cs, MACH_ABC(inc_op, val_r, val_r, 0)); + if (!is_postfix) + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); + if (ki < 256) { + mach_emit(cs, MACH_ABC(MACH_SETFIELD, obj_r, ki, val_r)); + } else { + int kr = mach_reserve_reg(cs); + mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); + mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, kr, val_r)); + } + mach_free_reg_to(cs, save); + return dest; + } + } + /* Computed property access: obj[idx]++ */ + if (op_kind && strcmp(op_kind, "[") == 0) { + int save = cs->freereg; + cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "expression"); + if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "left"); + cJSON *idx_expr = cJSON_GetObjectItemCaseSensitive(operand, "index"); + if (!idx_expr) idx_expr = cJSON_GetObjectItemCaseSensitive(operand, "right"); + int obj_r = mach_compile_expr(cs, obj_expr, -1); + if (cs->freereg <= obj_r) cs->freereg = obj_r + 1; + int idx_r = mach_compile_expr(cs, idx_expr, -1); + if (cs->freereg <= idx_r) cs->freereg = idx_r + 1; + int val_r = mach_reserve_reg(cs); + mach_emit(cs, MACH_ABC(MACH_GETINDEX, val_r, obj_r, idx_r)); + if (is_postfix) + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); + mach_emit(cs, MACH_ABC(inc_op, val_r, val_r, 0)); + if (!is_postfix) + mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0)); + mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, idx_r, val_r)); + mach_free_reg_to(cs, save); + return dest; + } /* Fallback: just compile operand */ return mach_compile_expr(cs, operand, dest); } @@ -999,7 +1080,13 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { int val_r = mach_compile_expr(cs, right, dest); if (prop_name) { int ki = mach_cpool_add_str(cs, prop_name); - mach_emit(cs, MACH_ABC(MACH_SETFIELD, obj_r, ki, val_r)); + if (ki < 256) { + mach_emit(cs, MACH_ABC(MACH_SETFIELD, obj_r, ki, val_r)); + } else { + int kr = mach_reserve_reg(cs); + mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); + mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, kr, val_r)); + } } mach_free_reg_to(cs, save); return val_r; @@ -1047,7 +1134,13 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { int obj_r = mach_compile_expr(cs, obj_expr, -1); if (prop_name) { int ki = mach_cpool_add_str(cs, prop_name); - mach_emit(cs, MACH_ABC(MACH_GETFIELD, dest, obj_r, ki)); + if (ki < 256) { + mach_emit(cs, MACH_ABC(MACH_GETFIELD, dest, obj_r, ki)); + } else { + int kr = mach_reserve_reg(cs); + mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); + mach_emit(cs, MACH_ABC(MACH_GETINDEX, dest, obj_r, kr)); + } } mach_free_reg_to(cs, save); return dest; @@ -1091,7 +1184,13 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) { int save = cs->freereg; int vr = mach_compile_expr(cs, val_node, -1); int ki = mach_cpool_add_str(cs, key); - mach_emit(cs, MACH_ABC(MACH_SETFIELD, dest, ki, vr)); + if (ki < 256) { + mach_emit(cs, MACH_ABC(MACH_SETFIELD, dest, ki, vr)); + } else { + int kr = mach_reserve_reg(cs); + mach_emit(cs, MACH_ABx(MACH_LOADK, kr, ki)); + mach_emit(cs, MACH_ABC(MACH_SETINDEX, dest, kr, vr)); + } mach_free_reg_to(cs, save); } } @@ -2552,7 +2651,7 @@ static JSValue reg_vm_binop(JSContext *ctx, int op, JSValue a, JSValue b) { } /* Type mismatch — disrupt */ - return JS_EXCEPTION; + return JS_ThrowTypeError(ctx, "type mismatch in binary operation"); } /* Check for interrupt */ @@ -3021,6 +3120,8 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue func_val = frame->slots[base]; if (!JS_IsFunction(func_val)) { + JS_ThrowTypeError(ctx, "not a function"); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); goto disrupt; } @@ -3123,13 +3224,18 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, goto disrupt; } else { /* Record method call: get property, call with this=obj */ + if (JS_IsNull(frame->slots[base])) { + JS_ThrowTypeError(ctx, "cannot read properties of null"); + JS_PopGCRef(ctx, &key_ref); + goto disrupt; + } JSValue method = JS_GetProperty(ctx, frame->slots[base], key_ref.val); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(method)) { JS_PopGCRef(ctx, &key_ref); goto disrupt; } if (!JS_IsFunction(method)) { - frame->slots[base] = JS_NULL; + JS_ThrowTypeError(ctx, "not a function"); JS_PopGCRef(ctx, &key_ref); - break; + goto disrupt; } JSFunction *fn = JS_VALUE_GET_FUNCTION(method); if (fn->kind == JS_FUNC_KIND_C) { @@ -3263,7 +3369,11 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, /* push R(B) onto array R(A) */ JSValue arr = frame->slots[a]; JSValue val = frame->slots[b]; - if (!JS_IsArray(arr)) goto disrupt; + if (!JS_IsArray(arr)) { + JS_ThrowTypeError(ctx, "cannot push to non-array"); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + goto disrupt; + } JSGCRef arr_gc; JS_PushGCRef(ctx, &arr_gc); arr_gc.val = arr; @@ -3278,7 +3388,11 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, case MACH_POP: { /* R(A) = pop last element from array R(B) */ JSValue arr = frame->slots[b]; - if (!JS_IsArray(arr)) goto disrupt; + if (!JS_IsArray(arr)) { + JS_ThrowTypeError(ctx, "cannot pop from non-array"); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + goto disrupt; + } JSValue val = JS_ArrayPop(ctx, arr); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(val)) goto disrupt; @@ -3353,7 +3467,13 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, break; } if (JS_IsNull(frame->caller)) { - fprintf(stderr, "unhandled disruption\n"); + if (!JS_HasException(ctx)) { + /* Bare disrupt with no error message — provide location */ + const char *fn_name = code->name_cstr ? code->name_cstr : ""; + fprintf(stderr, "unhandled disruption in %s\n", fn_name); + } else { + fprintf(stderr, "unhandled disruption\n"); + } result = JS_Throw(ctx, JS_NULL); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); goto done; diff --git a/source/runtime.c b/source/runtime.c index 903d7518..efca93e0 100644 --- a/source/runtime.c +++ b/source/runtime.c @@ -8255,12 +8255,17 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc, if (!result) { FMT_CLEANUP(); return JS_EXCEPTION; } res_ref.val = JS_MKPTR (result); } else { - JSValue orig = js_sub_string_val (ctx, text_ref.val, brace_start, brace_end + 1); - if (JS_IsException (orig)) { FMT_CLEANUP(); return JS_EXCEPTION; } + /* No substitution — treat the '{' as a literal character and rescan + from brace_start + 1 so that real placeholders like {0} inside + the skipped range are still found. */ + JSValue ch = js_sub_string_val (ctx, text_ref.val, brace_start, brace_start + 1); + if (JS_IsException (ch)) { FMT_CLEANUP(); return JS_EXCEPTION; } result = (JSText *)chase (res_ref.val); - result = pretext_concat_value (ctx, result, orig); + result = pretext_concat_value (ctx, result, ch); if (!result) { FMT_CLEANUP(); return JS_EXCEPTION; } res_ref.val = JS_MKPTR (result); + pos = brace_start + 1; + continue; } pos = brace_end + 1; @@ -9923,7 +9928,7 @@ static JSValue js_blob_constructor (JSContext *ctx, JSValue this_val, int argc, } } /* blob(blob, from, to) - copy from another blob */ - else if (argc >= 1 && JS_IsObject (argv[0])) { + else if (argc >= 1 && JS_IsObject (argv[0]) && !JS_IsText (argv[0])) { blob *src = js_get_blob (ctx, argv[0]); if (!src) return JS_ThrowTypeError (ctx, @@ -9941,9 +9946,7 @@ static JSValue js_blob_constructor (JSContext *ctx, JSValue this_val, int argc, bd = blob_new_from_blob (src, (size_t)from, (size_t)to); } /* blob(text) - create blob from UTF-8 string */ - else if (argc == 1 - && (JS_VALUE_GET_TAG (argv[0]) == JS_TAG_STRING - || JS_VALUE_GET_TAG (argv[0]) == JS_TAG_STRING_IMM)) { + else if (argc == 1 && JS_IsText (argv[0])) { const char *str = JS_ToCString (ctx, argv[0]); if (!str) return JS_EXCEPTION; size_t len = strlen (str); diff --git a/source/scheduler.c b/source/scheduler.c index df0ed40e..ad566855 100644 --- a/source/scheduler.c +++ b/source/scheduler.c @@ -304,6 +304,7 @@ void actor_free(cell_rt *actor) int actor_count = lockless_shlen(actors); if (actor_count == 0) { + fprintf(stderr, "all actors are dead\n"); pthread_mutex_lock(&engine.lock); engine.shutting_down = 1; pthread_cond_broadcast(&engine.wake_cond); @@ -533,7 +534,7 @@ const char *register_actor(const char *id, cell_rt *actor, int mainthread, doubl actor->main_thread_only = mainthread; actor->id = strdup(id); actor->ar_secs = ar; - int added = lockless_shput_unique(actors, id, actor); + int added = lockless_shput_unique(actors, actor->id, actor); if (!added) { free(actor->id); return "Actor with given ID already exists."; @@ -591,20 +592,22 @@ void actor_turn(cell_rt *actor) arrdel(actor->letters, 0); // O(N) but we kept array as requested pthread_mutex_unlock(actor->msg_mutex); - if (l.type == LETTER_BLOB) { + if (l.type == LETTER_BLOB) { // Create a JS blob from the C blob size_t size = blob_length(l.blob_data) / 8; // Convert bits to bytes JSValue arg = js_new_blob_stoned_copy(actor->context, (void*)blob_data(l.blob_data), size); blob_destroy(l.blob_data); result = JS_Call(actor->context, actor->message_handle_ref.val, JS_NULL, 1, &arg); - uncaught_exception(actor->context, result); + if (!uncaught_exception(actor->context, result)) + actor->disrupt = 1; JS_FreeValue(actor->context, arg); } else if (l.type == LETTER_CALLBACK) { result = JS_Call(actor->context, l.callback, JS_NULL, 0, NULL); - uncaught_exception(actor->context, result); + if (!uncaught_exception(actor->context, result)) + actor->disrupt = 1; JS_FreeValue(actor->context, l.callback); } - + if (actor->disrupt) goto ENDTURN; ENDTURN: @@ -612,9 +615,17 @@ void actor_turn(cell_rt *actor) if (actor->trace_hook) actor->trace_hook(actor->name, CELL_HOOK_EXIT); - + + if (actor->disrupt) { + /* Actor must die. Unlock before freeing so actor_free can + lock/unlock/destroy the mutex without use-after-free. */ + pthread_mutex_unlock(actor->mutex); + actor_free(actor); + return; + } + set_actor_state(actor); - + pthread_mutex_unlock(actor->mutex); } diff --git a/test.ce b/test.ce index 34ca1b35..7727b81c 100644 --- a/test.ce +++ b/test.ce @@ -304,9 +304,12 @@ function run_tests(package_name, specific_test) { } var _load_file = null + var load_error = false + var err_entry = null for (i = 0; i < length(test_files); i++) { f = test_files[i] mod_path = text(f, 0, -3) // remove .cm + load_error = false file_result = { name: f, @@ -328,14 +331,34 @@ function run_tests(package_name, specific_test) { var _test_error = null var end_time = null var _run_one = null + var all_keys = null + var fn_count = 0 + var null_count = 0 + var other_count = 0 + var first_null_key = null + var first_other_key = null if (is_function(test_mod)) { push(tests, {name: 'main', fn: test_mod}) } else if (is_object(test_mod)) { - arrfor(array(test_mod), function(k) { + all_keys = array(test_mod) + log.console(` Found ${length(all_keys)} test entries`) + arrfor(all_keys, function(k) { if (is_function(test_mod[k])) { + fn_count = fn_count + 1 push(tests, {name: k, fn: test_mod[k]}) + } else if (is_null(test_mod[k])) { + null_count = null_count + 1 + if (!first_null_key) first_null_key = k + } else { + other_count = other_count + 1 + if (!first_other_key) first_other_key = k } }) + log.console(` functions=${fn_count} nulls=${null_count} other=${other_count}`) + if (first_other_key) { + log.console(` first other key: ${first_other_key}`) + log.console(` is_number=${is_number(test_mod[first_other_key])} is_text=${is_text(test_mod[first_other_key])} is_logical=${is_logical(test_mod[first_other_key])} is_object=${is_object(test_mod[first_other_key])}`) + } } if (length(tests) > 0) { @@ -355,17 +378,15 @@ function run_tests(package_name, specific_test) { var ret = t.fn() if (is_text(ret)) { - _test_error = Error(ret) - disrupt - } else if (ret && (is_text(ret.message) || is_proto(ret, Error))) { _test_error = ret disrupt + } else if (ret && is_text(ret.message)) { + _test_error = ret.message + disrupt } test_entry.status = "passed" log.console(` PASS ${t.name}`) - pkg_result.passed++ - file_result.passed++ } disruption { var e = _test_error test_entry.status = "failed" @@ -383,16 +404,22 @@ function run_tests(package_name, specific_test) { if (test_entry.error.stack) { log.console(` ${text(array(test_entry.error.stack, '\n'), '\n ')}`) } - - pkg_result.failed++ - file_result.failed++ } _run_one() end_time = time.number() test_entry.duration_ns = round((end_time - start_time) * 1000000000) + // Update counters at _load_file level (not inside _run_one) + if (test_entry.status == "passed") { + pkg_result.passed = pkg_result.passed + 1 + file_result.passed = file_result.passed + 1 + } else { + pkg_result.failed = pkg_result.failed + 1 + file_result.failed = file_result.failed + 1 + } + push(file_result.tests, test_entry) - pkg_result.total++ + pkg_result.total = pkg_result.total + 1 if (gc_after_each_test) { dbg.gc() } @@ -400,23 +427,15 @@ function run_tests(package_name, specific_test) { } } disruption { - var test_entry = { - package: pkg_result.package, - test: "load_module", - status: "failed", - duration_ns: 0, - error: { message: `Error loading module` } - } - log.console(` Error loading ${f}`) - push(file_result.tests, test_entry) - pkg_result.failed++ - file_result.failed++ - pkg_result.total++ - if (gc_after_each_test) { - dbg.gc() - } + load_error = true } _load_file() + if (load_error) { + log.console(" Error loading " + f) + pkg_result.failed = pkg_result.failed + 1 + file_result.failed = file_result.failed + 1 + pkg_result.total = pkg_result.total + 1 + } push(pkg_result.files, file_result) } return pkg_result @@ -596,22 +615,22 @@ function finalize_results() { } push(file_result.tests, r) - pkg_result.total++ + pkg_result.total = pkg_result.total + 1 if (r.status == "passed") { - pkg_result.passed++ - file_result.passed++ + pkg_result.passed = pkg_result.passed + 1 + file_result.passed = file_result.passed + 1 } else { - pkg_result.failed++ - file_result.failed++ + pkg_result.failed = pkg_result.failed + 1 + file_result.failed = file_result.failed + 1 } } // Calculate totals var totals = { total: 0, passed: 0, failed: 0 } for (i = 0; i < length(all_results); i++) { - totals.total += all_results[i].total - totals.passed += all_results[i].passed - totals.failed += all_results[i].failed + totals.total = totals.total + all_results[i].total + totals.passed = totals.passed + all_results[i].passed + totals.failed = totals.failed + all_results[i].failed } log.console(`----------------------------------------`) @@ -626,9 +645,9 @@ var totals = null if (length(all_actor_tests) == 0) { totals = { total: 0, passed: 0, failed: 0 } for (i = 0; i < length(all_results); i++) { - totals.total += all_results[i].total - totals.passed += all_results[i].passed - totals.failed += all_results[i].failed + totals.total = totals.total + all_results[i].total + totals.passed = totals.passed + all_results[i].passed + totals.failed = totals.failed + all_results[i].failed } log.console(`----------------------------------------`) diff --git a/tests/suite.cm b/tests/suite.cm index cb1c2d97..8b9482ab 100644 --- a/tests/suite.cm +++ b/tests/suite.cm @@ -8,42 +8,42 @@ return { // ============================================================================ test_number_addition: function() { - if (1 + 2 != 3) throw "basic addition failed" - if (0 + 0 != 0) throw "zero addition failed" - if (-5 + 3 != -2) throw "negative addition failed" - if (0.1 + 0.2 - 0.3 > 0.0001) throw "float addition precision issue" + if (1 + 2 != 3) return "basic addition failed" + if (0 + 0 != 0) return "zero addition failed" + if (-5 + 3 != -2) return "negative addition failed" + if (0.1 + 0.2 - 0.3 > 0.0001) return "float addition precision issue" }, test_number_subtraction: function() { - if (5 - 3 != 2) throw "basic subtraction failed" - if (0 - 5 != -5) throw "zero subtraction failed" - if (-5 - -3 != -2) throw "negative subtraction failed" + if (5 - 3 != 2) return "basic subtraction failed" + if (0 - 5 != -5) return "zero subtraction failed" + if (-5 - -3 != -2) return "negative subtraction failed" }, test_number_multiplication: function() { - if (3 * 4 != 12) throw "basic multiplication failed" - if (0 * 100 != 0) throw "zero multiplication failed" - if (-3 * 4 != -12) throw "negative multiplication failed" - if (-3 * -4 != 12) throw "double negative multiplication failed" + if (3 * 4 != 12) return "basic multiplication failed" + if (0 * 100 != 0) return "zero multiplication failed" + if (-3 * 4 != -12) return "negative multiplication failed" + if (-3 * -4 != 12) return "double negative multiplication failed" }, test_number_division: function() { - if (12 / 4 != 3) throw "basic division failed" - if (1 / 2 != 0.5) throw "fractional division failed" - if (-12 / 4 != -3) throw "negative division failed" - if (12 / -4 != -3) throw "division by negative failed" + if (12 / 4 != 3) return "basic division failed" + if (1 / 2 != 0.5) return "fractional division failed" + if (-12 / 4 != -3) return "negative division failed" + if (12 / -4 != -3) return "division by negative failed" }, test_number_modulo: function() { - if (10 % 3 != 1) throw "basic modulo failed" - if (10 % 5 != 0) throw "even modulo failed" - if (-10 % 3 != -1) throw "negative modulo failed" + if (10 % 3 != 1) return "basic modulo failed" + if (10 % 5 != 0) return "even modulo failed" + if (-10 % 3 != -1) return "negative modulo failed" }, test_number_exponentiation: function() { - if (2 ** 3 != 8) throw "basic exponentiation failed" - if (5 ** 0 != 1) throw "zero exponent failed" - if (2 ** -1 != 0.5) throw "negative exponent failed" + if (2 ** 3 != 8) return "basic exponentiation failed" + if (5 ** 0 != 1) return "zero exponent failed" + if (2 ** -1 != 0.5) return "negative exponent failed" }, // ============================================================================ @@ -52,117 +52,127 @@ return { test_string_plus_string_works: function() { var x = "hello" + " world" - if (x != "hello world") throw "string + string should work" + if (x != "hello world") return "string + string should work" }, test_string_concatenation_empty: function() { - if ("" + "" != "") throw "empty string concatenation failed" - if ("hello" + "" != "hello") throw "concatenation with empty string failed" - if ("" + "world" != "world") throw "empty + string failed" + if ("" + "" != "") return "empty string concatenation failed" + if ("hello" + "" != "hello") return "concatenation with empty string failed" + if ("" + "world" != "world") return "empty + string failed" }, // ============================================================================ - // TYPE MIXING SHOULD THROW + // TYPE MIXING SHOULD DISRUPT // ============================================================================ - test_number_plus_string_throws: function() { + test_number_plus_string_disrupts: function() { var caught = false - try { + var _fn = function() { var x = 1 + "hello" - } catch (e) { + } disruption { caught = true } - if (!caught) throw "number + string should throw" + _fn() + if (!caught) return "number + string should disrupt" }, - test_string_plus_number_throws: function() { + test_string_plus_number_disrupts: function() { var caught = false - try { + var _fn = function() { var x = "hello" + 1 - } catch (e) { + } disruption { caught = true } - if (!caught) throw "string + number should throw" + _fn() + if (!caught) return "string + number should disrupt" }, - test_object_plus_string_throws: function() { + test_object_plus_string_disrupts: function() { var caught = false - try { + var _fn = function() { var x = {} + "hello" - } catch (e) { + } disruption { caught = true } - if (!caught) throw "object + string should throw" + _fn() + if (!caught) return "object + string should disrupt" }, - test_string_plus_object_throws: function() { + test_string_plus_object_disrupts: function() { var caught = false - try { + var _fn = function() { var x = "hello" + {} - } catch (e) { + } disruption { caught = true } - if (!caught) throw "string + object should throw" + _fn() + if (!caught) return "string + object should disrupt" }, - test_array_plus_string_throws: function() { + test_array_plus_string_disrupts: function() { var caught = false - try { + var _fn = function() { var x = [] + "hello" - } catch (e) { + } disruption { caught = true } - if (!caught) throw "array + string should throw" + _fn() + if (!caught) return "array + string should disrupt" }, - test_string_plus_array_throws: function() { + test_string_plus_array_disrupts: function() { var caught = false - try { + var _fn = function() { var x = "hello" + [] - } catch (e) { + } disruption { caught = true } - if (!caught) throw "string + array should throw" + _fn() + if (!caught) return "string + array should disrupt" }, - test_boolean_plus_string_throws: function() { + test_boolean_plus_string_disrupts: function() { var caught = false - try { + var _fn = function() { var x = true + "hello" - } catch (e) { + } disruption { caught = true } - if (!caught) throw "boolean + string should throw" + _fn() + if (!caught) return "boolean + string should disrupt" }, - test_string_plus_boolean_throws: function() { + test_string_plus_boolean_disrupts: function() { var caught = false - try { + var _fn = function() { var x = "hello" + false - } catch (e) { + } disruption { caught = true } - if (!caught) throw "string + boolean should throw" + _fn() + if (!caught) return "string + boolean should disrupt" }, - test_null_plus_string_throws: function() { + test_null_plus_string_disrupts: function() { var caught = false - try { + var _fn = function() { var x = null + "hello" - } catch (e) { + } disruption { caught = true } - if (!caught) throw "null + string should throw" + _fn() + if (!caught) return "null + string should disrupt" }, - test_string_plus_null_throws: function() { + test_string_plus_null_disrupts: function() { var caught = false - try { + var _fn = function() { var x = "hello" + null - } catch (e) { + } disruption { caught = true } - if (!caught) throw "string + null should throw" + _fn() + if (!caught) return "string + null should disrupt" }, // ============================================================================ @@ -170,58 +180,58 @@ return { // ============================================================================ test_equality_numbers: function() { - if (!(5 == 5)) throw "number equality failed" - if (5 == 6) throw "number inequality detection failed" - if (!(0 == 0)) throw "zero equality failed" - if (!(-5 == -5)) throw "negative equality failed" + if (!(5 == 5)) return "number equality failed" + if (5 == 6) return "number inequality detection failed" + if (!(0 == 0)) return "zero equality failed" + if (!(-5 == -5)) return "negative equality failed" }, test_inequality_numbers: function() { - if (5 != 5) throw "number inequality failed" - if (!(5 != 6)) throw "number difference detection failed" + if (5 != 5) return "number inequality failed" + if (!(5 != 6)) return "number difference detection failed" }, test_less_than: function() { - if (!(3 < 5)) throw "less than failed" - if (5 < 3) throw "not less than failed" - if (5 < 5) throw "equal not less than failed" + if (!(3 < 5)) return "less than failed" + if (5 < 3) return "not less than failed" + if (5 < 5) return "equal not less than failed" }, test_less_than_or_equal: function() { - if (!(3 <= 5)) throw "less than or equal failed" - if (!(5 <= 5)) throw "equal in less than or equal failed" - if (6 <= 5) throw "not less than or equal failed" + if (!(3 <= 5)) return "less than or equal failed" + if (!(5 <= 5)) return "equal in less than or equal failed" + if (6 <= 5) return "not less than or equal failed" }, test_greater_than: function() { - if (!(5 > 3)) throw "greater than failed" - if (3 > 5) throw "not greater than failed" - if (5 > 5) throw "equal not greater than failed" + if (!(5 > 3)) return "greater than failed" + if (3 > 5) return "not greater than failed" + if (5 > 5) return "equal not greater than failed" }, test_greater_than_or_equal: function() { - if (!(5 >= 3)) throw "greater than or equal failed" - if (!(5 >= 5)) throw "equal in greater than or equal failed" - if (3 >= 5) throw "not greater than or equal failed" + if (!(5 >= 3)) return "greater than or equal failed" + if (!(5 >= 5)) return "equal in greater than or equal failed" + if (3 >= 5) return "not greater than or equal failed" }, test_string_equality: function() { - if (!("hello" == "hello")) throw "string equality failed" - if ("hello" == "world") throw "string inequality detection failed" - if (!("" == "")) throw "empty string equality failed" + if (!("hello" == "hello")) return "string equality failed" + if ("hello" == "world") return "string inequality detection failed" + if (!("" == "")) return "empty string equality failed" }, test_null_equality: function() { - if (!(null == null)) throw "null equality failed" - if (null == 0) throw "null should not equal 0" - if (null == false) throw "null should not equal false" - if (null == "") throw "null should not equal empty string" + if (!(null == null)) return "null equality failed" + if (null == 0) return "null should not equal 0" + if (null == false) return "null should not equal false" + if (null == "") return "null should not equal empty string" }, test_boolean_equality: function() { - if (!(true == true)) throw "true equality failed" - if (!(false == false)) throw "false equality failed" - if (true == false) throw "boolean inequality detection failed" + if (!(true == true)) return "true equality failed" + if (!(false == false)) return "false equality failed" + if (true == false) return "boolean inequality detection failed" }, // ============================================================================ @@ -229,36 +239,36 @@ return { // ============================================================================ test_logical_and: function() { - if (!(true && true)) throw "true && true failed" - if (true && false) throw "true && false failed" - if (false && true) throw "false && true failed" - if (false && false) throw "false && false failed" + if (!(true && true)) return "true && true failed" + if (true && false) return "true && false failed" + if (false && true) return "false && true failed" + if (false && false) return "false && false failed" }, test_logical_or: function() { - if (!(true || true)) throw "true || true failed" - if (!(true || false)) throw "true || false failed" - if (!(false || true)) throw "false || true failed" - if (false || false) throw "false || false failed" + if (!(true || true)) return "true || true failed" + if (!(true || false)) return "true || false failed" + if (!(false || true)) return "false || true failed" + if (false || false) return "false || false failed" }, test_logical_not: function() { - if (!(!false)) throw "!false failed" - if (!true) throw "!true failed" + if (!(!false)) return "!false failed" + if (!true) return "!true failed" }, test_short_circuit_and: function() { var called = false var fn = function() { called = true; return true } var result = false && fn() - if (called) throw "AND should short circuit" + if (called) return "AND should short circuit" }, test_short_circuit_or: function() { var called = false var fn = function() { called = true; return false } var result = true || fn() - if (called) throw "OR should short circuit" + if (called) return "OR should short circuit" }, // ============================================================================ @@ -266,37 +276,37 @@ return { // ============================================================================ test_bitwise_and: function() { - if ((5 & 3) != 1) throw "bitwise AND failed" - if ((12 & 10) != 8) throw "bitwise AND failed" + if ((5 & 3) != 1) return "bitwise AND failed" + if ((12 & 10) != 8) return "bitwise AND failed" }, test_bitwise_or: function() { - if ((5 | 3) != 7) throw "bitwise OR failed" - if ((12 | 10) != 14) throw "bitwise OR failed" + if ((5 | 3) != 7) return "bitwise OR failed" + if ((12 | 10) != 14) return "bitwise OR failed" }, test_bitwise_xor: function() { - if ((5 ^ 3) != 6) throw "bitwise XOR failed" - if ((12 ^ 10) != 6) throw "bitwise XOR failed" + if ((5 ^ 3) != 6) return "bitwise XOR failed" + if ((12 ^ 10) != 6) return "bitwise XOR failed" }, test_bitwise_not: function() { - if (~5 != -6) throw "bitwise NOT failed" - if (~0 != -1) throw "bitwise NOT of zero failed" + if (~5 != -6) return "bitwise NOT failed" + if (~0 != -1) return "bitwise NOT of zero failed" }, test_left_shift: function() { - if ((5 << 2) != 20) throw "left shift failed" - if ((1 << 3) != 8) throw "left shift failed" + if ((5 << 2) != 20) return "left shift failed" + if ((1 << 3) != 8) return "left shift failed" }, test_right_shift: function() { - if ((20 >> 2) != 5) throw "right shift failed" - if ((8 >> 3) != 1) throw "right shift failed" + if ((20 >> 2) != 5) return "right shift failed" + if ((8 >> 3) != 1) return "right shift failed" }, test_unsigned_right_shift: function() { - if ((-1 >>> 1) != 2147483647) throw "unsigned right shift failed" + if ((-1 >>> 1) != 2147483647) return "unsigned right shift failed" }, // ============================================================================ @@ -305,114 +315,18 @@ return { test_var_declaration: function() { var x = 5 - if (x != 5) throw "var declaration failed" + if (x != 5) return "var declaration failed" }, test_var_reassignment: function() { var x = 5 x = 10 - if (x != 10) throw "var reassignment failed" - }, - - // ============================================================================ - // VAR BLOCK SCOPING (var now behaves like let) - // ============================================================================ - - test_var_block_scope_basic: function() { - var x = 1 - { - var x = 2 - if (x != 2) throw "var should be block scoped - inner scope failed" - } - if (x != 1) throw "var should be block scoped - outer scope affected" - }, - - test_var_block_scope_if: function() { - var x = 1 - if (true) { - var x = 2 - if (x != 2) throw "var in if block should be scoped" - } - if (x != 1) throw "var in if block should not affect outer scope" - }, - - test_var_block_scope_for: function() { - var x = 1 - for (var i = 0; i < 1; i = i + 1) { - var x = 2 - if (x != 2) throw "var in for block should be scoped" - } - if (x != 1) throw "var in for block should not affect outer scope" - }, - - test_var_for_loop_iterator_scope: function() { - var sum = 0 - for (var i = 0; i < 3; i = i + 1) { - sum = sum + i - } - if (sum != 3) throw "for loop should work with block scoped var" - var caught = false - try { - var y = i - } catch (e) { - caught = true - } - if (!caught) throw "for loop iterator should not leak to outer scope" - }, - - test_var_nested_blocks: function() { - var x = 1 - { - var x = 2 - { - var x = 3 - if (x != 3) throw "var in nested block level 2 failed" - } - if (x != 2) throw "var in nested block level 1 failed" - } - if (x != 1) throw "var in nested blocks outer scope failed" - }, - - test_var_redeclaration_different_scope: function() { - var x = 1 - { - var x = 2 - } - if (x != 1) throw "var in different scope should not affect outer" - }, - - test_var_switch_scope: function() { - var x = 1 - switch (1) { - case 1: - var x = 2 - if (x != 2) throw "var in switch should be block scoped" - break - } - if (x != 1) throw "var in switch should not affect outer scope" - }, - - test_var_while_scope: function() { - var x = 1 - var count = 0 - while (count < 1) { - var x = 2 - if (x != 2) throw "var in while should be block scoped" - count = count + 1 - } - if (x != 1) throw "var in while should not affect outer scope" - }, - - test_var_no_initialization: function() { - { - var x - if (x != null) throw "uninitialized var should be null" - } + if (x != 10) return "var reassignment failed" }, test_multiple_var_declaration: function() { var a = 1, b = 2, c = 3 - if (a != 1 || b != 2 || c != 3) throw "multiple var declaration failed" + if (a != 1 || b != 2 || c != 3) return "multiple var declaration failed" }, test_function_scope: function() { @@ -421,7 +335,7 @@ return { var inner = "inner" return inner } - if (fn() != "inner") throw "function scope failed" + if (fn() != "inner") return "function scope failed" }, // ============================================================================ @@ -430,32 +344,32 @@ return { test_function_call_no_args: function() { var fn = function() { return 42 } - if (fn() != 42) throw "function call with no args failed" + if (fn() != 42) return "function call with no args failed" }, test_function_call_one_arg: function() { var fn = function(x) { return x * 2 } - if (fn(5) != 10) throw "function call with one arg failed" + if (fn(5) != 10) return "function call with one arg failed" }, test_function_call_multiple_args: function() { var fn = function(a, b, c) { return a + b + c } - if (fn(1, 2, 3) != 6) throw "function call with multiple args failed" + if (fn(1, 2, 3) != 6) return "function call with multiple args failed" }, test_function_call_extra_args: function() { var fn = function(a, b) { return a + b } - if (fn(1, 2, 3, 4) != 3) throw "function call with extra args failed" + if (fn(1, 2, 3, 4) != 3) return "function call with extra args failed" }, test_function_call_missing_args: function() { var fn = function(a, b, c) { return (a || 0) + (b || 0) + (c || 0) } - if (fn(1) != 1) throw "function call with missing args failed" + if (fn(1) != 1) return "function call with missing args failed" }, test_function_return: function() { var fn = function() { return 5 } - if (fn() != 5) throw "function return failed" + if (fn() != 5) return "function return failed" }, test_function_return_early: function() { @@ -463,24 +377,24 @@ return { return 5 return 10 } - if (fn() != 5) throw "early return failed" + if (fn() != 5) return "early return failed" }, test_function_no_return: function() { var fn = function() { var x = 5 } - if (fn() != null) throw "function with no return should return null" + if (fn() != null) return "function with no return should return null" }, test_nested_function_calls: function() { var add = function(a, b) { return a + b } var mul = function(a, b) { return a * b } - if (add(mul(2, 3), mul(4, 5)) != 26) throw "nested function calls failed" + if (add(mul(2, 3), mul(4, 5)) != 26) return "nested function calls failed" }, test_function_as_value: function() { var fn = function() { return 42 } var fn2 = fn - if (fn2() != 42) throw "function as value failed" + if (fn2() != 42) return "function as value failed" }, test_function_closure: function() { @@ -490,7 +404,7 @@ return { } } var add5 = outer(5) - if (add5(3) != 8) throw "closure failed" + if (add5(3) != 8) return "closure failed" }, test_function_closure_mutation: function() { @@ -502,9 +416,9 @@ return { } } var c = counter() - if (c() != 1) throw "closure mutation failed (1)" - if (c() != 2) throw "closure mutation failed (2)" - if (c() != 3) throw "closure mutation failed (3)" + if (c() != 1) return "closure mutation failed (1)" + if (c() != 2) return "closure mutation failed (2)" + if (c() != 3) return "closure mutation failed (3)" }, // ============================================================================ @@ -516,7 +430,7 @@ return { if (n <= 1) return 1 return n * factorial(n - 1) } - if (factorial(5) != 120) throw "factorial recursion failed" + if (factorial(5) != 120) return "factorial recursion failed" }, test_mutual_recursion: function() { @@ -528,8 +442,8 @@ return { if (n == 0) return false return isEven(n - 1) } - if (!isEven(4)) throw "mutual recursion even failed" - if (isOdd(4)) throw "mutual recursion odd failed" + if (!isEven(4)) return "mutual recursion even failed" + if (isOdd(4)) return "mutual recursion odd failed" }, test_deep_recursion: function() { @@ -537,7 +451,7 @@ return { if (n == 0) return 0 return n + sum(n - 1) } - if (sum(100) != 5050) throw "deep recursion failed" + if (sum(100) != 5050) return "deep recursion failed" }, // ============================================================================ @@ -546,58 +460,58 @@ return { test_array_literal: function() { var arr = [1, 2, 3] - if (arr[0] != 1 || arr[1] != 2 || arr[2] != 3) throw "array literal failed" + if (arr[0] != 1 || arr[1] != 2 || arr[2] != 3) return "array literal failed" }, test_array_length: function() { var arr = [1, 2, 3, 4, 5] - if (length(arr) != 5) throw "array length failed" + if (length(arr) != 5) return "array length failed" }, test_array_empty: function() { var arr = [] - if (length(arr) != 0) throw "empty array length failed" + if (length(arr) != 0) return "empty array length failed" }, test_array_push: function() { var arr = [1, 2] - push(arr, 3) - if (length(arr) != 3) throw "array push length failed" - if (arr[2] != 3) throw "array push value failed" + arr[] = 3 + if (length(arr) != 3) return "array push length failed" + if (arr[2] != 3) return "array push value failed" }, test_array_pop: function() { var arr = [1, 2, 3] - var val = pop(arr) - if (val != 3) throw "array pop value failed" - if (length(arr) != 2) throw "array pop length failed" + var val = arr[] + if (val != 3) return "array pop value failed" + if (length(arr) != 2) return "array pop length failed" }, test_array_index_access: function() { var arr = [10, 20, 30] - if (arr[0] != 10) throw "array index 0 failed" - if (arr[1] != 20) throw "array index 1 failed" - if (arr[2] != 30) throw "array index 2 failed" + if (arr[0] != 10) return "array index 0 failed" + if (arr[1] != 20) return "array index 1 failed" + if (arr[2] != 30) return "array index 2 failed" }, test_array_index_assignment: function() { var arr = [1, 2, 3] arr[1] = 99 - if (arr[1] != 99) throw "array index assignment failed" + if (arr[1] != 99) return "array index assignment failed" }, test_array_mixed_types: function() { var arr = [1, "hello", true, null, {}] - if (arr[0] != 1) throw "mixed array number failed" - if (arr[1] != "hello") throw "mixed array string failed" - if (arr[2] != true) throw "mixed array boolean failed" - if (arr[3] != null) throw "mixed array null failed" + if (arr[0] != 1) return "mixed array number failed" + if (arr[1] != "hello") return "mixed array string failed" + if (arr[2] != true) return "mixed array boolean failed" + if (arr[3] != null) return "mixed array null failed" }, test_array_nested: function() { var arr = [[1, 2], [3, 4]] - if (arr[0][0] != 1) throw "nested array access failed" - if (arr[1][1] != 4) throw "nested array access failed" + if (arr[0][0] != 1) return "nested array access failed" + if (arr[1][1] != 4) return "nested array access failed" }, // ============================================================================ @@ -606,43 +520,43 @@ return { test_object_literal: function() { var obj = {a: 1, b: 2} - if (obj.a != 1 || obj.b != 2) throw "object literal failed" + if (obj.a != 1 || obj.b != 2) return "object literal failed" }, test_object_property_access: function() { var obj = {name: "Alice", age: 30} - if (obj.name != "Alice") throw "object property access failed" - if (obj.age != 30) throw "object property access failed" + if (obj.name != "Alice") return "object property access failed" + if (obj.age != 30) return "object property access failed" }, test_object_bracket_access: function() { var obj = {x: 10, y: 20} - if (obj["x"] != 10) throw "object bracket access failed" - if (obj["y"] != 20) throw "object bracket access failed" + if (obj["x"] != 10) return "object bracket access failed" + if (obj["y"] != 20) return "object bracket access failed" }, test_object_property_assignment: function() { var obj = {a: 1} obj.a = 99 - if (obj.a != 99) throw "object property assignment failed" + if (obj.a != 99) return "object property assignment failed" }, test_object_add_property: function() { var obj = {} obj.newProp = 42 - if (obj.newProp != 42) throw "object add property failed" + if (obj.newProp != 42) return "object add property failed" }, test_object_computed_property: function() { var key = "dynamicKey" var obj = {} obj[key] = 123 - if (obj.dynamicKey != 123) throw "object computed property failed" + if (obj.dynamicKey != 123) return "object computed property failed" }, test_object_nested: function() { var obj = {outer: {inner: 42}} - if (obj.outer.inner != 42) throw "nested object access failed" + if (obj.outer.inner != 42) return "nested object access failed" }, test_object_method: function() { @@ -650,7 +564,7 @@ return { value: 10, getValue: function() { return this.value } } - if (obj.getValue() != 10) throw "object method failed" + if (obj.getValue() != 10) return "object method failed" }, test_object_this_binding: function() { @@ -658,7 +572,7 @@ return { x: 5, getX: function() { return this.x } } - if (obj.getX() != 5) throw "this binding failed" + if (obj.getX() != 5) return "this binding failed" }, // ============================================================================ @@ -668,27 +582,27 @@ return { test_if_true: function() { var x = 0 if (true) x = 1 - if (x != 1) throw "if true failed" + if (x != 1) return "if true failed" }, test_if_false: function() { var x = 0 if (false) x = 1 - if (x != 0) throw "if false failed" + if (x != 0) return "if false failed" }, test_if_else_true: function() { var x = 0 if (true) x = 1 else x = 2 - if (x != 1) throw "if else true failed" + if (x != 1) return "if else true failed" }, test_if_else_false: function() { var x = 0 if (false) x = 1 else x = 2 - if (x != 2) throw "if else false failed" + if (x != 2) return "if else false failed" }, test_if_else_if: function() { @@ -696,7 +610,7 @@ return { if (false) x = 1 else if (true) x = 2 else x = 3 - if (x != 2) throw "if else if failed" + if (x != 2) return "if else if failed" }, test_nested_if: function() { @@ -706,7 +620,7 @@ return { x = 1 } } - if (x != 1) throw "nested if failed" + if (x != 1) return "nested if failed" }, // ============================================================================ @@ -720,7 +634,7 @@ return { sum = sum + i i = i + 1 } - if (sum != 10) throw "while loop failed" + if (sum != 10) return "while loop failed" }, test_while_never_executes: function() { @@ -728,7 +642,7 @@ return { while (false) { x = 1 } - if (x != 0) throw "while never executes failed" + if (x != 0) return "while never executes failed" }, test_while_break: function() { @@ -737,7 +651,7 @@ return { if (i >= 5) break i = i + 1 } - if (i != 5) throw "while break failed" + if (i != 5) return "while break failed" }, test_while_continue: function() { @@ -748,7 +662,7 @@ return { if (i % 2 == 0) continue sum = sum + i } - if (sum != 25) throw "while continue failed" + if (sum != 25) return "while continue failed" }, // ============================================================================ @@ -757,152 +671,101 @@ return { test_for_loop: function() { var sum = 0 - for (var i = 0; i < 5; i = i + 1) { + var i = 0 + for (i = 0; i < 5; i = i + 1) { sum = sum + i } - if (sum != 10) throw "for loop failed" + if (sum != 10) return "for loop failed" }, test_for_loop_break: function() { var sum = 0 - for (var i = 0; i < 10; i = i + 1) { + var i = 0 + for (i = 0; i < 10; i = i + 1) { if (i == 5) break sum = sum + i } - if (sum != 10) throw "for loop break failed" + if (sum != 10) return "for loop break failed" }, test_for_loop_continue: function() { var sum = 0 - for (var i = 0; i < 10; i = i + 1) { + var i = 0 + for (i = 0; i < 10; i = i + 1) { if (i % 2 == 0) continue sum = sum + i } - if (sum != 25) throw "for loop continue failed" + if (sum != 25) return "for loop continue failed" }, test_nested_for_loops: function() { var sum = 0 - for (var i = 0; i < 3; i = i + 1) { - for (var j = 0; j < 3; j = j + 1) { + var i = 0, j = 0 + for (i = 0; i < 3; i = i + 1) { + for (j = 0; j < 3; j = j + 1) { sum = sum + 1 } } - if (sum != 9) throw "nested for loops failed" + if (sum != 9) return "nested for loops failed" }, // ============================================================================ - // CONTROL FLOW - SWITCH + // DISRUPTION HANDLING // ============================================================================ - test_switch_case: function() { - var x = 2 - var result = 0 - switch (x) { - case 1: - result = 10 - break - case 2: - result = 20 - break - case 3: - result = 30 - break - } - if (result != 20) throw "switch case failed" - }, - - test_switch_default: function() { - var x = 99 - var result = 0 - switch (x) { - case 1: - result = 10 - break - default: - result = -1 - break - } - if (result != -1) throw "switch default failed" - }, - - test_switch_fallthrough: function() { - var x = 1 - var result = 0 - switch (x) { - case 1: - result = result + 1 - case 2: - result = result + 2 - break - case 3: - result = result + 3 - break - } - if (result != 3) throw "switch fallthrough failed" - }, - - // ============================================================================ - // ERROR HANDLING - TRY/CATCH - // ============================================================================ - - test_try_catch: function() { + test_disruption_caught: function() { var caught = false - try { - throw "error" - } catch (e) { + var _fn = function() { + disrupt + } disruption { caught = true } - if (!caught) throw "try catch failed" + _fn() + if (!caught) return "disruption not caught" }, - test_try_catch_error_value: function() { - var errorMsg = null - try { - throw "my error" - } catch (e) { - errorMsg = e - } - if (errorMsg != "my error") throw "try catch error value failed" - }, - - test_try_no_error: function() { + test_no_disruption_path: function() { var x = 0 - try { + var _fn = function() { x = 1 - } catch (e) { + } disruption { x = 2 } - if (x != 1) throw "try no error failed" + _fn() + if (x != 1) return "non-disrupting code should not trigger disruption clause" }, - test_nested_try_catch: function() { + test_nested_disruption: function() { var x = 0 - try { - try { - throw "inner" - } catch (e) { + var _outer = function() { + var _inner = function() { + disrupt + } disruption { x = 1 } + _inner() x = 2 - } catch (e) { + } disruption { x = 3 } - if (x != 2) throw "nested try catch failed" + _outer() + if (x != 2) return "nested disruption failed" }, - test_try_catch_rethrow: function() { - var outerCaught = false - try { - try { - throw "error" - } catch (e) { - throw e + test_disruption_re_raise: function() { + var outer_caught = false + var _outer = function() { + var _inner = function() { + disrupt + } disruption { + disrupt } - } catch (e) { - outerCaught = true + _inner() + } disruption { + outer_caught = true } - if (!outerCaught) throw "try catch rethrow failed" + _outer() + if (!outer_caught) return "disruption re-raise failed" }, // ============================================================================ @@ -910,96 +773,96 @@ return { // ============================================================================ test_is_number: function() { - if (!is_number(42)) throw "is_number 42 failed" - if (!is_number(3.14)) throw "is_number float failed" - if (!is_number(-5)) throw "is_number negative failed" - if (is_number("42")) throw "is_number string should be false" - if (is_number(true)) throw "is_number boolean should be false" - if (is_number(null)) throw "is_number null should be false" - if (is_number({})) throw "is_number object should be false" - if (is_number([])) throw "is_number array should be false" + if (!is_number(42)) return "is_number 42 failed" + if (!is_number(3.14)) return "is_number float failed" + if (!is_number(-5)) return "is_number negative failed" + if (is_number("42")) return "is_number string should be false" + if (is_number(true)) return "is_number boolean should be false" + if (is_number(null)) return "is_number null should be false" + if (is_number({})) return "is_number object should be false" + if (is_number([])) return "is_number array should be false" }, test_is_text: function() { - if (!is_text("hello")) throw "is_text string failed" - if (!is_text("")) throw "is_text empty string failed" - if (is_text(42)) throw "is_text number should be false" - if (is_text(true)) throw "is_text boolean should be false" - if (is_text(null)) throw "is_text null should be false" - if (is_text({})) throw "is_text object should be false" - if (is_text([])) throw "is_text array should be false" + if (!is_text("hello")) return "is_text string failed" + if (!is_text("")) return "is_text empty string failed" + if (is_text(42)) return "is_text number should be false" + if (is_text(true)) return "is_text boolean should be false" + if (is_text(null)) return "is_text null should be false" + if (is_text({})) return "is_text object should be false" + if (is_text([])) return "is_text array should be false" }, test_is_logical: function() { - if (!is_logical(true)) throw "is_logical true failed" - if (!is_logical(false)) throw "is_logical false failed" - if (is_logical(1)) throw "is_logical number should be false" - if (is_logical("true")) throw "is_logical string should be false" - if (is_logical(null)) throw "is_logical null should be false" - if (is_logical({})) throw "is_logical object should be false" - if (is_logical([])) throw "is_logical array should be false" + if (!is_logical(true)) return "is_logical true failed" + if (!is_logical(false)) return "is_logical false failed" + if (is_logical(1)) return "is_logical number should be false" + if (is_logical("true")) return "is_logical string should be false" + if (is_logical(null)) return "is_logical null should be false" + if (is_logical({})) return "is_logical object should be false" + if (is_logical([])) return "is_logical array should be false" }, test_is_object: function() { - if (!is_object({})) throw "is_object empty object failed" - if (!is_object({a: 1})) throw "is_object object failed" - if (is_object([])) throw "is_object array should be false" - if (is_object(null)) throw "is_object null should be false" - if (is_object(42)) throw "is_object number should be false" - if (is_object("hello")) throw "is_object string should be false" - if (is_object(true)) throw "is_object boolean should be false" + if (!is_object({})) return "is_object empty object failed" + if (!is_object({a: 1})) return "is_object object failed" + if (is_object([])) return "is_object array should be false" + if (is_object(null)) return "is_object null should be false" + if (is_object(42)) return "is_object number should be false" + if (is_object("hello")) return "is_object string should be false" + if (is_object(true)) return "is_object boolean should be false" }, test_is_array: function() { - if (!is_array([])) throw "is_array empty array failed" - if (!is_array([1, 2, 3])) throw "is_array array failed" - if (is_array({})) throw "is_array object should be false" - if (is_array(null)) throw "is_array null should be false" - if (is_array(42)) throw "is_array number should be false" - if (is_array("hello")) throw "is_array string should be false" - if (is_array(true)) throw "is_array boolean should be false" + if (!is_array([])) return "is_array empty array failed" + if (!is_array([1, 2, 3])) return "is_array array failed" + if (is_array({})) return "is_array object should be false" + if (is_array(null)) return "is_array null should be false" + if (is_array(42)) return "is_array number should be false" + if (is_array("hello")) return "is_array string should be false" + if (is_array(true)) return "is_array boolean should be false" }, test_is_function: function() { - if (!is_function(function(){})) throw "is_function function failed" + if (!is_function(function(){})) return "is_function function failed" var fn = function(x) { return x * 2 } - if (!is_function(fn)) throw "is_function named function failed" - if (is_function({})) throw "is_function object should be false" - if (is_function([])) throw "is_function array should be false" - if (is_function(null)) throw "is_function null should be false" - if (is_function(42)) throw "is_function number should be false" - if (is_function("hello")) throw "is_function string should be false" - if (is_function(true)) throw "is_function boolean should be false" + if (!is_function(fn)) return "is_function named function failed" + if (is_function({})) return "is_function object should be false" + if (is_function([])) return "is_function array should be false" + if (is_function(null)) return "is_function null should be false" + if (is_function(42)) return "is_function number should be false" + if (is_function("hello")) return "is_function string should be false" + if (is_function(true)) return "is_function boolean should be false" }, test_is_null: function() { - if (!is_null(null)) throw "is_null null failed" - if (is_null(0)) throw "is_null zero should be false" - if (is_null(false)) throw "is_null false should be false" - if (is_null("")) throw "is_null empty string should be false" - if (is_null({})) throw "is_null object should be false" - if (is_null([])) throw "is_null array should be false" - var x - if (!is_null(x)) throw "is_null undefined variable should be true" + if (!is_null(null)) return "is_null null failed" + if (is_null(0)) return "is_null zero should be false" + if (is_null(false)) return "is_null false should be false" + if (is_null("")) return "is_null empty string should be false" + if (is_null({})) return "is_null object should be false" + if (is_null([])) return "is_null array should be false" + var x = null + if (!is_null(x)) return "is_null null variable should be true" }, test_is_blob: function() { // Note: blob testing would require actual blob values // For now, just test that other types return false - if (is_blob(null)) throw "is_blob null should be false" - if (is_blob(42)) throw "is_blob number should be false" - if (is_blob("hello")) throw "is_blob string should be false" - if (is_blob(true)) throw "is_blob boolean should be false" - if (is_blob({})) throw "is_blob object should be false" - if (is_blob([])) throw "is_blob array should be false" - if (is_blob(function(){})) throw "is_blob function should be false" + if (is_blob(null)) return "is_blob null should be false" + if (is_blob(42)) return "is_blob number should be false" + if (is_blob("hello")) return "is_blob string should be false" + if (is_blob(true)) return "is_blob boolean should be false" + if (is_blob({})) return "is_blob object should be false" + if (is_blob([])) return "is_blob array should be false" + if (is_blob(function(){})) return "is_blob function should be false" }, test_is_proto: function() { var a = {} var b = meme(a) - if (!is_proto(b, a)) throw "is_proto failed on meme" + if (!is_proto(b, a)) return "is_proto failed on meme" }, // ============================================================================ @@ -1007,21 +870,21 @@ return { // ============================================================================ test_length_string: function() { - if (length("hello") != 5) throw "length string failed" - if (length("") != 0) throw "length empty string failed" + if (length("hello") != 5) return "length string failed" + if (length("") != 0) return "length empty string failed" }, test_length_array: function() { - if (length([1,2,3]) != 3) throw "length array failed" - if (length([]) != 0) throw "length empty array failed" + if (length([1,2,3]) != 3) return "length array failed" + if (length([]) != 0) return "length empty array failed" }, test_length_null: function() { - if (length(null) != null) throw "length null failed" + if (length(null) != null) return "length null failed" }, test_length_number: function() { - if (length(123) != null) throw "length number should return null" + if (length(123) != null) return "length number should return null" }, // ============================================================================ @@ -1031,20 +894,20 @@ return { test_reverse_array: function() { var arr = [1, 2, 3, 4, 5] var rev = reverse(arr) - if (rev[0] != 5) throw "reverse array first failed" - if (rev[4] != 1) throw "reverse array last failed" - if (length(rev) != 5) throw "reverse array length failed" + if (rev[0] != 5) return "reverse array first failed" + if (rev[4] != 1) return "reverse array last failed" + if (length(rev) != 5) return "reverse array length failed" }, test_reverse_empty_array: function() { var rev = reverse([]) - if (length(rev) != 0) throw "reverse empty array failed" + if (length(rev) != 0) return "reverse empty array failed" }, test_reverse_preserves_original: function() { var arr = [1, 2, 3] var rev = reverse(arr) - if (arr[0] != 1) throw "reverse should not mutate original" + if (arr[0] != 1) return "reverse should not mutate original" }, // ============================================================================ @@ -1054,23 +917,23 @@ return { test_meme_basic: function() { var parent = {x: 10} var child = meme(parent) - if (child.x != 10) throw "meme basic inheritance failed" + if (child.x != 10) return "meme basic inheritance failed" }, test_meme_with_mixins: function() { var parent = {x: 10} var mixin = {y: 20} var child = meme(parent, mixin) - if (child.x != 10) throw "meme with mixin parent prop failed" - if (child.y != 20) throw "meme with mixin own prop failed" + if (child.x != 10) return "meme with mixin parent prop failed" + if (child.y != 20) return "meme with mixin own prop failed" }, test_meme_override: function() { var parent = {x: 10} var child = meme(parent) child.x = 20 - if (child.x != 20) throw "meme override failed" - if (parent.x != 10) throw "meme should not mutate parent" + if (child.x != 20) return "meme override failed" + if (parent.x != 10) return "meme should not mutate parent" }, test_meme_multiple_mixins: function() { @@ -1078,7 +941,7 @@ return { var mixin1 = {b: 2} var mixin2 = {c: 3} var child = meme(parent, [mixin1, mixin2]) - if (child.a != 1 || child.b != 2 || child.c != 3) throw "meme multiple mixins failed" + if (child.a != 1 || child.b != 2 || child.c != 3) return "meme multiple mixins failed" }, // ============================================================================ @@ -1089,18 +952,18 @@ return { var parent = {x: 10} var child = meme(parent) var p = proto(child) - if (p != parent) throw "proto basic failed" + if (p != parent) return "proto basic failed" }, test_proto_object_literal: function() { var obj = {x: 10} var p = proto(obj) - if (p != null) throw "proto of object literal should be null" + if (p != null) return "proto of object literal should be null" }, test_proto_non_object: function() { - if (proto(42) != null) throw "proto of number should return null" - if (proto("hello") != null) throw "proto of string should return null" + if (proto(42) != null) return "proto of number should return null" + if (proto("hello") != null) return "proto of string should return null" }, // ============================================================================ @@ -1111,31 +974,33 @@ return { var obj = {x: 10} stone(obj) var caught = false - try { + var _fn = function() { obj.x = 20 - } catch (e) { + } disruption { caught = true } - if (!caught) throw "stone object should prevent modification" + _fn() + if (!caught) return "stone object should prevent modification" }, test_is_stone_frozen: function() { var obj = {x: 10} - if (is_stone(obj)) throw "stone.p should return false before freezing" + if (is_stone(obj)) return "stone.p should return false before freezing" stone(obj) - if (!is_stone(obj)) throw "stone.p should return true after freezing" + if (!is_stone(obj)) return "stone.p should return true after freezing" }, test_stone_array: function() { var arr = [1, 2, 3] stone(arr) var caught = false - try { + var _fn = function() { arr[0] = 99 - } catch (e) { + } disruption { caught = true } - if (!caught) throw "stone array should prevent modification" + _fn() + if (!caught) return "stone array should prevent modification" }, // ============================================================================ @@ -1144,24 +1009,24 @@ return { test_ternary_true: function() { var x = true ? 1 : 2 - if (x != 1) throw "ternary true failed" + if (x != 1) return "ternary true failed" }, test_ternary_false: function() { var x = false ? 1 : 2 - if (x != 2) throw "ternary false failed" + if (x != 2) return "ternary false failed" }, test_ternary_nested: function() { var x = true ? (false ? 1 : 2) : 3 - if (x != 2) throw "ternary nested failed" + if (x != 2) return "ternary nested failed" }, test_ternary_with_expressions: function() { var a = 5 var b = 10 var max = (a > b) ? a : b - if (max != 10) throw "ternary with expressions failed" + if (max != 10) return "ternary with expressions failed" }, // ============================================================================ @@ -1169,41 +1034,41 @@ return { // ============================================================================ test_unary_plus: function() { - if (+5 != 5) throw "unary plus positive failed" - if (+-5 != -5) throw "unary plus negative failed" + if (+5 != 5) return "unary plus positive failed" + if (+-5 != -5) return "unary plus negative failed" }, test_unary_minus: function() { - if (-5 != -5) throw "unary minus failed" - if (-(-5) != 5) throw "double unary minus failed" + if (-5 != -5) return "unary minus failed" + if (-(-5) != 5) return "double unary minus failed" }, test_increment_postfix: function() { var x = 5 var y = x++ - if (y != 5) throw "postfix increment return value failed" - if (x != 6) throw "postfix increment side effect failed" + if (y != 5) return "postfix increment return value failed" + if (x != 6) return "postfix increment side effect failed" }, test_increment_prefix: function() { var x = 5 var y = ++x - if (y != 6) throw "prefix increment return value failed" - if (x != 6) throw "prefix increment side effect failed" + if (y != 6) return "prefix increment return value failed" + if (x != 6) return "prefix increment side effect failed" }, test_decrement_postfix: function() { var x = 5 var y = x-- - if (y != 5) throw "postfix decrement return value failed" - if (x != 4) throw "postfix decrement side effect failed" + if (y != 5) return "postfix decrement return value failed" + if (x != 4) return "postfix decrement side effect failed" }, test_decrement_prefix: function() { var x = 5 var y = --x - if (y != 4) throw "prefix decrement return value failed" - if (x != 4) throw "prefix decrement side effect failed" + if (y != 4) return "prefix decrement return value failed" + if (x != 4) return "prefix decrement side effect failed" }, // ============================================================================ @@ -1213,31 +1078,31 @@ return { test_plus_equals: function() { var x = 5 x += 3 - if (x != 8) throw "plus equals failed" + if (x != 8) return "plus equals failed" }, test_minus_equals: function() { var x = 10 x -= 3 - if (x != 7) throw "minus equals failed" + if (x != 7) return "minus equals failed" }, test_times_equals: function() { var x = 4 x *= 3 - if (x != 12) throw "times equals failed" + if (x != 12) return "times equals failed" }, test_divide_equals: function() { var x = 12 x /= 3 - if (x != 4) throw "divide equals failed" + if (x != 4) return "divide equals failed" }, test_modulo_equals: function() { var x = 10 x %= 3 - if (x != 1) throw "modulo equals failed" + if (x != 1) return "modulo equals failed" }, // ============================================================================ @@ -1246,56 +1111,56 @@ return { test_division_by_zero_is_null: function() { var inf = 1 / 0 - if (inf != null) throw "division by zero should be null" + if (inf != null) return "division by zero should be null" var ninf = -1 / 0 - if (ninf != null) throw "negative division by zero should be null" + if (ninf != null) return "negative division by zero should be null" }, test_zero_div_zero_is_null: function() { var nan = 0 / 0 - if (nan != null) throw "0/0 should be null" + if (nan != null) return "0/0 should be null" }, test_max_safe_integer: function() { var max = 9007199254740991 - if (max + 1 - 1 != max) throw "max safe integer precision lost" + if (max + 1 - 1 != max) return "max safe integer precision lost" }, test_min_safe_integer: function() { var min = -9007199254740991 - if (min - 1 + 1 != min) throw "min safe integer precision lost" + if (min - 1 + 1 != min) return "min safe integer precision lost" }, test_empty_string_falsy: function() { - if ("") throw "empty string should be falsy" + if ("") return "empty string should be falsy" }, test_zero_falsy: function() { - if (0) throw "zero should be falsy" + if (0) return "zero should be falsy" }, test_null_falsy: function() { - if (null) throw "null should be falsy" + if (null) return "null should be falsy" }, test_false_falsy: function() { - if (false) throw "false should be falsy" + if (false) return "false should be falsy" }, test_nonempty_string_truthy: function() { - if (!"hello") throw "non-empty string should be truthy" + if (!"hello") return "non-empty string should be truthy" }, test_nonzero_number_truthy: function() { - if (!42) throw "non-zero number should be truthy" + if (!42) return "non-zero number should be truthy" }, test_object_truthy: function() { - if (!{}) throw "empty object should be truthy" + if (!{}) return "empty object should be truthy" }, test_array_truthy: function() { - if (![]) throw "empty array should be truthy" + if (![]) return "empty array should be truthy" }, // ============================================================================ @@ -1303,23 +1168,23 @@ return { // ============================================================================ test_precedence_multiply_add: function() { - if (2 + 3 * 4 != 14) throw "multiply before add precedence failed" + if (2 + 3 * 4 != 14) return "multiply before add precedence failed" }, test_precedence_parentheses: function() { - if ((2 + 3) * 4 != 20) throw "parentheses precedence failed" + if ((2 + 3) * 4 != 20) return "parentheses precedence failed" }, test_precedence_comparison_logical: function() { - if (!(1 < 2 && 3 < 4)) throw "comparison before logical precedence failed" + if (!(1 < 2 && 3 < 4)) return "comparison before logical precedence failed" }, test_precedence_equality_logical: function() { - if (!(1 == 1 || 2 == 3)) throw "equality before logical precedence failed" + if (!(1 == 1 || 2 == 3)) return "equality before logical precedence failed" }, test_precedence_unary_multiplication: function() { - if (-2 * 3 != -6) throw "unary before multiplication precedence failed" + if (-2 * 3 != -6) return "unary before multiplication precedence failed" }, // ============================================================================ @@ -1328,14 +1193,14 @@ return { test_comma_operator: function() { var x = (1, 2, 3) - if (x != 3) throw "comma operator failed" + if (x != 3) return "comma operator failed" }, test_comma_operator_with_side_effects: function() { var a = 0 var x = (a = 1, a = 2, a + 1) - if (x != 3) throw "comma operator with side effects failed" - if (a != 2) throw "comma operator side effects failed" + if (x != 3) return "comma operator with side effects failed" + if (a != 2) return "comma operator side effects failed" }, // ============================================================================ @@ -1348,8 +1213,8 @@ return { var x = 20 return x } - if (fn() != 20) throw "function shadowing failed" - if (x != 10) throw "outer variable after shadowing failed" + if (fn() != 20) return "function shadowing failed" + if (x != 10) return "outer variable after shadowing failed" }, test_variable_shadowing_nested: function() { @@ -1362,7 +1227,7 @@ return { } return fn2() + x } - if (fn1() != 50) throw "nested shadowing failed" + if (fn1() != 50) return "nested shadowing failed" }, // ============================================================================ @@ -1373,67 +1238,68 @@ return { var fn0 = function() {} var fn1 = function(a) {} var fn2 = function(a, b) {} - if (length(fn0) != 0) throw "function length 0 failed" - if (length(fn1) != 1) throw "function length 1 failed" - if (length(fn2) != 2) throw "function length 2 failed" + if (length(fn0) != 0) return "function length 0 failed" + if (length(fn1) != 1) return "function length 1 failed" + if (length(fn2) != 2) return "function length 2 failed" }, // ============================================================================ // NULL AND UNDEFINED BEHAVIOR // ============================================================================ - test_undefined_variable_is_null: function() { - var x - if (x != null) throw "undefined variable should be null" + test_null_initialized_variable: function() { + var x = null + if (x != null) return "null initialized variable should be null" }, // ============================================================================ // NUMBERS - SPECIAL OPERATIONS // ============================================================================ - test_number_toString_implicit: function() { + test_number_plus_empty_string_disrupts: function() { var n = 42 var caught = false - try { + var _fn = function() { var result = n + "" - } catch (e) { + } disruption { caught = true } - if (!caught) throw "number + string should throw" + _fn() + if (!caught) return "number + string should disrupt" }, test_number_division_by_zero: function() { var result = 1 / 0 - if (result != null) throw "division by zero should give null" + if (result != null) return "division by zero should give null" }, test_number_negative_division_by_zero: function() { var result = -1 / 0 - if (result != null) throw "negative division by zero should give null" + if (result != null) return "negative division by zero should give null" }, test_zero_division_by_zero: function() { var result = 0 / 0 - if (result != null) throw "0/0 should give null" + if (result != null) return "0/0 should give null" }, test_negative_zero_normalized: function() { var nz = -0 - if (nz != 0) throw "-0 should equal 0" + if (nz != 0) return "-0 should equal 0" var mul_nz = 0 * -1 - if (mul_nz != 0) throw "0 * -1 should be 0" + if (mul_nz != 0) return "0 * -1 should be 0" var neg_zero = -(0) - if (neg_zero != 0) throw "-(0) should be 0" + if (neg_zero != 0) return "-(0) should be 0" }, test_overflow_is_null: function() { var result = 1e38 * 1e38 - if (result != null) throw "overflow should give null" + if (result != null) return "overflow should give null" }, test_modulo_by_zero_is_null: function() { var result = 5 % 0 - if (result != null) throw "modulo by zero should give null" + if (result != null) return "modulo by zero should give null" }, // ============================================================================ @@ -1442,14 +1308,14 @@ return { test_in_operator: function() { var obj = {a: 1, b: 2} - if (!("a" in obj)) throw "in operator for existing property failed" - if ("c" in obj) throw "in operator for non-existing property failed" + if (!("a" in obj)) return "in operator for existing property failed" + if ("c" in obj) return "in operator for non-existing property failed" }, test_in_operator_prototype: function() { var parent = {x: 10} var child = meme(parent) - if (!("x" in child)) throw "in operator should find inherited property" + if (!("x" in child)) return "in operator should find inherited property" }, // ============================================================================ @@ -1457,27 +1323,27 @@ return { // ============================================================================ test_logical_function_numbers: function() { - if (logical(0) != false) throw "logical(0) should be false" - if (logical(1) != true) throw "logical(1) should be true" + if (logical(0) != false) return "logical(0) should be false" + if (logical(1) != true) return "logical(1) should be true" }, test_logical_function_strings: function() { - if (logical("false") != false) throw "logical('false') should be false" - if (logical("true") != true) throw "logical('true') should be true" + if (logical("false") != false) return "logical('false') should be false" + if (logical("true") != true) return "logical('true') should be true" }, test_logical_function_booleans: function() { - if (logical(false) != false) throw "logical(false) should be false" - if (logical(true) != true) throw "logical(true) should be true" + if (logical(false) != false) return "logical(false) should be false" + if (logical(true) != true) return "logical(true) should be true" }, test_logical_function_null: function() { - if (logical(null) != false) throw "logical(null) should be false" + if (logical(null) != false) return "logical(null) should be false" }, test_logical_function_invalid: function() { - if (logical("invalid") != null) throw "logical(invalid) should return null" - if (logical(42) != null) throw "logical(42) should return null" + if (logical("invalid") != null) return "logical(invalid) should return null" + if (logical(42) != null) return "logical(42) should return null" }, // ============================================================================ @@ -1488,29 +1354,26 @@ return { var arr1 = [1, 2] var arr2 = [3, 4] var combined = array(arr1, arr2) - if (length(combined) != 4) throw "array concat length failed" - if (combined[2] != 3) throw "array concat values failed" + if (length(combined) != 4) return "array concat length failed" + if (combined[2] != 3) return "array concat values failed" }, test_array_join: function() { var arr = ["a", "b", "c"] var str = text(arr, ",") - if (str != "a,b,c") throw "array join with text() failed" + if (str != "a,b,c") return "array join with text() failed" }, - test_text_array_join_numbers_throw: function() { + test_text_array_join_numbers_disrupts: function() { var caught = false - try { - text([1, 2, 3], ",") - } catch (e) { - caught = true - } - if (!caught) throw "text([numbers], sep) should throw (no implicit coercion)" + var _fn = function() { text([1, 2, 3], ",") } disruption { caught = true } + _fn() + if (!caught) return "text([numbers], sep) should disrupt (no implicit coercion)" }, test_text_array_join_numbers_explicit: function() { var arr = array([1, 2, 3], x => text(x)) - if (text(arr, ",") != "1,2,3") throw "explicit numeric join failed" + if (text(arr, ",") != "1,2,3") return "explicit numeric join failed" }, // ============================================================================ @@ -1519,52 +1382,52 @@ return { test_string_substring: function() { var str = "hello" - if (text(str, 1, 4) != "ell") throw "string substring failed" + if (text(str, 1, 4) != "ell") return "string substring failed" }, test_string_substring_first: function() { var str = "hello" - if (text(str, 1) != "ello") throw "string substring first failed" + if (text(str, 1) != "ello") return "string substring first failed" }, test_string_substring_to_neg: function() { var str = "hello" - if (text(str, 1, -2) != "el") throw "string substring to negative failed" + if (text(str, 1, -2) != "el") return "string substring to negative failed" }, test_string_slice: function() { var str = "hello" - if (text(str, 1, 4) != "ell") throw "string slice failed" - if (text(str, -2) != "lo") throw "string slice negative failed: " + text(str, -2) + if (text(str, 1, 4) != "ell") return "string slice failed" + if (text(str, -2) != "lo") return "string slice negative failed: " + text(str, -2) }, test_string_indexOf: function() { var str = "hello world" - if (search(str, "world") != 6) throw "string search failed" - if (search(str, "xyz") != null) throw "string search not found failed" + if (search(str, "world") != 6) return "string search failed" + if (search(str, "xyz") != null) return "string search not found failed" }, test_string_toLowerCase: function() { var str = "HELLO" - if (lower(str) != "hello") throw "string toLowerCase failed" + if (lower(str) != "hello") return "string toLowerCase failed" }, test_string_toUpperCase: function() { var str = "hello" - if (upper(str) != "HELLO") throw "string toUpperCase failed" + if (upper(str) != "HELLO") return "string toUpperCase failed" }, test_string_split: function() { var str = "a,b,c" var parts = array(str, ",") - if (length(parts) != 3) throw "string split length failed" - if (parts[1] != "b") throw "string split values failed" + if (length(parts) != 3) return "string split length failed" + if (parts[1] != "b") return "string split values failed" }, null_access: function() { var val = {} var nn = val.a - if (nn != null) throw "val.a should return null" + if (nn != null) return "val.a should return null" }, // ============================================================================ @@ -1577,22 +1440,22 @@ return { var o = {} o[k1] = 123 o[k2] = 456 - if (o[k1] != 123) throw "object key k1 failed" - if (o[k2] != 456) throw "object key k2 failed" + if (o[k1] != 123) return "object key k1 failed" + if (o[k2] != 456) return "object key k2 failed" }, test_object_key_new_object_different_key: function() { var k1 = {} var o = {} o[k1] = 123 - if (o[{}] != null) throw "new object should be different key" + if (o[{}] != null) return "new object should be different key" }, test_object_key_in_operator: function() { var k1 = {} var o = {} o[k1] = 123 - if (!(k1 in o)) throw "in operator should find object key" + if (!(k1 in o)) return "in operator should find object key" }, test_object_key_delete: function() { @@ -1600,7 +1463,7 @@ return { var o = {} o[k1] = 123 delete o[k1] - if ((k1 in o)) throw "delete should remove object key" + if ((k1 in o)) return "delete should remove object key" }, test_object_key_no_string_collision: function() { @@ -1609,8 +1472,8 @@ return { var o = {} o[a] = 1 o[b] = 2 - if (o[a] != 1) throw "object key a should be 1" - if (o[b] != 2) throw "object key b should be 2" + if (o[a] != 1) return "object key a should be 1" + if (o[b] != 2) return "object key b should be 2" }, test_object_key_same_object_same_key: function() { @@ -1618,14 +1481,14 @@ return { var o = {} o[k] = 100 o[k] = 200 - if (o[k] != 200) throw "same object should be same key" + if (o[k] != 200) return "same object should be same key" }, test_object_key_computed_property: function() { var k = {} var o = {} o[k] = function() { return 42 } - if (o[k]() != 42) throw "object key with function value failed" + if (o[k]() != 42) return "object key with function value failed" }, test_object_key_multiple_objects_multiple_keys: function() { @@ -1636,17 +1499,17 @@ return { o[k1] = "one" o[k2] = "two" o[k3] = "three" - if (o[k1] != "one") throw "multiple keys k1 failed" - if (o[k2] != "two") throw "multiple keys k2 failed" - if (o[k3] != "three") throw "multiple keys k3 failed" + if (o[k1] != "one") return "multiple keys k1 failed" + if (o[k2] != "two") return "multiple keys k2 failed" + if (o[k3] != "three") return "multiple keys k3 failed" }, test_object_key_with_string_keys: function() { var k = {} var o = {name: "test"} o[k] = "private" - if (o.name != "test") throw "string key should still work" - if (o[k] != "private") throw "object key should work with string keys" + if (o.name != "test") return "string key should still work" + if (o[k] != "private") return "object key should work with string keys" }, test_object_key_overwrite: function() { @@ -1655,7 +1518,7 @@ return { o[k] = 1 o[k] = 2 o[k] = 3 - if (o[k] != 3) throw "object key overwrite failed" + if (o[k] != 3) return "object key overwrite failed" }, test_object_key_nested_objects: function() { @@ -1665,181 +1528,142 @@ return { inner[k2] = "nested" var outer = {} outer[k1] = inner - if (outer[k1][k2] != "nested") throw "nested object keys failed" + if (outer[k1][k2] != "nested") return "nested object keys failed" }, test_array_for: function() { var a = [1,2,3] arrfor(a, (x,i) => { - if (x-1 != i) throw "array for failed" + if (x-1 != i) return "array for failed" }) }, - test_array_string_key_throws: function() { - var a = [] + test_array_string_key_disrupts: function() { var caught = false - try { - a["a"] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use string as key" + var _fn = function() { var a = []; a["a"] = 1 } disruption { caught = true } + _fn() + if (!caught) return "array should not use string as key" }, - test_array_object_key_throws: function() { - var a = [] - var b = {} + test_array_object_key_disrupts: function() { var caught = false - try { - a[b] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use object as key" + var _fn = function() { var a = []; var b = {}; a[b] = 1 } disruption { caught = true } + _fn() + if (!caught) return "array should not use object as key" }, - test_array_boolean_key_throws: function() { - var a = [] + test_array_boolean_key_disrupts: function() { var caught = false - try { - a[true] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use boolean as key" + var _fn = function() { var a = []; a[true] = 1 } disruption { caught = true } + _fn() + if (!caught) return "array should not use boolean as key" }, - test_array_null_key_throws: function() { - var a = [] + test_array_null_key_disrupts: function() { var caught = false - try { - a[null] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use null as key" + var _fn = function() { var a = []; a[null] = 1 } disruption { caught = true } + _fn() + if (!caught) return "array should not use null as key" }, - test_array_array_key_throws: function() { - var a = [] - var c = [] + test_array_array_key_disrupts: function() { var caught = false - try { - a[c] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use array as key" + var _fn = function() { var a = []; var c = []; a[c] = 1 } disruption { caught = true } + _fn() + if (!caught) return "array should not use array as key" }, - test_obj_number_key_throws: function() { - var a = {} + test_obj_number_key_disrupts: function() { var caught = false - try { - a[1] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "object should not be able to use number as key" + var _fn = function() { var a = {}; a[1] = 1 } disruption { caught = true } + _fn() + if (!caught) return "object should not use number as key" }, - test_obj_array_key_throws: function() { - var a = {} - var c = [] + test_obj_array_key_disrupts: function() { var caught = false - try { - a[c] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "object should not be able to use array as key" + var _fn = function() { var a = {}; var c = []; a[c] = 1 } disruption { caught = true } + _fn() + if (!caught) return "object should not use array as key" }, - test_obj_boolean_key_throws: function() { - var a = {} + test_obj_boolean_key_disrupts: function() { var caught = false - try { - a[true] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "object should not be able to use boolean as key" + var _fn = function() { var a = {}; a[true] = 1 } disruption { caught = true } + _fn() + if (!caught) return "object should not use boolean as key" }, - test_obj_null_key_throws: function() { - var a = {} + test_obj_null_key_disrupts: function() { var caught = false - try { - a[null] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "object should not be able to use null as key" + var _fn = function() { var a = {}; a[null] = 1 } disruption { caught = true } + _fn() + if (!caught) return "object should not use null as key" }, // ============================================================================ - // RETRIEVAL WITH INVALID KEY RETURNS NULL (not throw) + // RETRIEVAL WITH INVALID KEY RETURNS NULL (not disrupt) // ============================================================================ test_array_get_string_key_returns_null: function() { var a = [1, 2, 3] var result = a["x"] - if (result != null) throw "array get with string key should return null" + if (result != null) return "array get with string key should return null" }, test_array_get_negative_index_returns_null: function() { var a = [1, 2, 3] var result = a[-1] - if (result != null) throw "array get with negative index should return null" + if (result != null) return "array get with negative index should return null" }, test_array_get_object_key_returns_null: function() { var a = [1, 2, 3] var k = {} var result = a[k] - if (result != null) throw "array get with object key should return null" + if (result != null) return "array get with object key should return null" }, test_array_get_array_key_returns_null: function() { var a = [1, 2, 3] var result = a[[1, 2]] - if (result != null) throw "array get with array key should return null" + if (result != null) return "array get with array key should return null" }, test_array_get_boolean_key_returns_null: function() { var a = [1, 2, 3] var result = a[true] - if (result != null) throw "array get with boolean key should return null" + if (result != null) return "array get with boolean key should return null" }, test_array_get_null_key_returns_null: function() { var a = [1, 2, 3] var result = a[null] - if (result != null) throw "array get with null key should return null" + if (result != null) return "array get with null key should return null" }, test_obj_get_number_key_returns_null: function() { var o = {a: 1} var result = o[5] - if (result != null) throw "object get with number key should return null" + if (result != null) return "object get with number key should return null" }, test_obj_get_array_key_returns_null: function() { var o = {a: 1} var result = o[[1, 2]] - if (result != null) throw "object get with array key should return null" + if (result != null) return "object get with array key should return null" }, test_obj_get_boolean_key_returns_null: function() { var o = {a: 1} var result = o[true] - if (result != null) throw "object get with boolean key should return null" + if (result != null) return "object get with boolean key should return null" }, test_obj_get_null_key_returns_null: function() { var o = {a: 1} var result = o[null] - if (result != null) throw "object get with null key should return null" + if (result != null) return "object get with null key should return null" }, // ============================================================================ @@ -1849,29 +1673,21 @@ return { test_function_property_get_throws: function() { var fn = function(a, b) { return a + b } var arity = length(fn) - if (arity != 2) throw "length of function should return its arity" + if (arity != 2) return "length of function should return its arity" }, - test_function_property_set_throws: function() { - var fn = function() {} + test_function_property_set_disrupts: function() { var caught = false - try { - fn.foo = 123 - } catch (e) { - caught = true - } - if (!caught) throw "setting property on function should throw" + var _fn = function() { var fn = function() {}; fn.foo = 123 } disruption { caught = true } + _fn() + if (!caught) return "setting property on function should disrupt" }, - test_function_bracket_access_throws: function() { - var fn = function() {} + test_function_bracket_access_disrupts: function() { var caught = false - try { - var x = fn["length"]() - } catch (e) { - caught = true - } - if (!caught) throw "bracket access on function should throw" + var _fn = function() { var fn = function() {}; var x = fn["length"]() } disruption { caught = true } + _fn() + if (!caught) return "bracket access on function should disrupt" }, test_length_returns_function_arity: function() { @@ -1880,42 +1696,35 @@ return { var fn2 = function(a, b) { return a + b } var fn3 = function(a, b, c) { return a + b + c } - if (length(fn0) != 0) throw "length(fn0) should be 0" - if (length(fn1) != 1) throw "length(fn1) should be 1" - if (length(fn2) != 2) throw "length(fn2) should be 2" - if (length(fn3) != 3) throw "length(fn3) should be 3" - }, - - test_text_returns_function_source: function() { - var fn = function(x) { return x * 2 } - var src = text(fn) - if (search(src, "function") == null) throw "text(fn) should contain 'function'" - if (search(src, "return") == null) throw "text(fn) should contain function body" + if (length(fn0) != 0) return "length(fn0) should be 0" + if (length(fn1) != 1) return "length(fn1) should be 1" + if (length(fn2) != 2) return "length(fn2) should be 2" + if (length(fn3) != 3) return "length(fn3) should be 3" }, test_call_invokes_function: function() { var fn = function(a, b) { return a + b } var result = call(fn, null, [3, 4]) - if (result != 7) throw "call(fn, null, 3, 4) should return 7" + if (result != 7) return "call(fn, null, 3, 4) should return 7" }, test_call_with_this_binding: function() { var obj = { value: 10 } var fn = function(x) { return this.value + x } var result = call(fn, obj, [5]) - if (result != 15) throw "call(fn, obj, 5) should return 15" + if (result != 15) return "call(fn, obj, 5) should return 15" }, test_call_no_args: function() { var fn = function() { return 42 } var result = call(fn, null) - if (result != 42) throw "call(fn, null) should return 42" + if (result != 42) return "call(fn, null) should return 42" }, test_builtin_function_properties_still_work: function() { // Built-in functions like number, text, array should still have properties var min_result = min(5, 3) - if (min_result != 3) throw "min should work" + if (min_result != 3) return "min should work" }, // ============================================================================ @@ -1927,7 +1736,7 @@ return { return `called:${name}:${length(args)}` } var result = proxy.foo() - if (result != "called:foo:0") throw "basic proxy call failed" + if (result != "called:foo:0") return "basic proxy call failed" }, test_function_proxy_with_one_arg: function() { @@ -1935,19 +1744,20 @@ return { return `${name}-${args[0]}` } var result = proxy.test("value") - if (result != "test-value") throw "proxy with one arg failed" + if (result != "test-value") return "proxy with one arg failed" }, test_function_proxy_with_multiple_args: function() { var proxy = function(name, args) { var sum = 0 - for (var i = 0; i < length(args); i++) { + var i = 0 + for (i = 0; i < length(args); i++) { sum = sum + args[i] } return `${name}:${sum}` } var result = proxy.add(1, 2, 3, 4) - if (result != "add:10") throw "proxy with multiple args failed" + if (result != "add:10") return "proxy with multiple args failed" }, test_function_proxy_bracket_notation: function() { @@ -1955,7 +1765,7 @@ return { return `bracket:${name}` } var result = proxy["myMethod"]() - if (result != "bracket:myMethod") throw "proxy bracket notation failed" + if (result != "bracket:myMethod") return "proxy bracket notation failed" }, test_function_proxy_dynamic_method_name: function() { @@ -1964,7 +1774,7 @@ return { } var methodName = "dynamic" var result = proxy[methodName]() - if (result != "dynamic") throw "proxy dynamic method name failed" + if (result != "dynamic") return "proxy dynamic method name failed" }, test_function_proxy_dispatch_to_record: function() { @@ -1981,50 +1791,42 @@ return { if (is_function(my_record[name])) { return apply(my_record[name], args) } - throw `unknown method: ${name}` + return `unknown method: ${name}` } - if (proxy.greet("World") != "Hello, World") throw "proxy dispatch greet failed" - if (proxy.add(3, 4) != 7) throw "proxy dispatch add failed" + if (proxy.greet("World") != "Hello, World") return "proxy dispatch greet failed" + if (proxy.add(3, 4) != 7) return "proxy dispatch add failed" }, - test_function_proxy_unknown_method_throws: function() { + test_function_proxy_unknown_method_disrupts: function() { var proxy = function(name, args) { - throw `no such method: ${name}` + disrupt } var caught = false - try { - proxy.nonexistent() - } catch (e) { - caught = true - if (search(e, "no such method") == null) throw "wrong error message" - } - if (!caught) throw "proxy should throw for unknown method" + var _fn = function() { proxy.nonexistent() } disruption { caught = true } + _fn() + if (!caught) return "proxy should disrupt for unknown method" }, test_function_proxy_is_function: function() { var proxy = function(name, args) { return name } - if (!is_function(proxy)) throw "proxy should be a function" + if (!is_function(proxy)) return "proxy should be a function" }, test_function_proxy_length_is_2: function() { var proxy = function(name, args) { return name } - if (length(proxy) != 2) throw "proxy function should have length 2" + if (length(proxy) != 2) return "proxy function should have length 2" }, - test_function_proxy_property_read_still_throws: function() { - var fn = function() { return 1 } + test_function_proxy_property_read_disrupts: function() { var caught = false - try { - var x = fn.someProp - } catch (e) { - caught = true - } - if (!caught) throw "reading property from function (not method call) should throw" + var _fn = function() { var fn = function() { return 1 }; var x = fn.someProp } disruption { caught = true } + _fn() + if (!caught) return "reading property from non-proxy function should disrupt" }, test_function_proxy_nested_calls: function() { @@ -2041,7 +1843,7 @@ return { return "inner:" + name } var result = outer.inner(inner) - if (result != 10) throw "nested proxy calls failed" + if (result != 10) return "nested proxy calls failed" }, test_function_proxy_returns_null: function() { @@ -2049,7 +1851,7 @@ return { return null } var result = proxy.anything() - if (result != null) throw "proxy returning null failed" + if (result != null) return "proxy returning null failed" }, test_function_proxy_returns_object: function() { @@ -2057,8 +1859,8 @@ return { return {method: name, argCount: length(args)} } var result = proxy.test(1, 2, 3) - if (result.method != "test") throw "proxy returning object method failed" - if (result.argCount != 3) throw "proxy returning object argCount failed" + if (result.method != "test") return "proxy returning object method failed" + if (result.argCount != 3) return "proxy returning object argCount failed" }, test_function_proxy_returns_function: function() { @@ -2066,17 +1868,17 @@ return { return function() { return name } } var result = proxy.getFn() - if (result() != "getFn") throw "proxy returning function failed" + if (result() != "getFn") return "proxy returning function failed" }, test_function_proxy_args_array_is_real_array: function() { var proxy = function(name, args) { - if (!is_array(args)) throw "args should be array" - push(args, 4) + if (!is_array(args)) disrupt + args[] = 4 return length(args) } var result = proxy.test(1, 2, 3) - if (result != 4) throw "proxy args should be modifiable array" + if (result != 4) return "proxy args should be modifiable array" }, test_function_proxy_no_this_binding: function() { @@ -2084,20 +1886,17 @@ return { return this } var result = proxy.test() - if (result != null) throw "proxy should have null this" + if (result != null) return "proxy should have null this" }, - test_function_proxy_integer_bracket_key: function() { + test_function_proxy_integer_bracket_key_disrupts: function() { var proxy = function(name, args) { return `key:${name}` } var caught = false - try { - var result = proxy[42]() - } catch (e) { - caught = true - } - if (!caught) throw "proxy with integer bracket key should throw" + var _fn = function() { var result = proxy[42]() } disruption { caught = true } + _fn() + if (!caught) return "proxy with integer bracket key should disrupt" }, // ============================================================================ @@ -2107,61 +1906,61 @@ return { test_reduce_sum: function() { var arr = [1, 2, 3, 4, 5] var result = reduce(arr, (a, b) => a + b) - if (result != 15) throw "reduce sum failed" + if (result != 15) return "reduce sum failed" }, test_reduce_product: function() { var arr = [1, 2, 3, 4, 5] var result = reduce(arr, (a, b) => a * b) - if (result != 120) throw "reduce product failed" + if (result != 120) return "reduce product failed" }, test_reduce_with_initial: function() { var arr = [1, 2, 3] var result = reduce(arr, (a, b) => a + b, 10) - if (result != 16) throw "reduce with initial failed" + if (result != 16) return "reduce with initial failed" }, test_reduce_with_initial_zero: function() { var arr = [1, 2, 3] var result = reduce(arr, (a, b) => a + b, 0) - if (result != 6) throw "reduce with initial zero failed" + if (result != 6) return "reduce with initial zero failed" }, test_reduce_empty_array_no_initial: function() { var arr = [] var result = reduce(arr, (a, b) => a + b) - if (result != null) throw "reduce empty array without initial should return null" + if (result != null) return "reduce empty array without initial should return null" }, test_reduce_empty_array_with_initial: function() { var arr = [] var result = reduce(arr, (a, b) => a + b, 42) - if (result != 42) throw "reduce empty array with initial should return initial" + if (result != 42) return "reduce empty array with initial should return initial" }, test_reduce_single_element_no_initial: function() { var arr = [42] var result = reduce(arr, (a, b) => a + b) - if (result != 42) throw "reduce single element without initial failed" + if (result != 42) return "reduce single element without initial failed" }, test_reduce_single_element_with_initial: function() { var arr = [5] var result = reduce(arr, (a, b) => a + b, 10) - if (result != 15) throw "reduce single element with initial failed" + if (result != 15) return "reduce single element with initial failed" }, test_reduce_reverse: function() { var arr = [1, 2, 3, 4] var result = reduce(arr, (a, b) => a - b, 0, true) - if (result != -10) throw "reduce reverse failed: " + result + if (result != -10) return "reduce reverse failed: " + result }, test_reduce_string_concat: function() { var arr = ["a", "b", "c"] var result = reduce(arr, (a, b) => a + b) - if (result != "abc") throw "reduce string concat failed" + if (result != "abc") return "reduce string concat failed" }, // ============================================================================ @@ -2171,33 +1970,33 @@ return { test_sort_numbers: function() { var arr = [3, 1, 4, 1, 5, 9, 2, 6] var sorted = sort(arr) - if (sorted[0] != 1 || sorted[1] != 1 || sorted[2] != 2) throw "sort numbers failed" - if (sorted[7] != 9) throw "sort numbers last element failed" + if (sorted[0] != 1 || sorted[1] != 1 || sorted[2] != 2) return "sort numbers failed" + if (sorted[7] != 9) return "sort numbers last element failed" }, test_sort_strings: function() { var arr = ["banana", "apple", "cherry"] var sorted = sort(arr) - if (sorted[0] != "apple") throw "sort strings failed" - if (sorted[2] != "cherry") throw "sort strings last failed" + if (sorted[0] != "apple") return "sort strings failed" + if (sorted[2] != "cherry") return "sort strings last failed" }, test_sort_preserves_original: function() { var arr = [3, 1, 2] var sorted = sort(arr) - if (arr[0] != 3) throw "sort should not mutate original" + if (arr[0] != 3) return "sort should not mutate original" }, test_sort_empty_array: function() { var arr = [] var sorted = sort(arr) - if (length(sorted) != 0) throw "sort empty array failed" + if (length(sorted) != 0) return "sort empty array failed" }, test_sort_single_element: function() { var arr = [42] var sorted = sort(arr) - if (sorted[0] != 42) throw "sort single element failed" + if (sorted[0] != 42) return "sort single element failed" }, test_sort_by_field: function() { @@ -2207,14 +2006,14 @@ return { {name: "Bob", age: 35} ] var sorted = sort(arr, "name") - if (sorted[0].name != "Alice") throw "sort by field failed" - if (sorted[2].name != "Charlie") throw "sort by field last failed" + if (sorted[0].name != "Alice") return "sort by field failed" + if (sorted[2].name != "Charlie") return "sort by field last failed" }, test_sort_by_index: function() { var arr = [[3, "c"], [1, "a"], [2, "b"]] var sorted = sort(arr, 0) - if (sorted[0][1] != "a") throw "sort by index failed" + if (sorted[0][1] != "a") return "sort by index failed" }, test_sort_stable: function() { @@ -2225,14 +2024,14 @@ return { ] var sorted = sort(arr, "order") if (sorted[0].name != "A" || sorted[1].name != "B" || sorted[2].name != "C") { - throw "sort should be stable" + return "sort should be stable" } }, test_sort_negative_numbers: function() { var arr = [-5, 3, -1, 0, 2] var sorted = sort(arr) - if (sorted[0] != -5 || sorted[4] != 3) throw "sort negative numbers failed" + if (sorted[0] != -5 || sorted[4] != 3) return "sort negative numbers failed" }, // ============================================================================ @@ -2242,45 +2041,45 @@ return { test_filter_basic: function() { var arr = [1, 2, 3, 4, 5, 6] var evens = filter(arr, x => x % 2 == 0) - if (length(evens) != 3) throw "filter basic length failed" - if (evens[0] != 2 || evens[1] != 4 || evens[2] != 6) throw "filter basic values failed" + if (length(evens) != 3) return "filter basic length failed" + if (evens[0] != 2 || evens[1] != 4 || evens[2] != 6) return "filter basic values failed" }, test_filter_all_pass: function() { var arr = [2, 4, 6] var result = filter(arr, x => x % 2 == 0) - if (length(result) != 3) throw "filter all pass failed" + if (length(result) != 3) return "filter all pass failed" }, test_filter_none_pass: function() { var arr = [1, 3, 5] var result = filter(arr, x => x % 2 == 0) - if (length(result) != 0) throw "filter none pass failed" + if (length(result) != 0) return "filter none pass failed" }, test_filter_empty_array: function() { var arr = [] var result = filter(arr, x => true) - if (length(result) != 0) throw "filter empty array failed" + if (length(result) != 0) return "filter empty array failed" }, test_filter_with_index: function() { var arr = ["a", "b", "c", "d"] var result = filter(arr, (x, i) => i % 2 == 0) - if (length(result) != 2) throw "filter with index length failed" - if (result[0] != "a" || result[1] != "c") throw "filter with index values failed" + if (length(result) != 2) return "filter with index length failed" + if (result[0] != "a" || result[1] != "c") return "filter with index values failed" }, test_filter_preserves_original: function() { var arr = [1, 2, 3] var result = filter(arr, x => x > 1) - if (length(arr) != 3) throw "filter should not mutate original" + if (length(arr) != 3) return "filter should not mutate original" }, test_filter_objects: function() { var arr = [{active: true}, {active: false}, {active: true}] var result = filter(arr, x => x.active) - if (length(result) != 2) throw "filter objects failed" + if (length(result) != 2) return "filter objects failed" }, // ============================================================================ @@ -2290,55 +2089,55 @@ return { test_find_basic: function() { var arr = [1, 2, 3, 4, 5] var idx = find(arr, x => x > 3) - if (idx != 3) throw "find basic failed" + if (idx != 3) return "find basic failed" }, test_find_first_element: function() { var arr = [10, 2, 3] var idx = find(arr, x => x > 5) - if (idx != 0) throw "find first element failed" + if (idx != 0) return "find first element failed" }, test_find_last_element: function() { var arr = [1, 2, 10] var idx = find(arr, x => x > 5) - if (idx != 2) throw "find last element failed" + if (idx != 2) return "find last element failed" }, test_find_not_found: function() { var arr = [1, 2, 3] var idx = find(arr, x => x > 10) - if (idx != null) throw "find not found should return null" + if (idx != null) return "find not found should return null" }, test_find_empty_array: function() { var arr = [] var idx = find(arr, x => true) - if (idx != null) throw "find in empty array should return null" + if (idx != null) return "find in empty array should return null" }, test_find_by_value: function() { var arr = [10, 20, 30, 20] var idx = find(arr, 20) - if (idx != 1) throw "find by value failed" + if (idx != 1) return "find by value failed" }, test_find_reverse: function() { var arr = [10, 20, 30, 20] var idx = find(arr, 20, true) - if (idx != 3) throw "find reverse failed" + if (idx != 3) return "find reverse failed" }, test_find_with_from: function() { var arr = [10, 20, 30, 20] var idx = find(arr, 20, false, 2) - if (idx != 3) throw "find with from failed" + if (idx != 3) return "find with from failed" }, test_find_with_index_callback: function() { var arr = ["a", "b", "c"] var idx = find(arr, (x, i) => i == 1) - if (idx != 1) throw "find with index callback failed" + if (idx != 1) return "find with index callback failed" }, // ============================================================================ @@ -2346,24 +2145,24 @@ return { // ============================================================================ test_abs_positive: function() { - if (abs(5) != 5) throw "abs positive failed" + if (abs(5) != 5) return "abs positive failed" }, test_abs_negative: function() { - if (abs(-5) != 5) throw "abs negative failed" + if (abs(-5) != 5) return "abs negative failed" }, test_abs_zero: function() { - if (abs(0) != 0) throw "abs zero failed" + if (abs(0) != 0) return "abs zero failed" }, test_abs_float: function() { - if (abs(-3.14) != 3.14) throw "abs float failed" + if (abs(-3.14) != 3.14) return "abs float failed" }, test_abs_non_number: function() { - if (abs("5") != null) throw "abs non-number should return null" - if (abs(null) != null) throw "abs null should return null" + if (abs("5") != null) return "abs non-number should return null" + if (abs(null) != null) return "abs null should return null" }, // ============================================================================ @@ -2371,27 +2170,27 @@ return { // ============================================================================ test_floor_positive: function() { - if (floor(3.7) != 3) throw "floor positive failed" + if (floor(3.7) != 3) return "floor positive failed" }, test_floor_negative: function() { - if (floor(-3.7) != -4) throw "floor negative failed" + if (floor(-3.7) != -4) return "floor negative failed" }, test_floor_integer: function() { - if (floor(5) != 5) throw "floor integer failed" + if (floor(5) != 5) return "floor integer failed" }, test_floor_zero: function() { - if (floor(0) != 0) throw "floor zero failed" + if (floor(0) != 0) return "floor zero failed" }, test_floor_with_place: function() { - if (floor(12.3775, -2) != 12.37) throw "floor with place failed" + if (floor(12.3775, -2) != 12.37) return "floor with place failed" }, test_floor_negative_with_place: function() { - if (floor(-12.3775, -2) != -12.38) throw "floor negative with place failed" + if (floor(-12.3775, -2) != -12.38) return "floor negative with place failed" }, // ============================================================================ @@ -2399,27 +2198,27 @@ return { // ============================================================================ test_ceiling_positive: function() { - if (ceiling(3.2) != 4) throw "ceiling positive failed" + if (ceiling(3.2) != 4) return "ceiling positive failed" }, test_ceiling_negative: function() { - if (ceiling(-3.7) != -3) throw "ceiling negative failed" + if (ceiling(-3.7) != -3) return "ceiling negative failed" }, test_ceiling_integer: function() { - if (ceiling(5) != 5) throw "ceiling integer failed" + if (ceiling(5) != 5) return "ceiling integer failed" }, test_ceiling_zero: function() { - if (ceiling(0) != 0) throw "ceiling zero failed" + if (ceiling(0) != 0) return "ceiling zero failed" }, test_ceiling_with_place: function() { - if (ceiling(12.3775, -2) != 12.38) throw "ceiling with place failed" + if (ceiling(12.3775, -2) != 12.38) return "ceiling with place failed" }, test_ceiling_negative_with_place: function() { - if (ceiling(-12.3775, -2) != -12.37) throw "ceiling negative with place failed" + if (ceiling(-12.3775, -2) != -12.37) return "ceiling negative with place failed" }, // ============================================================================ @@ -2427,31 +2226,31 @@ return { // ============================================================================ test_round_down: function() { - if (round(3.4) != 3) throw "round down failed" + if (round(3.4) != 3) return "round down failed" }, test_round_up: function() { - if (round(3.6) != 4) throw "round up failed" + if (round(3.6) != 4) return "round up failed" }, test_round_half: function() { - if (round(3.5) != 4) throw "round half failed" + if (round(3.5) != 4) return "round half failed" }, test_round_negative: function() { - if (round(-3.5) != -3 && round(-3.5) != -4) throw "round negative failed" + if (round(-3.5) != -3 && round(-3.5) != -4) return "round negative failed" }, test_round_integer: function() { - if (round(5) != 5) throw "round integer failed" + if (round(5) != 5) return "round integer failed" }, test_round_with_places: function() { - if (round(12.3775, -2) != 12.38) throw "round with places failed" + if (round(12.3775, -2) != 12.38) return "round with places failed" }, test_round_to_tens: function() { - if (round(12.3775, 1) != 10) throw "round to tens failed" + if (round(12.3775, 1) != 10) return "round to tens failed" }, // ============================================================================ @@ -2459,27 +2258,27 @@ return { // ============================================================================ test_trunc_positive: function() { - if (trunc(3.7) != 3) throw "trunc positive failed" + if (trunc(3.7) != 3) return "trunc positive failed" }, test_trunc_negative: function() { - if (trunc(-3.7) != -3) throw "trunc negative failed" + if (trunc(-3.7) != -3) return "trunc negative failed" }, test_trunc_integer: function() { - if (trunc(5) != 5) throw "trunc integer failed" + if (trunc(5) != 5) return "trunc integer failed" }, test_trunc_zero: function() { - if (trunc(0) != 0) throw "trunc zero failed" + if (trunc(0) != 0) return "trunc zero failed" }, test_trunc_with_places: function() { - if (trunc(12.3775, -2) != 12.37) throw "trunc with places failed" + if (trunc(12.3775, -2) != 12.37) return "trunc with places failed" }, test_trunc_negative_with_places: function() { - if (trunc(-12.3775, -2) != -12.37) throw "trunc negative with places failed" + if (trunc(-12.3775, -2) != -12.37) return "trunc negative with places failed" }, // ============================================================================ @@ -2487,24 +2286,24 @@ return { // ============================================================================ test_sign_positive: function() { - if (sign(5) != 1) throw "sign positive failed" + if (sign(5) != 1) return "sign positive failed" }, test_sign_negative: function() { - if (sign(-5) != -1) throw "sign negative failed" + if (sign(-5) != -1) return "sign negative failed" }, test_sign_zero: function() { - if (sign(0) != 0) throw "sign zero failed" + if (sign(0) != 0) return "sign zero failed" }, test_sign_float: function() { - if (sign(0.001) != 1) throw "sign positive float failed" - if (sign(-0.001) != -1) throw "sign negative float failed" + if (sign(0.001) != 1) return "sign positive float failed" + if (sign(-0.001) != -1) return "sign negative float failed" }, test_sign_non_number: function() { - if (sign("5") != null) throw "sign non-number should return null" + if (sign("5") != null) return "sign non-number should return null" }, // ============================================================================ @@ -2512,37 +2311,37 @@ return { // ============================================================================ test_whole_positive: function() { - if (whole(3.7) != 3) throw "whole positive failed" + if (whole(3.7) != 3) return "whole positive failed" }, test_whole_negative: function() { - if (whole(-3.7) != -3) throw "whole negative failed" + if (whole(-3.7) != -3) return "whole negative failed" }, test_whole_integer: function() { - if (whole(5) != 5) throw "whole integer failed" + if (whole(5) != 5) return "whole integer failed" }, test_whole_non_number: function() { - if (whole("5") != null) throw "whole non-number should return null" + if (whole("5") != null) return "whole non-number should return null" }, test_fraction_positive: function() { var f = fraction(3.75) - if (f < 0.74 || f > 0.76) throw "fraction positive failed: " + f + if (f < 0.74 || f > 0.76) return "fraction positive failed: " + f }, test_fraction_negative: function() { var f = fraction(-3.75) - if (f > -0.74 || f < -0.76) throw "fraction negative failed: " + f + if (f > -0.74 || f < -0.76) return "fraction negative failed: " + f }, test_fraction_integer: function() { - if (fraction(5) != 0) throw "fraction integer failed" + if (fraction(5) != 0) return "fraction integer failed" }, test_fraction_non_number: function() { - if (fraction("5") != null) throw "fraction non-number should return null" + if (fraction("5") != null) return "fraction non-number should return null" }, // ============================================================================ @@ -2550,23 +2349,23 @@ return { // ============================================================================ test_neg_positive: function() { - if (neg(5) != -5) throw "neg positive failed" + if (neg(5) != -5) return "neg positive failed" }, test_neg_negative: function() { - if (neg(-5) != 5) throw "neg negative failed" + if (neg(-5) != 5) return "neg negative failed" }, test_neg_zero: function() { - if (neg(0) != 0) throw "neg zero failed" + if (neg(0) != 0) return "neg zero failed" }, test_neg_float: function() { - if (neg(3.14) != -3.14) throw "neg float failed" + if (neg(3.14) != -3.14) return "neg float failed" }, test_neg_non_number: function() { - if (neg("5") != null) throw "neg non-number should return null" + if (neg("5") != null) return "neg non-number should return null" }, // ============================================================================ @@ -2574,35 +2373,35 @@ return { // ============================================================================ test_modulo_positive: function() { - if (modulo(10, 3) != 1) throw "modulo positive failed" + if (modulo(10, 3) != 1) return "modulo positive failed" }, test_modulo_negative_dividend: function() { var result = modulo(-10, 3) - if (result != 2) throw "modulo negative dividend failed: " + result + if (result != 2) return "modulo negative dividend failed: " + result }, test_modulo_negative_divisor: function() { var result = modulo(10, -3) - if (result != -2) throw "modulo negative divisor failed: " + result + if (result != -2) return "modulo negative divisor failed: " + result }, test_modulo_both_negative: function() { var result = modulo(-10, -3) - if (result != -1) throw "modulo both negative failed: " + result + if (result != -1) return "modulo both negative failed: " + result }, test_modulo_zero_dividend: function() { - if (modulo(0, 5) != 0) throw "modulo zero dividend failed" + if (modulo(0, 5) != 0) return "modulo zero dividend failed" }, test_modulo_zero_divisor: function() { - if (modulo(10, 0) != null) throw "modulo zero divisor should return null" + if (modulo(10, 0) != null) return "modulo zero divisor should return null" }, test_modulo_floats: function() { var result = modulo(5.5, 2) - if (result < 1.4 || result > 1.6) throw "modulo floats failed: " + result + if (result < 1.4 || result > 1.6) return "modulo floats failed: " + result }, // ============================================================================ @@ -2610,62 +2409,62 @@ return { // ============================================================================ test_min_basic: function() { - if (min(3, 5) != 3) throw "min basic failed" + if (min(3, 5) != 3) return "min basic failed" }, test_min_equal: function() { - if (min(5, 5) != 5) throw "min equal failed" + if (min(5, 5) != 5) return "min equal failed" }, test_min_negative: function() { - if (min(-3, -5) != -5) throw "min negative failed" + if (min(-3, -5) != -5) return "min negative failed" }, test_min_mixed: function() { - if (min(-3, 5) != -3) throw "min mixed failed" + if (min(-3, 5) != -3) return "min mixed failed" }, test_min_float: function() { - if (min(3.14, 2.71) != 2.71) throw "min float failed" + if (min(3.14, 2.71) != 2.71) return "min float failed" }, test_min_non_number: function() { - if (min(3, "5") != null) throw "min non-number should return null" - if (min("3", 5) != null) throw "min first non-number should return null" + if (min(3, "5") != null) return "min non-number should return null" + if (min("3", 5) != null) return "min first non-number should return null" }, test_max_basic: function() { - if (max(3, 5) != 5) throw "max basic failed" + if (max(3, 5) != 5) return "max basic failed" }, test_max_equal: function() { - if (max(5, 5) != 5) throw "max equal failed" + if (max(5, 5) != 5) return "max equal failed" }, test_max_negative: function() { - if (max(-3, -5) != -3) throw "max negative failed" + if (max(-3, -5) != -3) return "max negative failed" }, test_max_mixed: function() { - if (max(-3, 5) != 5) throw "max mixed failed" + if (max(-3, 5) != 5) return "max mixed failed" }, test_max_float: function() { - if (max(3.14, 2.71) != 3.14) throw "max float failed" + if (max(3.14, 2.71) != 3.14) return "max float failed" }, test_max_non_number: function() { - if (max(3, "5") != null) throw "max non-number should return null" + if (max(3, "5") != null) return "max non-number should return null" }, test_min_max_constrain: function() { var val = 8 var constrained = min(max(val, 0), 10) - if (constrained != 8) throw "min max constrain in range failed" + if (constrained != 8) return "min max constrain in range failed" constrained = min(max(-5, 0), 10) - if (constrained != 0) throw "min max constrain below failed" + if (constrained != 0) return "min max constrain below failed" constrained = min(max(15, 0), 10) - if (constrained != 10) throw "min max constrain above failed" + if (constrained != 10) return "min max constrain above failed" }, // ============================================================================ @@ -2673,28 +2472,28 @@ return { // ============================================================================ test_codepoint_letter: function() { - if (codepoint("A") != 65) throw "codepoint A failed" - if (codepoint("a") != 97) throw "codepoint a failed" + if (codepoint("A") != 65) return "codepoint A failed" + if (codepoint("a") != 97) return "codepoint a failed" }, test_codepoint_digit: function() { - if (codepoint("0") != 48) throw "codepoint 0 failed" + if (codepoint("0") != 48) return "codepoint 0 failed" }, test_codepoint_unicode: function() { - if (codepoint("\u00E9") != 233) throw "codepoint unicode failed" + if (codepoint("\u00E9") != 233) return "codepoint unicode failed" }, test_codepoint_first_char: function() { - if (codepoint("ABC") != 65) throw "codepoint should return first char" + if (codepoint("ABC") != 65) return "codepoint should return first char" }, test_codepoint_empty: function() { - if (codepoint("") != null) throw "codepoint empty should return null" + if (codepoint("") != null) return "codepoint empty should return null" }, test_codepoint_non_text: function() { - if (codepoint(65) != null) throw "codepoint non-text should return null" + if (codepoint(65) != null) return "codepoint non-text should return null" }, // ============================================================================ @@ -2702,24 +2501,24 @@ return { // ============================================================================ test_character_letter: function() { - if (character(65) != "A") throw "character 65 failed" - if (character(97) != "a") throw "character 97 failed" + if (character(65) != "A") return "character 65 failed" + if (character(97) != "a") return "character 97 failed" }, test_character_digit: function() { - if (character(48) != "0") throw "character 48 failed" + if (character(48) != "0") return "character 48 failed" }, test_character_unicode: function() { - if (character(233) != "\u00E9") throw "character unicode failed" + if (character(233) != "\u00E9") return "character unicode failed" }, test_character_from_text: function() { - if (character("hello") != "h") throw "character from text failed" + if (character("hello") != "h") return "character from text failed" }, test_character_invalid: function() { - if (character(-1) != "") throw "character negative should return empty" + if (character(-1) != "") return "character negative should return empty" }, // ============================================================================ @@ -2727,32 +2526,32 @@ return { // ============================================================================ test_search_found: function() { - if (search("hello world", "world") != 6) throw "search found failed" + if (search("hello world", "world") != 6) return "search found failed" }, test_search_not_found: function() { - if (search("hello world", "xyz") != null) throw "search not found should return null" + if (search("hello world", "xyz") != null) return "search not found should return null" }, test_search_beginning: function() { - if (search("hello world", "hello") != 0) throw "search beginning failed" + if (search("hello world", "hello") != 0) return "search beginning failed" }, test_search_single_char: function() { - if (search("hello", "l") != 2) throw "search single char failed" + if (search("hello", "l") != 2) return "search single char failed" }, test_search_with_from: function() { - if (search("hello hello", "hello", 1) != 6) throw "search with from failed" + if (search("hello hello", "hello", 1) != 6) return "search with from failed" }, test_search_empty_pattern: function() { - if (search("hello", "") != 0) throw "search empty pattern failed" + if (search("hello", "") != 0) return "search empty pattern failed" }, test_search_negative_from: function() { var result = search("hello world", "world", -5) - if (result != 6) throw "search negative from failed: " + result + if (result != 6) return "search negative from failed: " + result }, // ============================================================================ @@ -2761,57 +2560,57 @@ return { test_replace_basic: function() { var result = replace("hello world", "world", "universe") - if (result != "hello universe") throw "replace basic failed" + if (result != "hello universe") return "replace basic failed" }, test_replace_not_found: function() { var result = replace("hello world", "xyz", "abc") - if (result != "hello world") throw "replace not found should return original" + if (result != "hello world") return "replace not found should return original" }, test_replace_multiple: function() { var result = replace("banana", "a", "o") - if (result != "bonono") throw "replace multiple failed: " + result + if (result != "bonono") return "replace multiple failed: " + result }, test_replace_with_limit: function() { var result = replace("banana", "a", "o", 1) - if (result != "bonana") throw "replace with limit failed: " + result + if (result != "bonana") return "replace with limit failed: " + result }, test_replace_empty_target: function() { var result = replace("abc", "", "-") - if (result != "-a-b-c-") throw "replace empty target failed: " + result + if (result != "-a-b-c-") return "replace empty target failed: " + result }, test_replace_to_empty: function() { var result = replace("hello", "l", "") - if (result != "heo") throw "replace to empty failed" + if (result != "heo") return "replace to empty failed" }, test_replace_with_function: function() { var result = replace("hello", "l", (match, pos) => `[${pos}]`) - if (result != "he[2][3]o") throw "replace with function failed: " + result + if (result != "he[2][3]o") return "replace with function failed: " + result }, test_replace_with_function_limit: function() { var result = replace("banana", "a", (match, pos) => `[${pos}]`, 2) - if (result != "b[1]n[3]na") throw "replace with function limit failed: " + result + if (result != "b[1]n[3]na") return "replace with function limit failed: " + result }, test_replace_with_regex: function() { var result = replace("banana", /a/, "o") - if (result != "bonono") throw "replace with regex failed" + if (result != "bonono") return "replace with regex failed" }, test_replace_with_regex_limit: function() { var result = replace("banana", /a/, "o", 2) - if (result != "bonona") throw "replace with regex limit failed: " + result + if (result != "bonona") return "replace with regex limit failed: " + result }, test_replace_with_regex_function: function() { var result = replace("hello", /l/, (match, pos) => `[${pos}]`) - if (result != "he[2][3]o") throw "replace with regex function failed: " + result + if (result != "he[2][3]o") return "replace with regex function failed: " + result }, // ============================================================================ @@ -2819,46 +2618,46 @@ return { // ============================================================================ test_text_number_basic: function() { - if (text(123) != "123") throw "text number basic failed" + if (text(123) != "123") return "text number basic failed" }, test_text_number_negative: function() { - if (text(-456) != "-456") throw "text number negative failed" + if (text(-456) != "-456") return "text number negative failed" }, test_text_number_float: function() { var result = text(3.14) - if (search(result, "3.14") != 0) throw "text number float failed" + if (search(result, "3.14") != 0) return "text number float failed" }, test_text_array_join_empty_sep: function() { var result = text(["a", "b", "c"], "") - if (result != "abc") throw "text array join empty sep failed" + if (result != "abc") return "text array join empty sep failed" }, test_text_slice_basic: function() { - if (text("hello", 1, 4) != "ell") throw "text slice basic failed" + if (text("hello", 1, 4) != "ell") return "text slice basic failed" }, test_text_slice_from_only: function() { - if (text("hello", 2) != "llo") throw "text slice from only failed" + if (text("hello", 2) != "llo") return "text slice from only failed" }, test_text_slice_negative_from: function() { - if (text("hello", -2) != "lo") throw "text slice negative from failed" + if (text("hello", -2) != "lo") return "text slice negative from failed" }, test_text_slice_negative_to: function() { - if (text("hello", 0, -2) != "hel") throw "text slice negative to failed" + if (text("hello", 0, -2) != "hel") return "text slice negative to failed" }, test_text_boolean: function() { - if (text(true) != "true") throw "text true failed" - if (text(false) != "false") throw "text false failed" + if (text(true) != "true") return "text true failed" + if (text(false) != "false") return "text false failed" }, test_text_null: function() { - if (text(null) != "null") throw "text null failed" + if (text(null) != "null") return "text null failed" }, // ============================================================================ @@ -2866,37 +2665,37 @@ return { // ============================================================================ test_number_from_string: function() { - if (number("123") != 123) throw "number from string failed" + if (number("123") != 123) return "number from string failed" }, test_number_from_negative_string: function() { - if (number("-456") != -456) throw "number from negative string failed" + if (number("-456") != -456) return "number from negative string failed" }, test_number_from_float_string: function() { - if (number("3.14") != 3.14) throw "number from float string failed" + if (number("3.14") != 3.14) return "number from float string failed" }, test_number_invalid_string: function() { - if (number("abc") != null) throw "number invalid string should return null" + if (number("abc") != null) return "number invalid string should return null" }, test_number_from_boolean: function() { - if (number(true) != 1) throw "number from true failed" - if (number(false) != 0) throw "number from false failed" + if (number(true) != 1) return "number from true failed" + if (number(false) != 0) return "number from false failed" }, test_number_from_number: function() { - if (number(42) != 42) throw "number from number failed" + if (number(42) != 42) return "number from number failed" }, test_number_with_radix: function() { - if (number("FF", 16) != 255) throw "number hex failed" - if (number("1010", 2) != 10) throw "number binary failed" + if (number("FF", 16) != 255) return "number hex failed" + if (number("1010", 2) != 10) return "number binary failed" }, test_number_leading_zeros: function() { - if (number("007") != 7) throw "number leading zeros failed" + if (number("007") != 7) return "number leading zeros failed" }, // ============================================================================ @@ -2905,57 +2704,57 @@ return { test_array_create_with_length: function() { var arr = array(5) - if (length(arr) != 5) throw "array create length failed" - if (arr[0] != null) throw "array create should init to null" + if (length(arr) != 5) return "array create length failed" + if (arr[0] != null) return "array create should init to null" }, test_array_create_with_initial: function() { var arr = array(3, 42) - if (arr[0] != 42 || arr[1] != 42 || arr[2] != 42) throw "array create with initial failed" + if (arr[0] != 42 || arr[1] != 42 || arr[2] != 42) return "array create with initial failed" }, test_array_create_with_function: function() { var arr = array(3, i => i * 2) - if (arr[0] != 0 || arr[1] != 2 || arr[2] != 4) throw "array create with function failed" + if (arr[0] != 0 || arr[1] != 2 || arr[2] != 4) return "array create with function failed" }, test_array_copy: function() { var orig = [1, 2, 3] var copy = array(orig) copy[0] = 99 - if (orig[0] != 1) throw "array copy should not affect original" + if (orig[0] != 1) return "array copy should not affect original" }, test_array_slice_basic: function() { var arr = [1, 2, 3, 4, 5] var sliced = array(arr, 1, 3) - if (length(sliced) != 2) throw "array slice length failed" - if (sliced[0] != 2 || sliced[1] != 3) throw "array slice values failed" + if (length(sliced) != 2) return "array slice length failed" + if (sliced[0] != 2 || sliced[1] != 3) return "array slice values failed" }, test_array_slice_negative: function() { var arr = [1, 2, 3, 4, 5] var sliced = array(arr, -3) - if (length(sliced) != 3) throw "array slice negative failed" - if (sliced[0] != 3) throw "array slice negative value failed" + if (length(sliced) != 3) return "array slice negative failed" + if (sliced[0] != 3) return "array slice negative value failed" }, test_array_from_object_keys: function() { var obj = {a: 1, b: 2, c: 3} var keys = array(obj) - if (length(keys) != 3) throw "array from object keys length failed" + if (length(keys) != 3) return "array from object keys length failed" }, test_array_from_text: function() { var arr = array("abc") - if (length(arr) != 3) throw "array from text length failed" - if (arr[0] != "a" || arr[1] != "b" || arr[2] != "c") throw "array from text values failed" + if (length(arr) != 3) return "array from text length failed" + if (arr[0] != "a" || arr[1] != "b" || arr[2] != "c") return "array from text values failed" }, test_array_split_text: function() { var arr = array("a,b,c", ",") - if (length(arr) != 3) throw "array split text length failed" - if (arr[1] != "b") throw "array split text value failed" + if (length(arr) != 3) return "array split text length failed" + if (arr[1] != "b") return "array split text value failed" }, // ============================================================================ @@ -2963,27 +2762,27 @@ return { // ============================================================================ test_trim_spaces: function() { - if (trim(" hello ") != "hello") throw "trim spaces failed" + if (trim(" hello ") != "hello") return "trim spaces failed" }, test_trim_tabs: function() { - if (trim("\thello\t") != "hello") throw "trim tabs failed" + if (trim("\thello\t") != "hello") return "trim tabs failed" }, test_trim_mixed: function() { - if (trim(" \t hello \n ") != "hello") throw "trim mixed failed" + if (trim(" \t hello \n ") != "hello") return "trim mixed failed" }, test_trim_no_whitespace: function() { - if (trim("hello") != "hello") throw "trim no whitespace failed" + if (trim("hello") != "hello") return "trim no whitespace failed" }, test_trim_empty: function() { - if (trim("") != "") throw "trim empty failed" + if (trim("") != "") return "trim empty failed" }, test_trim_all_whitespace: function() { - if (trim(" ") != "") throw "trim all whitespace failed" + if (trim(" ") != "") return "trim all whitespace failed" }, // ============================================================================ @@ -2991,35 +2790,35 @@ return { // ============================================================================ test_lower_basic: function() { - if (lower("HELLO") != "hello") throw "lower basic failed" + if (lower("HELLO") != "hello") return "lower basic failed" }, test_lower_mixed: function() { - if (lower("HeLLo WoRLD") != "hello world") throw "lower mixed failed" + if (lower("HeLLo WoRLD") != "hello world") return "lower mixed failed" }, test_lower_already_lower: function() { - if (lower("hello") != "hello") throw "lower already lower failed" + if (lower("hello") != "hello") return "lower already lower failed" }, test_lower_with_numbers: function() { - if (lower("ABC123") != "abc123") throw "lower with numbers failed" + if (lower("ABC123") != "abc123") return "lower with numbers failed" }, test_upper_basic: function() { - if (upper("hello") != "HELLO") throw "upper basic failed" + if (upper("hello") != "HELLO") return "upper basic failed" }, test_upper_mixed: function() { - if (upper("HeLLo WoRLD") != "HELLO WORLD") throw "upper mixed failed" + if (upper("HeLLo WoRLD") != "HELLO WORLD") return "upper mixed failed" }, test_upper_already_upper: function() { - if (upper("HELLO") != "HELLO") throw "upper already upper failed" + if (upper("HELLO") != "HELLO") return "upper already upper failed" }, test_upper_with_numbers: function() { - if (upper("abc123") != "ABC123") throw "upper with numbers failed" + if (upper("abc123") != "ABC123") return "upper with numbers failed" }, // ============================================================================ @@ -3029,30 +2828,30 @@ return { test_apply_basic: function() { var fn = function(a, b) { return a + b } var result = apply(fn, [3, 4]) - if (result != 7) throw "apply basic failed" + if (result != 7) return "apply basic failed" }, test_apply_no_args: function() { var fn = function() { return 42 } var result = apply(fn, []) - if (result != 42) throw "apply no args failed" + if (result != 42) return "apply no args failed" }, test_apply_single_arg: function() { var fn = function(x) { return x * 2 } var result = apply(fn, [5]) - if (result != 10) throw "apply single arg failed" + if (result != 10) return "apply single arg failed" }, test_apply_many_args: function() { var fn = function(a, b, c, d) { return a + b + c + d } var result = apply(fn, [1, 2, 3, 4]) - if (result != 10) throw "apply many args failed" + if (result != 10) return "apply many args failed" }, test_apply_non_function: function() { var result = apply(42, [1, 2]) - if (result != 42) throw "apply non-function should return first arg" + if (result != 42) return "apply non-function should return first arg" }, // ============================================================================ @@ -3062,7 +2861,7 @@ return { test_call_many_args: function() { var fn = function(a, b, c, d) { return a * b + c * d } var result = call(fn, null, [2, 3, 4, 5]) - if (result != 26) throw "call many args failed" + if (result != 26) return "call many args failed" }, test_call_method_style: function() { @@ -3071,15 +2870,15 @@ return { multiply: function(x) { return this.value * x } } var result = call(obj.multiply, obj, [5]) - if (result != 50) throw "call method style failed" + if (result != 50) return "call method style failed" }, test_call_change_this: function() { var obj1 = { value: 10 } var obj2 = { value: 20 } var fn = function() { return this.value } - if (call(fn, obj1) != 10) throw "call this obj1 failed" - if (call(fn, obj2) != 20) throw "call this obj2 failed" + if (call(fn, obj1) != 10) return "call this obj1 failed" + if (call(fn, obj2) != 20) return "call this obj2 failed" }, // ============================================================================ @@ -3090,27 +2889,27 @@ return { var arr = [1, 2, 3] var sum = 0 arrfor(arr, x => { sum = sum + x }) - if (sum != 6) throw "arrfor basic failed" + if (sum != 6) return "arrfor basic failed" }, test_arrfor_with_index: function() { var arr = ["a", "b", "c"] var indices = [] arrfor(arr, (x, i) => { push(indices, i) }) - if (indices[0] != 0 || indices[2] != 2) throw "arrfor with index failed" + if (indices[0] != 0 || indices[2] != 2) return "arrfor with index failed" }, test_arrfor_empty: function() { var called = false arrfor([], x => { called = true }) - if (called) throw "arrfor empty should not call function" + if (called) return "arrfor empty should not call function" }, test_arrfor_mutation: function() { var arr = [1, 2, 3] var results = [] arrfor(arr, x => { push(results, x * 2) }) - if (results[0] != 2 || results[1] != 4 || results[2] != 6) throw "arrfor mutation failed" + if (results[0] != 2 || results[1] != 4 || results[2] != 6) return "arrfor mutation failed" }, // ============================================================================ @@ -3120,14 +2919,14 @@ return { test_stone_returns_value: function() { var obj = {x: 1} var result = stone(obj) - if (result != obj) throw "stone should return the value" + if (result != obj) return "stone should return the value" }, test_stone_idempotent: function() { var obj = {x: 1} stone(obj) stone(obj) - if (!is_stone(obj)) throw "stone should be idempotent" + if (!is_stone(obj)) return "stone should be idempotent" }, // ============================================================================ @@ -3138,14 +2937,8 @@ return { var grandparent = {a: 1} var parent = meme(grandparent) var child = meme(parent) - if (proto(child) != parent) throw "proto chain child->parent failed" - if (proto(parent) != grandparent) throw "proto chain parent->grandparent failed" - }, - - test_proto_array: function() { - var arr = [1, 2, 3] - var p = proto(arr) - if (p == null) throw "proto of array should not be null" + if (proto(child) != parent) return "proto chain child->parent failed" + if (proto(parent) != grandparent) return "proto chain parent->grandparent failed" }, // ============================================================================ @@ -3157,7 +2950,7 @@ return { greet: function() { return "hello" } } var child = meme(parent) - if (child.greet() != "hello") throw "meme method inheritance failed" + if (child.greet() != "hello") return "meme method inheritance failed" }, test_meme_this_in_inherited_method: function() { @@ -3166,7 +2959,7 @@ return { } var child = meme(parent) child.value = 42 - if (child.getValue() != 42) throw "meme this in inherited method failed" + if (child.getValue() != 42) return "meme this in inherited method failed" }, test_meme_deep_chain: function() { @@ -3174,7 +2967,7 @@ return { var b = meme(a) var c = meme(b) var d = meme(c) - if (d.x != 1) throw "meme deep chain failed" + if (d.x != 1) return "meme deep chain failed" }, // ============================================================================ @@ -3184,25 +2977,22 @@ return { test_delete_property: function() { var obj = {a: 1, b: 2} delete obj.a - if ("a" in obj) throw "delete property failed" - if (obj.b != 2) throw "delete should not affect other properties" + if ("a" in obj) return "delete property failed" + if (obj.b != 2) return "delete should not affect other properties" }, - test_delete_array_element: function() { + test_delete_array_element_disrupts: function() { var arr = [1, 2, 3] var caught = false - try { - delete arr[1] - } catch (e) { - caught = true - } - if (!caught) throw "delete on array element should throw" + var _fn = function() { delete arr[1] } disruption { caught = true } + _fn() + if (!caught) return "delete on array element should disrupt" }, test_delete_nonexistent: function() { var obj = {a: 1} delete obj.b - if (obj.a != 1) throw "delete nonexistent should not affect object" + if (obj.a != 1) return "delete nonexistent should not affect object" }, // ============================================================================ @@ -3210,9 +3000,9 @@ return { // ============================================================================ test_is_integer: function() { - if (!is_number(5) || 5 % 1 != 0) throw "is_integer positive failed" - if (!is_number(-5) || -5 % 1 != 0) throw "is_integer negative failed" - if (is_number(5.5) && 5.5 % 1 == 0) throw "is_integer float should not be integer" + if (!is_number(5) || 5 % 1 != 0) return "is_integer positive failed" + if (!is_number(-5) || -5 % 1 != 0) return "is_integer negative failed" + if (is_number(5.5) && 5.5 % 1 == 0) return "is_integer float should not be integer" }, // ============================================================================ @@ -3222,19 +3012,19 @@ return { test_array_map_basic: function() { var arr = [1, 2, 3] var doubled = array(arr, x => x * 2) - if (doubled[0] != 2 || doubled[1] != 4 || doubled[2] != 6) throw "array map basic failed" + if (doubled[0] != 2 || doubled[1] != 4 || doubled[2] != 6) return "array map basic failed" }, test_array_map_with_index: function() { var arr = ["a", "b", "c"] var result = array(arr, (x, i) => `${x}${i}`) - if (result[0] != "a0" || result[1] != "b1") throw "array map with index failed" + if (result[0] != "a0" || result[1] != "b1") return "array map with index failed" }, test_array_map_reverse: function() { var arr = [1, 2, 3] var result = array(arr, x => x * 2, true) - if (result[0] != 6 || result[2] != 2) throw "array map reverse failed" + if (result[0] != 6 || result[2] != 2) return "array map reverse failed" }, test_array_map_with_exit: function() { @@ -3243,26 +3033,25 @@ return { if (x > 3) return null return x * 2 }, false, null) - if (length(result) != 5) throw "array map with exit length unexpected" + if (length(result) != 5) return "array map with exit length unexpected" }, // ============================================================================ - // ERROR OBJECTS + // DISRUPTION IN EXPRESSIONS // ============================================================================ - test_error_creation: function() { - var e = Error("test message") - if (e.message != "test message") throw "Error creation failed" - }, - - test_throw_error_object: function() { - var caught = null - try { - throw Error("my error") - } catch (e) { - caught = e + test_disruption_in_nested_function: function() { + var caught = false + var _outer = function() { + var _inner = function() { + disrupt + } disruption { + caught = true + } + _inner() } - if (!caught || caught.message != "my error") throw "throw Error object failed" + _outer() + if (!caught) return "disruption in nested function failed" }, // ============================================================================ @@ -3270,21 +3059,21 @@ return { // ============================================================================ test_string_startsWith: function() { - if (!starts_with("hello", "hel")) throw "startsWith match failed" - if (starts_with("hello", "ell")) throw "startsWith no match failed" - if (!starts_with("hello", "")) throw "startsWith empty should match" + if (!starts_with("hello", "hel")) return "startsWith match failed" + if (starts_with("hello", "ell")) return "startsWith no match failed" + if (!starts_with("hello", "")) return "startsWith empty should match" }, test_string_endsWith: function() { - if (!ends_with("hello", "llo")) throw "endsWith match failed" - if (ends_with("hello", "ell")) throw "endsWith no match failed" - if (!ends_with("hello", "")) throw "endsWith empty should match" + if (!ends_with("hello", "llo")) return "endsWith match failed" + if (ends_with("hello", "ell")) return "endsWith no match failed" + if (!ends_with("hello", "")) return "endsWith empty should match" }, test_string_includes: function() { - if (search("hello world", "world") == null) throw "includes match failed" - if (search("hello", "xyz") != null) throw "includes no match failed" - if (search("hello", "") == null) throw "includes empty should match" + if (search("hello world", "world") == null) return "includes match failed" + if (search("hello", "xyz") != null) return "includes no match failed" + if (search("hello", "") == null) return "includes empty should match" }, // ============================================================================ @@ -3293,21 +3082,21 @@ return { test_array_includes: function() { var arr = [1, 2, 3] - if (find(arr, 2) == null) throw "array includes match failed" - if (find(arr, 5) != null) throw "array includes no match failed" + if (find(arr, 2) == null) return "array includes match failed" + if (find(arr, 5) != null) return "array includes no match failed" }, test_array_every: function() { var arr = [2, 4, 6] - if (!every(arr, x => x % 2 == 0)) throw "array every all pass failed" + if (!every(arr, x => x % 2 == 0)) return "array every all pass failed" arr = [2, 3, 6] - if (every(arr, x => x % 2 == 0)) throw "array every not all pass failed" + if (every(arr, x => x % 2 == 0)) return "array every not all pass failed" }, test_array_some: function() { var arr = [1, 2, 3] - if (!some(arr, x => x > 2)) throw "array some match failed" - if (some(arr, x => x > 5)) throw "array some no match failed" + if (!some(arr, x => x > 2)) return "array some match failed" + if (some(arr, x => x > 5)) return "array some no match failed" }, // ============================================================================ @@ -3315,37 +3104,37 @@ return { // ============================================================================ test_logical_zero: function() { - if (logical(0) != false) throw "logical(0) should be false" + if (logical(0) != false) return "logical(0) should be false" }, test_logical_one: function() { - if (logical(1) != true) throw "logical(1) should be true" + if (logical(1) != true) return "logical(1) should be true" }, test_logical_string_true: function() { - if (logical("true") != true) throw "logical('true') should be true" + if (logical("true") != true) return "logical('true') should be true" }, test_logical_string_false: function() { - if (logical("false") != false) throw "logical('false') should be false" + if (logical("false") != false) return "logical('false') should be false" }, test_logical_boolean_true: function() { - if (logical(true) != true) throw "logical(true) should be true" + if (logical(true) != true) return "logical(true) should be true" }, test_logical_boolean_false: function() { - if (logical(false) != false) throw "logical(false) should be false" + if (logical(false) != false) return "logical(false) should be false" }, test_logical_null: function() { - if (logical(null) != false) throw "logical(null) should be false" + if (logical(null) != false) return "logical(null) should be false" }, test_logical_invalid: function() { - if (logical("invalid") != null) throw "logical('invalid') should be null" - if (logical(42) != null) throw "logical(42) should be null" - if (logical({}) != null) throw "logical({}) should be null" + if (logical("invalid") != null) return "logical('invalid') should be null" + if (logical(42) != null) return "logical(42) should be null" + if (logical({}) != null) return "logical({}) should be null" }, // ============================================================================ @@ -3354,30 +3143,30 @@ return { test_nested_array_access: function() { var arr = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] - if (arr[0][1][0] != 3) throw "nested array access failed" - if (arr[1][1][1] != 8) throw "nested array access deep failed" + if (arr[0][1][0] != 3) return "nested array access failed" + if (arr[1][1][1] != 8) return "nested array access deep failed" }, test_nested_object_access: function() { var obj = {a: {b: {c: {d: 42}}}} - if (obj.a.b.c.d != 42) throw "nested object access failed" + if (obj.a.b.c.d != 42) return "nested object access failed" }, test_mixed_nested_access: function() { var data = {users: [{name: "Alice"}, {name: "Bob"}]} - if (data.users[1].name != "Bob") throw "mixed nested access failed" + if (data.users[1].name != "Bob") return "mixed nested access failed" }, test_object_with_null_value: function() { var obj = {a: null, b: 2} - if (obj.a != null) throw "object null value failed" - if (!("a" in obj)) throw "object with null should have key" + if (obj.a != null) return "object null value failed" + if (!("a" in obj)) return "object with null should have key" }, test_array_with_null_values: function() { var arr = [1, null, 3] - if (arr[1] != null) throw "array null value failed" - if (length(arr) != 3) throw "array with null length failed" + if (arr[1] != null) return "array null value failed" + if (length(arr) != 3) return "array with null length failed" }, test_function_returning_function: function() { @@ -3388,91 +3177,91 @@ return { } } } - if (outer(1)(2)(3) != 6) throw "function returning function failed" + if (outer(1)(2)(3) != 6) return "function returning function failed" }, test_immediately_invoked_function: function() { var result = (function(x) { return x * 2 })(21) - if (result != 42) throw "immediately invoked function failed" + if (result != 42) return "immediately invoked function failed" }, test_text_split_text: function() { var text = "hello world" var result = array(text, " ") - if (length(result) != 2) throw "text split failed" - if (result[0] != "hello") throw "text split failed" - if (result[1] != "world") throw "text split failed" + if (length(result) != 2) return "text split failed" + if (result[0] != "hello") return "text split failed" + if (result[1] != "world") return "text split failed" }, test_text_split_regex: function() { var text = "hello world" var result = array(text, /\s+/) - if (length(result) != 2) throw "text split failed" - if (result[0] != "hello") throw "text split failed" - if (result[1] != "world") throw "text split failed" + if (length(result) != 2) return "text split failed" + if (result[0] != "hello") return "text split failed" + if (result[1] != "world") return "text split failed" }, test_text_search_text: function() { var text = "hello world" var result = search(text, "world") - if (result != 6) throw "text search failed" + if (result != 6) return "text search failed" }, test_text_search_regex: function() { var text = "hello world" var result = search(text, /world/) - if (result != 6) throw "text search failed" + if (result != 6) return "text search failed" }, test_extract_basic_text: function() { var text = "hello world" var result = extract(text, "world") - if (result[0] != "world") throw "extract basic text failed" + if (result[0] != "world") return "extract basic text failed" }, test_extract_text_not_found: function() { var text = "hello world" var result = extract(text, "xyz") - if (result != null) throw "extract not found should return null" + if (result != null) return "extract not found should return null" }, test_extract_regex_basic: function() { var text = "hello world" var result = extract(text, /world/) - if (result[0] != "world") throw "extract regex basic failed" + if (result[0] != "world") return "extract regex basic failed" }, test_extract_regex_with_capture_group: function() { var text = "hello world" var result = extract(text, /(\w+) (\w+)/) - if (result[0] != "hello world") throw "extract regex full match failed" - if (result[1] != "hello") throw "extract regex capture group 1 failed" - if (result[2] != "world") throw "extract regex capture group 2 failed" + if (result[0] != "hello world") return "extract regex full match failed" + if (result[1] != "hello") return "extract regex capture group 1 failed" + if (result[2] != "world") return "extract regex capture group 2 failed" }, test_extract_regex_digits: function() { var text = "abc123def456" var result = extract(text, /(\d+)/) - if (result[0] != "123") throw "extract regex digits failed" - if (result[1] != "123") throw "extract regex digits capture failed" + if (result[0] != "123") return "extract regex digits failed" + if (result[1] != "123") return "extract regex digits capture failed" }, test_extract_with_from: function() { var text = "hello hello world" var result = extract(text, "hello", 1) - if (result[0] != "hello") throw "extract with from failed" + if (result[0] != "hello") return "extract with from failed" }, test_extract_with_from_to: function() { var text = "hello world hello" var result = extract(text, "hello", 0, 10) - if (result[0] != "hello") throw "extract with from to failed" + if (result[0] != "hello") return "extract with from to failed" }, test_extract_regex_case_insensitive: function() { var text = "Hello World" var result = extract(text, /hello/i) - if (result[0] != "Hello") throw "extract regex case insensitive failed" + if (result[0] != "Hello") return "extract regex case insensitive failed" }, // ============================================================================ @@ -3482,29 +3271,30 @@ return { test_gc_cycle_object_self: function() { var obj = {name: "root"} obj.self = obj - if (obj.self != obj) throw "self cycle failed" + if (obj.self != obj) return "self cycle failed" }, test_gc_cycle_array_self: function() { var arr = [] - for (var i = 0; i < 10; i++) { - push(arr, arr) + var i = 0 + for (i = 0; i < 10; i++) { + arr[] = arr } - if (arr[0] != arr) throw "array self cycle failed" + if (arr[0] != arr) return "array self cycle failed" }, test_gc_cycle_object_array_pair: function() { var obj = {kind: "node"} var arr = [obj] obj.arr = arr - if (obj.arr[0] != obj) throw "object/array cycle failed" + if (obj.arr[0] != obj) return "object/array cycle failed" }, test_gc_shared_references: function() { var shared = {value: 42} var a = {ref: shared} var b = {ref: shared} - if (a.ref != shared || b.ref != shared) throw "shared reference failed" + if (a.ref != shared || b.ref != shared) return "shared reference failed" }, test_gc_object_key_cycle: function() { @@ -3513,7 +3303,7 @@ return { var o = {} o[k] = v v.back = o - if (o[k].back != o) throw "object key cycle failed" + if (o[k].back != o) return "object key cycle failed" }, test_gc_object_text_key_mix: function() { @@ -3522,8 +3312,8 @@ return { var inner = {token: "x"} obj[key] = inner obj["beta"] = [inner, obj] - if (obj.alpha.token != "x") throw "text key value failed" - if (obj.beta[1] != obj) throw "text key cycle failed" + if (obj.alpha.token != "x") return "text key value failed" + if (obj.beta[1] != obj) return "text key cycle failed" }, // ============================================================================ @@ -3533,55 +3323,55 @@ return { test_object_shallow_copy: function() { var orig = {a: 1, b: 2, c: 3} var copy = object(orig) - if (copy.a != 1) throw "object copy a failed" - if (copy.b != 2) throw "object copy b failed" - if (copy.c != 3) throw "object copy c failed" + if (copy.a != 1) return "object copy a failed" + if (copy.b != 2) return "object copy b failed" + if (copy.c != 3) return "object copy c failed" copy.a = 99 - if (orig.a != 1) throw "object copy should not mutate original" + if (orig.a != 1) return "object copy should not mutate original" }, test_object_combine: function() { var obj1 = {a: 1, b: 2} var obj2 = {c: 3, d: 4} var combined = object(obj1, obj2) - if (combined.a != 1) throw "object combine a failed" - if (combined.b != 2) throw "object combine b failed" - if (combined.c != 3) throw "object combine c failed" - if (combined.d != 4) throw "object combine d failed" + if (combined.a != 1) return "object combine a failed" + if (combined.b != 2) return "object combine b failed" + if (combined.c != 3) return "object combine c failed" + if (combined.d != 4) return "object combine d failed" }, test_object_combine_override: function() { var obj1 = {a: 1, b: 2} var obj2 = {b: 99, c: 3} var combined = object(obj1, obj2) - if (combined.a != 1) throw "object combine override a failed" - if (combined.b != 99) throw "object combine should override with second arg" - if (combined.c != 3) throw "object combine override c failed" + if (combined.a != 1) return "object combine override a failed" + if (combined.b != 99) return "object combine should override with second arg" + if (combined.c != 3) return "object combine override c failed" }, test_object_select_keys: function() { var orig = {a: 1, b: 2, c: 3, d: 4} var selected = object(orig, ["a", "c"]) - if (selected.a != 1) throw "object select a failed" - if (selected.c != 3) throw "object select c failed" - if (selected.b != null) throw "object select should not include b" - if (selected.d != null) throw "object select should not include d" + if (selected.a != 1) return "object select a failed" + if (selected.c != 3) return "object select c failed" + if (selected.b != null) return "object select should not include b" + if (selected.d != null) return "object select should not include d" }, test_object_from_keys_true: function() { var keys = ["x", "y", "z"] var obj = object(keys) - if (obj.x != true) throw "object from keys x failed" - if (obj.y != true) throw "object from keys y failed" - if (obj.z != true) throw "object from keys z failed" + if (obj.x != true) return "object from keys x failed" + if (obj.y != true) return "object from keys y failed" + if (obj.z != true) return "object from keys z failed" }, test_object_from_keys_function: function() { var keys = ["a", "b", "c"] var obj = object(keys, function(k) { return k + "_val" }) - if (obj.a != "a_val") throw "object from keys func a failed" - if (obj.b != "b_val") throw "object from keys func b failed" - if (obj.c != "c_val") throw "object from keys func c failed" + if (obj.a != "a_val") return "object from keys func a failed" + if (obj.b != "b_val") return "object from keys func b failed" + if (obj.c != "c_val") return "object from keys func c failed" }, // ============================================================================ @@ -3589,13 +3379,12 @@ return { // ============================================================================ test_splat_prototype_flattening: function() { - var proto = {x: 10, y: 20} - var obj = {z: 30} - obj.__proto__ = proto + var proto_obj = {x: 10, y: 20} + var obj = meme(proto_obj, {z: 30}) var flat = splat(obj) - if (flat.x != 10) throw "splat x failed" - if (flat.y != 20) throw "splat y failed" - if (flat.z != 30) throw "splat z failed" + if (flat.x != 10) return "splat x failed" + if (flat.y != 20) return "splat y failed" + if (flat.z != 30) return "splat z failed" }, // ============================================================================ @@ -3605,12 +3394,12 @@ return { test_reverse_array: function() { var arr = [1, 2, 3, 4, 5] var rev = reverse(arr) - if (rev[0] != 5) throw "reverse[0] failed" - if (rev[1] != 4) throw "reverse[1] failed" - if (rev[2] != 3) throw "reverse[2] failed" - if (rev[3] != 2) throw "reverse[3] failed" - if (rev[4] != 1) throw "reverse[4] failed" - if (arr[0] != 1) throw "reverse should not mutate original" + if (rev[0] != 5) return "reverse[0] failed" + if (rev[1] != 4) return "reverse[1] failed" + if (rev[2] != 3) return "reverse[2] failed" + if (rev[3] != 2) return "reverse[3] failed" + if (rev[4] != 1) return "reverse[4] failed" + if (arr[0] != 1) return "reverse should not mutate original" }, // ============================================================================ @@ -3618,21 +3407,21 @@ return { // ============================================================================ test_apply_with_array_args: function() { - def sum = function(a, b, c) { return a + b + c } - var result = fn.apply(sum, [1, 2, 3]) - if (result != 6) throw "apply with array args failed" + var sum = function(a, b, c) { return a + b + c } + var result = apply(sum, [1, 2, 3]) + if (result != 6) return "apply with array args failed" }, test_apply_with_no_args: function() { - def ret42 = function() { return 42 } - var result = fn.apply(ret42) - if (result != 42) throw "apply with no args failed" + var ret42 = function() { return 42 } + var result = apply(ret42, []) + if (result != 42) return "apply with no args failed" }, test_apply_with_single_value: function() { - def double = function(x) { return x * 2 } - var result = fn.apply(double, 10) - if (result != 20) throw "apply with single value failed" + var double = function(x) { return x * 2 } + var result = apply(double, [10]) + if (result != 20) return "apply with single value failed" }, // ============================================================================ @@ -3642,41 +3431,48 @@ return { test_gc_reverse_under_pressure: function() { // Create GC pressure by making many arrays, then reverse var arrays = [] - for (var i = 0; i < 100; i = i + 1) { + var i = 0 + var rev = null + for (i = 0; i < 100; i = i + 1) { arrays[i] = [i, i+1, i+2, i+3, i+4] } // Now reverse each one - this tests re-chase after allocation - for (var i = 0; i < 100; i = i + 1) { - var rev = reverse(arrays[i]) - if (rev[0] != i+4) throw "gc reverse stress failed at " + text(i) + for (i = 0; i < 100; i = i + 1) { + rev = reverse(arrays[i]) + if (rev[0] != i+4) return "gc reverse stress failed at " + text(i) } }, test_gc_object_select_under_pressure: function() { // Create GC pressure var objs = [] - for (var i = 0; i < 100; i = i + 1) { + var i = 0 + var selected = null + for (i = 0; i < 100; i = i + 1) { objs[i] = {a: i, b: i+1, c: i+2, d: i+3} } // Select keys - tests re-chase in loop - for (var i = 0; i < 100; i = i + 1) { - var selected = object(objs[i], ["a", "c"]) - if (selected.a != i) throw "gc object select stress failed at " + text(i) - if (selected.c != i+2) throw "gc object select stress c failed at " + text(i) + for (i = 0; i < 100; i = i + 1) { + selected = object(objs[i], ["a", "c"]) + if (selected.a != i) return "gc object select stress failed at " + text(i) + if (selected.c != i+2) return "gc object select stress c failed at " + text(i) } }, test_gc_object_from_keys_function_under_pressure: function() { // Create GC pressure var keysets = [] - for (var i = 0; i < 50; i = i + 1) { + var i = 0 + var obj = null + var expected = null + for (i = 0; i < 50; i = i + 1) { keysets[i] = ["k" + text(i), "j" + text(i), "m" + text(i)] } // Create objects with function - tests JS_PUSH/POP and re-chase - for (var i = 0; i < 50; i = i + 1) { - var obj = object(keysets[i], function(k) { return k + "_value" }) - var expected = "k" + text(i) + "_value" - if (obj["k" + text(i)] != expected) throw "gc object from keys func stress failed at " + text(i) + for (i = 0; i < 50; i = i + 1) { + obj = object(keysets[i], function(k) { return k + "_value" }) + expected = "k" + text(i) + "_value" + if (obj["k" + text(i)] != expected) return "gc object from keys func stress failed at " + text(i) } }, diff --git a/time.cm b/time.cm index a54ba85b..50b49081 100644 --- a/time.cm +++ b/time.cm @@ -1,5 +1,5 @@ // epoch = 0000-01-01 00:00:00 +0000 -var time = this +var time = native var now = time.now var computer_zone = time.computer_zone diff --git a/vm_suite.ce b/vm_suite.ce index 9a9a2eeb..b62856f9 100644 --- a/vm_suite.ce +++ b/vm_suite.ce @@ -738,6 +738,95 @@ run("disruption re-raise", function() { if (!outer_caught) fail("disruption re-raise failed") }) +run("disruption handler reads outer vars", function() { + var msg = "hello" + var result = null + var fn = function() { + disrupt + } disruption { + result = msg + } + fn() + if (result != "hello") fail("handler could not read outer var") +}) + +run("disruption handler modifies outer vars", function() { + var count = 0 + var name = "before" + var fn = function() { + count = count + 1 + disrupt + } disruption { + count = count + 10 + name = "after" + } + fn() + if (count != 11) fail("expected 11 got " + text(count)) + if (name != "after") fail("expected 'after' got " + name) +}) + +run("disruption handler reads function locals", function() { + var result = null + var fn = function() { + var local_val = 42 + var inner = function() { + disrupt + } disruption { + result = local_val + } + inner() + } + fn() + if (result != 42) fail("handler could not read local, got " + text(result)) +}) + +run("disruption handler with closure", function() { + var results = [] + var make_fn = function(tag) { + return function() { + disrupt + } disruption { + results[] = tag + } + } + var fn_a = make_fn("a") + var fn_b = make_fn("b") + fn_a() + fn_b() + if (length(results) != 2) fail("expected 2 results") + if (results[0] != "a") fail("first closure tag wrong") + if (results[1] != "b") fail("second closure tag wrong") +}) + +run("disruption handler modifies loop state", function() { + var total = 0 + var i = 0 + var fn = null + while (i < 3) { + fn = function() { + disrupt + } disruption { + total = total + i + } + fn() + i = i + 1 + } + if (total != 3) fail("expected 3 got " + text(total)) +}) + +run("disruption handler accesses object from outer scope", function() { + var obj = {x: 1, y: 2} + var fn = function() { + obj.x = 10 + disrupt + } disruption { + obj.y = 20 + } + fn() + if (obj.x != 10) fail("body mutation lost, x=" + text(obj.x)) + if (obj.y != 20) fail("handler mutation lost, y=" + text(obj.y)) +}) + // ============================================================================ // TYPE CHECKING WITH is_* FUNCTIONS // ============================================================================ @@ -1182,6 +1271,141 @@ run("prefix decrement on array element", function() { if (arr[0] != 9) fail("side effect") }) +// ============================================================================ +// POSTFIX INCREMENT/DECREMENT ON PROPERTIES (Bug: never worked) +// ============================================================================ + +run("postfix increment on property", function() { + var obj = {x: 5} + obj.x++ + assert_eq(obj.x, 6, "obj.x should be 6 after obj.x++") +}) + +run("postfix decrement on property", function() { + var obj = {x: 5} + obj.x-- + assert_eq(obj.x, 4, "obj.x should be 4 after obj.x--") +}) + +run("postfix increment on property return value", function() { + var obj = {x: 5} + var y = obj.x++ + assert_eq(y, 5, "return value should be old value") + assert_eq(obj.x, 6, "property should be incremented") +}) + +run("postfix decrement on property return value", function() { + var obj = {x: 5} + var y = obj.x-- + assert_eq(y, 5, "return value should be old value") + assert_eq(obj.x, 4, "property should be decremented") +}) + +run("postfix increment on array element", function() { + var arr = [10, 20, 30] + arr[1]++ + assert_eq(arr[1], 21, "arr[1] should be 21 after arr[1]++") +}) + +run("postfix decrement on array element", function() { + var arr = [10, 20, 30] + arr[1]-- + assert_eq(arr[1], 19, "arr[1] should be 19 after arr[1]--") +}) + +run("postfix increment on array element return value", function() { + var arr = [10] + var y = arr[0]++ + assert_eq(y, 10, "return value should be old value") + assert_eq(arr[0], 11, "array element should be incremented") +}) + +run("postfix increment on computed property", function() { + var obj = {a: 10} + var key = "a" + obj[key]++ + assert_eq(obj.a, 11, "computed property should be incremented") +}) + +run("postfix increment on nested property", function() { + var obj = {inner: {val: 10}} + obj.inner.val++ + assert_eq(obj.inner.val, 11, "nested property should be incremented") +}) + +run("postfix increment property in loop", function() { + var obj = {count: 0} + var i = 0 + for (i = 0; i < 5; i++) { + obj.count++ + } + assert_eq(obj.count, 5, "property should be 5 after 5 increments") +}) + +// ============================================================================ +// POSTFIX INCREMENT ON CLOSURE PROPERTIES (Original reported bug) +// ============================================================================ + +run("postfix increment closure property", function() { + var obj = {x: 0} + var fn = function() { + obj.x++ + } + fn() + assert_eq(obj.x, 1, "closure property should be incremented") +}) + +run("postfix decrement closure property", function() { + var obj = {x: 5} + var fn = function() { + obj.x-- + } + fn() + assert_eq(obj.x, 4, "closure property should be decremented") +}) + +run("postfix increment closure counter pattern", function() { + var counter = {passed: 0, failed: 0} + var pass = function() { counter.passed++ } + var fail_fn = function() { counter.failed++ } + pass() + pass() + pass() + fail_fn() + assert_eq(counter.passed, 3, "passed count") + assert_eq(counter.failed, 1, "failed count") +}) + +run("postfix increment deep closure property", function() { + var obj = {x: 0} + var fn = function() { + var inner = function() { + obj.x++ + } + inner() + } + fn() + assert_eq(obj.x, 1, "deep closure property should be incremented") +}) + +run("postfix increment closure array element", function() { + var arr = [10] + var fn = function() { + arr[0]++ + } + fn() + assert_eq(arr[0], 11, "closure array element should be incremented") +}) + +run("postfix increment on closure variable", function() { + var x = 5 + var fn = function() { + x++ + } + fn() + assert_eq(x, 6, "closure variable should be incremented by postfix ++") +}) + // ============================================================================ // INCREMENT/DECREMENT IN LOOPS AND EXPRESSIONS // ============================================================================ @@ -4262,6 +4486,465 @@ run("IIFE with arguments", function() { assert_eq(result, 30, "IIFE sum") }) +// ============================================================================ +// PATHOLOGICAL OBJECT LITERALS - Diagnose large object key/value limits +// ============================================================================ + +// Test: object with 100 simple keys (number values) +run("object literal 100 number keys", function() { + var obj = { + k000: 0, k001: 1, k002: 2, k003: 3, k004: 4, k005: 5, k006: 6, k007: 7, k008: 8, k009: 9, + k010: 10, k011: 11, k012: 12, k013: 13, k014: 14, k015: 15, k016: 16, k017: 17, k018: 18, k019: 19, + k020: 20, k021: 21, k022: 22, k023: 23, k024: 24, k025: 25, k026: 26, k027: 27, k028: 28, k029: 29, + k030: 30, k031: 31, k032: 32, k033: 33, k034: 34, k035: 35, k036: 36, k037: 37, k038: 38, k039: 39, + k040: 40, k041: 41, k042: 42, k043: 43, k044: 44, k045: 45, k046: 46, k047: 47, k048: 48, k049: 49, + k050: 50, k051: 51, k052: 52, k053: 53, k054: 54, k055: 55, k056: 56, k057: 57, k058: 58, k059: 59, + k060: 60, k061: 61, k062: 62, k063: 63, k064: 64, k065: 65, k066: 66, k067: 67, k068: 68, k069: 69, + k070: 70, k071: 71, k072: 72, k073: 73, k074: 74, k075: 75, k076: 76, k077: 77, k078: 78, k079: 79, + k080: 80, k081: 81, k082: 82, k083: 83, k084: 84, k085: 85, k086: 86, k087: 87, k088: 88, k089: 89, + k090: 90, k091: 91, k092: 92, k093: 93, k094: 94, k095: 95, k096: 96, k097: 97, k098: 98, k099: 99 + } + var keys = array(obj) + assert_eq(length(keys), 100, "should have 100 keys") + assert_eq(obj.k000, 0, "first key") + assert_eq(obj.k099, 99, "last key") +}) + +// Test: object with 200 simple keys (number values) +run("object literal 200 number keys", function() { + var obj = { + k000: 0, k001: 1, k002: 2, k003: 3, k004: 4, k005: 5, k006: 6, k007: 7, k008: 8, k009: 9, + k010: 10, k011: 11, k012: 12, k013: 13, k014: 14, k015: 15, k016: 16, k017: 17, k018: 18, k019: 19, + k020: 20, k021: 21, k022: 22, k023: 23, k024: 24, k025: 25, k026: 26, k027: 27, k028: 28, k029: 29, + k030: 30, k031: 31, k032: 32, k033: 33, k034: 34, k035: 35, k036: 36, k037: 37, k038: 38, k039: 39, + k040: 40, k041: 41, k042: 42, k043: 43, k044: 44, k045: 45, k046: 46, k047: 47, k048: 48, k049: 49, + k050: 50, k051: 51, k052: 52, k053: 53, k054: 54, k055: 55, k056: 56, k057: 57, k058: 58, k059: 59, + k060: 60, k061: 61, k062: 62, k063: 63, k064: 64, k065: 65, k066: 66, k067: 67, k068: 68, k069: 69, + k070: 70, k071: 71, k072: 72, k073: 73, k074: 74, k075: 75, k076: 76, k077: 77, k078: 78, k079: 79, + k080: 80, k081: 81, k082: 82, k083: 83, k084: 84, k085: 85, k086: 86, k087: 87, k088: 88, k089: 89, + k090: 90, k091: 91, k092: 92, k093: 93, k094: 94, k095: 95, k096: 96, k097: 97, k098: 98, k099: 99, + k100: 100, k101: 101, k102: 102, k103: 103, k104: 104, k105: 105, k106: 106, k107: 107, k108: 108, k109: 109, + k110: 110, k111: 111, k112: 112, k113: 113, k114: 114, k115: 115, k116: 116, k117: 117, k118: 118, k119: 119, + k120: 120, k121: 121, k122: 122, k123: 123, k124: 124, k125: 125, k126: 126, k127: 127, k128: 128, k129: 129, + k130: 130, k131: 131, k132: 132, k133: 133, k134: 134, k135: 135, k136: 136, k137: 137, k138: 138, k139: 139, + k140: 140, k141: 141, k142: 142, k143: 143, k144: 144, k145: 145, k146: 146, k147: 147, k148: 148, k149: 149, + k150: 150, k151: 151, k152: 152, k153: 153, k154: 154, k155: 155, k156: 156, k157: 157, k158: 158, k159: 159, + k160: 160, k161: 161, k162: 162, k163: 163, k164: 164, k165: 165, k166: 166, k167: 167, k168: 168, k169: 169, + k170: 170, k171: 171, k172: 172, k173: 173, k174: 174, k175: 175, k176: 176, k177: 177, k178: 178, k179: 179, + k180: 180, k181: 181, k182: 182, k183: 183, k184: 184, k185: 185, k186: 186, k187: 187, k188: 188, k189: 189, + k190: 190, k191: 191, k192: 192, k193: 193, k194: 194, k195: 195, k196: 196, k197: 197, k198: 198, k199: 199 + } + var keys = array(obj) + assert_eq(length(keys), 200, "should have 200 keys") + assert_eq(obj.k000, 0, "first key") + assert_eq(obj.k199, 199, "last key") +}) + +// Test: object with 256 simple keys (number values) - exact boundary +run("object literal 256 number keys", function() { + var obj = { + k000: 0, k001: 1, k002: 2, k003: 3, k004: 4, k005: 5, k006: 6, k007: 7, k008: 8, k009: 9, + k010: 10, k011: 11, k012: 12, k013: 13, k014: 14, k015: 15, k016: 16, k017: 17, k018: 18, k019: 19, + k020: 20, k021: 21, k022: 22, k023: 23, k024: 24, k025: 25, k026: 26, k027: 27, k028: 28, k029: 29, + k030: 30, k031: 31, k032: 32, k033: 33, k034: 34, k035: 35, k036: 36, k037: 37, k038: 38, k039: 39, + k040: 40, k041: 41, k042: 42, k043: 43, k044: 44, k045: 45, k046: 46, k047: 47, k048: 48, k049: 49, + k050: 50, k051: 51, k052: 52, k053: 53, k054: 54, k055: 55, k056: 56, k057: 57, k058: 58, k059: 59, + k060: 60, k061: 61, k062: 62, k063: 63, k064: 64, k065: 65, k066: 66, k067: 67, k068: 68, k069: 69, + k070: 70, k071: 71, k072: 72, k073: 73, k074: 74, k075: 75, k076: 76, k077: 77, k078: 78, k079: 79, + k080: 80, k081: 81, k082: 82, k083: 83, k084: 84, k085: 85, k086: 86, k087: 87, k088: 88, k089: 89, + k090: 90, k091: 91, k092: 92, k093: 93, k094: 94, k095: 95, k096: 96, k097: 97, k098: 98, k099: 99, + k100: 100, k101: 101, k102: 102, k103: 103, k104: 104, k105: 105, k106: 106, k107: 107, k108: 108, k109: 109, + k110: 110, k111: 111, k112: 112, k113: 113, k114: 114, k115: 115, k116: 116, k117: 117, k118: 118, k119: 119, + k120: 120, k121: 121, k122: 122, k123: 123, k124: 124, k125: 125, k126: 126, k127: 127, k128: 128, k129: 129, + k130: 130, k131: 131, k132: 132, k133: 133, k134: 134, k135: 135, k136: 136, k137: 137, k138: 138, k139: 139, + k140: 140, k141: 141, k142: 142, k143: 143, k144: 144, k145: 145, k146: 146, k147: 147, k148: 148, k149: 149, + k150: 150, k151: 151, k152: 152, k153: 153, k154: 154, k155: 155, k156: 156, k157: 157, k158: 158, k159: 159, + k160: 160, k161: 161, k162: 162, k163: 163, k164: 164, k165: 165, k166: 166, k167: 167, k168: 168, k169: 169, + k170: 170, k171: 171, k172: 172, k173: 173, k174: 174, k175: 175, k176: 176, k177: 177, k178: 178, k179: 179, + k180: 180, k181: 181, k182: 182, k183: 183, k184: 184, k185: 185, k186: 186, k187: 187, k188: 188, k189: 189, + k190: 190, k191: 191, k192: 192, k193: 193, k194: 194, k195: 195, k196: 196, k197: 197, k198: 198, k199: 199, + k200: 200, k201: 201, k202: 202, k203: 203, k204: 204, k205: 205, k206: 206, k207: 207, k208: 208, k209: 209, + k210: 210, k211: 211, k212: 212, k213: 213, k214: 214, k215: 215, k216: 216, k217: 217, k218: 218, k219: 219, + k220: 220, k221: 221, k222: 222, k223: 223, k224: 224, k225: 225, k226: 226, k227: 227, k228: 228, k229: 229, + k230: 230, k231: 231, k232: 232, k233: 233, k234: 234, k235: 235, k236: 236, k237: 237, k238: 238, k239: 239, + k240: 240, k241: 241, k242: 242, k243: 243, k244: 244, k245: 245, k246: 246, k247: 247, k248: 248, k249: 249, + k250: 250, k251: 251, k252: 252, k253: 253, k254: 254, k255: 255 + } + var keys = array(obj) + assert_eq(length(keys), 256, "should have 256 keys") + assert_eq(obj.k000, 0, "first key") + assert_eq(obj.k255, 255, "last key") +}) + +// Test: object with 257 keys - just past 256 boundary +run("object literal 257 number keys", function() { + var obj = { + k000: 0, k001: 1, k002: 2, k003: 3, k004: 4, k005: 5, k006: 6, k007: 7, k008: 8, k009: 9, + k010: 10, k011: 11, k012: 12, k013: 13, k014: 14, k015: 15, k016: 16, k017: 17, k018: 18, k019: 19, + k020: 20, k021: 21, k022: 22, k023: 23, k024: 24, k025: 25, k026: 26, k027: 27, k028: 28, k029: 29, + k030: 30, k031: 31, k032: 32, k033: 33, k034: 34, k035: 35, k036: 36, k037: 37, k038: 38, k039: 39, + k040: 40, k041: 41, k042: 42, k043: 43, k044: 44, k045: 45, k046: 46, k047: 47, k048: 48, k049: 49, + k050: 50, k051: 51, k052: 52, k053: 53, k054: 54, k055: 55, k056: 56, k057: 57, k058: 58, k059: 59, + k060: 60, k061: 61, k062: 62, k063: 63, k064: 64, k065: 65, k066: 66, k067: 67, k068: 68, k069: 69, + k070: 70, k071: 71, k072: 72, k073: 73, k074: 74, k075: 75, k076: 76, k077: 77, k078: 78, k079: 79, + k080: 80, k081: 81, k082: 82, k083: 83, k084: 84, k085: 85, k086: 86, k087: 87, k088: 88, k089: 89, + k090: 90, k091: 91, k092: 92, k093: 93, k094: 94, k095: 95, k096: 96, k097: 97, k098: 98, k099: 99, + k100: 100, k101: 101, k102: 102, k103: 103, k104: 104, k105: 105, k106: 106, k107: 107, k108: 108, k109: 109, + k110: 110, k111: 111, k112: 112, k113: 113, k114: 114, k115: 115, k116: 116, k117: 117, k118: 118, k119: 119, + k120: 120, k121: 121, k122: 122, k123: 123, k124: 124, k125: 125, k126: 126, k127: 127, k128: 128, k129: 129, + k130: 130, k131: 131, k132: 132, k133: 133, k134: 134, k135: 135, k136: 136, k137: 137, k138: 138, k139: 139, + k140: 140, k141: 141, k142: 142, k143: 143, k144: 144, k145: 145, k146: 146, k147: 147, k148: 148, k149: 149, + k150: 150, k151: 151, k152: 152, k153: 153, k154: 154, k155: 155, k156: 156, k157: 157, k158: 158, k159: 159, + k160: 160, k161: 161, k162: 162, k163: 163, k164: 164, k165: 165, k166: 166, k167: 167, k168: 168, k169: 169, + k170: 170, k171: 171, k172: 172, k173: 173, k174: 174, k175: 175, k176: 176, k177: 177, k178: 178, k179: 179, + k180: 180, k181: 181, k182: 182, k183: 183, k184: 184, k185: 185, k186: 186, k187: 187, k188: 188, k189: 189, + k190: 190, k191: 191, k192: 192, k193: 193, k194: 194, k195: 195, k196: 196, k197: 197, k198: 198, k199: 199, + k200: 200, k201: 201, k202: 202, k203: 203, k204: 204, k205: 205, k206: 206, k207: 207, k208: 208, k209: 209, + k210: 210, k211: 211, k212: 212, k213: 213, k214: 214, k215: 215, k216: 216, k217: 217, k218: 218, k219: 219, + k220: 220, k221: 221, k222: 222, k223: 223, k224: 224, k225: 225, k226: 226, k227: 227, k228: 228, k229: 229, + k230: 230, k231: 231, k232: 232, k233: 233, k234: 234, k235: 235, k236: 236, k237: 237, k238: 238, k239: 239, + k240: 240, k241: 241, k242: 242, k243: 243, k244: 244, k245: 245, k246: 246, k247: 247, k248: 248, k249: 249, + k250: 250, k251: 251, k252: 252, k253: 253, k254: 254, k255: 255, k256: 256 + } + var keys = array(obj) + assert_eq(length(keys), 257, "should have 257 keys") + assert_eq(obj.k000, 0, "first key") + assert_eq(obj.k256, 256, "last key") +}) + +// Test: object with 100 function values +run("object literal 100 function values", function() { + var obj = { + f000: function() { return 0 }, f001: function() { return 1 }, f002: function() { return 2 }, f003: function() { return 3 }, f004: function() { return 4 }, + f005: function() { return 5 }, f006: function() { return 6 }, f007: function() { return 7 }, f008: function() { return 8 }, f009: function() { return 9 }, + f010: function() { return 10 }, f011: function() { return 11 }, f012: function() { return 12 }, f013: function() { return 13 }, f014: function() { return 14 }, + f015: function() { return 15 }, f016: function() { return 16 }, f017: function() { return 17 }, f018: function() { return 18 }, f019: function() { return 19 }, + f020: function() { return 20 }, f021: function() { return 21 }, f022: function() { return 22 }, f023: function() { return 23 }, f024: function() { return 24 }, + f025: function() { return 25 }, f026: function() { return 26 }, f027: function() { return 27 }, f028: function() { return 28 }, f029: function() { return 29 }, + f030: function() { return 30 }, f031: function() { return 31 }, f032: function() { return 32 }, f033: function() { return 33 }, f034: function() { return 34 }, + f035: function() { return 35 }, f036: function() { return 36 }, f037: function() { return 37 }, f038: function() { return 38 }, f039: function() { return 39 }, + f040: function() { return 40 }, f041: function() { return 41 }, f042: function() { return 42 }, f043: function() { return 43 }, f044: function() { return 44 }, + f045: function() { return 45 }, f046: function() { return 46 }, f047: function() { return 47 }, f048: function() { return 48 }, f049: function() { return 49 }, + f050: function() { return 50 }, f051: function() { return 51 }, f052: function() { return 52 }, f053: function() { return 53 }, f054: function() { return 54 }, + f055: function() { return 55 }, f056: function() { return 56 }, f057: function() { return 57 }, f058: function() { return 58 }, f059: function() { return 59 }, + f060: function() { return 60 }, f061: function() { return 61 }, f062: function() { return 62 }, f063: function() { return 63 }, f064: function() { return 64 }, + f065: function() { return 65 }, f066: function() { return 66 }, f067: function() { return 67 }, f068: function() { return 68 }, f069: function() { return 69 }, + f070: function() { return 70 }, f071: function() { return 71 }, f072: function() { return 72 }, f073: function() { return 73 }, f074: function() { return 74 }, + f075: function() { return 75 }, f076: function() { return 76 }, f077: function() { return 77 }, f078: function() { return 78 }, f079: function() { return 79 }, + f080: function() { return 80 }, f081: function() { return 81 }, f082: function() { return 82 }, f083: function() { return 83 }, f084: function() { return 84 }, + f085: function() { return 85 }, f086: function() { return 86 }, f087: function() { return 87 }, f088: function() { return 88 }, f089: function() { return 89 }, + f090: function() { return 90 }, f091: function() { return 91 }, f092: function() { return 92 }, f093: function() { return 93 }, f094: function() { return 94 }, + f095: function() { return 95 }, f096: function() { return 96 }, f097: function() { return 97 }, f098: function() { return 98 }, f099: function() { return 99 } + } + var keys = array(obj) + var i = 0 + var bad_count = 0 + assert_eq(length(keys), 100, "should have 100 keys") + for (i = 0; i < length(keys); i++) { + if (!is_function(obj[keys[i]])) { + bad_count = bad_count + 1 + } + } + assert_eq(bad_count, 0, "all 100 values should be functions") + assert_eq(obj.f000(), 0, "first fn returns 0") + assert_eq(obj.f099(), 99, "last fn returns 99") +}) + +// Test: object with 256 function values - exact boundary +run("object literal 256 function values", function() { + var obj = { + f000: function() { return 0 }, f001: function() { return 1 }, f002: function() { return 2 }, f003: function() { return 3 }, f004: function() { return 4 }, + f005: function() { return 5 }, f006: function() { return 6 }, f007: function() { return 7 }, f008: function() { return 8 }, f009: function() { return 9 }, + f010: function() { return 10 }, f011: function() { return 11 }, f012: function() { return 12 }, f013: function() { return 13 }, f014: function() { return 14 }, + f015: function() { return 15 }, f016: function() { return 16 }, f017: function() { return 17 }, f018: function() { return 18 }, f019: function() { return 19 }, + f020: function() { return 20 }, f021: function() { return 21 }, f022: function() { return 22 }, f023: function() { return 23 }, f024: function() { return 24 }, + f025: function() { return 25 }, f026: function() { return 26 }, f027: function() { return 27 }, f028: function() { return 28 }, f029: function() { return 29 }, + f030: function() { return 30 }, f031: function() { return 31 }, f032: function() { return 32 }, f033: function() { return 33 }, f034: function() { return 34 }, + f035: function() { return 35 }, f036: function() { return 36 }, f037: function() { return 37 }, f038: function() { return 38 }, f039: function() { return 39 }, + f040: function() { return 40 }, f041: function() { return 41 }, f042: function() { return 42 }, f043: function() { return 43 }, f044: function() { return 44 }, + f045: function() { return 45 }, f046: function() { return 46 }, f047: function() { return 47 }, f048: function() { return 48 }, f049: function() { return 49 }, + f050: function() { return 50 }, f051: function() { return 51 }, f052: function() { return 52 }, f053: function() { return 53 }, f054: function() { return 54 }, + f055: function() { return 55 }, f056: function() { return 56 }, f057: function() { return 57 }, f058: function() { return 58 }, f059: function() { return 59 }, + f060: function() { return 60 }, f061: function() { return 61 }, f062: function() { return 62 }, f063: function() { return 63 }, f064: function() { return 64 }, + f065: function() { return 65 }, f066: function() { return 66 }, f067: function() { return 67 }, f068: function() { return 68 }, f069: function() { return 69 }, + f070: function() { return 70 }, f071: function() { return 71 }, f072: function() { return 72 }, f073: function() { return 73 }, f074: function() { return 74 }, + f075: function() { return 75 }, f076: function() { return 76 }, f077: function() { return 77 }, f078: function() { return 78 }, f079: function() { return 79 }, + f080: function() { return 80 }, f081: function() { return 81 }, f082: function() { return 82 }, f083: function() { return 83 }, f084: function() { return 84 }, + f085: function() { return 85 }, f086: function() { return 86 }, f087: function() { return 87 }, f088: function() { return 88 }, f089: function() { return 89 }, + f090: function() { return 90 }, f091: function() { return 91 }, f092: function() { return 92 }, f093: function() { return 93 }, f094: function() { return 94 }, + f095: function() { return 95 }, f096: function() { return 96 }, f097: function() { return 97 }, f098: function() { return 98 }, f099: function() { return 99 }, + f100: function() { return 100 }, f101: function() { return 101 }, f102: function() { return 102 }, f103: function() { return 103 }, f104: function() { return 104 }, + f105: function() { return 105 }, f106: function() { return 106 }, f107: function() { return 107 }, f108: function() { return 108 }, f109: function() { return 109 }, + f110: function() { return 110 }, f111: function() { return 111 }, f112: function() { return 112 }, f113: function() { return 113 }, f114: function() { return 114 }, + f115: function() { return 115 }, f116: function() { return 116 }, f117: function() { return 117 }, f118: function() { return 118 }, f119: function() { return 119 }, + f120: function() { return 120 }, f121: function() { return 121 }, f122: function() { return 122 }, f123: function() { return 123 }, f124: function() { return 124 }, + f125: function() { return 125 }, f126: function() { return 126 }, f127: function() { return 127 }, f128: function() { return 128 }, f129: function() { return 129 }, + f130: function() { return 130 }, f131: function() { return 131 }, f132: function() { return 132 }, f133: function() { return 133 }, f134: function() { return 134 }, + f135: function() { return 135 }, f136: function() { return 136 }, f137: function() { return 137 }, f138: function() { return 138 }, f139: function() { return 139 }, + f140: function() { return 140 }, f141: function() { return 141 }, f142: function() { return 142 }, f143: function() { return 143 }, f144: function() { return 144 }, + f145: function() { return 145 }, f146: function() { return 146 }, f147: function() { return 147 }, f148: function() { return 148 }, f149: function() { return 149 }, + f150: function() { return 150 }, f151: function() { return 151 }, f152: function() { return 152 }, f153: function() { return 153 }, f154: function() { return 154 }, + f155: function() { return 155 }, f156: function() { return 156 }, f157: function() { return 157 }, f158: function() { return 158 }, f159: function() { return 159 }, + f160: function() { return 160 }, f161: function() { return 161 }, f162: function() { return 162 }, f163: function() { return 163 }, f164: function() { return 164 }, + f165: function() { return 165 }, f166: function() { return 166 }, f167: function() { return 167 }, f168: function() { return 168 }, f169: function() { return 169 }, + f170: function() { return 170 }, f171: function() { return 171 }, f172: function() { return 172 }, f173: function() { return 173 }, f174: function() { return 174 }, + f175: function() { return 175 }, f176: function() { return 176 }, f177: function() { return 177 }, f178: function() { return 178 }, f179: function() { return 179 }, + f180: function() { return 180 }, f181: function() { return 181 }, f182: function() { return 182 }, f183: function() { return 183 }, f184: function() { return 184 }, + f185: function() { return 185 }, f186: function() { return 186 }, f187: function() { return 187 }, f188: function() { return 188 }, f189: function() { return 189 }, + f190: function() { return 190 }, f191: function() { return 191 }, f192: function() { return 192 }, f193: function() { return 193 }, f194: function() { return 194 }, + f195: function() { return 195 }, f196: function() { return 196 }, f197: function() { return 197 }, f198: function() { return 198 }, f199: function() { return 199 }, + f200: function() { return 200 }, f201: function() { return 201 }, f202: function() { return 202 }, f203: function() { return 203 }, f204: function() { return 204 }, + f205: function() { return 205 }, f206: function() { return 206 }, f207: function() { return 207 }, f208: function() { return 208 }, f209: function() { return 209 }, + f210: function() { return 210 }, f211: function() { return 211 }, f212: function() { return 212 }, f213: function() { return 213 }, f214: function() { return 214 }, + f215: function() { return 215 }, f216: function() { return 216 }, f217: function() { return 217 }, f218: function() { return 218 }, f219: function() { return 219 }, + f220: function() { return 220 }, f221: function() { return 221 }, f222: function() { return 222 }, f223: function() { return 223 }, f224: function() { return 224 }, + f225: function() { return 225 }, f226: function() { return 226 }, f227: function() { return 227 }, f228: function() { return 228 }, f229: function() { return 229 }, + f230: function() { return 230 }, f231: function() { return 231 }, f232: function() { return 232 }, f233: function() { return 233 }, f234: function() { return 234 }, + f235: function() { return 235 }, f236: function() { return 236 }, f237: function() { return 237 }, f238: function() { return 238 }, f239: function() { return 239 }, + f240: function() { return 240 }, f241: function() { return 241 }, f242: function() { return 242 }, f243: function() { return 243 }, f244: function() { return 244 }, + f245: function() { return 245 }, f246: function() { return 246 }, f247: function() { return 247 }, f248: function() { return 248 }, f249: function() { return 249 }, + f250: function() { return 250 }, f251: function() { return 251 }, f252: function() { return 252 }, f253: function() { return 253 }, f254: function() { return 254 }, + f255: function() { return 255 } + } + var keys = array(obj) + var i = 0 + var bad_count = 0 + var first_bad = "" + assert_eq(length(keys), 256, "should have 256 keys") + for (i = 0; i < length(keys); i++) { + if (!is_function(obj[keys[i]])) { + if (first_bad == "") { + first_bad = keys[i] + } + bad_count = bad_count + 1 + } + } + if (bad_count > 0) { + fail(text(bad_count) + " of 256 values not functions, first bad: " + first_bad) + } +}) + +// Test: object with 300 function values - well past boundary +run("object literal 300 function values", function() { + var obj = { + f000: function() { return 0 }, f001: function() { return 1 }, f002: function() { return 2 }, f003: function() { return 3 }, f004: function() { return 4 }, + f005: function() { return 5 }, f006: function() { return 6 }, f007: function() { return 7 }, f008: function() { return 8 }, f009: function() { return 9 }, + f010: function() { return 10 }, f011: function() { return 11 }, f012: function() { return 12 }, f013: function() { return 13 }, f014: function() { return 14 }, + f015: function() { return 15 }, f016: function() { return 16 }, f017: function() { return 17 }, f018: function() { return 18 }, f019: function() { return 19 }, + f020: function() { return 20 }, f021: function() { return 21 }, f022: function() { return 22 }, f023: function() { return 23 }, f024: function() { return 24 }, + f025: function() { return 25 }, f026: function() { return 26 }, f027: function() { return 27 }, f028: function() { return 28 }, f029: function() { return 29 }, + f030: function() { return 30 }, f031: function() { return 31 }, f032: function() { return 32 }, f033: function() { return 33 }, f034: function() { return 34 }, + f035: function() { return 35 }, f036: function() { return 36 }, f037: function() { return 37 }, f038: function() { return 38 }, f039: function() { return 39 }, + f040: function() { return 40 }, f041: function() { return 41 }, f042: function() { return 42 }, f043: function() { return 43 }, f044: function() { return 44 }, + f045: function() { return 45 }, f046: function() { return 46 }, f047: function() { return 47 }, f048: function() { return 48 }, f049: function() { return 49 }, + f050: function() { return 50 }, f051: function() { return 51 }, f052: function() { return 52 }, f053: function() { return 53 }, f054: function() { return 54 }, + f055: function() { return 55 }, f056: function() { return 56 }, f057: function() { return 57 }, f058: function() { return 58 }, f059: function() { return 59 }, + f060: function() { return 60 }, f061: function() { return 61 }, f062: function() { return 62 }, f063: function() { return 63 }, f064: function() { return 64 }, + f065: function() { return 65 }, f066: function() { return 66 }, f067: function() { return 67 }, f068: function() { return 68 }, f069: function() { return 69 }, + f070: function() { return 70 }, f071: function() { return 71 }, f072: function() { return 72 }, f073: function() { return 73 }, f074: function() { return 74 }, + f075: function() { return 75 }, f076: function() { return 76 }, f077: function() { return 77 }, f078: function() { return 78 }, f079: function() { return 79 }, + f080: function() { return 80 }, f081: function() { return 81 }, f082: function() { return 82 }, f083: function() { return 83 }, f084: function() { return 84 }, + f085: function() { return 85 }, f086: function() { return 86 }, f087: function() { return 87 }, f088: function() { return 88 }, f089: function() { return 89 }, + f090: function() { return 90 }, f091: function() { return 91 }, f092: function() { return 92 }, f093: function() { return 93 }, f094: function() { return 94 }, + f095: function() { return 95 }, f096: function() { return 96 }, f097: function() { return 97 }, f098: function() { return 98 }, f099: function() { return 99 }, + f100: function() { return 100 }, f101: function() { return 101 }, f102: function() { return 102 }, f103: function() { return 103 }, f104: function() { return 104 }, + f105: function() { return 105 }, f106: function() { return 106 }, f107: function() { return 107 }, f108: function() { return 108 }, f109: function() { return 109 }, + f110: function() { return 110 }, f111: function() { return 111 }, f112: function() { return 112 }, f113: function() { return 113 }, f114: function() { return 114 }, + f115: function() { return 115 }, f116: function() { return 116 }, f117: function() { return 117 }, f118: function() { return 118 }, f119: function() { return 119 }, + f120: function() { return 120 }, f121: function() { return 121 }, f122: function() { return 122 }, f123: function() { return 123 }, f124: function() { return 124 }, + f125: function() { return 125 }, f126: function() { return 126 }, f127: function() { return 127 }, f128: function() { return 128 }, f129: function() { return 129 }, + f130: function() { return 130 }, f131: function() { return 131 }, f132: function() { return 132 }, f133: function() { return 133 }, f134: function() { return 134 }, + f135: function() { return 135 }, f136: function() { return 136 }, f137: function() { return 137 }, f138: function() { return 138 }, f139: function() { return 139 }, + f140: function() { return 140 }, f141: function() { return 141 }, f142: function() { return 142 }, f143: function() { return 143 }, f144: function() { return 144 }, + f145: function() { return 145 }, f146: function() { return 146 }, f147: function() { return 147 }, f148: function() { return 148 }, f149: function() { return 149 }, + f150: function() { return 150 }, f151: function() { return 151 }, f152: function() { return 152 }, f153: function() { return 153 }, f154: function() { return 154 }, + f155: function() { return 155 }, f156: function() { return 156 }, f157: function() { return 157 }, f158: function() { return 158 }, f159: function() { return 159 }, + f160: function() { return 160 }, f161: function() { return 161 }, f162: function() { return 162 }, f163: function() { return 163 }, f164: function() { return 164 }, + f165: function() { return 165 }, f166: function() { return 166 }, f167: function() { return 167 }, f168: function() { return 168 }, f169: function() { return 169 }, + f170: function() { return 170 }, f171: function() { return 171 }, f172: function() { return 172 }, f173: function() { return 173 }, f174: function() { return 174 }, + f175: function() { return 175 }, f176: function() { return 176 }, f177: function() { return 177 }, f178: function() { return 178 }, f179: function() { return 179 }, + f180: function() { return 180 }, f181: function() { return 181 }, f182: function() { return 182 }, f183: function() { return 183 }, f184: function() { return 184 }, + f185: function() { return 185 }, f186: function() { return 186 }, f187: function() { return 187 }, f188: function() { return 188 }, f189: function() { return 189 }, + f190: function() { return 190 }, f191: function() { return 191 }, f192: function() { return 192 }, f193: function() { return 193 }, f194: function() { return 194 }, + f195: function() { return 195 }, f196: function() { return 196 }, f197: function() { return 197 }, f198: function() { return 198 }, f199: function() { return 199 }, + f200: function() { return 200 }, f201: function() { return 201 }, f202: function() { return 202 }, f203: function() { return 203 }, f204: function() { return 204 }, + f205: function() { return 205 }, f206: function() { return 206 }, f207: function() { return 207 }, f208: function() { return 208 }, f209: function() { return 209 }, + f210: function() { return 210 }, f211: function() { return 211 }, f212: function() { return 212 }, f213: function() { return 213 }, f214: function() { return 214 }, + f215: function() { return 215 }, f216: function() { return 216 }, f217: function() { return 217 }, f218: function() { return 218 }, f219: function() { return 219 }, + f220: function() { return 220 }, f221: function() { return 221 }, f222: function() { return 222 }, f223: function() { return 223 }, f224: function() { return 224 }, + f225: function() { return 225 }, f226: function() { return 226 }, f227: function() { return 227 }, f228: function() { return 228 }, f229: function() { return 229 }, + f230: function() { return 230 }, f231: function() { return 231 }, f232: function() { return 232 }, f233: function() { return 233 }, f234: function() { return 234 }, + f235: function() { return 235 }, f236: function() { return 236 }, f237: function() { return 237 }, f238: function() { return 238 }, f239: function() { return 239 }, + f240: function() { return 240 }, f241: function() { return 241 }, f242: function() { return 242 }, f243: function() { return 243 }, f244: function() { return 244 }, + f245: function() { return 245 }, f246: function() { return 246 }, f247: function() { return 247 }, f248: function() { return 248 }, f249: function() { return 249 }, + f250: function() { return 250 }, f251: function() { return 251 }, f252: function() { return 252 }, f253: function() { return 253 }, f254: function() { return 254 }, + f255: function() { return 255 }, f256: function() { return 256 }, f257: function() { return 257 }, f258: function() { return 258 }, f259: function() { return 259 }, + f260: function() { return 260 }, f261: function() { return 261 }, f262: function() { return 262 }, f263: function() { return 263 }, f264: function() { return 264 }, + f265: function() { return 265 }, f266: function() { return 266 }, f267: function() { return 267 }, f268: function() { return 268 }, f269: function() { return 269 }, + f270: function() { return 270 }, f271: function() { return 271 }, f272: function() { return 272 }, f273: function() { return 273 }, f274: function() { return 274 }, + f275: function() { return 275 }, f276: function() { return 276 }, f277: function() { return 277 }, f278: function() { return 278 }, f279: function() { return 279 }, + f280: function() { return 280 }, f281: function() { return 281 }, f282: function() { return 282 }, f283: function() { return 283 }, f284: function() { return 284 }, + f285: function() { return 285 }, f286: function() { return 286 }, f287: function() { return 287 }, f288: function() { return 288 }, f289: function() { return 289 }, + f290: function() { return 290 }, f291: function() { return 291 }, f292: function() { return 292 }, f293: function() { return 293 }, f294: function() { return 294 }, + f295: function() { return 295 }, f296: function() { return 296 }, f297: function() { return 297 }, f298: function() { return 298 }, f299: function() { return 299 } + } + var keys = array(obj) + var i = 0 + var bad_count = 0 + var first_bad = "" + assert_eq(length(keys), 300, "should have 300 keys") + for (i = 0; i < length(keys); i++) { + if (!is_function(obj[keys[i]])) { + if (first_bad == "") { + first_bad = keys[i] + } + bad_count = bad_count + 1 + } + } + if (bad_count > 0) { + fail(text(bad_count) + " of 300 values not functions, first bad: " + first_bad) + } +}) + +// Test: object built incrementally (not literal) with 300 keys +run("object incremental 300 number keys", function() { + var obj = {} + var i = 0 + for (i = 0; i < 300; i++) { + obj["k" + text(i)] = i + } + var keys = array(obj) + assert_eq(length(keys), 300, "should have 300 keys") + assert_eq(obj.k0, 0, "first key") + assert_eq(obj.k299, 299, "last key") +}) + +// Test: object built incrementally with 300 function values +run("object incremental 300 function values", function() { + var obj = {} + var i = 0 + var make_fn = function(n) { return function() { return n } } + for (i = 0; i < 300; i++) { + obj["f" + text(i)] = make_fn(i) + } + var keys = array(obj) + var bad_count = 0 + assert_eq(length(keys), 300, "should have 300 keys") + for (i = 0; i < length(keys); i++) { + if (!is_function(obj[keys[i]])) { + bad_count = bad_count + 1 + } + } + assert_eq(bad_count, 0, "all 300 values should be functions") + assert_eq(obj.f0(), 0, "first fn") + assert_eq(obj.f299(), 299, "last fn") +}) + +// Test: object with very long key names +run("object literal long key names", function() { + var obj = { + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_01: 1, + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_02: 2, + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_03: 3, + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_04: 4, + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_05: 5, + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_06: 6, + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_07: 7, + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_08: 8, + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_09: 9, + this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_10: 10 + } + var keys = array(obj) + assert_eq(length(keys), 10, "should have 10 keys") + assert_eq(obj.this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_01, 1, "long key 1") + assert_eq(obj.this_is_a_really_long_key_name_that_tests_whether_long_identifiers_cause_issues_key_10, 10, "long key 10") +}) + +// Test: return object literal from function with many keys +run("object literal 300 function values returned from function", function() { + var make_obj = function() { + return { + f000: function() { return 0 }, f001: function() { return 1 }, f002: function() { return 2 }, f003: function() { return 3 }, f004: function() { return 4 }, + f005: function() { return 5 }, f006: function() { return 6 }, f007: function() { return 7 }, f008: function() { return 8 }, f009: function() { return 9 }, + f010: function() { return 10 }, f011: function() { return 11 }, f012: function() { return 12 }, f013: function() { return 13 }, f014: function() { return 14 }, + f015: function() { return 15 }, f016: function() { return 16 }, f017: function() { return 17 }, f018: function() { return 18 }, f019: function() { return 19 }, + f020: function() { return 20 }, f021: function() { return 21 }, f022: function() { return 22 }, f023: function() { return 23 }, f024: function() { return 24 }, + f025: function() { return 25 }, f026: function() { return 26 }, f027: function() { return 27 }, f028: function() { return 28 }, f029: function() { return 29 }, + f030: function() { return 30 }, f031: function() { return 31 }, f032: function() { return 32 }, f033: function() { return 33 }, f034: function() { return 34 }, + f035: function() { return 35 }, f036: function() { return 36 }, f037: function() { return 37 }, f038: function() { return 38 }, f039: function() { return 39 }, + f040: function() { return 40 }, f041: function() { return 41 }, f042: function() { return 42 }, f043: function() { return 43 }, f044: function() { return 44 }, + f045: function() { return 45 }, f046: function() { return 46 }, f047: function() { return 47 }, f048: function() { return 48 }, f049: function() { return 49 }, + f050: function() { return 50 }, f051: function() { return 51 }, f052: function() { return 52 }, f053: function() { return 53 }, f054: function() { return 54 }, + f055: function() { return 55 }, f056: function() { return 56 }, f057: function() { return 57 }, f058: function() { return 58 }, f059: function() { return 59 }, + f060: function() { return 60 }, f061: function() { return 61 }, f062: function() { return 62 }, f063: function() { return 63 }, f064: function() { return 64 }, + f065: function() { return 65 }, f066: function() { return 66 }, f067: function() { return 67 }, f068: function() { return 68 }, f069: function() { return 69 }, + f070: function() { return 70 }, f071: function() { return 71 }, f072: function() { return 72 }, f073: function() { return 73 }, f074: function() { return 74 }, + f075: function() { return 75 }, f076: function() { return 76 }, f077: function() { return 77 }, f078: function() { return 78 }, f079: function() { return 79 }, + f080: function() { return 80 }, f081: function() { return 81 }, f082: function() { return 82 }, f083: function() { return 83 }, f084: function() { return 84 }, + f085: function() { return 85 }, f086: function() { return 86 }, f087: function() { return 87 }, f088: function() { return 88 }, f089: function() { return 89 }, + f090: function() { return 90 }, f091: function() { return 91 }, f092: function() { return 92 }, f093: function() { return 93 }, f094: function() { return 94 }, + f095: function() { return 95 }, f096: function() { return 96 }, f097: function() { return 97 }, f098: function() { return 98 }, f099: function() { return 99 }, + f100: function() { return 100 }, f101: function() { return 101 }, f102: function() { return 102 }, f103: function() { return 103 }, f104: function() { return 104 }, + f105: function() { return 105 }, f106: function() { return 106 }, f107: function() { return 107 }, f108: function() { return 108 }, f109: function() { return 109 }, + f110: function() { return 110 }, f111: function() { return 111 }, f112: function() { return 112 }, f113: function() { return 113 }, f114: function() { return 114 }, + f115: function() { return 115 }, f116: function() { return 116 }, f117: function() { return 117 }, f118: function() { return 118 }, f119: function() { return 119 }, + f120: function() { return 120 }, f121: function() { return 121 }, f122: function() { return 122 }, f123: function() { return 123 }, f124: function() { return 124 }, + f125: function() { return 125 }, f126: function() { return 126 }, f127: function() { return 127 }, f128: function() { return 128 }, f129: function() { return 129 }, + f130: function() { return 130 }, f131: function() { return 131 }, f132: function() { return 132 }, f133: function() { return 133 }, f134: function() { return 134 }, + f135: function() { return 135 }, f136: function() { return 136 }, f137: function() { return 137 }, f138: function() { return 138 }, f139: function() { return 139 }, + f140: function() { return 140 }, f141: function() { return 141 }, f142: function() { return 142 }, f143: function() { return 143 }, f144: function() { return 144 }, + f145: function() { return 145 }, f146: function() { return 146 }, f147: function() { return 147 }, f148: function() { return 148 }, f149: function() { return 149 }, + f150: function() { return 150 }, f151: function() { return 151 }, f152: function() { return 152 }, f153: function() { return 153 }, f154: function() { return 154 }, + f155: function() { return 155 }, f156: function() { return 156 }, f157: function() { return 157 }, f158: function() { return 158 }, f159: function() { return 159 }, + f160: function() { return 160 }, f161: function() { return 161 }, f162: function() { return 162 }, f163: function() { return 163 }, f164: function() { return 164 }, + f165: function() { return 165 }, f166: function() { return 166 }, f167: function() { return 167 }, f168: function() { return 168 }, f169: function() { return 169 }, + f170: function() { return 170 }, f171: function() { return 171 }, f172: function() { return 172 }, f173: function() { return 173 }, f174: function() { return 174 }, + f175: function() { return 175 }, f176: function() { return 176 }, f177: function() { return 177 }, f178: function() { return 178 }, f179: function() { return 179 }, + f180: function() { return 180 }, f181: function() { return 181 }, f182: function() { return 182 }, f183: function() { return 183 }, f184: function() { return 184 }, + f185: function() { return 185 }, f186: function() { return 186 }, f187: function() { return 187 }, f188: function() { return 188 }, f189: function() { return 189 }, + f190: function() { return 190 }, f191: function() { return 191 }, f192: function() { return 192 }, f193: function() { return 193 }, f194: function() { return 194 }, + f195: function() { return 195 }, f196: function() { return 196 }, f197: function() { return 197 }, f198: function() { return 198 }, f199: function() { return 199 }, + f200: function() { return 200 }, f201: function() { return 201 }, f202: function() { return 202 }, f203: function() { return 203 }, f204: function() { return 204 }, + f205: function() { return 205 }, f206: function() { return 206 }, f207: function() { return 207 }, f208: function() { return 208 }, f209: function() { return 209 }, + f210: function() { return 210 }, f211: function() { return 211 }, f212: function() { return 212 }, f213: function() { return 213 }, f214: function() { return 214 }, + f215: function() { return 215 }, f216: function() { return 216 }, f217: function() { return 217 }, f218: function() { return 218 }, f219: function() { return 219 }, + f220: function() { return 220 }, f221: function() { return 221 }, f222: function() { return 222 }, f223: function() { return 223 }, f224: function() { return 224 }, + f225: function() { return 225 }, f226: function() { return 226 }, f227: function() { return 227 }, f228: function() { return 228 }, f229: function() { return 229 }, + f230: function() { return 230 }, f231: function() { return 231 }, f232: function() { return 232 }, f233: function() { return 233 }, f234: function() { return 234 }, + f235: function() { return 235 }, f236: function() { return 236 }, f237: function() { return 237 }, f238: function() { return 238 }, f239: function() { return 239 }, + f240: function() { return 240 }, f241: function() { return 241 }, f242: function() { return 242 }, f243: function() { return 243 }, f244: function() { return 244 }, + f245: function() { return 245 }, f246: function() { return 246 }, f247: function() { return 247 }, f248: function() { return 248 }, f249: function() { return 249 }, + f250: function() { return 250 }, f251: function() { return 251 }, f252: function() { return 252 }, f253: function() { return 253 }, f254: function() { return 254 }, + f255: function() { return 255 }, f256: function() { return 256 }, f257: function() { return 257 }, f258: function() { return 258 }, f259: function() { return 259 }, + f260: function() { return 260 }, f261: function() { return 261 }, f262: function() { return 262 }, f263: function() { return 263 }, f264: function() { return 264 }, + f265: function() { return 265 }, f266: function() { return 266 }, f267: function() { return 267 }, f268: function() { return 268 }, f269: function() { return 269 }, + f270: function() { return 270 }, f271: function() { return 271 }, f272: function() { return 272 }, f273: function() { return 273 }, f274: function() { return 274 }, + f275: function() { return 275 }, f276: function() { return 276 }, f277: function() { return 277 }, f278: function() { return 278 }, f279: function() { return 279 }, + f280: function() { return 280 }, f281: function() { return 281 }, f282: function() { return 282 }, f283: function() { return 283 }, f284: function() { return 284 }, + f285: function() { return 285 }, f286: function() { return 286 }, f287: function() { return 287 }, f288: function() { return 288 }, f289: function() { return 289 }, + f290: function() { return 290 }, f291: function() { return 291 }, f292: function() { return 292 }, f293: function() { return 293 }, f294: function() { return 294 }, + f295: function() { return 295 }, f296: function() { return 296 }, f297: function() { return 297 }, f298: function() { return 298 }, f299: function() { return 299 } + } + } + var obj = make_obj() + var keys = array(obj) + var i = 0 + var bad_count = 0 + var first_bad = "" + assert_eq(length(keys), 300, "should have 300 keys") + for (i = 0; i < length(keys); i++) { + if (!is_function(obj[keys[i]])) { + if (first_bad == "") { + first_bad = keys[i] + } + bad_count = bad_count + 1 + } + } + if (bad_count > 0) { + fail(text(bad_count) + " of 300 values not functions, first bad: " + first_bad) + } +}) + // ============================================================================ // SUMMARY // ============================================================================ diff --git a/vm_test/arrow_block.txt b/vm_test/arrow_block.txt deleted file mode 100644 index 667813cb..00000000 --- a/vm_test/arrow_block.txt +++ /dev/null @@ -1 +0,0 @@ -var f = x => { return x }; f(1) diff --git a/vm_test/arrow_default.txt b/vm_test/arrow_default.txt deleted file mode 100644 index 9a90bc50..00000000 --- a/vm_test/arrow_default.txt +++ /dev/null @@ -1 +0,0 @@ -var f = (x = 10) => x; f() diff --git a/vm_test/arrow_expr.txt b/vm_test/arrow_expr.txt deleted file mode 100644 index d898b6ee..00000000 --- a/vm_test/arrow_expr.txt +++ /dev/null @@ -1 +0,0 @@ -var f = x => x * 2; f(5) diff --git a/vm_test/arrow_multi.txt b/vm_test/arrow_multi.txt deleted file mode 100644 index 352b71f0..00000000 --- a/vm_test/arrow_multi.txt +++ /dev/null @@ -1 +0,0 @@ -var f = (a, b) => a + b; f(2, 3) diff --git a/vm_test/arrow_no_param.txt b/vm_test/arrow_no_param.txt deleted file mode 100644 index 96309939..00000000 --- a/vm_test/arrow_no_param.txt +++ /dev/null @@ -1 +0,0 @@ -var f = () => 42; f() diff --git a/vm_test/assign_add.txt b/vm_test/assign_add.txt deleted file mode 100644 index efcb4ba7..00000000 --- a/vm_test/assign_add.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x += 3; x diff --git a/vm_test/assign_and.txt b/vm_test/assign_and.txt deleted file mode 100644 index 836a9e39..00000000 --- a/vm_test/assign_and.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 7; x &= 3; x diff --git a/vm_test/assign_div.txt b/vm_test/assign_div.txt deleted file mode 100644 index e89b6a07..00000000 --- a/vm_test/assign_div.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 6; x /= 2; x diff --git a/vm_test/assign_land.txt b/vm_test/assign_land.txt deleted file mode 100644 index 424711cc..00000000 --- a/vm_test/assign_land.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x &&= 10; x diff --git a/vm_test/assign_lor.txt b/vm_test/assign_lor.txt deleted file mode 100644 index 165208bc..00000000 --- a/vm_test/assign_lor.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 0; x ||= 10; x diff --git a/vm_test/assign_mod.txt b/vm_test/assign_mod.txt deleted file mode 100644 index 60725e7f..00000000 --- a/vm_test/assign_mod.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 7; x %= 3; x diff --git a/vm_test/assign_mul.txt b/vm_test/assign_mul.txt deleted file mode 100644 index 9df1a1bf..00000000 --- a/vm_test/assign_mul.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x *= 3; x diff --git a/vm_test/assign_nullish.txt b/vm_test/assign_nullish.txt deleted file mode 100644 index db07bfb9..00000000 --- a/vm_test/assign_nullish.txt +++ /dev/null @@ -1 +0,0 @@ -var x = null; x ??= 10; x diff --git a/vm_test/assign_or.txt b/vm_test/assign_or.txt deleted file mode 100644 index d9d6dbd5..00000000 --- a/vm_test/assign_or.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x |= 2; x diff --git a/vm_test/assign_power.txt b/vm_test/assign_power.txt deleted file mode 100644 index e2345c16..00000000 --- a/vm_test/assign_power.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 2; x **= 3; x diff --git a/vm_test/assign_shl.txt b/vm_test/assign_shl.txt deleted file mode 100644 index dd4b03d4..00000000 --- a/vm_test/assign_shl.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 2; x <<= 3; x diff --git a/vm_test/assign_shr.txt b/vm_test/assign_shr.txt deleted file mode 100644 index bdf25edb..00000000 --- a/vm_test/assign_shr.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 8; x >>= 2; x diff --git a/vm_test/assign_shru.txt b/vm_test/assign_shru.txt deleted file mode 100644 index bff9c9e7..00000000 --- a/vm_test/assign_shru.txt +++ /dev/null @@ -1 +0,0 @@ -var x = -8; x >>>= 2; x diff --git a/vm_test/assign_sub.txt b/vm_test/assign_sub.txt deleted file mode 100644 index dd9b8dd5..00000000 --- a/vm_test/assign_sub.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x -= 3; x diff --git a/vm_test/assign_xor.txt b/vm_test/assign_xor.txt deleted file mode 100644 index f1fb2261..00000000 --- a/vm_test/assign_xor.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x ^= 3; x diff --git a/vm_test/chained_assign.txt b/vm_test/chained_assign.txt deleted file mode 100644 index 9c06c45b..00000000 --- a/vm_test/chained_assign.txt +++ /dev/null @@ -1 +0,0 @@ -var x, y; x = y = 5; x + y diff --git a/vm_test/closure_basic.txt b/vm_test/closure_basic.txt deleted file mode 100644 index ea174b3e..00000000 --- a/vm_test/closure_basic.txt +++ /dev/null @@ -1 +0,0 @@ -var f = function(x) { return function() { return x } }; f(5)() diff --git a/vm_test/closure_mutate.txt b/vm_test/closure_mutate.txt deleted file mode 100644 index db236752..00000000 --- a/vm_test/closure_mutate.txt +++ /dev/null @@ -1,11 +0,0 @@ -var counter = function() { - var n = 0 - return function() { - n = n + 1 - return n - } -} -var c = counter() -c() -c() -c() diff --git a/vm_test/comment.txt b/vm_test/comment.txt deleted file mode 100644 index a55fe414..00000000 --- a/vm_test/comment.txt +++ /dev/null @@ -1,3 +0,0 @@ -// simple test that comments work -var x = 5 -// other comment diff --git a/vm_test/comment_block.txt b/vm_test/comment_block.txt deleted file mode 100644 index effe80e0..00000000 --- a/vm_test/comment_block.txt +++ /dev/null @@ -1 +0,0 @@ -/* comment */ 5 diff --git a/vm_test/comment_multi.txt b/vm_test/comment_multi.txt deleted file mode 100644 index 31f637fe..00000000 --- a/vm_test/comment_multi.txt +++ /dev/null @@ -1 +0,0 @@ -1 /* a */ + /* b */ 2 diff --git a/vm_test/def_basic.txt b/vm_test/def_basic.txt deleted file mode 100644 index 3c26a38f..00000000 --- a/vm_test/def_basic.txt +++ /dev/null @@ -1 +0,0 @@ -def x = 5; x diff --git a/vm_test/do_while.txt b/vm_test/do_while.txt deleted file mode 100644 index 971ea372..00000000 --- a/vm_test/do_while.txt +++ /dev/null @@ -1 +0,0 @@ -var i = 0; do { i = i + 1 } while (i < 3); i diff --git a/vm_test/do_while_continue.txt b/vm_test/do_while_continue.txt deleted file mode 100644 index 8fca189a..00000000 --- a/vm_test/do_while_continue.txt +++ /dev/null @@ -1 +0,0 @@ -var s = 0; var i = 0; do { i = i + 1; if (i == 2) continue; s = s + i } while (i < 5); s diff --git a/vm_test/empty_statement.txt b/vm_test/empty_statement.txt deleted file mode 100644 index 5ab484f5..00000000 --- a/vm_test/empty_statement.txt +++ /dev/null @@ -1 +0,0 @@ -;;; 5 diff --git a/vm_test/for_basic.txt b/vm_test/for_basic.txt deleted file mode 100644 index a8b5a744..00000000 --- a/vm_test/for_basic.txt +++ /dev/null @@ -1 +0,0 @@ -var s = 0; for (var i = 0; i < 3; i++) s = s + i; s diff --git a/vm_test/for_break.txt b/vm_test/for_break.txt deleted file mode 100644 index 63492284..00000000 --- a/vm_test/for_break.txt +++ /dev/null @@ -1 +0,0 @@ -var s = 0; for (var i = 0; i < 10; i++) { if (i == 4) break; s = s + i }; s diff --git a/vm_test/for_continue.txt b/vm_test/for_continue.txt deleted file mode 100644 index 5d1e9e00..00000000 --- a/vm_test/for_continue.txt +++ /dev/null @@ -1 +0,0 @@ -var s = 0; for (var i = 0; i < 5; i++) { if (i == 2) continue; s = s + i }; s diff --git a/vm_test/func_expr.txt b/vm_test/func_expr.txt deleted file mode 100644 index 65485b1d..00000000 --- a/vm_test/func_expr.txt +++ /dev/null @@ -1 +0,0 @@ -var f = function(x) { return x * 2 }; f(3) diff --git a/vm_test/func_iife.txt b/vm_test/func_iife.txt deleted file mode 100644 index e03daadf..00000000 --- a/vm_test/func_iife.txt +++ /dev/null @@ -1 +0,0 @@ -(function(x) { return x * 2 })(5) diff --git a/vm_test/func_recursive.txt b/vm_test/func_recursive.txt deleted file mode 100644 index d79c9d26..00000000 --- a/vm_test/func_recursive.txt +++ /dev/null @@ -1 +0,0 @@ -function fac(n) { if (n <= 1) return 1; return n * fac(n - 1) }; fac(5) diff --git a/vm_test/go_basic.txt b/vm_test/go_basic.txt deleted file mode 100644 index 4ac50c5a..00000000 --- a/vm_test/go_basic.txt +++ /dev/null @@ -1,2 +0,0 @@ -function a() { go b() } -function b() { 1 } diff --git a/vm_test/go_method.txt b/vm_test/go_method.txt deleted file mode 100644 index 6b5ef154..00000000 --- a/vm_test/go_method.txt +++ /dev/null @@ -1,2 +0,0 @@ -var o = {m: function() { 1 }} -function f() { go o.m() } diff --git a/vm_test/if_basic.txt b/vm_test/if_basic.txt deleted file mode 100644 index 669bca8a..00000000 --- a/vm_test/if_basic.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 0; if (true) x = 1; x diff --git a/vm_test/if_else.txt b/vm_test/if_else.txt deleted file mode 100644 index 27b33cbb..00000000 --- a/vm_test/if_else.txt +++ /dev/null @@ -1 +0,0 @@ -if (false) 1 else 2 diff --git a/vm_test/intrisic_link.txt b/vm_test/intrisic_link.txt deleted file mode 100644 index e1b904b7..00000000 --- a/vm_test/intrisic_link.txt +++ /dev/null @@ -1 +0,0 @@ -print("a") diff --git a/vm_test/label_continue.txt b/vm_test/label_continue.txt deleted file mode 100644 index e1173b80..00000000 --- a/vm_test/label_continue.txt +++ /dev/null @@ -1 +0,0 @@ -var s = 0; outer: for (var i = 0; i < 3; i++) { for (var j = 0; j < 3; j++) { if (j == 1) continue outer; s = s + 1 } }; s diff --git a/vm_test/multi_var.txt b/vm_test/multi_var.txt deleted file mode 100644 index 2a577920..00000000 --- a/vm_test/multi_var.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 1, y = 2; x + y diff --git a/vm_test/nested_block.txt b/vm_test/nested_block.txt deleted file mode 100644 index fef7625f..00000000 --- a/vm_test/nested_block.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 1; { var y = 2; { var z = 3; x = x + y + z } }; x diff --git a/vm_test/num_binary.txt b/vm_test/num_binary.txt deleted file mode 100644 index 22573f61..00000000 --- a/vm_test/num_binary.txt +++ /dev/null @@ -1 +0,0 @@ -0b1010 diff --git a/vm_test/num_exp.txt b/vm_test/num_exp.txt deleted file mode 100644 index f1c9880c..00000000 --- a/vm_test/num_exp.txt +++ /dev/null @@ -1 +0,0 @@ -1e3 diff --git a/vm_test/num_float.txt b/vm_test/num_float.txt deleted file mode 100644 index 6324d401..00000000 --- a/vm_test/num_float.txt +++ /dev/null @@ -1 +0,0 @@ -3.14 diff --git a/vm_test/num_hex.txt b/vm_test/num_hex.txt deleted file mode 100644 index 7a35b297..00000000 --- a/vm_test/num_hex.txt +++ /dev/null @@ -1 +0,0 @@ -0xff diff --git a/vm_test/num_octal.txt b/vm_test/num_octal.txt deleted file mode 100644 index b99d581b..00000000 --- a/vm_test/num_octal.txt +++ /dev/null @@ -1 +0,0 @@ -0o17 diff --git a/vm_test/num_underscore.txt b/vm_test/num_underscore.txt deleted file mode 100644 index 20d36fa5..00000000 --- a/vm_test/num_underscore.txt +++ /dev/null @@ -1 +0,0 @@ -1_000_000 diff --git a/vm_test/op_arith.txt b/vm_test/op_arith.txt deleted file mode 100644 index cba549f0..00000000 --- a/vm_test/op_arith.txt +++ /dev/null @@ -1 +0,0 @@ -1 + 2 * 3 diff --git a/vm_test/op_bitwise.txt b/vm_test/op_bitwise.txt deleted file mode 100644 index 1e8229ec..00000000 --- a/vm_test/op_bitwise.txt +++ /dev/null @@ -1 +0,0 @@ -5 & 3 diff --git a/vm_test/op_bitwise_not.txt b/vm_test/op_bitwise_not.txt deleted file mode 100644 index 072132fe..00000000 --- a/vm_test/op_bitwise_not.txt +++ /dev/null @@ -1 +0,0 @@ -~5 diff --git a/vm_test/op_bitwise_or.txt b/vm_test/op_bitwise_or.txt deleted file mode 100644 index c59fb1f2..00000000 --- a/vm_test/op_bitwise_or.txt +++ /dev/null @@ -1 +0,0 @@ -5 | 2 diff --git a/vm_test/op_bitwise_xor.txt b/vm_test/op_bitwise_xor.txt deleted file mode 100644 index c04a0a77..00000000 --- a/vm_test/op_bitwise_xor.txt +++ /dev/null @@ -1 +0,0 @@ -5 ^ 3 diff --git a/vm_test/op_comma.txt b/vm_test/op_comma.txt deleted file mode 100644 index caa68212..00000000 --- a/vm_test/op_comma.txt +++ /dev/null @@ -1 +0,0 @@ -(1, 2, 3) diff --git a/vm_test/op_compare.txt b/vm_test/op_compare.txt deleted file mode 100644 index 029c195d..00000000 --- a/vm_test/op_compare.txt +++ /dev/null @@ -1 +0,0 @@ -5 > 3 diff --git a/vm_test/op_compare_eq.txt b/vm_test/op_compare_eq.txt deleted file mode 100644 index fdb417f1..00000000 --- a/vm_test/op_compare_eq.txt +++ /dev/null @@ -1 +0,0 @@ -3 == 3 diff --git a/vm_test/op_compare_gte.txt b/vm_test/op_compare_gte.txt deleted file mode 100644 index 3ee8c9c3..00000000 --- a/vm_test/op_compare_gte.txt +++ /dev/null @@ -1 +0,0 @@ -5 >= 5 diff --git a/vm_test/op_compare_lt.txt b/vm_test/op_compare_lt.txt deleted file mode 100644 index 0a724e35..00000000 --- a/vm_test/op_compare_lt.txt +++ /dev/null @@ -1 +0,0 @@ -3 < 5 diff --git a/vm_test/op_compare_lte.txt b/vm_test/op_compare_lte.txt deleted file mode 100644 index de31adce..00000000 --- a/vm_test/op_compare_lte.txt +++ /dev/null @@ -1 +0,0 @@ -3 <= 3 diff --git a/vm_test/op_compare_neq.txt b/vm_test/op_compare_neq.txt deleted file mode 100644 index 14025437..00000000 --- a/vm_test/op_compare_neq.txt +++ /dev/null @@ -1 +0,0 @@ -3 != 4 diff --git a/vm_test/op_decrement_post.txt b/vm_test/op_decrement_post.txt deleted file mode 100644 index d7a4a711..00000000 --- a/vm_test/op_decrement_post.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x--; x diff --git a/vm_test/op_decrement_pre.txt b/vm_test/op_decrement_pre.txt deleted file mode 100644 index 96a884dc..00000000 --- a/vm_test/op_decrement_pre.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; --x diff --git a/vm_test/op_delete.txt b/vm_test/op_delete.txt deleted file mode 100644 index 578d1bd5..00000000 --- a/vm_test/op_delete.txt +++ /dev/null @@ -1 +0,0 @@ -var o = {x: 1}; delete o.x; o.x diff --git a/vm_test/op_in.txt b/vm_test/op_in.txt deleted file mode 100644 index ab6ccdc5..00000000 --- a/vm_test/op_in.txt +++ /dev/null @@ -1 +0,0 @@ -var o = {x: 1}; "x" in o diff --git a/vm_test/op_increment_post.txt b/vm_test/op_increment_post.txt deleted file mode 100644 index 5e7db6a1..00000000 --- a/vm_test/op_increment_post.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x++; x diff --git a/vm_test/op_increment_pre.txt b/vm_test/op_increment_pre.txt deleted file mode 100644 index 8b484611..00000000 --- a/vm_test/op_increment_pre.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; ++x diff --git a/vm_test/op_logical.txt b/vm_test/op_logical.txt deleted file mode 100644 index c4d456a0..00000000 --- a/vm_test/op_logical.txt +++ /dev/null @@ -1 +0,0 @@ -true && false diff --git a/vm_test/op_logical_not.txt b/vm_test/op_logical_not.txt deleted file mode 100644 index 8c9104a7..00000000 --- a/vm_test/op_logical_not.txt +++ /dev/null @@ -1 +0,0 @@ -!false diff --git a/vm_test/op_logical_or.txt b/vm_test/op_logical_or.txt deleted file mode 100644 index 84ffdd6e..00000000 --- a/vm_test/op_logical_or.txt +++ /dev/null @@ -1 +0,0 @@ -false || true diff --git a/vm_test/op_nullish.txt b/vm_test/op_nullish.txt deleted file mode 100644 index 89287bf3..00000000 --- a/vm_test/op_nullish.txt +++ /dev/null @@ -1 +0,0 @@ -null ?? 5 diff --git a/vm_test/op_power.txt b/vm_test/op_power.txt deleted file mode 100644 index faa4f383..00000000 --- a/vm_test/op_power.txt +++ /dev/null @@ -1 +0,0 @@ -2 ** 3 diff --git a/vm_test/op_shift_left.txt b/vm_test/op_shift_left.txt deleted file mode 100644 index b96791ec..00000000 --- a/vm_test/op_shift_left.txt +++ /dev/null @@ -1 +0,0 @@ -2 << 3 diff --git a/vm_test/op_shift_right.txt b/vm_test/op_shift_right.txt deleted file mode 100644 index c5e98a1f..00000000 --- a/vm_test/op_shift_right.txt +++ /dev/null @@ -1 +0,0 @@ -8 >> 2 diff --git a/vm_test/op_shift_right_unsigned.txt b/vm_test/op_shift_right_unsigned.txt deleted file mode 100644 index 3032ae48..00000000 --- a/vm_test/op_shift_right_unsigned.txt +++ /dev/null @@ -1 +0,0 @@ --8 >>> 2 diff --git a/vm_test/op_ternary.txt b/vm_test/op_ternary.txt deleted file mode 100644 index 839ba219..00000000 --- a/vm_test/op_ternary.txt +++ /dev/null @@ -1 +0,0 @@ -true ? 1 : 2 diff --git a/vm_test/op_unary_minus.txt b/vm_test/op_unary_minus.txt deleted file mode 100644 index d83d2bc1..00000000 --- a/vm_test/op_unary_minus.txt +++ /dev/null @@ -1 +0,0 @@ --5 diff --git a/vm_test/op_unary_plus.txt b/vm_test/op_unary_plus.txt deleted file mode 100644 index 19977310..00000000 --- a/vm_test/op_unary_plus.txt +++ /dev/null @@ -1 +0,0 @@ -+"5" diff --git a/vm_test/optional_bracket.txt b/vm_test/optional_bracket.txt deleted file mode 100644 index e946f644..00000000 --- a/vm_test/optional_bracket.txt +++ /dev/null @@ -1 +0,0 @@ -var o = {a: 1}; o?.["a"] diff --git a/vm_test/optional_call.txt b/vm_test/optional_call.txt deleted file mode 100644 index 47dcf31b..00000000 --- a/vm_test/optional_call.txt +++ /dev/null @@ -1 +0,0 @@ -var o = {f: () => 1}; o.f?.() diff --git a/vm_test/optional_null.txt b/vm_test/optional_null.txt deleted file mode 100644 index bf3b5c38..00000000 --- a/vm_test/optional_null.txt +++ /dev/null @@ -1 +0,0 @@ -var o = null; o?.a diff --git a/vm_test/optional_prop.txt b/vm_test/optional_prop.txt deleted file mode 100644 index 2b00babb..00000000 --- a/vm_test/optional_prop.txt +++ /dev/null @@ -1 +0,0 @@ -var o = {a: 1}; o?.a diff --git a/vm_test/paren_precedence.txt b/vm_test/paren_precedence.txt deleted file mode 100644 index 5dfe5729..00000000 --- a/vm_test/paren_precedence.txt +++ /dev/null @@ -1 +0,0 @@ -(1 + 2) * 3 diff --git a/vm_test/record_bracket.txt b/vm_test/record_bracket.txt deleted file mode 100644 index 26cf0f30..00000000 --- a/vm_test/record_bracket.txt +++ /dev/null @@ -1 +0,0 @@ -var a = {x: 1}; a["x"] diff --git a/vm_test/record_chain.txt b/vm_test/record_chain.txt deleted file mode 100644 index 81b5f9fd..00000000 --- a/vm_test/record_chain.txt +++ /dev/null @@ -1 +0,0 @@ -var o = {a: {b: {c: 1}}}; o.a.b.c diff --git a/vm_test/record_computed.txt b/vm_test/record_computed.txt deleted file mode 100644 index 8e59125d..00000000 --- a/vm_test/record_computed.txt +++ /dev/null @@ -1 +0,0 @@ -var k = "x"; var a = {x: 1}; a[k] diff --git a/vm_test/record_dot.txt b/vm_test/record_dot.txt deleted file mode 100644 index 1834ce78..00000000 --- a/vm_test/record_dot.txt +++ /dev/null @@ -1 +0,0 @@ -var a = {x: 1}; a.x diff --git a/vm_test/record_nested.txt b/vm_test/record_nested.txt deleted file mode 100644 index 7369e5f4..00000000 --- a/vm_test/record_nested.txt +++ /dev/null @@ -1 +0,0 @@ -var o = {a: {b: 1}}; o.a.b diff --git a/vm_test/record_numeric_key.txt b/vm_test/record_numeric_key.txt deleted file mode 100644 index 742006ff..00000000 --- a/vm_test/record_numeric_key.txt +++ /dev/null @@ -1 +0,0 @@ -var o = {1: "one"}; o[1] diff --git a/vm_test/record_object_key.txt b/vm_test/record_object_key.txt deleted file mode 100644 index abc75df4..00000000 --- a/vm_test/record_object_key.txt +++ /dev/null @@ -1 +0,0 @@ -var k = {}; var a = {}; a[k] = 1; a[k] diff --git a/vm_test/record_this.txt b/vm_test/record_this.txt deleted file mode 100644 index ffca4701..00000000 --- a/vm_test/record_this.txt +++ /dev/null @@ -1 +0,0 @@ -var o = {v: 1, f: function() { return this.v }}; o.f() diff --git a/vm_test/regex_flags.txt b/vm_test/regex_flags.txt deleted file mode 100644 index 421bde4c..00000000 --- a/vm_test/regex_flags.txt +++ /dev/null @@ -1 +0,0 @@ -/hello/i diff --git a/vm_test/regex_literal.txt b/vm_test/regex_literal.txt deleted file mode 100644 index af6717c1..00000000 --- a/vm_test/regex_literal.txt +++ /dev/null @@ -1 +0,0 @@ -/hello/ diff --git a/vm_test/shorthand_method.txt b/vm_test/shorthand_method.txt deleted file mode 100644 index 3ce19ea6..00000000 --- a/vm_test/shorthand_method.txt +++ /dev/null @@ -1 +0,0 @@ -{double(x) { return x + x }} diff --git a/vm_test/shorthand_prop.txt b/vm_test/shorthand_prop.txt deleted file mode 100644 index f00fd35d..00000000 --- a/vm_test/shorthand_prop.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 1; var y = 2; {x, y} diff --git a/vm_test/string_escape.txt b/vm_test/string_escape.txt deleted file mode 100644 index 9839e480..00000000 --- a/vm_test/string_escape.txt +++ /dev/null @@ -1 +0,0 @@ -"hello\nworld" diff --git a/vm_test/string_unicode.txt b/vm_test/string_unicode.txt deleted file mode 100644 index 0d6d3bf7..00000000 --- a/vm_test/string_unicode.txt +++ /dev/null @@ -1 +0,0 @@ -"\u0041" diff --git a/vm_test/syntax.txt b/vm_test/syntax.txt deleted file mode 100644 index 3af3d06d..00000000 --- a/vm_test/syntax.txt +++ /dev/null @@ -1,336 +0,0 @@ -/* comprehensive syntax test - exercises all AST paths */ - -// --- variables and constants --- -var a -var b = 1 -var c = 2, d = 3 -def PI = 3.14159 -def MSG = "hello" - -// --- number literals --- -var n_int = 42 -var n_float = 3.14 -var n_hex = 0xFF -var n_octal = 0o77 -var n_binary = 0b1010 -var n_underscore = 1_000_000 -var n_exp = 1e10 -var n_exp_neg = 2.5e-3 - -// --- string literals --- -var s_single = 'single' -var s_double = "double" -var s_escape = "tab\there\nnewline\\slash\"quote" -var s_unicode = "\u0041\u0042" - -// --- template literals --- -var t_basic = `hello ${b} world` -var t_nested = `a ${b + c} b ${d}` -var t_expr = `result: ${b > 0 ? "yes" : "no"}` - -// --- array literals --- -var arr_empty = [] -var arr_simple = [1, 2, 3] -var arr_mixed = [1, "two", true, null] -var arr_nested = [[1, 2], [3, [4, 5]]] - -// --- record/object literals --- -var rec_empty = {} -var rec_simple = {x: 1, y: 2} -var rec_nested = {a: {b: {c: 3}}} -var shorthand_var = 10 -var rec_shorthand = {shorthand_var} -var rec_computed = {["key" + "1"]: 100} -var rec_method = { - greet() { return "hi" } -} -var rec_mixed = { - name: "test", - value: 42, - nested: {inner: true}, - items: [1, 2, 3] -} - -// --- all binary arithmetic operators --- -var arith_add = 1 + 2 -var arith_sub = 5 - 3 -var arith_mul = 4 * 5 -var arith_div = 10 / 3 -var arith_mod = 10 % 3 -var arith_pow = 2 ** 8 - -// --- comparison operators --- -var cmp_lt = 1 < 2 -var cmp_gt = 2 > 1 -var cmp_lte = 1 <= 1 -var cmp_gte = 2 >= 2 -var cmp_eq = 1 == 1 -var cmp_neq = 1 != 2 - -// --- logical operators --- -var log_and = true && false -var log_or = false || true -var log_not = !false - -// --- bitwise operators --- -var bit_and = 0xFF & 0x0F -var bit_or = 0xF0 | 0x0F -var bit_xor = 0xFF ^ 0x0F -var bit_not = ~0 -var bit_shl = 1 << 8 -var bit_shr = 256 >> 4 -var bit_shru = -1 >>> 0 - -// --- nullish coalescing --- -var nullish = null ?? "default" - -// --- ternary operator --- -var tern = b > 0 ? "pos" : "neg" -var tern_nested = b > 0 ? (b > 10 ? "big" : "small") : "neg" - -// --- comma operator --- -var comma_result = (1, 2, 3) - -// --- unary operators --- -var unary_pos = +b -var unary_neg = -b - -// --- increment/decrement --- -var inc_val = 0 -inc_val++ -inc_val-- -;++inc_val -;--inc_val - -// --- all compound assignments --- -var ca = 10 -ca += 5 -ca -= 3 -ca *= 2 -ca /= 4 -ca %= 3 -ca **= 2 -ca <<= 1 -ca >>= 1 -ca >>>= 0 -ca &= 0xFF -ca |= 0x01 -ca ^= 0x10 -ca &&= true -ca ||= false -ca ??= 1 - -// --- chained assignment --- -var ch1, ch2 -ch1 = ch2 = 42 - -// --- property access --- -var obj = {a: {b: {c: 1}}, items: [10, 20, 30]} -var dot_access = obj.a -var dot_chain = obj.a.b.c -var bracket_access = obj["a"] -var bracket_dynamic = obj["it" + "ems"] -var bracket_index = obj.items[1] - -// --- optional chaining --- -var opt_obj = {x: {y: 1}} -var opt_prop = opt_obj?.x -var opt_deep = opt_obj?.x?.y -var opt_null = null?.foo -var opt_bracket = opt_obj?.["x"] -var opt_call_obj = {f: function() { return 1 }} -var opt_call = opt_call_obj?.f?.() - -// --- function declarations --- -function add(a, b) { - return a + b -} - -function no_params() { - return 42 -} - -function with_default(x = 10, y = 20) { - return x + y -} - -// --- function expressions --- -var func_expr = function(x) { return x * 2 } -var func_named = function multiply(x, y) { return x * y } - -// --- arrow functions (all forms) --- -var arrow_no_param = () => 0 -var arrow_one = x => x + 1 -var arrow_multi = (a, b) => a + b -var arrow_block = (a, b) => { return a + b } -var arrow_default = (x = 1, y = 2) => x + y -var arrow_default_expr = (x = 1 + 2, y = arr_simple[0]) => x + y - -// --- closures --- -function make_counter() { - var count = 0 - return function() { - count += 1 - return count - } -} - -function outer_fn() { - var x = 10 - function middle() { - var y = 20 - function inner() { - return x + y - } - return inner() - } - return middle() -} - -// --- this in methods --- -var counter = { - val: 0, - inc() { this.val += 1 }, - get() { return this.val } -} - -// --- if/else/else-if --- -var if_result -if (b > 0) { - if_result = "positive" -} else if (b == 0) { - if_result = "zero" -} else { - if_result = "negative" -} - -// --- while loop --- -var w = 0 -while (w < 5) { - w += 1 -} - -// --- do-while loop --- -var dw = 0 -do { - dw += 1 -} while (dw < 3) - -// --- for loop --- -var f_sum = 0 -for (var i = 0; i < 10; i++) { - f_sum += i -} - -// --- for loop with break --- -var fb = 0 -for (var j = 0; j < 100; j++) { - if (j == 5) break - fb = j -} - -// --- for loop with continue --- -var fc = 0 -for (var k = 0; k < 10; k++) { - if (k % 2 == 0) continue - fc += k -} - -// --- labeled break --- -outer: for (var x = 0; x < 3; x++) { - for (var y = 0; y < 3; y++) { - if (y == 1) break outer - } -} - -// --- labeled continue --- -var lc = 0 -loop: for (var m = 0; m < 3; m++) { - for (var n = 0; n < 3; n++) { - if (n == 1) continue loop - lc += 1 - } -} - -// --- disrupt and disruption --- -function disrupt_test() { - disrupt -} - -function disruption_test() { - var x = 1 -} disruption { - var y = 2 -} - -function disrupt_with_disruption() { - disrupt -} disruption { - var handled = true -} - -// --- delete operator --- -var del_obj = {a: 1, b: 2} -delete del_obj.a - -// --- in operator --- -var in_result = "b" in del_obj - -// --- go statement --- -function async_task() { return 1 } -function caller() { go async_task() } - -// --- IIFE --- -var iife = (function() { return 99 })() - -// --- recursive function --- -function factorial(n) { - if (n <= 1) return 1 - return n * factorial(n - 1) -} - -// --- mutually recursive functions --- -function is_even(n) { - if (n == 0) return true - return is_odd(n - 1) -} -function is_odd(n) { - if (n == 0) return false - return is_even(n - 1) -} - -// --- block scoping --- -var block_val = 1 -{ - var block_val = 2 -} - -// --- nested blocks --- -{ - var nb = 1 - { - var nb2 = nb + 1 - { - var nb3 = nb2 + 1 - } - } -} - -// --- empty statement --- -; - -// --- parenthesized expression for precedence --- -var prec = (1 + 2) * (3 + 4) - -// --- complex expressions --- -var complex = arr_simple[0] + rec_simple.x * (b > 0 ? 2 : 1) - -// --- operator precedence chain --- -var prec_chain = 1 + 2 * 3 ** 2 - 4 / 2 % 3 - -// --- regex literals --- -var re = /hello/ -var re_flags = /world/gi - -// --- line comment at end --- -var end = 1 // done diff --git a/vm_test/template_basic.txt b/vm_test/template_basic.txt deleted file mode 100644 index db7bd6e8..00000000 --- a/vm_test/template_basic.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; `value: ${x}` diff --git a/vm_test/var_basic.txt b/vm_test/var_basic.txt deleted file mode 100644 index 829c02b3..00000000 --- a/vm_test/var_basic.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x diff --git a/vm_test/var_block.txt b/vm_test/var_block.txt deleted file mode 100644 index 2d8ca4e1..00000000 --- a/vm_test/var_block.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 1; { var x = 2 }; x diff --git a/vm_test/var_reassign.txt b/vm_test/var_reassign.txt deleted file mode 100644 index 8cd59db0..00000000 --- a/vm_test/var_reassign.txt +++ /dev/null @@ -1 +0,0 @@ -var x = 5; x = 10; x diff --git a/vm_test/while_basic.txt b/vm_test/while_basic.txt deleted file mode 100644 index 5f89c0e7..00000000 --- a/vm_test/while_basic.txt +++ /dev/null @@ -1 +0,0 @@ -var i = 0; while (i < 3) i = i + 1; i diff --git a/vm_test/while_break.txt b/vm_test/while_break.txt deleted file mode 100644 index caef4168..00000000 --- a/vm_test/while_break.txt +++ /dev/null @@ -1 +0,0 @@ -var i = 0; while (i < 10) { i = i + 1; if (i == 5) break }; i diff --git a/vm_test/while_continue.txt b/vm_test/while_continue.txt deleted file mode 100644 index 5c7c4ac5..00000000 --- a/vm_test/while_continue.txt +++ /dev/null @@ -1 +0,0 @@ -var s = 0; var i = 0; while (i < 5) { i = i + 1; if (i == 3) continue; s = s + i }; s