11 Commits

Author SHA1 Message Date
John Alanbrook
b771b2b5d8 suite passes now with mcode->mach lowering 2026-02-12 09:40:24 -06:00
John Alanbrook
68fb440502 Merge branch 'mach' into bytecode_cleanup 2026-02-12 07:50:09 -06:00
John Alanbrook
e7a2f16004 mcode to mach 2026-02-12 05:23:33 -06:00
John Alanbrook
3a8a17ab60 mcode->mach 2026-02-12 04:28:14 -06:00
John Alanbrook
8a84be65e1 new path 2026-02-11 14:41:37 -06:00
John Alanbrook
c1910ee1db Merge branch 'mcode2' into mach 2026-02-11 13:16:07 -06:00
John Alanbrook
7036cdf2d1 Merge branch 'mach' into bytecode_cleanup 2026-02-11 13:15:20 -06:00
John Alanbrook
fbeec17ce5 simplifications 2026-02-11 13:15:04 -06:00
John Alanbrook
2c55ae8cb2 quiesence exit 2026-02-11 11:50:29 -06:00
John Alanbrook
259bc139fc rm stack usage 2026-02-11 10:17:55 -06:00
John Alanbrook
a252412eca removal of old code 2026-02-11 09:47:30 -06:00
47 changed files with 2548 additions and 5949 deletions

View File

@@ -319,7 +319,7 @@ JSValue js_reader_list(JSContext *js, JSValue self, int argc, JSValue *argv)
JS_FreeValue(js, arr); JS_FreeValue(js, arr);
return filename; return filename;
} }
JS_SetPropertyUint32(js, arr, arr_index++, filename); JS_SetPropertyNumber(js, arr, arr_index++, filename);
} }
return arr; return arr;

View File

@@ -4,7 +4,7 @@
JSC_CCALL(debug_stack_depth, return number2js(js,js_debugger_stack_depth(js))) JSC_CCALL(debug_stack_depth, return number2js(js,js_debugger_stack_depth(js)))
// Return a backtrace of the current call stack. // Return a backtrace of the current call stack.
JSC_CCALL(debug_build_backtrace, return js_debugger_build_backtrace(js,NULL)) JSC_CCALL(debug_build_backtrace, return js_debugger_build_backtrace(js))
// Return the closure variables for a given function. // Return the closure variables for a given function.
JSC_CCALL(debug_closure_vars, return js_debugger_closure_variables(js,argv[0])) JSC_CCALL(debug_closure_vars, return js_debugger_closure_variables(js,argv[0]))
@@ -21,7 +21,7 @@ JSC_CCALL(debug_local_vars, return js_debugger_local_variables(js, js2number(js,
JSC_CCALL(debug_fn_info, return js_debugger_fn_info(js, argv[0])) JSC_CCALL(debug_fn_info, return js_debugger_fn_info(js, argv[0]))
// Return an array of functions in the current backtrace. // Return an array of functions in the current backtrace.
JSC_CCALL(debug_backtrace_fns, return js_debugger_backtrace_fns(js,NULL)) JSC_CCALL(debug_backtrace_fns, return js_debugger_backtrace_fns(js))
static const JSCFunctionListEntry js_debug_funcs[] = { static const JSCFunctionListEntry js_debug_funcs[] = {
MIST_FUNC_DEF(debug, stack_depth, 0), MIST_FUNC_DEF(debug, stack_depth, 0),

View File

@@ -154,3 +154,12 @@ struct JSCodeRegister {
``` ```
The constant pool holds all non-immediate values referenced by `LOADK` instructions: strings, large numbers, and other constants. The constant pool holds all non-immediate values referenced by `LOADK` instructions: strings, large numbers, and other constants.
### Constant Pool Index Overflow
Named property instructions (`LOAD_FIELD`, `STORE_FIELD`, `DELETE`) use the iABC format where the constant pool key index occupies an 8-bit field (max 255). When a function references more than 256 unique property names, the serializer automatically falls back to a two-instruction sequence:
1. `LOADK tmp, key_index` — load the key string into a temporary register (iABx, 16-bit index)
2. `LOAD_DYNAMIC` / `STORE_DYNAMIC` / `DELETEINDEX` — use the register-based variant
This is transparent to the mcode compiler and streamline optimizer.

View File

@@ -10,11 +10,44 @@ Mcode is a JSON-based intermediate representation that can be interpreted direct
## Pipeline ## Pipeline
``` ```
Source → Tokenize → Parse (AST) → Fold → Mcode (JSON) → Streamline → Interpret Source → Tokenize → Parse (AST) → Fold → Mcode (JSON) → Streamline → Mach VM (default)
→ Mcode Interpreter
→ QBE → Native → QBE → Native
``` ```
Mcode is produced by `mcode.cm`, which lowers the folded AST to JSON instruction arrays. The streamline optimizer (`streamline.cm`) then eliminates redundant operations. The result can be interpreted by `mcode.c`, or lowered to QBE IL by `qbe_emit.cm` for native compilation. See [Compilation Pipeline](pipeline.md) for the full overview. Mcode is produced by `mcode.cm`, which lowers the folded AST to JSON instruction arrays. The streamline optimizer (`streamline.cm`) then eliminates redundant operations. The result is serialized to binary bytecode by the Mach compiler (`mach.c`), interpreted directly by `mcode.c`, or lowered to QBE IL by `qbe_emit.cm` for native compilation. See [Compilation Pipeline](pipeline.md) for the full overview.
### Function Proxy Decomposition
When the compiler encounters a method call `obj.method(args)`, it emits a branching pattern to handle ƿit's function proxy protocol. An arity-2 function used as a proxy target receives the method name and argument array instead of a normal method call:
```json
["is_proxy", check, obj]
["jump_false", check, "record_path"]
// Proxy path: call obj(name, [args...]) with this=null
["access", name_slot, "method"]
["array", args_arr, N, arg0, arg1, ...]
["null", null_slot]
["frame", f, obj, 2]
["setarg", f, 0, null_slot]
["setarg", f, 1, name_slot]
["setarg", f, 2, args_arr]
["invoke", f, dest]
["jump", "done"]
["LABEL", "record_path"]
["load_field", method, obj, "method"]
["frame", f2, method, N]
["setarg", f2, 0, obj]
["setarg", f2, 1, arg0]
...
["invoke", f2, dest]
["LABEL", "done"]
```
The streamline optimizer can eliminate the dead branch when the type of `obj` is statically known.
## JSMCode Structure ## JSMCode Structure

View File

@@ -5,11 +5,11 @@ description: "Overview of the compilation stages and optimizations"
## Overview ## Overview
The compilation pipeline transforms source code through several stages, each adding information or lowering the representation toward execution. There are three execution backends: the Mach register VM (default), the Mcode interpreter (debug), and native code via QBE (experimental). The compilation pipeline transforms source code through several stages, each adding information or lowering the representation toward execution. All backends share the same path through mcode and streamline. There are three execution backends: the Mach register VM (default), the Mcode interpreter (debug), and native code via QBE (experimental).
``` ```
Source → Tokenize → Parse → Fold → Mach VM (default) Source → Tokenize → Parse → Fold → Mcode → Streamline → Mach VM (default)
→ Mcode → Streamline → Mcode Interpreter → Mcode Interpreter
→ QBE → Native → QBE → Native
``` ```
@@ -72,12 +72,18 @@ Provides operation implementations as QBE IL templates. Each arithmetic, compari
### Mach VM (default) ### Mach VM (default)
Binary 32-bit register VM. Used for production execution and bootstrapping. Binary 32-bit register VM. The Mach serializer (`mach.c`) converts streamlined mcode JSON into compact 32-bit bytecode with a constant pool. Used for production execution and bootstrapping.
``` ```
./cell script.ce ./cell script.ce
``` ```
Debug the mach bytecode output:
```
./cell --core . --dump-mach script.ce
```
### Mcode Interpreter ### Mcode Interpreter
JSON-based interpreter. Used for debugging the compilation pipeline. JSON-based interpreter. Used for debugging the compilation pipeline.

20
dump_mcode.cm Normal file
View File

@@ -0,0 +1,20 @@
var fd = use("fd")
var json = use("json")
var tokenize = use("tokenize")
var parse = use("parse")
var fold = use("fold")
var mcode = use("mcode")
var streamline = use("streamline")
var name = args[0]
var src = text(fd.slurp(name))
var tok = tokenize(src, name)
var ast = parse(tok.tokens, src, name, tokenize)
var folded = fold(ast)
var compiled = mcode(folded)
var optimized = streamline(compiled)
var out = json.encode(optimized)
var f = fd.open("/tmp/mcode_dump.json", "w")
fd.write(f, out)
fd.close(f)
print("wrote /tmp/mcode_dump.json")

4
fd.c
View File

@@ -602,7 +602,7 @@ static void visit_directory(JSContext *js, JSValue results, int *result_count, c
} else { } else {
strcpy(item_rel, ffd.cFileName); strcpy(item_rel, ffd.cFileName);
} }
JS_SetPropertyUint32(js, results, (*result_count)++, JS_NewString(js, item_rel)); JS_SetPropertyNumber(js, results, (*result_count)++, JS_NewString(js, item_rel));
if (recurse) { if (recurse) {
struct stat st; struct stat st;
@@ -627,7 +627,7 @@ static void visit_directory(JSContext *js, JSValue results, int *result_count, c
} else { } else {
strcpy(item_rel, dir->d_name); strcpy(item_rel, dir->d_name);
} }
JS_SetPropertyUint32(js, results, (*result_count)++, JS_NewString(js, item_rel)); JS_SetPropertyNumber(js, results, (*result_count)++, JS_NewString(js, item_rel));
if (recurse) { if (recurse) {
struct stat st; struct stat st;

View File

@@ -324,7 +324,7 @@ static void listfiles_cb(const char *path, void *userdata) {
// Playdate listfiles returns just the name, but sometimes with slash for dir? // Playdate listfiles returns just the name, but sometimes with slash for dir?
// Docs say "names of files". // Docs say "names of files".
JS_SetPropertyUint32(ctx->js, ctx->array, ctx->index++, JS_NewString(ctx->js, path)); JS_SetPropertyNumber(ctx->js, ctx->array, ctx->index++, JS_NewString(ctx->js, path));
} }
JSC_SCALL(fd_readdir, JSC_SCALL(fd_readdir,
@@ -427,7 +427,7 @@ static void enum_cb(const char *name, void *userdata) {
strcpy(item_rel, name); strcpy(item_rel, name);
} }
JS_SetPropertyUint32(ctx->js, ctx->results, (*ctx->count)++, JS_NewString(ctx->js, item_rel)); JS_SetPropertyNumber(ctx->js, ctx->results, (*ctx->count)++, JS_NewString(ctx->js, item_rel));
if (ctx->recurse) { if (ctx->recurse) {
// Check if directory // Check if directory

BIN
fold.mach

Binary file not shown.

BIN
fold_new.mach Normal file

Binary file not shown.

View File

@@ -1,5 +1,5 @@
// Hidden vars come from env: // Hidden vars come from env:
// CLI mode (cell_init): os, args, core_path, shop_path, use_mcode // CLI mode (cell_init): os, args, core_path, shop_path, emit_qbe, dump_mach
// Actor spawn (script_startup): os, json, nota, wota, actorsym, init, core_path, shop_path // Actor spawn (script_startup): os, json, nota, wota, actorsym, init, core_path, shop_path
// args[0] = script name, args[1..] = user args // args[0] = script name, args[1..] = user args
var load_internal = os.load_internal var load_internal = os.load_internal
@@ -24,7 +24,7 @@ function use_basic(path) {
return result return result
} }
// Load a module from .mach bytecode, falling back to .ast.json // Load a module from .mach bytecode (bootstrap modules have no source fallback)
function boot_load(name, env) { function boot_load(name, env) {
var mach_path = core_path + '/' + name + ".mach" var mach_path = core_path + '/' + name + ".mach"
var data = null var data = null
@@ -32,9 +32,8 @@ function boot_load(name, env) {
data = fd.slurp(mach_path) data = fd.slurp(mach_path)
return mach_load(data, env) return mach_load(data, env)
} }
var ast_path = core_path + '/' + name + ".ast.json" print("error: missing bootstrap bytecode: " + mach_path + "\n")
data = text(fd.slurp(ast_path)) disrupt
return mach_eval_ast(name, data, env)
} }
var boot_env = {use: use_basic} var boot_env = {use: use_basic}
@@ -45,14 +44,11 @@ use_cache['tokenize'] = tokenize_mod
use_cache['parse'] = parse_mod use_cache['parse'] = parse_mod
use_cache['fold'] = fold_mod use_cache['fold'] = fold_mod
// Optionally load mcode compiler module // Always load mcode compiler module
var mcode_mod = null var mcode_mod = boot_load("mcode", boot_env)
use_cache['mcode'] = mcode_mod
var streamline_mod = null var streamline_mod = null
var qbe_emit_mod = null var qbe_emit_mod = null
if (use_mcode) {
mcode_mod = boot_load("mcode", boot_env)
use_cache['mcode'] = mcode_mod
}
// Warn if any .cm source is newer than its .mach bytecode // Warn if any .cm source is newer than its .mach bytecode
function check_mach_stale() { function check_mach_stale() {
@@ -134,6 +130,8 @@ function load_module(name, env) {
var src_path = null var src_path = null
var src = null var src = null
var ast = null var ast = null
var compiled = null
var optimized = null
if (fd.is_file(mach_path)) { if (fd.is_file(mach_path)) {
data = fd.slurp(mach_path) data = fd.slurp(mach_path)
return mach_load(data, env) return mach_load(data, env)
@@ -141,12 +139,13 @@ function load_module(name, env) {
src_path = core_path + '/' + name + ".cm" src_path = core_path + '/' + name + ".cm"
src = text(fd.slurp(src_path)) src = text(fd.slurp(src_path))
ast = analyze(src, src_path) ast = analyze(src, src_path)
return mach_eval_ast(name, json.encode(ast), env) compiled = mcode_mod(ast)
optimized = streamline_mod(compiled)
return mach_eval_mcode(name, json.encode(optimized), env)
} }
// Load optimization pipeline modules (needs analyze to be defined) // Load optimization pipeline modules (needs analyze to be defined)
var qbe_macros = null var qbe_macros = null
if (use_mcode) {
streamline_mod = load_module("streamline", boot_env) streamline_mod = load_module("streamline", boot_env)
use_cache['streamline'] = streamline_mod use_cache['streamline'] = streamline_mod
if (emit_qbe) { if (emit_qbe) {
@@ -155,24 +154,22 @@ if (use_mcode) {
use_cache['qbe'] = qbe_macros use_cache['qbe'] = qbe_macros
use_cache['qbe_emit'] = qbe_emit_mod use_cache['qbe_emit'] = qbe_emit_mod
} }
}
// Run AST through either mcode or mach pipeline // Run AST through mcode pipeline → register VM
function run_ast(name, ast, env) { function run_ast(name, ast, env) {
var compiled = null var compiled = mcode_mod(ast)
var optimized = null var optimized = streamline_mod(compiled)
var qbe_il = null var qbe_il = null
if (use_mcode) {
compiled = mcode_mod(ast)
optimized = streamline_mod(compiled)
if (emit_qbe) { if (emit_qbe) {
qbe_il = qbe_emit_mod(optimized, qbe_macros) qbe_il = qbe_emit_mod(optimized, qbe_macros)
print(qbe_il) print(qbe_il)
return null return null
} }
return mcode_run(name, json.encode(optimized), env) if (dump_mach) {
mach_dump_mcode(name, json.encode(optimized), env)
return null
} }
return mach_eval_ast(name, json.encode(ast), env) return mach_eval_mcode(name, json.encode(optimized), env)
} }
// use() with ƿit pipeline for .cm modules // use() with ƿit pipeline for .cm modules

Binary file not shown.

Binary file not shown.

View File

@@ -423,28 +423,46 @@ function inject_env(inject) {
return env return env
} }
// Lazy-loaded compiler modules for on-the-fly compilation
var _mcode_mod = null
var _streamline_mod = null
// Compile a module and return its bytecode blob. // Compile a module and return its bytecode blob.
// The bytecode is cached on disk by content hash. // The bytecode is cached on disk by content hash.
function resolve_mod_fn(path, pkg) { function resolve_mod_fn(path, pkg) {
if (!fd.is_file(path)) { print(`path ${path} is not a file`); disrupt } if (!fd.is_file(path)) { print(`path ${path} is not a file`); disrupt }
var content = text(fd.slurp(path)) var content = text(fd.slurp(path))
// Check cache for pre-compiled .mach blob
var cached = pull_from_cache(stone(blob(content))) var cached = pull_from_cache(stone(blob(content)))
var ast = null var ast = null
var ast_json = null
var compiled = null var compiled = null
var mach_path = null
var mach_blob = null
var ir = null
var optimized = null
// Check cache for pre-compiled .mach blob
if (cached) { if (cached) {
return cached return cached
} }
// Compile via new pipeline // Check for pre-compiled .mach file alongside .cm source
ast = analyze(content, path) if (ends_with(path, '.cm')) {
ast_json = shop_json.encode(ast) mach_path = text(path, 0, length(path) - 3) + '.mach'
if (fd.is_file(mach_path)) {
mach_blob = fd.slurp(mach_path)
put_into_cache(stone(blob(content)), mach_blob)
return mach_blob
}
}
// Cache compiled binary // Compile via full pipeline: analyze → mcode → streamline → serialize
compiled = mach_compile_ast(path, ast_json) if (!_mcode_mod) _mcode_mod = Shop.use("mcode", null)
if (!_streamline_mod) _streamline_mod = Shop.use("streamline", null)
ast = analyze(content, path)
ir = _mcode_mod(ast)
optimized = _streamline_mod(ir)
compiled = mach_compile_mcode_bin(path, shop_json.encode(optimized))
put_into_cache(stone(blob(content)), compiled) put_into_cache(stone(blob(content)), compiled)
return compiled return compiled

122
mcode.cm
View File

@@ -43,6 +43,8 @@ var mcode = function(ast) {
var s_func_counter = 0 var s_func_counter = 0
var s_loop_break = null var s_loop_break = null
var s_loop_continue = null var s_loop_continue = null
var s_label_map = {}
var s_pending_label = null
var s_is_arrow = false var s_is_arrow = false
var s_function_nr = 0 var s_function_nr = 0
var s_scopes = null var s_scopes = null
@@ -71,6 +73,7 @@ var mcode = function(ast) {
max_slot: s_max_slot, max_slot: s_max_slot,
loop_break: s_loop_break, loop_break: s_loop_break,
loop_continue: s_loop_continue, loop_continue: s_loop_continue,
label_map: s_label_map,
is_arrow: s_is_arrow, is_arrow: s_is_arrow,
function_nr: s_function_nr, function_nr: s_function_nr,
intrinsic_cache: s_intrinsic_cache, intrinsic_cache: s_intrinsic_cache,
@@ -90,6 +93,7 @@ var mcode = function(ast) {
s_max_slot = saved.max_slot s_max_slot = saved.max_slot
s_loop_break = saved.loop_break s_loop_break = saved.loop_break
s_loop_continue = saved.loop_continue s_loop_continue = saved.loop_continue
s_label_map = saved.label_map
s_is_arrow = saved.is_arrow s_is_arrow = saved.is_arrow
s_function_nr = saved.function_nr s_function_nr = saved.function_nr
s_intrinsic_cache = saved.intrinsic_cache s_intrinsic_cache = saved.intrinsic_cache
@@ -761,37 +765,114 @@ var mcode = function(ast) {
} }
var emit_call_method = function(dest, obj, prop, args) { var emit_call_method = function(dest, obj, prop, args) {
var argc = length(args)
var check = alloc_slot()
var record_path = gen_label("record_path")
var done_label = gen_label("call_done")
var _i = 0
var arg_idx = 0
// Check if obj is a proxy function (arity 2)
emit_2("is_proxy", check, obj)
emit_jump_cond("jump_false", check, record_path)
// Function proxy path: call obj(prop_name, [args...]) with this=null
var null_slot = alloc_slot()
emit_const_null(null_slot)
var name_str = alloc_slot()
emit_const_str(name_str, prop)
var args_arr = alloc_slot()
var arr_instr = ["array", args_arr, argc]
_i = 0
while (_i < argc) {
push(arr_instr, args[_i])
_i = _i + 1
}
add_instr(arr_instr)
var pf = alloc_slot()
emit_3("frame", pf, obj, 2)
emit_3("setarg", pf, 0, null_slot)
emit_3("setarg", pf, 1, name_str)
emit_3("setarg", pf, 2, args_arr)
emit_2("invoke", pf, dest)
emit_jump(done_label)
// Record path: load method, call with this=obj
emit_label(record_path)
var method_slot = alloc_slot() var method_slot = alloc_slot()
add_instr(["load_field", method_slot, obj, prop]) add_instr(["load_field", method_slot, obj, prop])
var argc = length(args)
var frame_slot = alloc_slot() var frame_slot = alloc_slot()
emit_3("frame", frame_slot, method_slot, argc) emit_3("frame", frame_slot, method_slot, argc)
emit_3("setarg", frame_slot, 0, obj) emit_3("setarg", frame_slot, 0, obj)
var arg_idx = 1 arg_idx = 1
var _i = 0 _i = 0
while (_i < argc) { while (_i < argc) {
emit_3("setarg", frame_slot, arg_idx, args[_i]) emit_3("setarg", frame_slot, arg_idx, args[_i])
arg_idx = arg_idx + 1 arg_idx = arg_idx + 1
_i = _i + 1 _i = _i + 1
} }
emit_2("invoke", frame_slot, dest) emit_2("invoke", frame_slot, dest)
emit_label(done_label)
} }
var emit_call_method_dyn = function(dest, obj, key_reg, args) { var emit_call_method_dyn = function(dest, obj, key_reg, args) {
var argc = length(args)
var check = alloc_slot()
var record_path = gen_label("dyn_record_path")
var done_label = gen_label("dyn_call_done")
var _i = 0
var arg_idx = 0
// Check if obj is a proxy function (arity 2)
emit_2("is_proxy", check, obj)
emit_jump_cond("jump_false", check, record_path)
// Function proxy path (dynamic key): must be text
var key_ok = alloc_slot()
var error_path = gen_label("dyn_error")
emit_2("is_text", key_ok, key_reg)
emit_jump_cond("jump_false", key_ok, error_path)
var null_slot = alloc_slot()
emit_const_null(null_slot)
var args_arr = alloc_slot()
var arr_instr = ["array", args_arr, argc]
_i = 0
while (_i < argc) {
push(arr_instr, args[_i])
_i = _i + 1
}
add_instr(arr_instr)
var pf = alloc_slot()
emit_3("frame", pf, obj, 2)
emit_3("setarg", pf, 0, null_slot)
emit_3("setarg", pf, 1, key_reg)
emit_3("setarg", pf, 2, args_arr)
emit_2("invoke", pf, dest)
emit_jump(done_label)
// Error path: non-text key on function disrupts
emit_label(error_path)
emit_0("disrupt")
emit_jump(done_label)
// Record path: load method dynamically, call with this=obj
emit_label(record_path)
var method_slot = alloc_slot() var method_slot = alloc_slot()
emit_3("load_dynamic", method_slot, obj, key_reg) emit_3("load_dynamic", method_slot, obj, key_reg)
var argc = length(args)
var frame_slot = alloc_slot() var frame_slot = alloc_slot()
emit_3("frame", frame_slot, method_slot, argc) emit_3("frame", frame_slot, method_slot, argc)
emit_3("setarg", frame_slot, 0, obj) emit_3("setarg", frame_slot, 0, obj)
var arg_idx = 1 arg_idx = 1
var _i = 0 _i = 0
while (_i < argc) { while (_i < argc) {
emit_3("setarg", frame_slot, arg_idx, args[_i]) emit_3("setarg", frame_slot, arg_idx, args[_i])
arg_idx = arg_idx + 1 arg_idx = arg_idx + 1
_i = _i + 1 _i = _i + 1
} }
emit_2("invoke", frame_slot, dest) emit_2("invoke", frame_slot, dest)
emit_label(done_label)
} }
var emit_go_call = function(func_slot, args) { var emit_go_call = function(func_slot, args) {
@@ -1813,6 +1894,13 @@ var mcode = function(ast) {
return null return null
} }
if (kind == "label") {
s_pending_label = stmt.name
gen_statement(stmt.statement)
s_pending_label = null
return null
}
if (kind == "while") { if (kind == "while") {
cond = stmt.expression cond = stmt.expression
stmts = stmt.statements stmts = stmt.statements
@@ -1822,6 +1910,10 @@ var mcode = function(ast) {
old_continue = s_loop_continue old_continue = s_loop_continue
s_loop_break = end_label s_loop_break = end_label
s_loop_continue = start_label s_loop_continue = start_label
if (s_pending_label != null) {
s_label_map[s_pending_label] = {break_target: end_label, continue_target: start_label}
s_pending_label = null
}
emit_label(start_label) emit_label(start_label)
cond_slot = gen_expr(cond, -1) cond_slot = gen_expr(cond, -1)
emit_jump_cond("jump_false", cond_slot, end_label) emit_jump_cond("jump_false", cond_slot, end_label)
@@ -1847,6 +1939,10 @@ var mcode = function(ast) {
old_continue = s_loop_continue old_continue = s_loop_continue
s_loop_break = end_label s_loop_break = end_label
s_loop_continue = cond_label s_loop_continue = cond_label
if (s_pending_label != null) {
s_label_map[s_pending_label] = {break_target: end_label, continue_target: cond_label}
s_pending_label = null
}
emit_label(start_label) emit_label(start_label)
_i = 0 _i = 0
while (_i < length(stmts)) { while (_i < length(stmts)) {
@@ -1874,6 +1970,10 @@ var mcode = function(ast) {
old_continue = s_loop_continue old_continue = s_loop_continue
s_loop_break = end_label s_loop_break = end_label
s_loop_continue = update_label s_loop_continue = update_label
if (s_pending_label != null) {
s_label_map[s_pending_label] = {break_target: end_label, continue_target: update_label}
s_pending_label = null
}
if (init != null) { if (init != null) {
init_kind = init.kind init_kind = init.kind
if (init_kind == "var" || init_kind == "def") { if (init_kind == "var" || init_kind == "def") {
@@ -1949,14 +2049,18 @@ var mcode = function(ast) {
} }
if (kind == "break") { if (kind == "break") {
if (s_loop_break != null) { if (stmt.name != null && s_label_map[stmt.name] != null) {
emit_jump(s_label_map[stmt.name].break_target)
} else if (s_loop_break != null) {
emit_jump(s_loop_break) emit_jump(s_loop_break)
} }
return null return null
} }
if (kind == "continue") { if (kind == "continue") {
if (s_loop_continue != null) { if (stmt.name != null && s_label_map[stmt.name] != null) {
emit_jump(s_label_map[stmt.name].continue_target)
} else if (s_loop_continue != null) {
emit_jump(s_loop_continue) emit_jump(s_loop_continue)
} }
return null return null
@@ -2082,6 +2186,7 @@ var mcode = function(ast) {
s_intrinsic_cache = [] s_intrinsic_cache = []
s_loop_break = null s_loop_break = null
s_loop_continue = null s_loop_continue = null
s_label_map = {}
s_is_arrow = is_arrow s_is_arrow = is_arrow
@@ -2275,6 +2380,7 @@ var mcode = function(ast) {
s_func_counter = 0 s_func_counter = 0
s_loop_break = null s_loop_break = null
s_loop_continue = null s_loop_continue = null
s_label_map = {}
s_function_nr = 0 s_function_nr = 0
// Scan scope // Scan scope

Binary file not shown.

View File

@@ -46,7 +46,6 @@ src += [ # core
'miniz.c', 'miniz.c',
'runtime.c', 'runtime.c',
'mach.c', 'mach.c',
'mcode.c',
'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c' 'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c'
] ]

View File

@@ -14,11 +14,11 @@ static void js_enet_host_finalizer(JSRuntime *rt, JSValue val)
if (host) enet_host_destroy(host); if (host) enet_host_destroy(host);
} }
static void js_enet_peer_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) //static void js_enet_peer_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
{ //{
ENetPeer *peer = JS_GetOpaque(val, enet_peer_class_id); // ENetPeer *peer = JS_GetOpaque(val, enet_peer_class_id);
JS_MarkValue(rt, *(JSValue*)peer->data, mark_func); // JS_MarkValue(rt, *(JSValue*)peer->data, mark_func);
} //}
static void js_enet_peer_finalizer(JSRuntime *rt, JSValue val) static void js_enet_peer_finalizer(JSRuntime *rt, JSValue val)
{ {
@@ -62,7 +62,7 @@ static JSValue js_enet_host_create(JSContext *ctx, JSValueConst this_val, int ar
enet_uint32 outgoing_bandwidth = 0; enet_uint32 outgoing_bandwidth = 0;
JSValue obj; JSValue obj;
if (argc < 1 || !JS_IsObject(argv[0])) { if (argc < 1 || !JS_IsRecord(argv[0])) {
host = enet_host_create(NULL, peer_count, channel_limit, incoming_bandwidth, outgoing_bandwidth); host = enet_host_create(NULL, peer_count, channel_limit, incoming_bandwidth, outgoing_bandwidth);
if (!host) return JS_ThrowInternalError(ctx, "Failed to create ENet client host"); if (!host) return JS_ThrowInternalError(ctx, "Failed to create ENet client host");
goto wrap; goto wrap;
@@ -414,7 +414,7 @@ static JSClassDef enet_host = {
static JSClassDef enet_peer_class = { static JSClassDef enet_peer_class = {
"ENetPeer", "ENetPeer",
.finalizer = js_enet_peer_finalizer, .finalizer = js_enet_peer_finalizer,
.gc_mark = js_enet_peer_mark // .gc_mark = js_enet_peer_mark
}; };
JSValue js_enet_resolve_hostname(JSContext *js, JSValue self, int argc, JSValue *argv) JSValue js_enet_resolve_hostname(JSContext *js, JSValue self, int argc, JSValue *argv)

View File

@@ -132,7 +132,7 @@ JSC_CCALL(socket_getaddrinfo,
// Store the addrinfo pointer as an internal property // Store the addrinfo pointer as an internal property
// We'll need to handle this differently since we can't wrap it // We'll need to handle this differently since we can't wrap it
// For now, we'll skip storing the raw addrinfo // For now, we'll skip storing the raw addrinfo
JS_SetPropertyUint32(js, ret, idx++, info); JS_SetPropertyNumber(js, ret, idx++, info);
} }
freeaddrinfo(res); freeaddrinfo(res);

31
num_torture.cm Normal file
View File

@@ -0,0 +1,31 @@
// num_torture.cm — integer math torture test
// Pure integer arithmetic so it stays on the fast int path.
// Returns the final checksum so the caller can verify correctness.
var n = 5000000
var sum = 0
var i = 0
var a = 0
var b = 0
while (i < n) {
a = (i * 7 + 13) % 10007
b = (a * a) % 10007
sum = (sum + b) % 1000000007
i = i + 1
}
return function(n) {
var i = 0
var a = 0
var b = 0
var sum = 0
while (i < n) {
a = (i * 7 + 13) % 10007
b = (a * a) % 10007
sum = (sum + b) % 1000000007
i = i + 1
}
return sum
}

View File

@@ -65,7 +65,7 @@ var parse = function(tokens, src, filename, tokenizer) {
var errors = [] var errors = []
var error_count = 0 var error_count = 0
var function_nr = 1 var fn_counter = 1
var ast_node = function(kind, token) { var ast_node = function(kind, token) {
return { return {
@@ -422,8 +422,8 @@ var parse = function(tokens, src, filename, tokenizer) {
_control_depth = meth_old_cd _control_depth = meth_old_cd
_control_type = meth_old_ct _control_type = meth_old_ct
_expecting_body = meth_old_eb _expecting_body = meth_old_eb
fn.function_nr = function_nr fn.function_nr = fn_counter
function_nr = function_nr + 1 fn_counter = fn_counter + 1
ast_node_end(fn) ast_node_end(fn)
pair.right = fn pair.right = fn
} else if (is_ident && (tok.kind == "," || tok.kind == "}")) { } else if (is_ident && (tok.kind == "," || tok.kind == "}")) {
@@ -918,8 +918,8 @@ var parse = function(tokens, src, filename, tokenizer) {
_control_depth = old_cd _control_depth = old_cd
_control_type = old_ct _control_type = old_ct
_expecting_body = old_eb _expecting_body = old_eb
node.function_nr = function_nr node.function_nr = fn_counter
function_nr = function_nr + 1 fn_counter = fn_counter + 1
ast_node_end(node) ast_node_end(node)
return node return node
} }
@@ -1008,8 +1008,8 @@ var parse = function(tokens, src, filename, tokenizer) {
_control_depth = old_cd _control_depth = old_cd
_control_type = old_ct _control_type = old_ct
_expecting_body = old_eb _expecting_body = old_eb
node.function_nr = function_nr node.function_nr = fn_counter
function_nr = function_nr + 1 fn_counter = fn_counter + 1
ast_node_end(node) ast_node_end(node)
return node return node
} }

Binary file not shown.

View File

@@ -128,7 +128,7 @@ struct listfiles_ctx {
static void listfiles_cb(const char *path, void *userdata) { static void listfiles_cb(const char *path, void *userdata) {
struct listfiles_ctx *ctx = (struct listfiles_ctx*)userdata; struct listfiles_ctx *ctx = (struct listfiles_ctx*)userdata;
JS_SetPropertyUint32(ctx->js, ctx->array, ctx->index++, JS_NewString(ctx->js, path)); JS_SetPropertyNumber(ctx->js, ctx->array, ctx->index++, JS_NewString(ctx->js, path));
} }
JSC_SCALL(file_listfiles, JSC_SCALL(file_listfiles,

View File

@@ -79,7 +79,7 @@ static void json_decode_array_value(json_decoder *decoder, int pos, json_value v
case kJSONString: jsval = JS_NewString(ctx->js, value.data.stringval); break; case kJSONString: jsval = JS_NewString(ctx->js, value.data.stringval); break;
default: jsval = JS_NULL; break; default: jsval = JS_NULL; break;
} }
JS_SetPropertyUint32(ctx->js, container, pos, jsval); JS_SetPropertyNumber(ctx->js, container, pos, jsval);
} }
// --- JSON Encoder Context --- // --- JSON Encoder Context ---
@@ -128,7 +128,7 @@ static void encode_js_array(json_encoder *enc, JSContext *js, JSValue arr) {
JS_FreeValue(js, lenVal); JS_FreeValue(js, lenVal);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
enc->addArrayMember(enc); enc->addArrayMember(enc);
JSValue val = JS_GetPropertyUint32(js, arr, i); JSValue val = JS_GetPropertyNumber(js, arr, i);
encode_js_value(enc, js, val); encode_js_value(enc, js, val);
JS_FreeValue(js, val); JS_FreeValue(js, val);
} }

View File

@@ -57,7 +57,7 @@ static void boards_list_cb(PDBoardsList *boards, const char *errorMessage) {
boards->boards[i].boardID ? JS_NewString(g_scoreboard_js, boards->boards[i].boardID) : JS_NULL); boards->boards[i].boardID ? JS_NewString(g_scoreboard_js, boards->boards[i].boardID) : JS_NULL);
JS_SetPropertyStr(g_scoreboard_js, board, "name", JS_SetPropertyStr(g_scoreboard_js, board, "name",
boards->boards[i].name ? JS_NewString(g_scoreboard_js, boards->boards[i].name) : JS_NULL); boards->boards[i].name ? JS_NewString(g_scoreboard_js, boards->boards[i].name) : JS_NULL);
JS_SetPropertyUint32(g_scoreboard_js, arr, i, board); JS_SetPropertyNumber(g_scoreboard_js, arr, i, board);
} }
args[0] = arr; args[0] = arr;
} else { } else {
@@ -83,7 +83,7 @@ static void scores_cb(PDScoresList *scores, const char *errorMessage) {
JS_SetPropertyStr(g_scoreboard_js, obj, "limit", JS_NewInt32(g_scoreboard_js, scores->limit)); JS_SetPropertyStr(g_scoreboard_js, obj, "limit", JS_NewInt32(g_scoreboard_js, scores->limit));
JSValue arr = JS_NewArray(g_scoreboard_js); JSValue arr = JS_NewArray(g_scoreboard_js);
for (unsigned int i = 0; i < scores->count; i++) { for (unsigned int i = 0; i < scores->count; i++) {
JS_SetPropertyUint32(g_scoreboard_js, arr, i, score_to_js(g_scoreboard_js, &scores->scores[i])); JS_SetPropertyNumber(g_scoreboard_js, arr, i, score_to_js(g_scoreboard_js, &scores->scores[i]));
} }
JS_SetPropertyStr(g_scoreboard_js, obj, "scores", arr); JS_SetPropertyStr(g_scoreboard_js, obj, "scores", arr);
args[0] = obj; args[0] = obj;

BIN
qbe.mach

Binary file not shown.

View File

@@ -405,7 +405,7 @@ var qbe_emit = function(ir, qbe) {
else if (op == "le_float") fop_id = 3 else if (op == "le_float") fop_id = 3
else if (op == "gt_float") fop_id = 4 else if (op == "gt_float") fop_id = 4
else if (op == "ge_float") fop_id = 5 else if (op == "ge_float") fop_id = 5
emit(qbe.cmp_float != null ? cmp_float(p, "%ctx", s(a2), s(a3), fop_id) : ` %${p} =l call $qbe_float_cmp(l %ctx, w ${text(fop_id)}, l ${s(a2)}, l ${s(a3)})`) emit(qbe.cmp_float != null ? qbe.cmp_float(p, "%ctx", s(a2), s(a3), fop_id) : ` %${p} =l call $qbe_float_cmp(l %ctx, w ${text(fop_id)}, l ${s(a2)}, l ${s(a3)})`)
emit(` ${s(a1)} =l copy %${p}`) emit(` ${s(a1)} =l copy %${p}`)
wb(a1) wb(a1)
continue continue
@@ -675,7 +675,7 @@ var qbe_emit = function(ir, qbe) {
ei = 0 ei = 0
while (ei < nr_elems) { while (ei < nr_elems) {
elem_slot = instr[3 + ei] elem_slot = instr[3 + ei]
emit(` call $JS_SetPropertyUint32(l %ctx, l ${s(a1)}, l ${text(ei)}, l ${s(elem_slot)})`) emit(` call $JS_SetPropertyNumber(l %ctx, l ${s(a1)}, l ${text(ei)}, l ${s(elem_slot)})`)
ei = ei + 1 ei = ei + 1
} }
wb(a1) wb(a1)

Binary file not shown.

2
qop.c
View File

@@ -267,7 +267,7 @@ static JSValue js_qop_list(JSContext *js, JSValue self, int argc, JSValue *argv)
JSValue str = JS_NewStringLen(js, path, len - 1); // -1 for null terminator JSValue str = JS_NewStringLen(js, path, len - 1); // -1 for null terminator
js_free(js, path); js_free(js, path);
JS_SetPropertyUint32(js, arr, count++, str); JS_SetPropertyNumber(js, arr, count++, str);
} }
return arr; return arr;

View File

@@ -6,6 +6,8 @@ var json = use("json")
var tokenize = use("tokenize") var tokenize = use("tokenize")
var parse = use("parse") var parse = use("parse")
var fold = use("fold") var fold = use("fold")
var mcode = use("mcode")
var streamline = use("streamline")
var files = [ var files = [
{src: "tokenize.cm", name: "tokenize", out: "tokenize.mach"}, {src: "tokenize.cm", name: "tokenize", out: "tokenize.mach"},
@@ -25,21 +27,49 @@ var src = null
var tok_result = null var tok_result = null
var ast = null var ast = null
var folded = null var folded = null
var ast_json = null var compiled = null
var optimized = null
var mcode_json = null
var bytecode = null var bytecode = null
var f = null var f = null
var errs = null
var ei = 0
var e = null
var had_errors = false
while (i < length(files)) { while (i < length(files)) {
entry = files[i] entry = files[i]
src = text(fd.slurp(entry.src)) src = text(fd.slurp(entry.src))
tok_result = tokenize(src, entry.src) tok_result = tokenize(src, entry.src)
ast = parse(tok_result.tokens, src, entry.src, tokenize) ast = parse(tok_result.tokens, src, entry.src, tokenize)
// Check for parse/semantic errors
errs = ast.errors
if (errs != null && length(errs) > 0) {
ei = 0
while (ei < length(errs)) {
e = errs[ei]
if (e.line != null) {
print(`${entry.src}:${text(e.line)}:${text(e.column)}: error: ${e.message}`)
} else {
print(`${entry.src}: error: ${e.message}`)
}
ei = ei + 1
}
had_errors = true
i = i + 1
continue
}
folded = fold(ast) folded = fold(ast)
ast_json = json.encode(folded) compiled = mcode(folded)
bytecode = mach_compile_ast(entry.name, ast_json) optimized = streamline(compiled)
mcode_json = json.encode(optimized)
bytecode = mach_compile_mcode_bin(entry.name, mcode_json)
f = fd.open(entry.out, "w") f = fd.open(entry.out, "w")
fd.write(f, bytecode) fd.write(f, bytecode)
fd.close(f) fd.close(f)
print(`wrote ${entry.out}`) print(`wrote ${entry.out}`)
i = i + 1 i = i + 1
} }
if (had_errors) {
print("regen aborted: fix errors above")
}

View File

@@ -12,7 +12,6 @@
#include "cJSON.h" #include "cJSON.h"
#define BOOTSTRAP_MACH "internal/bootstrap.mach" #define BOOTSTRAP_MACH "internal/bootstrap.mach"
#define BOOTSTRAP_AST "internal/bootstrap.ast.json"
#define BOOTSTRAP_SRC "internal/bootstrap.cm" #define BOOTSTRAP_SRC "internal/bootstrap.cm"
#define CELL_SHOP_DIR ".cell" #define CELL_SHOP_DIR ".cell"
#define CELL_CORE_DIR "packages/core" #define CELL_CORE_DIR "packages/core"
@@ -179,14 +178,9 @@ void script_startup(cell_rt *prt)
cell_rt *crt = JS_GetContextOpaque(js); cell_rt *crt = JS_GetContextOpaque(js);
JS_FreeValue(js, js_blob_use(js)); JS_FreeValue(js, js_blob_use(js));
// Load pre-compiled bootstrap bytecode (.mach), fall back to AST JSON // Load pre-compiled bootstrap bytecode (.mach)
size_t boot_size; size_t boot_size;
int boot_is_bin = 1;
char *boot_data = load_core_file(BOOTSTRAP_MACH, &boot_size); char *boot_data = load_core_file(BOOTSTRAP_MACH, &boot_size);
if (!boot_data) {
boot_is_bin = 0;
boot_data = load_core_file(BOOTSTRAP_AST, &boot_size);
}
if (!boot_data) { if (!boot_data) {
printf("ERROR: Could not load bootstrap from %s!\n", core_path); printf("ERROR: Could not load bootstrap from %s!\n", core_path);
return; return;
@@ -211,9 +205,8 @@ void script_startup(cell_rt *prt)
JS_SetPropertyStr(js, hidden_env, "init", JS_NULL); JS_SetPropertyStr(js, hidden_env, "init", JS_NULL);
} }
// Set args and use_mcode to null/false for actor spawn (not CLI mode) // Set args to null for actor spawn (not CLI mode)
JS_SetPropertyStr(js, hidden_env, "args", JS_NULL); JS_SetPropertyStr(js, hidden_env, "args", JS_NULL);
JS_SetPropertyStr(js, hidden_env, "use_mcode", JS_NewBool(js, 0));
if (core_path) if (core_path)
JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path)); JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path));
@@ -225,17 +218,8 @@ void script_startup(cell_rt *prt)
// Run through MACH VM // Run through MACH VM
crt->state = ACTOR_RUNNING; crt->state = ACTOR_RUNNING;
JSValue v; JSValue v = JS_RunMachBin(js, (const uint8_t *)boot_data, boot_size, hidden_env);
if (boot_is_bin) {
v = JS_RunMachBin(js, (const uint8_t *)boot_data, boot_size, hidden_env);
free(boot_data); free(boot_data);
} else {
cJSON *ast = cJSON_Parse(boot_data);
free(boot_data);
if (!ast) { printf("ERROR: Failed to parse bootstrap AST\n"); return; }
v = JS_RunMachTree(js, ast, hidden_env);
cJSON_Delete(ast);
}
uncaught_exception(js, v); uncaught_exception(js, v);
crt->state = ACTOR_IDLE; crt->state = ACTOR_IDLE;
set_actor_state(crt); set_actor_state(crt);
@@ -290,7 +274,8 @@ static void print_usage(const char *prog)
printf("Options:\n"); printf("Options:\n");
printf(" --core <path> Set core path directly (overrides CELL_CORE)\n"); printf(" --core <path> Set core path directly (overrides CELL_CORE)\n");
printf(" --shop <path> Set shop path (overrides CELL_SHOP)\n"); printf(" --shop <path> Set shop path (overrides CELL_SHOP)\n");
printf(" --mcode <script> [args] Run through mcode compilation pipeline\n"); printf(" --emit-qbe Emit QBE IL (for native compilation)\n");
printf(" --dump-mach Dump MACH bytecode disassembly\n");
printf(" --test [heap_size] Run C test suite\n"); printf(" --test [heap_size] Run C test suite\n");
printf(" -h, --help Show this help message\n"); printf(" -h, --help Show this help message\n");
printf("\nEnvironment:\n"); printf("\nEnvironment:\n");
@@ -322,8 +307,8 @@ int cell_init(int argc, char **argv)
} }
/* Default: run script through bootstrap pipeline */ /* Default: run script through bootstrap pipeline */
int use_mcode = 0;
int emit_qbe = 0; int emit_qbe = 0;
int dump_mach = 0;
int arg_start = 1; int arg_start = 1;
const char *shop_override = NULL; const char *shop_override = NULL;
const char *core_override = NULL; const char *core_override = NULL;
@@ -331,12 +316,14 @@ int cell_init(int argc, char **argv)
// Parse flags (order-independent) // Parse flags (order-independent)
while (arg_start < argc && argv[arg_start][0] == '-') { while (arg_start < argc && argv[arg_start][0] == '-') {
if (strcmp(argv[arg_start], "--mcode") == 0) { if (strcmp(argv[arg_start], "--mcode") == 0) {
use_mcode = 1; /* --mcode is now always on; accept and ignore for compat */
arg_start++; arg_start++;
} else if (strcmp(argv[arg_start], "--emit-qbe") == 0) { } else if (strcmp(argv[arg_start], "--emit-qbe") == 0) {
use_mcode = 1; // QBE requires mcode pipeline
emit_qbe = 1; emit_qbe = 1;
arg_start++; arg_start++;
} else if (strcmp(argv[arg_start], "--dump-mach") == 0) {
dump_mach = 1;
arg_start++;
} else if (strcmp(argv[arg_start], "--shop") == 0) { } else if (strcmp(argv[arg_start], "--shop") == 0) {
if (arg_start + 1 >= argc) { if (arg_start + 1 >= argc) {
printf("ERROR: --shop requires a path argument\n"); printf("ERROR: --shop requires a path argument\n");
@@ -361,12 +348,7 @@ int cell_init(int argc, char **argv)
actor_initialize(); actor_initialize();
size_t boot_size; size_t boot_size;
int boot_is_bin = 1;
char *boot_data = load_core_file(BOOTSTRAP_MACH, &boot_size); char *boot_data = load_core_file(BOOTSTRAP_MACH, &boot_size);
if (!boot_data) {
boot_is_bin = 0;
boot_data = load_core_file(BOOTSTRAP_AST, &boot_size);
}
if (!boot_data) { if (!boot_data) {
printf("ERROR: Could not load bootstrap from %s\n", core_path); printf("ERROR: Could not load bootstrap from %s\n", core_path);
return 1; return 1;
@@ -420,8 +402,8 @@ int cell_init(int argc, char **argv)
JS_SetPropertyStr(ctx, hidden_env, "core_path", JS_NewString(ctx, core_path)); JS_SetPropertyStr(ctx, hidden_env, "core_path", JS_NewString(ctx, core_path));
JS_SetPropertyStr(ctx, hidden_env, "shop_path", JS_SetPropertyStr(ctx, hidden_env, "shop_path",
shop_path ? JS_NewString(ctx, shop_path) : JS_NULL); shop_path ? JS_NewString(ctx, shop_path) : JS_NULL);
JS_SetPropertyStr(ctx, hidden_env, "use_mcode", JS_NewBool(ctx, use_mcode));
JS_SetPropertyStr(ctx, hidden_env, "emit_qbe", JS_NewBool(ctx, emit_qbe)); JS_SetPropertyStr(ctx, hidden_env, "emit_qbe", JS_NewBool(ctx, emit_qbe));
JS_SetPropertyStr(ctx, hidden_env, "dump_mach", JS_NewBool(ctx, dump_mach));
JS_SetPropertyStr(ctx, hidden_env, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val)); JS_SetPropertyStr(ctx, hidden_env, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
JS_SetPropertyStr(ctx, hidden_env, "json", js_json_use(ctx)); JS_SetPropertyStr(ctx, hidden_env, "json", js_json_use(ctx));
JS_SetPropertyStr(ctx, hidden_env, "nota", js_nota_use(ctx)); JS_SetPropertyStr(ctx, hidden_env, "nota", js_nota_use(ctx));
@@ -435,17 +417,8 @@ int cell_init(int argc, char **argv)
JS_SetPropertyStr(ctx, hidden_env, "args", args_arr); JS_SetPropertyStr(ctx, hidden_env, "args", args_arr);
hidden_env = JS_Stone(ctx, hidden_env); hidden_env = JS_Stone(ctx, hidden_env);
JSValue result; JSValue result = JS_RunMachBin(ctx, (const uint8_t *)boot_data, boot_size, hidden_env);
if (boot_is_bin) {
result = JS_RunMachBin(ctx, (const uint8_t *)boot_data, boot_size, hidden_env);
free(boot_data); free(boot_data);
} else {
cJSON *ast = cJSON_Parse(boot_data);
free(boot_data);
if (!ast) { printf("Failed to parse bootstrap AST\n"); JS_FreeContext(ctx); JS_FreeRuntime(g_runtime); return 1; }
result = JS_RunMachTree(ctx, ast, hidden_env);
cJSON_Delete(ast);
}
int exit_code = 0; int exit_code = 0;
if (JS_IsException(result)) { if (JS_IsException(result)) {
@@ -460,6 +433,7 @@ int cell_init(int argc, char **argv)
} }
if (scheduler_actor_count() > 0) { if (scheduler_actor_count() > 0) {
scheduler_enable_quiescence();
actor_loop(); actor_loop();
exit_handler(); exit_handler();
exit(0); exit(0);

View File

@@ -54,6 +54,7 @@ typedef struct cell_rt {
double ar_secs; // time for unneeded double ar_secs; // time for unneeded
int disrupt; int disrupt;
int is_quiescent; // tracked by scheduler for quiescence detection
int main_thread_only; int main_thread_only;
int affinity; int affinity;
@@ -82,6 +83,7 @@ void actor_loop();
void actor_initialize(void); void actor_initialize(void);
void actor_free(cell_rt *actor); void actor_free(cell_rt *actor);
int scheduler_actor_count(void); int scheduler_actor_count(void);
void scheduler_enable_quiescence(void);
uint64_t cell_ns(); uint64_t cell_ns();
void cell_sleep(double seconds); void cell_sleep(double seconds);

View File

@@ -1327,9 +1327,6 @@ static int re_parse_nested_class(REParseState *s, REStringList *cr, const uint8_
REStringList cr1_s, *cr1 = &cr1_s; REStringList cr1_s, *cr1 = &cr1_s;
BOOL invert, is_first; BOOL invert, is_first;
if (lre_check_stack_overflow(s->opaque, 0))
return re_parse_error(s, "stack overflow");
re_string_list_init(s, cr); re_string_list_init(s, cr);
p = *pp; p = *pp;
p++; /* skip '[' */ p++; /* skip '[' */
@@ -2356,9 +2353,6 @@ static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir)
{ {
int start, len, pos; int start, len, pos;
if (lre_check_stack_overflow(s->opaque, 0))
return re_parse_error(s, "stack overflow");
start = s->byte_code.size; start = s->byte_code.size;
if (re_parse_alternative(s, is_backward_dir)) if (re_parse_alternative(s, is_backward_dir))
return -1; return -1;
@@ -3205,11 +3199,6 @@ const char *lre_get_groupnames(const uint8_t *bc_buf)
#ifdef TEST #ifdef TEST
BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
{
return FALSE;
}
void *lre_realloc(void *opaque, void *ptr, size_t size) void *lre_realloc(void *opaque, void *ptr, size_t size)
{ {
return realloc(ptr, size); return realloc(ptr, size);

View File

@@ -52,8 +52,6 @@ int lre_exec(uint8_t **capture,
int lre_parse_escape(const uint8_t **pp, int allow_utf16); int lre_parse_escape(const uint8_t **pp, int allow_utf16);
/* must be provided by the user, return non zero if overflow */
int lre_check_stack_overflow(void *opaque, size_t alloca_size);
/* must be provided by the user, return non zero if time out */ /* must be provided by the user, return non zero if time out */
int lre_check_timeout(void *opaque); int lre_check_timeout(void *opaque);
void *lre_realloc(void *opaque, void *ptr, size_t size); void *lre_realloc(void *opaque, void *ptr, size_t size);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -213,28 +213,28 @@ void cell_rt_store_field(JSContext *ctx, JSValue val, JSValue obj,
JSValue cell_rt_load_dynamic(JSContext *ctx, JSValue obj, JSValue key) { JSValue cell_rt_load_dynamic(JSContext *ctx, JSValue obj, JSValue key) {
if (JS_IsInt(key)) if (JS_IsInt(key))
return JS_GetPropertyUint32(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key)); return JS_GetPropertyNumber(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key));
return JS_GetProperty(ctx, obj, key); return JS_GetProperty(ctx, obj, key);
} }
void cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj, void cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj,
JSValue key) { JSValue key) {
if (JS_IsInt(key)) if (JS_IsInt(key))
JS_SetPropertyUint32(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key), val); JS_SetPropertyNumber(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key), val);
else else
JS_SetProperty(ctx, obj, key, val); JS_SetProperty(ctx, obj, key, val);
} }
JSValue cell_rt_load_index(JSContext *ctx, JSValue arr, JSValue idx) { JSValue cell_rt_load_index(JSContext *ctx, JSValue arr, JSValue idx) {
if (JS_IsInt(idx)) if (JS_IsInt(idx))
return JS_GetPropertyUint32(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx)); return JS_GetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx));
return JS_GetProperty(ctx, arr, idx); return JS_GetProperty(ctx, arr, idx);
} }
void cell_rt_store_index(JSContext *ctx, JSValue val, JSValue arr, void cell_rt_store_index(JSContext *ctx, JSValue val, JSValue arr,
JSValue idx) { JSValue idx) {
if (JS_IsInt(idx)) if (JS_IsInt(idx))
JS_SetPropertyUint32(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx), val); JS_SetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx), val);
else else
JS_SetProperty(ctx, arr, idx, val); JS_SetProperty(ctx, arr, idx, val);
} }
@@ -336,15 +336,12 @@ JSValue cell_rt_invoke(JSContext *ctx, JSValue frame_val) {
int nr_slots = (int)objhdr_cap56(fr->hdr); int nr_slots = (int)objhdr_cap56(fr->hdr);
int c_argc = (nr_slots >= 2) ? nr_slots - 2 : 0; int c_argc = (nr_slots >= 2) ? nr_slots - 2 : 0;
/* Push args onto value stack (GC-safe) */ /* Copy args to C stack */
int vs_base = ctx->value_stack_top; JSValue args[c_argc > 0 ? c_argc : 1];
for (int i = 0; i < c_argc; i++) for (int i = 0; i < c_argc; i++)
ctx->value_stack[vs_base + i] = fr->slots[i + 1]; args[i] = fr->slots[i + 1];
ctx->value_stack_top = vs_base + c_argc;
JSValue result = JS_Call(ctx, fr->function, fr->slots[0], JSValue result = JS_Call(ctx, fr->function, fr->slots[0], c_argc, args);
c_argc, &ctx->value_stack[vs_base]);
ctx->value_stack_top = vs_base;
if (JS_IsException(result)) if (JS_IsException(result))
return JS_EXCEPTION; return JS_EXCEPTION;
return result; return result;

View File

@@ -54,14 +54,6 @@
#include "nota.h" #include "nota.h"
#include "wota.h" #include "wota.h"
#define OPTIMIZE 1
#define SHORT_OPCODES 1
#if defined(EMSCRIPTEN)
#define DIRECT_DISPATCH 0
#else
#define DIRECT_DISPATCH 1
#endif
#if !defined(_WIN32) #if !defined(_WIN32)
/* define it if printf uses the RNDN rounding mode instead of RNDNA */ /* define it if printf uses the RNDN rounding mode instead of RNDNA */
#define CONFIG_PRINTF_RNDN #define CONFIG_PRINTF_RNDN
@@ -154,7 +146,6 @@ typedef struct JSCode JSCode;
/* Extract pointer (clear low bits) */ /* Extract pointer (clear low bits) */
#define JS_VALUE_GET_PTR(v) ((void *)((v) & ~((JSValue)(JSW - 1)))) #define JS_VALUE_GET_PTR(v) ((void *)((v) & ~((JSValue)(JSW - 1))))
static inline JS_BOOL JS_VALUE_IS_TEXT (JSValue v) { static inline JS_BOOL JS_VALUE_IS_TEXT (JSValue v) {
int tag = JS_VALUE_GET_TAG (v); int tag = JS_VALUE_GET_TAG (v);
return tag == JS_TAG_STRING_IMM || (JS_IsPtr(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_TEXT); return tag == JS_TAG_STRING_IMM || (JS_IsPtr(v) && objhdr_type(*(objhdr_t *)JS_VALUE_GET_PTR(v)) == OBJ_TEXT);
@@ -361,70 +352,21 @@ typedef struct BuddyAllocator {
/* Forward declarations for buddy allocator functions */ /* Forward declarations for buddy allocator functions */
static void buddy_destroy (BuddyAllocator *b); static void buddy_destroy (BuddyAllocator *b);
/* controls a host of contexts, handing out memory and scheduling */
struct JSRuntime { struct JSRuntime {
const char *rt_info;
size_t malloc_limit; size_t malloc_limit;
/* Buddy allocator for actor memory blocks */
BuddyAllocator buddy; BuddyAllocator buddy;
/* see JS_SetStripInfo() */
uint8_t strip_flags;
/* User data */
void *user_opaque;
}; };
struct JSClass { struct JSClass {
const char *class_name; const char *class_name;
JSClassFinalizer *finalizer; JSClassFinalizer *finalizer;
JSClassGCMark *gc_mark;
uint32_t class_id; /* 0 means free entry */ uint32_t class_id; /* 0 means free entry */
}; };
#define JS_MODE_BACKTRACE_BARRIER \ #define JS_MODE_BACKTRACE_BARRIER \
(1 << 3) /* stop backtrace before this frame */ (1 << 3) /* stop backtrace before this frame */
typedef struct JSStackFrame {
struct JSStackFrame *prev_frame; /* NULL if first stack frame */
JSValue cur_func; /* current function, JS_NULL if the frame is detached */
JSValue *arg_buf; /* arguments */
JSValue *var_buf; /* variables */
const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
instruction after the call */
int arg_count;
int js_mode; /* not supported for C functions */
JSValue js_frame; /* GC-managed JSFrame (use JS_VALUE_GET_FRAME to access) */
JSValue *stack_buf; /* operand stack base (for GC scanning) */
JSValue **p_sp; /* pointer to current sp (for GC scanning) */
} JSStackFrame;
/* Heap-allocated VM frame for trampoline execution */
struct VMFrame {
struct JSFunctionBytecode *b; /* current function bytecode */
JSContext *ctx; /* execution context / realm */
const uint8_t *pc; /* program counter */
/* Offset-based storage (safe with realloc) */
int value_stack_base; /* base index into ctx->value_stack */
int sp_offset; /* sp offset from base */
int arg_buf_offset; /* arg buffer offset from base (or -1 if aliased) */
int var_buf_offset; /* var buffer offset from base */
/* Continuation info for return */
const uint8_t *ret_pc; /* where to resume in caller */
int ret_sp_offset; /* caller's sp before call (for cleanup) */
int call_argc; /* number of args to clean up */
int call_has_this; /* whether call had this (method call) */
JSValue cur_func; /* current function object */
JSValue this_obj; /* this binding */
int arg_count;
int js_mode;
int stack_size_allocated; /* total size allocated for this frame */
};
typedef struct JSFrameRegister { typedef struct JSFrameRegister {
objhdr_t hdr; // capacity in this is the total number of words of the object, including the 4 words of overhead and all slots objhdr_t hdr; // capacity in this is the total number of words of the object, including the 4 words of overhead and all slots
JSValue function; // JSFunction, function object being invoked JSValue function; // JSFunction, function object being invoked
@@ -464,6 +406,8 @@ typedef struct { uint16_t line; uint16_t col; } MachLineEntry;
#define MACH_GET_sJ(i) ((int32_t)((i) & 0xFFFFFF00) >> 8) #define MACH_GET_sJ(i) ((int32_t)((i) & 0xFFFFFF00) >> 8)
typedef enum MachOpcode { typedef enum MachOpcode {
/* === Legacy opcodes (used by existing .mach files) === */
/* Constants & Loading */ /* Constants & Loading */
MACH_LOADK, /* R(A) = K(Bx) — load from constant pool (ABx) */ MACH_LOADK, /* R(A) = K(Bx) — load from constant pool (ABx) */
MACH_LOADI, /* R(A) = (int16_t)sBx — load small integer (AsBx) */ MACH_LOADI, /* R(A) = (int16_t)sBx — load small integer (AsBx) */
@@ -474,7 +418,7 @@ typedef enum MachOpcode {
/* Movement */ /* Movement */
MACH_MOVE, /* R(A) = R(B) */ MACH_MOVE, /* R(A) = R(B) */
/* Arithmetic (ABC) */ /* Generic arithmetic (ABC) — used by legacy .mach */
MACH_ADD, /* R(A) = R(B) + R(C) */ MACH_ADD, /* R(A) = R(B) + R(C) */
MACH_SUB, /* R(A) = R(B) - R(C) */ MACH_SUB, /* R(A) = R(B) - R(C) */
MACH_MUL, /* R(A) = R(B) * R(C) */ MACH_MUL, /* R(A) = R(B) * R(C) */
@@ -485,7 +429,7 @@ typedef enum MachOpcode {
MACH_INC, /* R(A) = R(B) + 1 */ MACH_INC, /* R(A) = R(B) + 1 */
MACH_DEC, /* R(A) = R(B) - 1 */ MACH_DEC, /* R(A) = R(B) - 1 */
/* Comparison (ABC) */ /* Generic comparison (ABC) — used by legacy .mach */
MACH_EQ, /* R(A) = (R(B) == R(C)) */ MACH_EQ, /* R(A) = (R(B) == R(C)) */
MACH_NEQ, /* R(A) = (R(B) != R(C)) */ MACH_NEQ, /* R(A) = (R(B) != R(C)) */
MACH_LT, /* R(A) = (R(B) < R(C)) */ MACH_LT, /* R(A) = (R(B) < R(C)) */
@@ -493,7 +437,7 @@ typedef enum MachOpcode {
MACH_GT, /* R(A) = (R(B) > R(C)) */ MACH_GT, /* R(A) = (R(B) > R(C)) */
MACH_GE, /* R(A) = (R(B) >= R(C)) */ MACH_GE, /* R(A) = (R(B) >= R(C)) */
/* Logical/Bitwise */ /* Logical/Bitwise — used by legacy .mach */
MACH_LNOT, /* R(A) = !R(B) */ MACH_LNOT, /* R(A) = !R(B) */
MACH_BNOT, /* R(A) = ~R(B) */ MACH_BNOT, /* R(A) = ~R(B) */
MACH_BAND, /* R(A) = R(B) & R(C) */ MACH_BAND, /* R(A) = R(B) & R(C) */
@@ -503,7 +447,7 @@ typedef enum MachOpcode {
MACH_SHR, /* R(A) = R(B) >> R(C) */ MACH_SHR, /* R(A) = R(B) >> R(C) */
MACH_USHR, /* R(A) = R(B) >>> R(C) */ MACH_USHR, /* R(A) = R(B) >>> R(C) */
/* Property access */ /* Property access — used by legacy .mach */
MACH_GETFIELD, /* R(A) = R(B)[K(C)] — named property */ MACH_GETFIELD, /* R(A) = R(B)[K(C)] — named property */
MACH_SETFIELD, /* R(A)[K(B)] = R(C) — named property */ MACH_SETFIELD, /* R(A)[K(B)] = R(C) — named property */
MACH_GETINDEX, /* R(A) = R(B)[R(C)] — computed property */ MACH_GETINDEX, /* R(A) = R(B)[R(C)] — computed property */
@@ -524,12 +468,12 @@ typedef enum MachOpcode {
MACH_JMPFALSE, /* if !R(A): pc += sBx — (iAsBx format) */ MACH_JMPFALSE, /* if !R(A): pc += sBx — (iAsBx format) */
MACH_JMPNULL, /* if R(A)==null: pc += sBx */ MACH_JMPNULL, /* if R(A)==null: pc += sBx */
/* Function calls — Lua-style consecutive registers */ /* Function calls — Lua-style consecutive registers (legacy .mach) */
MACH_CALL, /* Call R(A) with B args R(A+1)..R(A+B), C=0 discard, C=1 keep result in R(A) */ MACH_CALL, /* Call R(A) with B args R(A+1)..R(A+B), C=0 discard, C=1 keep result in R(A) */
MACH_RETURN, /* Return R(A) */ MACH_RETURN, /* Return R(A) */
MACH_RETNIL, /* Return null */ MACH_RETNIL, /* Return null */
/* Object/array creation */ /* Object/array creation — legacy .mach */
MACH_NEWOBJECT, /* R(A) = {} */ MACH_NEWOBJECT, /* R(A) = {} */
MACH_NEWARRAY, /* R(A) = new array, B = element count in R(A+1)..R(A+B) */ MACH_NEWARRAY, /* R(A) = new array, B = element count in R(A+1)..R(A+B) */
MACH_CLOSURE, /* R(A) = closure(functions[Bx]) (ABx) */ MACH_CLOSURE, /* R(A) = closure(functions[Bx]) (ABx) */
@@ -551,10 +495,114 @@ typedef enum MachOpcode {
MACH_NOP, MACH_NOP,
/* === New mcode-derived opcodes (1:1 mapping to mcode IR) === */
/* Typed integer arithmetic (ABC) */
MACH_ADD_INT, /* R(A) = R(B) + R(C) — int, overflow → float */
MACH_SUB_INT, /* R(A) = R(B) - R(C) — int */
MACH_MUL_INT, /* R(A) = R(B) * R(C) — int */
MACH_DIV_INT, /* R(A) = R(B) / R(C) — int */
MACH_MOD_INT, /* R(A) = R(B) % R(C) — int */
MACH_NEG_INT, /* R(A) = -R(B) — int (AB) */
/* Typed float arithmetic (ABC) */
MACH_ADD_FLOAT, /* R(A) = R(B) + R(C) — float */
MACH_SUB_FLOAT, /* R(A) = R(B) - R(C) — float */
MACH_MUL_FLOAT, /* R(A) = R(B) * R(C) — float */
MACH_DIV_FLOAT, /* R(A) = R(B) / R(C) — float */
MACH_MOD_FLOAT, /* R(A) = R(B) % R(C) — float */
MACH_NEG_FLOAT, /* R(A) = -R(B) — float (AB) */
/* Text */
MACH_CONCAT, /* R(A) = R(B) ++ R(C) — string concatenation */
/* Typed integer comparisons (ABC) */
MACH_EQ_INT, /* R(A) = (R(B) == R(C)) — int */
MACH_NE_INT, /* R(A) = (R(B) != R(C)) — int */
MACH_LT_INT, /* R(A) = (R(B) < R(C)) — int */
MACH_LE_INT, /* R(A) = (R(B) <= R(C)) — int */
MACH_GT_INT, /* R(A) = (R(B) > R(C)) — int */
MACH_GE_INT, /* R(A) = (R(B) >= R(C)) — int */
/* Typed float comparisons (ABC) */
MACH_EQ_FLOAT, /* R(A) = (R(B) == R(C)) — float */
MACH_NE_FLOAT, /* R(A) = (R(B) != R(C)) — float */
MACH_LT_FLOAT, /* R(A) = (R(B) < R(C)) — float */
MACH_LE_FLOAT, /* R(A) = (R(B) <= R(C)) — float */
MACH_GT_FLOAT, /* R(A) = (R(B) > R(C)) — float */
MACH_GE_FLOAT, /* R(A) = (R(B) >= R(C)) — float */
/* Typed text comparisons (ABC) */
MACH_EQ_TEXT, /* R(A) = (R(B) == R(C)) — text */
MACH_NE_TEXT, /* R(A) = (R(B) != R(C)) — text */
MACH_LT_TEXT, /* R(A) = (R(B) < R(C)) — text */
MACH_LE_TEXT, /* R(A) = (R(B) <= R(C)) — text */
MACH_GT_TEXT, /* R(A) = (R(B) > R(C)) — text */
MACH_GE_TEXT, /* R(A) = (R(B) >= R(C)) — text */
/* Typed bool comparisons (ABC) */
MACH_EQ_BOOL, /* R(A) = (R(B) == R(C)) — bool */
MACH_NE_BOOL, /* R(A) = (R(B) != R(C)) — bool */
/* Special comparisons */
MACH_IS_IDENTICAL, /* R(A) = (R(B) === R(C)) — identity check (ABC) */
/* Type checks (AB) */
MACH_IS_INT, /* R(A) = is_int(R(B)) */
MACH_IS_NUM, /* R(A) = is_num(R(B)) */
MACH_IS_TEXT, /* R(A) = is_text(R(B)) */
MACH_IS_BOOL, /* R(A) = is_bool(R(B)) */
MACH_IS_NULL, /* R(A) = is_null(R(B)) */
MACH_TYPEOF, /* R(A) = typeof(R(B)) */
/* Logical (mcode-style) */
MACH_NOT, /* R(A) = !R(B) — boolean not (AB) */
MACH_AND, /* R(A) = R(B) && R(C) (ABC) */
MACH_OR, /* R(A) = R(B) || R(C) (ABC) */
/* Bitwise (mcode names) */
MACH_BITNOT, /* R(A) = ~R(B) (AB) */
MACH_BITAND, /* R(A) = R(B) & R(C) (ABC) */
MACH_BITOR, /* R(A) = R(B) | R(C) (ABC) */
MACH_BITXOR, /* R(A) = R(B) ^ R(C) (ABC) */
/* Property access (mcode names) */
MACH_LOAD_FIELD, /* R(A) = R(B).K(C) — named property (ABC) */
MACH_STORE_FIELD, /* R(A).K(B) = R(C) — named property (ABC) */
MACH_LOAD_INDEX, /* R(A) = R(B)[R(C)] — integer index (ABC) */
MACH_STORE_INDEX, /* R(A)[R(B)] = R(C) — integer index (ABC) */
MACH_LOAD_DYNAMIC, /* R(A) = R(B)[R(C)] — dynamic key (ABC) */
MACH_STORE_DYNAMIC, /* R(A)[R(B)] = R(C) — dynamic key (ABC) */
/* Object/Array creation (mcode names) */
MACH_NEWRECORD, /* R(A) = {} — new empty record (A only) */
/* Decomposed function calls (mcode-style) */
MACH_FRAME, /* R(A) = frame(R(B), C) — alloc call frame (ABC) */
MACH_SETARG, /* frame R(A)[B] = R(C) — set arg in frame (ABC) */
MACH_INVOKE, /* R(B) = invoke(R(A)) — call frame, result in R(B) (AB) */
MACH_GOFRAME, /* R(A) = goframe(R(B), C) — async frame (ABC) */
MACH_GOINVOKE, /* goinvoke(R(A)) — async invoke, no result (A only) */
/* Control flow */
MACH_JMPNOTNULL, /* if R(A)!=null: pc += sBx (iAsBx) */
/* Error handling */
MACH_DISRUPT, /* trigger disruption (A only) */
/* Variable storage */
MACH_SET_VAR, /* env/global[K(Bx)] = R(A) — store to var (ABx) */
/* Misc */
MACH_IN, /* R(A) = (R(B) in R(C)) — has property (ABC) */
MACH_IS_FUNC, /* R(A) = is_function(R(B)) */
MACH_IS_PROXY, /* R(A) = is_function(R(B)) && R(B).length == 2 */
MACH_OP_COUNT MACH_OP_COUNT
} MachOpcode; } MachOpcode;
static const char *mach_opcode_names[MACH_OP_COUNT] = { static const char *mach_opcode_names[MACH_OP_COUNT] = {
/* Legacy */
[MACH_LOADK] = "loadk", [MACH_LOADK] = "loadk",
[MACH_LOADI] = "loadi", [MACH_LOADI] = "loadi",
[MACH_LOADNULL] = "loadnull", [MACH_LOADNULL] = "loadnull",
@@ -614,6 +662,72 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_EQ_TOL] = "eq_tol", [MACH_EQ_TOL] = "eq_tol",
[MACH_NEQ_TOL] = "neq_tol", [MACH_NEQ_TOL] = "neq_tol",
[MACH_NOP] = "nop", [MACH_NOP] = "nop",
/* Mcode-derived */
[MACH_ADD_INT] = "add_int",
[MACH_SUB_INT] = "sub_int",
[MACH_MUL_INT] = "mul_int",
[MACH_DIV_INT] = "div_int",
[MACH_MOD_INT] = "mod_int",
[MACH_NEG_INT] = "neg_int",
[MACH_ADD_FLOAT] = "add_float",
[MACH_SUB_FLOAT] = "sub_float",
[MACH_MUL_FLOAT] = "mul_float",
[MACH_DIV_FLOAT] = "div_float",
[MACH_MOD_FLOAT] = "mod_float",
[MACH_NEG_FLOAT] = "neg_float",
[MACH_CONCAT] = "concat",
[MACH_EQ_INT] = "eq_int",
[MACH_NE_INT] = "ne_int",
[MACH_LT_INT] = "lt_int",
[MACH_LE_INT] = "le_int",
[MACH_GT_INT] = "gt_int",
[MACH_GE_INT] = "ge_int",
[MACH_EQ_FLOAT] = "eq_float",
[MACH_NE_FLOAT] = "ne_float",
[MACH_LT_FLOAT] = "lt_float",
[MACH_LE_FLOAT] = "le_float",
[MACH_GT_FLOAT] = "gt_float",
[MACH_GE_FLOAT] = "ge_float",
[MACH_EQ_TEXT] = "eq_text",
[MACH_NE_TEXT] = "ne_text",
[MACH_LT_TEXT] = "lt_text",
[MACH_LE_TEXT] = "le_text",
[MACH_GT_TEXT] = "gt_text",
[MACH_GE_TEXT] = "ge_text",
[MACH_EQ_BOOL] = "eq_bool",
[MACH_NE_BOOL] = "ne_bool",
[MACH_IS_IDENTICAL] = "is_identical",
[MACH_IS_INT] = "is_int",
[MACH_IS_NUM] = "is_num",
[MACH_IS_TEXT] = "is_text",
[MACH_IS_BOOL] = "is_bool",
[MACH_IS_NULL] = "is_null",
[MACH_TYPEOF] = "typeof",
[MACH_NOT] = "not",
[MACH_AND] = "and",
[MACH_OR] = "or",
[MACH_BITNOT] = "bitnot",
[MACH_BITAND] = "bitand",
[MACH_BITOR] = "bitor",
[MACH_BITXOR] = "bitxor",
[MACH_LOAD_FIELD] = "load_field",
[MACH_STORE_FIELD] = "store_field",
[MACH_LOAD_INDEX] = "load_index",
[MACH_STORE_INDEX] = "store_index",
[MACH_LOAD_DYNAMIC] = "load_dynamic",
[MACH_STORE_DYNAMIC] = "store_dynamic",
[MACH_NEWRECORD] = "newrecord",
[MACH_FRAME] = "frame",
[MACH_SETARG] = "setarg",
[MACH_INVOKE] = "invoke",
[MACH_GOFRAME] = "goframe",
[MACH_GOINVOKE] = "goinvoke",
[MACH_JMPNOTNULL] = "jmpnotnull",
[MACH_DISRUPT] = "disrupt",
[MACH_SET_VAR] = "set_var",
[MACH_IN] = "in",
[MACH_IS_FUNC] = "is_func",
[MACH_IS_PROXY] = "is_proxy",
}; };
/* Compiled register-based code (off-heap, never GC'd). /* Compiled register-based code (off-heap, never GC'd).
@@ -644,28 +758,6 @@ typedef struct JSCodeRegister {
uint16_t disruption_pc; /* start of disruption handler (0 = none) */ uint16_t disruption_pc; /* start of disruption handler (0 = none) */
} JSCodeRegister; } JSCodeRegister;
/* Pre-parsed MCODE for a single function (off-heap, never GC'd).
Created by jsmcode_parse from cJSON MCODE output.
Instructions remain as cJSON pointers for string-based dispatch. */
typedef struct JSMCode {
uint16_t nr_args;
uint16_t nr_slots;
/* Pre-flattened instruction array (cJSON array items → C array for O(1) access) */
cJSON **instrs;
uint32_t instr_count;
/* Label map: label string → instruction index */
struct { const char *name; uint32_t index; } *labels;
uint32_t label_count;
/* Nested function definitions (indexes into top-level functions array) */
struct JSMCode **functions;
uint32_t func_count;
/* Keep root cJSON alive (owns all the cJSON nodes instrs[] point into) */
cJSON *json_root;
MachLineEntry *line_table; /* [instr_count], parallel to instrs[] */
const char *name; /* function name (points into cJSON tree) */
const char *filename; /* source filename (points into cJSON tree) */
uint16_t disruption_pc; /* start of disruption handler (0 = none) */
} JSMCode;
/* Frame for closures - used by link-time relocation model where closures /* Frame for closures - used by link-time relocation model where closures
reference outer frames via (depth, slot) addressing. reference outer frames via (depth, slot) addressing.
@@ -675,7 +767,7 @@ typedef struct JSFrame {
JSValue function; /* JSValue for GC safety (use JS_VALUE_GET_FUNCTION) */ JSValue function; /* JSValue for GC safety (use JS_VALUE_GET_FUNCTION) */
JSValue caller; /* JSValue for GC safety (unused currently) */ JSValue caller; /* JSValue for GC safety (unused currently) */
uint32_t return_pc; uint32_t return_pc;
JSValue slots[]; /* args, captured, locals, temps */ JSValue slots[]; /* [this][args][captured][locals][temps] */
} JSFrame; } JSFrame;
/* Execution state returned by vm_execute_frame */ /* Execution state returned by vm_execute_frame */
@@ -957,31 +1049,19 @@ struct JSContext {
int trace_type; int trace_type;
void *trace_data; void *trace_data;
/* Trampoline VM stacks (per actor/context) */
struct VMFrame *frame_stack; /* array of frames */
int frame_stack_top; /* current frame index (-1 = empty) */
int frame_stack_capacity; /* allocated capacity */
JSValue *value_stack; /* array of JSValues for locals/operands */
int value_stack_top; /* current top index */
int value_stack_capacity; /* allocated capacity */
/* Register VM frame root (updated by GC when frame moves) */ /* Register VM frame root (updated by GC when frame moves) */
JSValue reg_current_frame; /* current JSFrameRegister being executed */ JSValue reg_current_frame; /* current JSFrameRegister being executed */
uint32_t current_register_pc; /* PC at exception time */ uint32_t current_register_pc; /* PC at exception time */
/* Execution state (moved from JSRuntime — per-actor) */
JSValue current_exception;
struct JSStackFrame *current_stack_frame;
BOOL current_exception_is_uncatchable : 8;
BOOL in_out_of_memory : 8;
JSInterruptHandler *interrupt_handler; JSInterruptHandler *interrupt_handler;
void *interrupt_opaque; void *interrupt_opaque;
JSValue current_exception;
/* Stack overflow protection */ /* Stack overflow protection */
size_t stack_size; // todo: want this, but should be a simple increment/decrement counter while frames are pushed
const uint8_t *stack_top; size_t stack_depth;
const uint8_t *stack_limit; size_t stack_limit;
/* Parser state (for GC to scan cpool during parsing) */ /* Parser state (for GC to scan cpool during parsing) */
struct JSFunctionDef *current_parse_fd; struct JSFunctionDef *current_parse_fd;
@@ -1138,7 +1218,6 @@ typedef enum {
JS_FUNC_KIND_BYTECODE, JS_FUNC_KIND_BYTECODE,
JS_FUNC_KIND_C_DATA, JS_FUNC_KIND_C_DATA,
JS_FUNC_KIND_REGISTER, /* register-based VM function */ JS_FUNC_KIND_REGISTER, /* register-based VM function */
JS_FUNC_KIND_MCODE, /* MCODE JSON interpreter */
} JSFunctionKind; } JSFunctionKind;
typedef struct JSFunction { typedef struct JSFunction {
@@ -1162,11 +1241,6 @@ typedef struct JSFunction {
JSValue env_record; /* stone record, module environment */ JSValue env_record; /* stone record, module environment */
JSValue outer_frame; /* JSFrame JSValue, for closures */ JSValue outer_frame; /* JSFrame JSValue, for closures */
} reg; } reg;
struct {
JSMCode *code; /* pre-parsed MCODE (off-heap) */
JSValue outer_frame; /* lexical parent frame for closures */
JSValue env_record; /* module env or JS_NULL */
} mcode;
} u; } u;
} JSFunction; } JSFunction;
@@ -1325,42 +1399,10 @@ typedef struct JSProperty {
#define JS_ARRAY_MAX_CAP ((word_t)((1UL << 24) - 1)) #define JS_ARRAY_MAX_CAP ((word_t)((1UL << 24) - 1))
#endif #endif
typedef enum OPCodeFormat {
#define FMT(f) OP_FMT_##f,
#define DEF(id, size, n_pop, n_push, f)
#include "quickjs-opcode.h"
#undef DEF
#undef FMT
} OPCodeFormat;
enum OPCodeEnum {
#define FMT(f)
#define DEF(id, size, n_pop, n_push, f) OP_##id,
#define def(id, size, n_pop, n_push, f)
#include "quickjs-opcode.h"
#undef def
#undef DEF
#undef FMT
OP_COUNT, /* excluding temporary opcodes */
/* temporary opcodes : overlap with the short opcodes */
OP_TEMP_START = OP_nop + 1,
OP___dummy = OP_TEMP_START - 1,
#define FMT(f)
#define DEF(id, size, n_pop, n_push, f)
#define def(id, size, n_pop, n_push, f) OP_##id,
#include "quickjs-opcode.h"
#undef def
#undef DEF
#undef FMT
OP_TEMP_END,
};
JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv); JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
JSValue js_call_bound_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv); JSValue js_call_bound_function (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv, int flags); JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv, int flags);
JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue this_obj, int argc, JSValue *argv, JSValue env, JSValue outer_frame); JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue this_obj, int argc, JSValue *argv, JSValue env, JSValue outer_frame);
JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
int argc, JSValue *argv, JSValue outer_frame);
int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop); int JS_DeleteProperty (JSContext *ctx, JSValue obj, JSValue prop);
JSValue __attribute__ ((format (printf, 2, 3))) JSValue __attribute__ ((format (printf, 2, 3)))
JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...); JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
@@ -1399,8 +1441,6 @@ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue
static JSValue js_cell_push (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_push (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_cell_pop (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_pop (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_cell_array_find (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_array_find (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_cell_eval (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_mach_eval (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_cell_stone (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_stone (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_cell_length (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_length (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_cell_reverse (JSContext *ctx, JSValue this_val, int argc, JSValue *argv); static JSValue js_cell_reverse (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
@@ -1544,22 +1584,6 @@ static inline void set_value (JSContext *ctx, JSValue *pval, JSValue new_val) {
*pval = new_val; *pval = new_val;
} }
#if !defined(CONFIG_STACK_CHECK)
static inline uintptr_t js_get_stack_pointer (void) { return 0; }
static inline BOOL js_check_stack_overflow (JSContext *ctx, size_t alloca_size) {
return FALSE;
}
#else
static inline uintptr_t js_get_stack_pointer (void) {
return (uintptr_t)__builtin_frame_address (0);
}
static inline BOOL js_check_stack_overflow (JSContext *ctx, size_t alloca_size) {
uintptr_t sp;
sp = js_get_stack_pointer () - alloca_size;
return unlikely (sp < (uintptr_t)ctx->stack_limit);
}
#endif
void JS_ThrowInterrupted (JSContext *ctx); void JS_ThrowInterrupted (JSContext *ctx);
static no_inline __exception int __js_poll_interrupts (JSContext *ctx) { static no_inline __exception int __js_poll_interrupts (JSContext *ctx) {
@@ -1692,13 +1716,6 @@ typedef struct {
} GetLineColCache; } GetLineColCache;
/* === MachVarInfo (shared by mach.c and mcode.c) === */
typedef struct MachVarInfo {
char *name;
int slot;
int is_const; /* 1 for def, function args; 0 for var */
int is_closure; /* 1 if captured by a nested function */
} MachVarInfo;
/* === PPretext (parser pretext, system-malloc, used by cell_js.c parser) === */ /* === PPretext (parser pretext, system-malloc, used by cell_js.c parser) === */
typedef struct PPretext { typedef struct PPretext {
@@ -1721,7 +1738,6 @@ int get_line_col_cached (GetLineColCache *s, int *pcol_num, const uint8_t *ptr);
/* runtime.c exports */ /* runtime.c exports */
JSValue JS_ThrowStackOverflow (JSContext *ctx); JSValue JS_ThrowStackOverflow (JSContext *ctx);
JSValue JS_ThrowReferenceErrorUninitialized (JSContext *ctx, JSValue name);
int JS_DefineObjectName (JSContext *ctx, JSValue obj, JSValue name); int JS_DefineObjectName (JSContext *ctx, JSValue obj, JSValue name);
int JS_DefineObjectNameComputed (JSContext *ctx, JSValue obj, JSValue str); int JS_DefineObjectNameComputed (JSContext *ctx, JSValue obj, JSValue str);
int js_method_set_properties (JSContext *ctx, JSValue func_obj, JSValue name, int flags, JSValue home_obj); int js_method_set_properties (JSContext *ctx, JSValue func_obj, JSValue name, int flags, JSValue home_obj);
@@ -1889,8 +1905,5 @@ JSFrameRegister *alloc_frame_register(JSContext *ctx, int slot_count);
cJSON *mach_find_scope_record(cJSON *scopes, int function_nr); cJSON *mach_find_scope_record(cJSON *scopes, int function_nr);
int reg_vm_check_interrupt(JSContext *ctx); int reg_vm_check_interrupt(JSContext *ctx);
/* mcode.c exports */
JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, int argc, JSValue *argv, JSValue outer_frame);
#endif /* QUICKJS_INTERNAL_H */ #endif /* QUICKJS_INTERNAL_H */

View File

@@ -1,296 +0,0 @@
/*
* QuickJS opcode definitions
*
* Copyright (c) 2017-2018 Fabrice Bellard
* Copyright (c) 2017-2018 Charlie Gordon
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifdef FMT
FMT(none)
FMT(none_int)
FMT(none_loc)
FMT(none_arg)
FMT(u8)
FMT(i8)
FMT(loc8)
FMT(const8)
FMT(label8)
FMT(u16)
FMT(i16)
FMT(label16)
FMT(npop)
FMT(npopx)
FMT(npop_u16)
FMT(loc)
FMT(arg)
FMT(u32)
FMT(i32)
FMT(const)
FMT(label)
FMT(label_u16)
FMT(key)
FMT(key_u8)
FMT(key_u16)
FMT(key_label_u16)
FMT(u8_u16) /* 1 byte + 2 bytes for upvalue access */
#undef FMT
#endif /* FMT */
#ifdef DEF
#ifndef def
#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
#endif
DEF(invalid, 1, 0, 0, none) /* never emitted */
/* push values */
DEF( push_i32, 5, 0, 1, i32)
DEF( push_const, 5, 0, 1, const)
DEF( fclosure, 5, 0, 1, const) /* must follow push_const */
DEF( null, 1, 0, 1, none)
DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */
DEF( push_false, 1, 0, 1, none)
DEF( push_true, 1, 0, 1, none)
DEF( object, 1, 0, 1, none)
DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */
DEF( drop, 1, 1, 0, none) /* a -> */
DEF( nip, 1, 2, 1, none) /* a b -> b */
DEF( nip1, 1, 3, 2, none) /* a b c -> b c */
DEF( dup, 1, 1, 2, none) /* a -> a a */
DEF( dup1, 1, 2, 3, none) /* a b -> a a b */
DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */
DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */
DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
DEF( swap, 1, 2, 2, none) /* a b -> b a */
DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */
DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */
DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */
DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */
DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */
DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
DEF( return, 1, 1, 0, none)
DEF( return_undef, 1, 0, 0, none)
DEF( throw, 1, 1, 0, none)
DEF( throw_error, 6, 0, 0, key_u8)
DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
bytecode string */
/* Global variable access - resolved by linker to get/set_global_slot */
DEF( check_var, 5, 0, 1, key) /* check if a variable exists - resolved by linker */
DEF( get_var_undef, 5, 0, 1, key) /* resolved by linker to get_global_slot */
DEF( get_var, 5, 0, 1, key) /* resolved by linker to get_global_slot */
DEF( put_var, 5, 1, 0, key) /* resolved by linker to set_global_slot */
DEF( put_var_init, 5, 1, 0, key) /* resolved by linker to set_global_slot */
DEF( put_var_strict, 5, 2, 0, key) /* resolved by linker to set_global_slot */
/* Global variable opcodes - resolved by linker to get/set_global_slot */
DEF( define_var, 6, 0, 0, key_u8)
DEF(check_define_var, 6, 0, 0, key_u8)
DEF( define_func, 6, 1, 0, key_u8)
DEF( get_field, 5, 1, 1, key)
DEF( get_field2, 5, 1, 2, key)
DEF( put_field, 5, 2, 0, key)
DEF( get_array_el, 1, 2, 1, none)
DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
DEF( get_array_el3, 1, 2, 3, none) /* obj prop -> obj prop1 value */
DEF( put_array_el, 1, 3, 0, none)
DEF( define_field, 5, 2, 1, key)
DEF( set_name, 5, 1, 1, key)
DEF(set_name_computed, 1, 2, 2, none)
DEF(define_array_el, 1, 3, 2, none)
DEF(copy_data_properties, 2, 3, 3, u8)
DEF( define_method, 6, 2, 1, key_u8)
DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */
DEF( define_class, 6, 2, 2, key_u8) /* parent ctor -> ctor proto */
DEF( define_class_computed, 6, 3, 3, key_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */
DEF( get_loc, 3, 0, 1, loc)
DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */
DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */
DEF( get_arg, 3, 0, 1, arg)
DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */
DEF(set_loc_uninitialized, 3, 0, 0, loc)
DEF( get_loc_check, 3, 0, 1, loc)
DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
DEF( put_loc_check_init, 3, 1, 0, loc)
DEF(get_loc_checkthis, 3, 0, 1, loc)
DEF( if_false, 5, 1, 0, label)
DEF( if_true, 5, 1, 0, label) /* must come after if_false */
DEF( goto, 5, 0, 0, label) /* must come after if_true */
DEF( catch, 5, 0, 1, label)
DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */
DEF( to_propkey, 1, 1, 1, none)
/* arithmetic/logic operations */
DEF( neg, 1, 1, 1, none)
DEF( plus, 1, 1, 1, none)
DEF( dec, 1, 1, 1, none)
DEF( inc, 1, 1, 1, none)
DEF( post_dec, 1, 1, 2, none)
DEF( post_inc, 1, 1, 2, none)
DEF( dec_loc, 2, 0, 0, loc8)
DEF( inc_loc, 2, 0, 0, loc8)
DEF( add_loc, 2, 1, 0, loc8)
DEF( not, 1, 1, 1, none)
DEF( lnot, 1, 1, 1, none)
DEF( delete, 1, 2, 1, none)
DEF( delete_var, 5, 0, 1, key) /* deprecated - global object is immutable */
DEF( mul, 1, 2, 1, none)
DEF( mul_float, 1, 2, 1, none)
DEF( div, 1, 2, 1, none)
DEF( div_float, 1, 2, 1, none)
DEF( mod, 1, 2, 1, none)
DEF( add, 1, 2, 1, none)
DEF( add_float, 1, 2, 1, none)
DEF( sub, 1, 2, 1, none)
DEF( sub_float, 1, 2, 1, none)
DEF( pow, 1, 2, 1, none)
DEF( shl, 1, 2, 1, none)
DEF( sar, 1, 2, 1, none)
DEF( shr, 1, 2, 1, none)
DEF( lt, 1, 2, 1, none)
DEF( lte, 1, 2, 1, none)
DEF( gt, 1, 2, 1, none)
DEF( gte, 1, 2, 1, none)
DEF( in, 1, 2, 1, none)
DEF( strict_eq, 1, 2, 1, none)
DEF( strict_neq, 1, 2, 1, none)
DEF( and, 1, 2, 1, none)
DEF( xor, 1, 2, 1, none)
DEF( or, 1, 2, 1, none)
/* format template - format_string_cpool_idx(u32), expr_count(u16)
Note: n_push=2 ensures stack has room for temp [format_str, arr] pair,
even though we only leave 1 value (the result) on the stack. */
DEF(format_template, 7, 0, 1, npop_u16)
/* Upvalue access (closures via outer_frame chain) */
DEF( get_up, 4, 0, 1, u8_u16) /* depth:u8, slot:u16 -> value */
DEF( set_up, 4, 1, 0, u8_u16) /* value, depth:u8, slot:u16 -> */
/* Name resolution with bytecode patching */
DEF( get_name, 5, 0, 1, const) /* cpool_idx -> value, patches itself */
DEF( get_env_slot, 3, 0, 1, u16) /* slot -> value (patched from get_name) */
DEF( set_env_slot, 3, 1, 0, u16) /* value -> slot (patched from put_var) */
DEF(get_global_slot, 3, 0, 1, u16) /* slot -> value (patched from get_var) */
DEF(set_global_slot, 3, 1, 0, u16) /* value -> slot (patched from put_var) */
/* must be the last non short and non temporary opcode */
DEF( nop, 1, 0, 0, none)
/* temporary opcodes: never emitted in the final bytecode */
def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
/* the following opcodes must be in the same order as the 'with_x' and
get_var_undef, get_var and put_var opcodes */
def(scope_get_var_undef, 7, 0, 1, key_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_get_var, 7, 0, 1, key_u16) /* emitted in phase 1, removed in phase 2 */
def( scope_put_var, 7, 1, 0, key_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_delete_var, 7, 0, 1, key_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_put_var_init, 7, 0, 2, key_u16) /* emitted in phase 1, removed in phase 2 */
def(scope_get_var_checkthis, 7, 0, 1, key_u16) /* emitted in phase 1, removed in phase 2, only used to return 'this' in derived class constructors */
def(get_field_opt_chain, 5, 1, 1, key) /* emitted in phase 1, removed in phase 2 */
def(get_array_el_opt_chain, 1, 2, 1, none) /* emitted in phase 1, removed in phase 2 */
def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
#if SHORT_OPCODES
DEF( push_minus1, 1, 0, 1, none_int)
DEF( push_0, 1, 0, 1, none_int)
DEF( push_1, 1, 0, 1, none_int)
DEF( push_2, 1, 0, 1, none_int)
DEF( push_3, 1, 0, 1, none_int)
DEF( push_4, 1, 0, 1, none_int)
DEF( push_5, 1, 0, 1, none_int)
DEF( push_6, 1, 0, 1, none_int)
DEF( push_7, 1, 0, 1, none_int)
DEF( push_i8, 2, 0, 1, i8)
DEF( push_i16, 3, 0, 1, i16)
DEF( push_const8, 2, 0, 1, const8)
DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
DEF(push_empty_string, 1, 0, 1, none)
DEF( get_loc8, 2, 0, 1, loc8)
DEF( put_loc8, 2, 1, 0, loc8)
DEF( set_loc8, 2, 1, 1, loc8)
DEF( get_loc0, 1, 0, 1, none_loc)
DEF( get_loc1, 1, 0, 1, none_loc)
DEF( get_loc2, 1, 0, 1, none_loc)
DEF( get_loc3, 1, 0, 1, none_loc)
DEF( put_loc0, 1, 1, 0, none_loc)
DEF( put_loc1, 1, 1, 0, none_loc)
DEF( put_loc2, 1, 1, 0, none_loc)
DEF( put_loc3, 1, 1, 0, none_loc)
DEF( set_loc0, 1, 1, 1, none_loc)
DEF( set_loc1, 1, 1, 1, none_loc)
DEF( set_loc2, 1, 1, 1, none_loc)
DEF( set_loc3, 1, 1, 1, none_loc)
DEF( get_arg0, 1, 0, 1, none_arg)
DEF( get_arg1, 1, 0, 1, none_arg)
DEF( get_arg2, 1, 0, 1, none_arg)
DEF( get_arg3, 1, 0, 1, none_arg)
DEF( put_arg0, 1, 1, 0, none_arg)
DEF( put_arg1, 1, 1, 0, none_arg)
DEF( put_arg2, 1, 1, 0, none_arg)
DEF( put_arg3, 1, 1, 0, none_arg)
DEF( set_arg0, 1, 1, 1, none_arg)
DEF( set_arg1, 1, 1, 1, none_arg)
DEF( set_arg2, 1, 1, 1, none_arg)
DEF( set_arg3, 1, 1, 1, none_arg)
DEF( if_false8, 2, 1, 0, label8)
DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */
DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */
DEF( goto16, 3, 0, 0, label16)
DEF( call0, 1, 1, 1, npopx)
DEF( call1, 1, 1, 1, npopx)
DEF( call2, 1, 1, 1, npopx)
DEF( call3, 1, 1, 1, npopx)
DEF( is_null, 1, 1, 1, none)
#endif
#undef DEF
#undef def
#endif /* DEF */

View File

@@ -55,13 +55,13 @@ enum mist_obj_type {
OBJ_FORWARD = 7 OBJ_FORWARD = 7
}; };
typedef uint64_t JSValue;
#define OBJHDR_S_BIT 3u #define OBJHDR_S_BIT 3u
#define OBJHDR_P_BIT 4u #define OBJHDR_P_BIT 4u
#define OBJHDR_A_BIT 5u #define OBJHDR_A_BIT 5u
#define OBJHDR_R_BIT 7u #define OBJHDR_R_BIT 7u
#define OBJHDR_FLAG(bit) ((objhdr_t)1ull << (bit)) #define OBJHDR_FLAG(bit) ((objhdr_t)1ull << (bit))
#define OBJHDR_S_MASK OBJHDR_FLAG (OBJHDR_S_BIT) #define OBJHDR_S_MASK OBJHDR_FLAG (OBJHDR_S_BIT)
#define OBJHDR_P_MASK OBJHDR_FLAG (OBJHDR_P_BIT) #define OBJHDR_P_MASK OBJHDR_FLAG (OBJHDR_P_BIT)
@@ -115,39 +115,24 @@ struct JSGCRef;
============================================================ */ ============================================================ */
#if INTPTR_MAX >= INT64_MAX
#define JS_PTR64
#define JS_PTR64_DEF(a) a
typedef uint64_t JSValue;
#define JSW 8
#else
typedef uint32_t JSValue;
#define JSW 4
#define JS_PTR64_DEF(a)
#endif
#define JSValue JSValue
/* JSValueConst is just JSValue (const is not needed in value semantics) */ /* JSValueConst is just JSValue (const is not needed in value semantics) */
typedef JSValue JSValueConst; typedef JSValue JSValueConst;
#define JSW 8
/* LSB-based tags */ /* LSB-based tags */
enum { enum {
/* Primary tags (low bits) */ /* Primary tags (low bits) */
JS_TAG_INT = 0, /* LSB = 0 */ JS_TAG_INT = 0, /* LSB = 0 */
JS_TAG_PTR = 1, /* LSB = 01 */ JS_TAG_PTR = 1, /* LSB = 01 */
#ifdef JS_PTR64
JS_TAG_SHORT_FLOAT = 5, /* LSB = 101 */ JS_TAG_SHORT_FLOAT = 5, /* LSB = 101 */
#endif
JS_TAG_SPECIAL = 3, /* LSB = 11 */ JS_TAG_SPECIAL = 3, /* LSB = 11 */
/* Special subtypes (5 bits: xxxx11) */ /* Special subtypes (5 bits: xxxx11) */
JS_TAG_BOOL = 0x03, /* 00011 */ JS_TAG_BOOL = 0x03, /* 00011 */
JS_TAG_NULL = 0x07, /* 00111 */ JS_TAG_NULL = 0x07, /* 00111 */
JS_TAG_EXCEPTION = 0x0F, /* 01111 */ JS_TAG_EXCEPTION = 0x0F, /* 01111 */
JS_TAG_UNINITIALIZED = 0x17, /* 10111 */ JS_TAG_STRING_IMM = 0x0B, /* 01011 - immediate ASCII (up to 7 chars) */
JS_TAG_STRING_IMM = 0x1B, /* 11011 - immediate ASCII (up to 7 chars) */
JS_TAG_CATCH_OFFSET = 0x1F, /* 11111 */
}; };
/* Compatibility tag aliases for external code */ /* Compatibility tag aliases for external code */
@@ -180,16 +165,10 @@ void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref);
/* Get primary tag (low 2-3 bits) */ /* Get primary tag (low 2-3 bits) */
static inline int static inline int
JS_VALUE_GET_TAG (JSValue v) { JS_VALUE_GET_TAG (JSValue v) {
#ifdef JS_PTR64
if ((v & 1) == 0) return JS_TAG_INT; if ((v & 1) == 0) return JS_TAG_INT;
if ((v & 7) == JS_TAG_SHORT_FLOAT) return JS_TAG_SHORT_FLOAT; if ((v & 7) == JS_TAG_SHORT_FLOAT) return JS_TAG_SHORT_FLOAT;
if ((v & 3) == JS_TAG_PTR) return JS_TAG_PTR; if ((v & 3) == JS_TAG_PTR) return JS_TAG_PTR;
return (int)(v & 0x1F); /* special tag */ return (int)(v & 0x1F); /* special tag */
#else
if ((v & 1) == 0) return JS_TAG_INT;
if ((v & 3) == JS_TAG_PTR) return JS_TAG_PTR;
return (int)(v & 0x1F);
#endif
} }
#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG (v) #define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG (v)
@@ -220,7 +199,6 @@ static inline JSValue _JS_MkVal (int tag, int32_t val) {
Out of range → JS_NULL Out of range → JS_NULL
============================================================ */ ============================================================ */
#ifdef JS_PTR64
static inline JSValue static inline JSValue
__JS_NewFloat64 (JSContext *ctx, double d) { __JS_NewFloat64 (JSContext *ctx, double d) {
union { union {
@@ -280,17 +258,6 @@ static inline double JS_VALUE_GET_FLOAT64 (JSValue v) {
#define JS_TAG_IS_FLOAT64(tag) ((tag) == JS_TAG_SHORT_FLOAT) #define JS_TAG_IS_FLOAT64(tag) ((tag) == JS_TAG_SHORT_FLOAT)
#define JS_NAN JS_MKVAL (JS_TAG_NULL, 0) #define JS_NAN JS_MKVAL (JS_TAG_NULL, 0)
#else /* 32-bit: no short float, use boxed double */
static inline JSValue __JS_NewFloat64 (JSContext *ctx,
double d); /* forward decl */
static inline double JS_VALUE_GET_FLOAT64 (JSValue v);
#define JS_TAG_IS_FLOAT64(tag) (0)
#define JS_NAN JS_MKVAL (JS_TAG_NULL, 0)
#endif /* JS_PTR64 */
/* ============================================================ /* ============================================================
Type Checks Type Checks
============================================================ */ ============================================================ */
@@ -299,14 +266,10 @@ static inline JS_BOOL JS_IsInt (JSValue v) { return (v & 1) == 0; }
static inline JS_BOOL JS_IsPtr (JSValue v) { return (v & 7) == JS_TAG_PTR; } static inline JS_BOOL JS_IsPtr (JSValue v) { return (v & 7) == JS_TAG_PTR; }
static inline JS_BOOL JS_IsSpecial (JSValue v) { return (v & 3) == JS_TAG_SPECIAL; } static inline JS_BOOL JS_IsSpecial (JSValue v) { return (v & 3) == JS_TAG_SPECIAL; }
#ifdef JS_PTR64
static inline JS_BOOL static inline JS_BOOL
JS_IsShortFloat (JSValue v) { JS_IsShortFloat (JSValue v) {
return (v & 7) == JS_TAG_SHORT_FLOAT; return (v & 7) == JS_TAG_SHORT_FLOAT;
} }
#endif
#define JS_VALUE_IS_BOTH_INT(v1, v2) (((v1) & 1) == 0 && ((v2) & 1) == 0) #define JS_VALUE_IS_BOTH_INT(v1, v2) (((v1) & 1) == 0 && ((v2) & 1) == 0)
#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) \ #define JS_VALUE_IS_BOTH_FLOAT(v1, v2) \
@@ -320,7 +283,6 @@ JS_IsShortFloat (JSValue v) {
#define JS_FALSE ((JSValue)JS_TAG_BOOL) #define JS_FALSE ((JSValue)JS_TAG_BOOL)
#define JS_TRUE ((JSValue)(JS_TAG_BOOL | (1 << 5))) #define JS_TRUE ((JSValue)(JS_TAG_BOOL | (1 << 5)))
#define JS_EXCEPTION ((JSValue)JS_TAG_EXCEPTION) #define JS_EXCEPTION ((JSValue)JS_TAG_EXCEPTION)
#define JS_UNINITIALIZED ((JSValue)JS_TAG_UNINITIALIZED)
/* flags for object properties - simplified model: /* flags for object properties - simplified model:
- No per-property writable/configurable (use stone() for immutability) - No per-property writable/configurable (use stone() for immutability)
@@ -344,27 +306,10 @@ typedef JSValue JSCFunctionData (JSContext *ctx, JSValue this_val,
int argc, JSValue *argv, int magic, int argc, JSValue *argv, int magic,
JSValue *data); JSValue *data);
typedef struct JSMallocState {
size_t malloc_count;
size_t malloc_size;
size_t malloc_limit;
void *opaque; /* user opaque */
} JSMallocState;
typedef struct JSMallocFunctions {
void *(*js_malloc) (JSMallocState *s, size_t size);
void (*js_free) (JSMallocState *s, void *ptr);
void *(*js_realloc) (JSMallocState *s, void *ptr, size_t size);
size_t (*js_malloc_usable_size) (const void *ptr);
} JSMallocFunctions;
typedef struct JSGCObjectHeader JSGCObjectHeader;
JSValue JS_Stone (JSContext *ctx, JSValue this_val); JSValue JS_Stone (JSContext *ctx, JSValue this_val);
JSRuntime *JS_NewRuntime (void); JSRuntime *JS_NewRuntime (void);
/* info lifetime must exceed that of rt */ /* info lifetime must exceed that of rt */
void JS_SetRuntimeInfo (JSRuntime *rt, const char *info);
void JS_SetMemoryLimit (JSRuntime *rt, size_t limit); void JS_SetMemoryLimit (JSRuntime *rt, size_t limit);
/* use 0 to disable maximum stack size check */ /* use 0 to disable maximum stack size check */
void JS_SetMaxStackSize (JSContext *ctx, size_t stack_size); void JS_SetMaxStackSize (JSContext *ctx, size_t stack_size);
@@ -372,11 +317,6 @@ void JS_SetMaxStackSize (JSContext *ctx, size_t stack_size);
used to check stack overflow. */ used to check stack overflow. */
void JS_UpdateStackTop (JSContext *ctx); void JS_UpdateStackTop (JSContext *ctx);
void JS_FreeRuntime (JSRuntime *rt); void JS_FreeRuntime (JSRuntime *rt);
void *JS_GetRuntimeOpaque (JSRuntime *rt);
void JS_SetRuntimeOpaque (JSRuntime *rt, void *opaque);
typedef void JS_MarkFunc (JSRuntime *rt, JSGCObjectHeader *gp);
/* JS_MarkValue is a no-op with copying GC (values are traced from roots) */
void JS_MarkValue (JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func);
JS_BOOL JS_IsLiveObject (JSRuntime *rt, JSValue obj); JS_BOOL JS_IsLiveObject (JSRuntime *rt, JSValue obj);
JSContext *JS_NewContext (JSRuntime *rt); JSContext *JS_NewContext (JSRuntime *rt);
@@ -410,8 +350,6 @@ void JS_ComputeMemoryUsage (JSRuntime *rt, JSMemoryUsage *s);
void JS_DumpMemoryUsage (FILE *fp, const JSMemoryUsage *s, JSRuntime *rt); void JS_DumpMemoryUsage (FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);
typedef void JSClassFinalizer (JSRuntime *rt, JSValue val); typedef void JSClassFinalizer (JSRuntime *rt, JSValue val);
typedef void JSClassGCMark (JSRuntime *rt, JSValue val,
JS_MarkFunc *mark_func);
typedef JSValue JSClassCall (JSContext *ctx, JSValue func_obj, typedef JSValue JSClassCall (JSContext *ctx, JSValue func_obj,
JSValue this_val, int argc, JSValue this_val, int argc,
JSValue *argv, int flags); JSValue *argv, int flags);
@@ -419,8 +357,6 @@ typedef JSValue JSClassCall (JSContext *ctx, JSValue func_obj,
typedef struct JSClassDef { typedef struct JSClassDef {
const char *class_name; const char *class_name;
JSClassFinalizer *finalizer; JSClassFinalizer *finalizer;
JSClassGCMark *gc_mark;
/* if call != NULL, the object is a function */
JSClassCall *call; JSClassCall *call;
} JSClassDef; } JSClassDef;
@@ -459,11 +395,6 @@ JS_NewInt32 (JSContext *ctx, int32_t val) {
return JS_MKVAL (JS_TAG_INT, val); return JS_MKVAL (JS_TAG_INT, val);
} }
static inline JSValue
JS_NewCatchOffset (JSContext *ctx, int32_t val) {
return JS_MKVAL (JS_TAG_CATCH_OFFSET, val);
}
static inline JSValue static inline JSValue
JS_NewInt64 (JSContext *ctx, int64_t val) { JS_NewInt64 (JSContext *ctx, int64_t val) {
JSValue v; JSValue v;
@@ -521,10 +452,6 @@ static inline JS_BOOL JS_IsException (JSValue v) {
return (JS_VALUE_GET_TAG (v) == JS_TAG_EXCEPTION); return (JS_VALUE_GET_TAG (v) == JS_TAG_EXCEPTION);
} }
static inline JS_BOOL JS_IsUninitialized (JSValue v) {
return (JS_VALUE_GET_TAG (v) == JS_TAG_UNINITIALIZED);
}
/* Immediate String Helpers */ /* Immediate String Helpers */
#define MIST_ASCII_MAX_LEN 7 #define MIST_ASCII_MAX_LEN 7
@@ -538,13 +465,11 @@ MIST_GetImmediateASCIILen (JSValue v) {
return (int)((v >> 5) & 0x7); return (int)((v >> 5) & 0x7);
} }
static inline int static inline int MIST_GetImmediateASCIIChar (JSValue v, int idx) {
MIST_GetImmediateASCIIChar (JSValue v, int idx) {
return (int)((v >> (8 + idx * 8)) & 0xFF); return (int)((v >> (8 + idx * 8)) & 0xFF);
} }
static inline JSValue static inline JSValue MIST_TryNewImmediateASCII (const char *str, size_t len) {
MIST_TryNewImmediateASCII (const char *str, size_t len) {
if (len > MIST_ASCII_MAX_LEN) return JS_NULL; if (len > MIST_ASCII_MAX_LEN) return JS_NULL;
JSValue v = (JSValue)JS_TAG_STRING_IMM | ((JSValue)len << 5); JSValue v = (JSValue)JS_TAG_STRING_IMM | ((JSValue)len << 5);
for (size_t i = 0; i < len; i++) { for (size_t i = 0; i < len; i++) {
@@ -555,20 +480,10 @@ MIST_TryNewImmediateASCII (const char *str, size_t len) {
return v; return v;
} }
static inline JS_BOOL JS_IsInteger (JSValue v) {
return JS_VALUE_GET_TAG (v) == JS_TAG_INT;
}
static inline JS_BOOL JS_IsObject (JSValue v) {
return JS_IsPtr (v);
}
JS_BOOL JS_IsArray(JSValue v); JS_BOOL JS_IsArray(JSValue v);
JS_BOOL JS_IsRecord(JSValue v); JS_BOOL JS_IsRecord(JSValue v);
#define JS_IsObject JS_IsRecord
JS_BOOL JS_IsFunction(JSValue v); JS_BOOL JS_IsFunction(JSValue v);
JS_BOOL JS_IsCode(JSValue v);
JS_BOOL JS_IsForwarded(JSValue v);
JS_BOOL JS_IsFrame(JSValue v);
JS_BOOL JS_IsBlob(JSValue v); JS_BOOL JS_IsBlob(JSValue v);
JS_BOOL JS_IsText(JSValue v); JS_BOOL JS_IsText(JSValue v);
static JS_BOOL JS_IsStone(JSValue v); static JS_BOOL JS_IsStone(JSValue v);
@@ -591,6 +506,7 @@ JSValue __js_printf_like (2, 3)
JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...); JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
JSValue JS_ThrowOutOfMemory (JSContext *ctx); JSValue JS_ThrowOutOfMemory (JSContext *ctx);
// TODO: rename this to just "eq"
JS_BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2); JS_BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2);
int JS_ToBool (JSContext *ctx, JSValue val); /* return -1 for JS_EXCEPTION */ int JS_ToBool (JSContext *ctx, JSValue val); /* return -1 for JS_EXCEPTION */
@@ -625,7 +541,6 @@ JSValue JS_NewObject (JSContext *ctx);
JSValue JS_NewArray (JSContext *ctx); JSValue JS_NewArray (JSContext *ctx);
JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len); JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len);
/* GC-safe push: takes pointer to array, updates it if array grows */
int JS_ArrayPush (JSContext *ctx, JSValue *arr_ptr, JSValue val); int JS_ArrayPush (JSContext *ctx, JSValue *arr_ptr, JSValue val);
JSValue JS_ArrayPop (JSContext *ctx, JSValue obj); JSValue JS_ArrayPop (JSContext *ctx, JSValue obj);
@@ -690,24 +605,17 @@ void JS_PrintTextLn (JSContext *ctx, JSValue val);
void JS_PrintFormatted (JSContext *ctx, const char *fmt, int count, JSValue *values); void JS_PrintFormatted (JSContext *ctx, const char *fmt, int count, JSValue *values);
JSValue JS_GetProperty (JSContext *ctx, JSValue this_obj, JSValue prop); JSValue JS_GetProperty (JSContext *ctx, JSValue this_obj, JSValue prop);
int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val);
// For records // For records
JSValue JS_GetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop); JSValue JS_GetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop);
int JS_SetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop, JSValue val); int JS_SetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop, JSValue val);
// Set property on the global object
int JS_SetGlobalStr (JSContext *ctx, const char *prop, JSValue val);
int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val);
JSValue JS_GetPrototype (JSContext *ctx, JSValue val);
// Must be an array // Must be an array
JSValue JS_GetPropertyNumber (JSContext *ctx, JSValue this_obj, int idx); JSValue JS_GetPropertyNumber (JSContext *ctx, JSValue this_obj, int idx);
JSValue JS_SetPropertyNumber (JSContext *ctx, JSValue obj, int idx, JSValue val); JSValue JS_SetPropertyNumber (JSContext *ctx, JSValue obj, int idx, JSValue val);
// Indexed property access (works with arrays and objects) JSValue JS_GetPrototype (JSContext *ctx, JSValue val);
JSValue JS_GetPropertyUint32 (JSContext *ctx, JSValue this_obj, uint32_t idx);
int JS_SetPropertyUint32 (JSContext *ctx, JSValue this_obj, uint32_t idx, JSValue val);
int JS_SetPropertyInt64 (JSContext *ctx, JSValue this_obj, int64_t idx, JSValue val);
/* Get property keys as array of text */ /* Get property keys as array of text */
JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj); JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj);
@@ -732,13 +640,6 @@ JSValue JS_JSONStringify (JSContext *ctx, JSValue obj,
typedef int JSInterruptHandler (JSRuntime *rt, void *opaque); typedef int JSInterruptHandler (JSRuntime *rt, void *opaque);
void JS_SetInterruptHandler (JSContext *ctx, JSInterruptHandler *cb, void JS_SetInterruptHandler (JSContext *ctx, JSInterruptHandler *cb,
void *opaque); void *opaque);
/* select which debug info is stripped from the compiled code */
#define JS_STRIP_SOURCE (1 << 0) /* strip source code */
#define JS_STRIP_DEBUG \
(1 << 1) /* strip all debug info including source code */
void JS_SetStripInfo (JSRuntime *rt, int flags);
int JS_GetStripInfo (JSRuntime *rt);
/* C function definition */ /* C function definition */
typedef enum JSCFunctionEnum { typedef enum JSCFunctionEnum {
@@ -1046,12 +947,12 @@ typedef void (*js_hook) (JSContext *, int type, js_debug *dbg, void *user);
void js_debug_sethook (JSContext *ctx, js_hook, int type, void *user); void js_debug_sethook (JSContext *ctx, js_hook, int type, void *user);
uint32_t js_debugger_stack_depth (JSContext *ctx); uint32_t js_debugger_stack_depth (JSContext *ctx);
JSValue js_debugger_backtrace_fns (JSContext *ctx, const uint8_t *cur_pc); JSValue js_debugger_backtrace_fns (JSContext *ctx);
JSValue js_debugger_closure_variables (JSContext *ctx, JSValue fn); JSValue js_debugger_closure_variables (JSContext *ctx, JSValue fn);
JSValue js_debugger_local_variables (JSContext *ctx, int stack_index); JSValue js_debugger_local_variables (JSContext *ctx, int stack_index);
void js_debugger_set_closure_variable (JSContext *js, JSValue fn, void js_debugger_set_closure_variable (JSContext *js, JSValue fn,
JSValue var_name, JSValue val); JSValue var_name, JSValue val);
JSValue js_debugger_build_backtrace (JSContext *ctx, const uint8_t *cur_pc); JSValue js_debugger_build_backtrace (JSContext *ctx);
JSValue js_debugger_fn_info (JSContext *ctx, JSValue fn); JSValue js_debugger_fn_info (JSContext *ctx, JSValue fn);
JSValue js_debugger_fn_bytecode (JSContext *js, JSValue fn); JSValue js_debugger_fn_bytecode (JSContext *js, JSValue fn);
void *js_debugger_val_address (JSContext *js, JSValue val); void *js_debugger_val_address (JSContext *js, JSValue val);
@@ -1091,30 +992,14 @@ MachCode *JS_DeserializeMachCode(const uint8_t *data, size_t size);
/* Load compiled MachCode into a JSContext, materializing JSValues. */ /* Load compiled MachCode into a JSContext, materializing JSValues. */
struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env); struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env);
/* Dump MACH bytecode to stdout. Takes AST cJSON tree. */
void JS_DumpMachTree (JSContext *ctx, struct cJSON *ast, JSValue env);
/* Dump MACH bytecode to stdout. Takes AST JSON string. */
void JS_DumpMach (JSContext *ctx, const char *ast_json, JSValue env);
/* Compile and execute MACH bytecode from AST cJSON tree. */
JSValue JS_RunMachTree (JSContext *ctx, struct cJSON *ast, JSValue env);
/* Compile and execute MACH bytecode from AST JSON string. */
JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env);
/* Deserialize and execute pre-compiled MACH binary bytecode. */ /* Deserialize and execute pre-compiled MACH binary bytecode. */
JSValue JS_RunMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env); JSValue JS_RunMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env);
/* Execute MCODE from cJSON tree. Takes ownership of root. */ /* Dump disassembly of pre-compiled MACH binary bytecode. */
JSValue JS_CallMcodeTree (JSContext *ctx, struct cJSON *root); void JS_DumpMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env);
/* Execute MCODE from cJSON tree with hidden env. Takes ownership of root. */ /* Compile mcode JSON IR to MachCode binary. */
JSValue JS_CallMcodeTreeEnv (JSContext *ctx, struct cJSON *root, JSValue env); MachCode *mach_compile_mcode(struct cJSON *mcode_json);
/* Parse and execute MCODE JSON string.
Returns result of execution, or JS_EXCEPTION on error. */
JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json);
/* Get stack trace as cJSON array of frame objects. /* Get stack trace as cJSON array of frame objects.
Returns NULL if no register VM frame is active. Returns NULL if no register VM frame is active.

File diff suppressed because it is too large Load Diff

View File

@@ -48,6 +48,8 @@ static struct {
actor_node *main_tail; // Main Thread Queue Tail actor_node *main_tail; // Main Thread Queue Tail
int shutting_down; int shutting_down;
int quiescence_enabled; // set after bootstrap, before actor_loop
_Atomic int quiescent_count; // actors idle with no messages and no timers
pthread_t *worker_threads; pthread_t *worker_threads;
int num_workers; int num_workers;
@@ -258,6 +260,10 @@ void actor_initialize(void) {
void actor_free(cell_rt *actor) void actor_free(cell_rt *actor)
{ {
if (actor->is_quiescent) {
actor->is_quiescent = 0;
atomic_fetch_sub(&engine.quiescent_count, 1);
}
lockless_shdel(actors, actor->id); lockless_shdel(actors, actor->id);
// Do not go forward with actor destruction until the actor is completely free // Do not go forward with actor destruction until the actor is completely free
@@ -318,6 +324,21 @@ int scheduler_actor_count(void) {
return (int)lockless_shlen(actors); return (int)lockless_shlen(actors);
} }
void scheduler_enable_quiescence(void) {
engine.quiescence_enabled = 1;
// Check if all actors are already quiescent
int qc = atomic_load(&engine.quiescent_count);
int total = (int)lockless_shlen(actors);
if (qc >= total && total > 0) {
pthread_mutex_lock(&engine.lock);
engine.shutting_down = 1;
pthread_cond_broadcast(&engine.wake_cond);
pthread_cond_broadcast(&engine.timer_cond);
pthread_cond_broadcast(&engine.main_cond);
pthread_mutex_unlock(&engine.lock);
}
}
void exit_handler(void) { void exit_handler(void) {
static int already_exiting = 0; static int already_exiting = 0;
if (already_exiting) return; if (already_exiting) return;
@@ -371,6 +392,10 @@ void set_actor_state(cell_rt *actor)
case ACTOR_IDLE: case ACTOR_IDLE:
if (arrlen(actor->letters)) { if (arrlen(actor->letters)) {
if (actor->is_quiescent) {
actor->is_quiescent = 0;
atomic_fetch_sub(&engine.quiescent_count, 1);
}
actor->state = ACTOR_READY; actor->state = ACTOR_READY;
actor->ar = 0; actor->ar = 0;
@@ -398,7 +423,25 @@ void set_actor_state(cell_rt *actor)
} }
pthread_mutex_unlock(&engine.lock); pthread_mutex_unlock(&engine.lock);
} else if (!arrlen(actor->letters) && !hmlen(actor->timers)) { } else if (!hmlen(actor->timers)) {
// No messages AND no timers
// Only count as quiescent if no $unneeded callback registered
int has_unneeded = !JS_IsNull(actor->unneeded_ref.val);
if (!actor->is_quiescent && actor->id && !has_unneeded) {
actor->is_quiescent = 1;
int qc = atomic_fetch_add(&engine.quiescent_count, 1) + 1;
int total = (int)lockless_shlen(actors);
if (qc >= total && total > 0 && engine.quiescence_enabled) {
pthread_mutex_lock(&engine.lock);
engine.shutting_down = 1;
pthread_cond_broadcast(&engine.wake_cond);
pthread_cond_broadcast(&engine.timer_cond);
pthread_cond_broadcast(&engine.main_cond);
pthread_mutex_unlock(&engine.lock);
}
}
if (!engine.shutting_down) {
// Schedule remove timer // Schedule remove timer
static uint32_t global_timer_id = 1; static uint32_t global_timer_id = 1;
uint32_t id = global_timer_id++; uint32_t id = global_timer_id++;
@@ -414,6 +457,13 @@ void set_actor_state(cell_rt *actor)
} }
pthread_mutex_unlock(&engine.lock); pthread_mutex_unlock(&engine.lock);
} }
} else {
// Has timers but no letters — waiting, not quiescent
if (actor->is_quiescent) {
actor->is_quiescent = 0;
atomic_fetch_sub(&engine.quiescent_count, 1);
}
}
break; break;
} }
@@ -539,6 +589,12 @@ const char *register_actor(const char *id, cell_rt *actor, int mainthread, doubl
free(actor->id); free(actor->id);
return "Actor with given ID already exists."; return "Actor with given ID already exists.";
} }
// Now that actor is in the registry, track its quiescent state
if (actor->state == ACTOR_IDLE && !arrlen(actor->letters)
&& !hmlen(actor->timers) && JS_IsNull(actor->unneeded_ref.val)) {
actor->is_quiescent = 1;
atomic_fetch_add(&engine.quiescent_count, 1);
}
return NULL; return NULL;
} }

View File

@@ -21,7 +21,6 @@ static const char *js_type_name(JSValue v) {
if (JS_IsText(v)) return "string"; if (JS_IsText(v)) return "string";
if (JS_IsArray(v)) return "array"; if (JS_IsArray(v)) return "array";
if (JS_IsRecord(v)) return "object"; if (JS_IsRecord(v)) return "object";
if (JS_IsObject(v)) return "object";
return "unknown"; return "unknown";
} }
@@ -310,7 +309,6 @@ TEST(string_heap_to_cstring) {
TEST(object_create) { TEST(object_create) {
JSValue obj = JS_NewObject(ctx); JSValue obj = JS_NewObject(ctx);
ASSERT(JS_IsObject(obj));
ASSERT(JS_IsRecord(obj)); ASSERT(JS_IsRecord(obj));
return 1; return 1;
} }
@@ -435,7 +433,6 @@ TEST(object_many_properties_resize) {
TEST(array_create) { TEST(array_create) {
JSValue arr = JS_NewArray(ctx); JSValue arr = JS_NewArray(ctx);
ASSERT(JS_IsObject(arr));
ASSERT(JS_IsArray(arr)); ASSERT(JS_IsArray(arr));
return 1; return 1;
} }
@@ -465,9 +462,9 @@ TEST(array_get_by_index) {
JS_ArrayPush(ctx, &arr_ref.val, JS_NewInt32(ctx, 200)); JS_ArrayPush(ctx, &arr_ref.val, JS_NewInt32(ctx, 200));
JS_ArrayPush(ctx, &arr_ref.val, JS_NewInt32(ctx, 300)); JS_ArrayPush(ctx, &arr_ref.val, JS_NewInt32(ctx, 300));
JSValue v0 = JS_GetPropertyUint32(ctx, arr_ref.val, 0); JSValue v0 = JS_GetPropertyNumber(ctx, arr_ref.val, 0);
JSValue v1 = JS_GetPropertyUint32(ctx, arr_ref.val, 1); JSValue v1 = JS_GetPropertyNumber(ctx, arr_ref.val, 1);
JSValue v2 = JS_GetPropertyUint32(ctx, arr_ref.val, 2); JSValue v2 = JS_GetPropertyNumber(ctx, arr_ref.val, 2);
JS_PopGCRef(ctx, &arr_ref); JS_PopGCRef(ctx, &arr_ref);
ASSERT_INT(v0, 100); ASSERT_INT(v0, 100);
@@ -486,12 +483,12 @@ TEST(array_set_by_index) {
/* Create values first, then read arr_ref.val */ /* Create values first, then read arr_ref.val */
JSValue v55 = JS_NewInt32(ctx, 55); JSValue v55 = JS_NewInt32(ctx, 55);
JS_SetPropertyUint32(ctx, arr_ref.val, 0, v55); JS_SetPropertyNumber(ctx, arr_ref.val, 0, v55);
JSValue v66 = JS_NewInt32(ctx, 66); JSValue v66 = JS_NewInt32(ctx, 66);
JS_SetPropertyUint32(ctx, arr_ref.val, 1, v66); JS_SetPropertyNumber(ctx, arr_ref.val, 1, v66);
JSValue v0 = JS_GetPropertyUint32(ctx, arr_ref.val, 0); JSValue v0 = JS_GetPropertyNumber(ctx, arr_ref.val, 0);
JSValue v1 = JS_GetPropertyUint32(ctx, arr_ref.val, 1); JSValue v1 = JS_GetPropertyNumber(ctx, arr_ref.val, 1);
JS_PopGCRef(ctx, &arr_ref); JS_PopGCRef(ctx, &arr_ref);
ASSERT_INT(v0, 55); ASSERT_INT(v0, 55);
@@ -525,7 +522,7 @@ TEST(array_out_of_bounds_is_null) {
arr_ref.val = JS_NewArray(ctx); arr_ref.val = JS_NewArray(ctx);
JS_ArrayPush(ctx, &arr_ref.val, JS_NewInt32(ctx, 1)); JS_ArrayPush(ctx, &arr_ref.val, JS_NewInt32(ctx, 1));
JSValue val = JS_GetPropertyUint32(ctx, arr_ref.val, 999); JSValue val = JS_GetPropertyNumber(ctx, arr_ref.val, 999);
JS_PopGCRef(ctx, &arr_ref); JS_PopGCRef(ctx, &arr_ref);
ASSERT(JS_IsNull(val)); ASSERT(JS_IsNull(val));
return 1; return 1;
@@ -544,10 +541,10 @@ TEST(array_mixed_types) {
JS_ArrayPush(ctx, &arr_ref.val, JS_TRUE); JS_ArrayPush(ctx, &arr_ref.val, JS_TRUE);
JS_ArrayPush(ctx, &arr_ref.val, JS_NULL); JS_ArrayPush(ctx, &arr_ref.val, JS_NULL);
JSValue v0 = JS_GetPropertyUint32(ctx, arr_ref.val, 0); JSValue v0 = JS_GetPropertyNumber(ctx, arr_ref.val, 0);
JSValue v1 = JS_GetPropertyUint32(ctx, arr_ref.val, 1); JSValue v1 = JS_GetPropertyNumber(ctx, arr_ref.val, 1);
JSValue v2 = JS_GetPropertyUint32(ctx, arr_ref.val, 2); JSValue v2 = JS_GetPropertyNumber(ctx, arr_ref.val, 2);
JSValue v3 = JS_GetPropertyUint32(ctx, arr_ref.val, 3); JSValue v3 = JS_GetPropertyNumber(ctx, arr_ref.val, 3);
JS_PopGCRef(ctx, &str_ref); JS_PopGCRef(ctx, &str_ref);
JS_PopGCRef(ctx, &arr_ref); JS_PopGCRef(ctx, &arr_ref);
@@ -571,9 +568,9 @@ TEST(array_many_elements_resize) {
JS_GetLength(ctx, arr_ref.val, &len); JS_GetLength(ctx, arr_ref.val, &len);
/* Verify some values */ /* Verify some values */
JSValue v0 = JS_GetPropertyUint32(ctx, arr_ref.val, 0); JSValue v0 = JS_GetPropertyNumber(ctx, arr_ref.val, 0);
JSValue v500 = JS_GetPropertyUint32(ctx, arr_ref.val, 500); JSValue v500 = JS_GetPropertyNumber(ctx, arr_ref.val, 500);
JSValue v999 = JS_GetPropertyUint32(ctx, arr_ref.val, 999); JSValue v999 = JS_GetPropertyNumber(ctx, arr_ref.val, 999);
/* Pop BEFORE assertions */ /* Pop BEFORE assertions */
JS_PopGCRef(ctx, &arr_ref); JS_PopGCRef(ctx, &arr_ref);
@@ -716,9 +713,9 @@ TEST(array_slice_basic) {
JS_GetLength(ctx, sliced, &len); JS_GetLength(ctx, sliced, &len);
ASSERT(len == 3); ASSERT(len == 3);
JSValue v0 = JS_GetPropertyUint32(ctx, sliced, 0); JSValue v0 = JS_GetPropertyNumber(ctx, sliced, 0);
JSValue v1 = JS_GetPropertyUint32(ctx, sliced, 1); JSValue v1 = JS_GetPropertyNumber(ctx, sliced, 1);
JSValue v2 = JS_GetPropertyUint32(ctx, sliced, 2); JSValue v2 = JS_GetPropertyNumber(ctx, sliced, 2);
ASSERT_INT(v0, 10); ASSERT_INT(v0, 10);
ASSERT_INT(v1, 20); ASSERT_INT(v1, 20);
ASSERT_INT(v2, 30); ASSERT_INT(v2, 30);
@@ -747,10 +744,10 @@ TEST(array_concat_basic) {
JS_GetLength(ctx, result, &len); JS_GetLength(ctx, result, &len);
ASSERT(len == 4); ASSERT(len == 4);
ASSERT_INT(JS_GetPropertyUint32(ctx, result, 0), 1); ASSERT_INT(JS_GetPropertyNumber(ctx, result, 0), 1);
ASSERT_INT(JS_GetPropertyUint32(ctx, result, 1), 2); ASSERT_INT(JS_GetPropertyNumber(ctx, result, 1), 2);
ASSERT_INT(JS_GetPropertyUint32(ctx, result, 2), 3); ASSERT_INT(JS_GetPropertyNumber(ctx, result, 2), 3);
ASSERT_INT(JS_GetPropertyUint32(ctx, result, 3), 4); ASSERT_INT(JS_GetPropertyNumber(ctx, result, 3), 4);
return 1; return 1;
} }
@@ -772,11 +769,11 @@ TEST(array_sort_numbers) {
JS_GetLength(ctx, sorted, &len); JS_GetLength(ctx, sorted, &len);
ASSERT(len == 5); ASSERT(len == 5);
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 0), 10); ASSERT_INT(JS_GetPropertyNumber(ctx, sorted, 0), 10);
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 1), 20); ASSERT_INT(JS_GetPropertyNumber(ctx, sorted, 1), 20);
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 2), 30); ASSERT_INT(JS_GetPropertyNumber(ctx, sorted, 2), 30);
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 3), 40); ASSERT_INT(JS_GetPropertyNumber(ctx, sorted, 3), 40);
ASSERT_INT(JS_GetPropertyUint32(ctx, sorted, 4), 50); ASSERT_INT(JS_GetPropertyNumber(ctx, sorted, 4), 50);
return 1; return 1;
} }
@@ -840,8 +837,8 @@ TEST(array_filter_basic) {
JS_GetLength(ctx, filtered, &len); JS_GetLength(ctx, filtered, &len);
ASSERT(len == 5); /* 6, 7, 8, 9, 10 */ ASSERT(len == 5); /* 6, 7, 8, 9, 10 */
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 0), 6); ASSERT_INT(JS_GetPropertyNumber(ctx, filtered, 0), 6);
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 4), 10); ASSERT_INT(JS_GetPropertyNumber(ctx, filtered, 4), 10);
return 1; return 1;
} }
@@ -865,11 +862,11 @@ TEST(array_filter_even) {
JS_GetLength(ctx, filtered, &len); JS_GetLength(ctx, filtered, &len);
ASSERT(len == 5); /* 2, 4, 6, 8, 10 */ ASSERT(len == 5); /* 2, 4, 6, 8, 10 */
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 0), 2); ASSERT_INT(JS_GetPropertyNumber(ctx, filtered, 0), 2);
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 1), 4); ASSERT_INT(JS_GetPropertyNumber(ctx, filtered, 1), 4);
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 2), 6); ASSERT_INT(JS_GetPropertyNumber(ctx, filtered, 2), 6);
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 3), 8); ASSERT_INT(JS_GetPropertyNumber(ctx, filtered, 3), 8);
ASSERT_INT(JS_GetPropertyUint32(ctx, filtered, 4), 10); ASSERT_INT(JS_GetPropertyNumber(ctx, filtered, 4), 10);
return 1; return 1;
} }
@@ -893,9 +890,9 @@ TEST(array_map_double) {
JS_GetLength(ctx, mapped, &len); JS_GetLength(ctx, mapped, &len);
ASSERT(len == 3); ASSERT(len == 3);
ASSERT_INT(JS_GetPropertyUint32(ctx, mapped, 0), 2); ASSERT_INT(JS_GetPropertyNumber(ctx, mapped, 0), 2);
ASSERT_INT(JS_GetPropertyUint32(ctx, mapped, 1), 4); ASSERT_INT(JS_GetPropertyNumber(ctx, mapped, 1), 4);
ASSERT_INT(JS_GetPropertyUint32(ctx, mapped, 2), 6); ASSERT_INT(JS_GetPropertyNumber(ctx, mapped, 2), 6);
return 1; return 1;
} }
@@ -1356,9 +1353,9 @@ TEST(cell_reverse_array) {
JSValue reversed = JS_CellReverse(ctx, arr_ref.val); JSValue reversed = JS_CellReverse(ctx, arr_ref.val);
JS_PopGCRef(ctx, &arr_ref); JS_PopGCRef(ctx, &arr_ref);
ASSERT(JS_IsArray(reversed)); ASSERT(JS_IsArray(reversed));
ASSERT_INT(JS_GetPropertyUint32(ctx, reversed, 0), 3); ASSERT_INT(JS_GetPropertyNumber(ctx, reversed, 0), 3);
ASSERT_INT(JS_GetPropertyUint32(ctx, reversed, 1), 2); ASSERT_INT(JS_GetPropertyNumber(ctx, reversed, 1), 2);
ASSERT_INT(JS_GetPropertyUint32(ctx, reversed, 2), 1); ASSERT_INT(JS_GetPropertyNumber(ctx, reversed, 2), 1);
return 1; return 1;
} }
@@ -1408,9 +1405,9 @@ TEST(parse_json_array) {
int64_t len; int64_t len;
JS_GetLength(ctx, arr, &len); JS_GetLength(ctx, arr, &len);
ASSERT(len == 3); ASSERT(len == 3);
ASSERT_INT(JS_GetPropertyUint32(ctx, arr, 0), 1); ASSERT_INT(JS_GetPropertyNumber(ctx, arr, 0), 1);
ASSERT_INT(JS_GetPropertyUint32(ctx, arr, 1), 2); ASSERT_INT(JS_GetPropertyNumber(ctx, arr, 1), 2);
ASSERT_INT(JS_GetPropertyUint32(ctx, arr, 2), 3); ASSERT_INT(JS_GetPropertyNumber(ctx, arr, 2), 3);
return 1; return 1;
} }
@@ -1565,12 +1562,12 @@ TEST(property_type_restrictions) {
/* Setting numeric properties on non-arrays should throw */ /* Setting numeric properties on non-arrays should throw */
JSValue v100 = JS_NewInt32(ctx, 100); JSValue v100 = JS_NewInt32(ctx, 100);
int ret1 = JS_SetPropertyUint32(ctx, obj_ref.val, 0, v100); int ret1 = JS_IsException(JS_SetPropertyNumber(ctx, obj_ref.val, 0, v100)) ? -1 : 0;
int has_exc1 = JS_HasException(ctx); int has_exc1 = JS_HasException(ctx);
JS_GetException(ctx); /* Clear the exception */ JS_GetException(ctx); /* Clear the exception */
/* Getting numeric properties on objects should return null */ /* Getting numeric properties on objects should return null */
JSValue v0 = JS_GetPropertyUint32(ctx, obj_ref.val, 0); JSValue v0 = JS_GetPropertyNumber(ctx, obj_ref.val, 0);
int v0_is_null = JS_IsNull(v0); int v0_is_null = JS_IsNull(v0);
/* Getting text keys from arrays should return null */ /* Getting text keys from arrays should return null */
@@ -1646,8 +1643,8 @@ TEST(new_array_from) {
int64_t len; int64_t len;
JS_GetLength(ctx, arr, &len); JS_GetLength(ctx, arr, &len);
ASSERT(len == 4); ASSERT(len == 4);
ASSERT_INT(JS_GetPropertyUint32(ctx, arr, 0), 10); ASSERT_INT(JS_GetPropertyNumber(ctx, arr, 0), 10);
ASSERT_INT(JS_GetPropertyUint32(ctx, arr, 3), 40); ASSERT_INT(JS_GetPropertyNumber(ctx, arr, 3), 40);
return 1; return 1;
} }
@@ -1833,9 +1830,7 @@ TEST(is_function_check) {
TEST(is_integer_vs_number) { TEST(is_integer_vs_number) {
JSValue i = JS_NewInt32(ctx, 42); JSValue i = JS_NewInt32(ctx, 42);
JSValue f = JS_NewFloat64(ctx, 3.14); JSValue f = JS_NewFloat64(ctx, 3.14);
ASSERT(JS_IsInteger(i));
ASSERT(JS_IsInt(i)); ASSERT(JS_IsInt(i));
ASSERT(!JS_IsInteger(f));
ASSERT(!JS_IsInt(f)); ASSERT(!JS_IsInt(f));
ASSERT(JS_IsNumber(i)); ASSERT(JS_IsNumber(i));
ASSERT(JS_IsNumber(f)); ASSERT(JS_IsNumber(f));
@@ -1983,9 +1978,9 @@ TEST(wota_encode_nested_array) {
int is_arr = JS_IsArray(decoded); int is_arr = JS_IsArray(decoded);
int64_t len; int64_t len;
JS_GetLength(ctx, decoded, &len); JS_GetLength(ctx, decoded, &len);
JSValue v0 = JS_GetPropertyUint32(ctx, decoded, 0); JSValue v0 = JS_GetPropertyNumber(ctx, decoded, 0);
JSValue v2 = JS_GetPropertyUint32(ctx, decoded, 2); JSValue v2 = JS_GetPropertyNumber(ctx, decoded, 2);
JSValue inner = JS_GetPropertyUint32(ctx, decoded, 1); JSValue inner = JS_GetPropertyNumber(ctx, decoded, 1);
int inner_is_arr = JS_IsArray(inner); int inner_is_arr = JS_IsArray(inner);
int64_t inner_len; int64_t inner_len;
JS_GetLength(ctx, inner, &inner_len); JS_GetLength(ctx, inner, &inner_len);

Binary file not shown.

42
test_parse_boot.cm Normal file
View File

@@ -0,0 +1,42 @@
var fd = use("fd")
var json = use("json")
var tokenize = use("tokenize")
var parse = use("parse")
var fold = use("fold")
var src = text(fd.slurp("internal/bootstrap.cm"))
var tok_result = tokenize(src, "bootstrap.cm")
var ast = parse(tok_result.tokens, src, "bootstrap.cm", tokenize)
var i = 0
var folded = null
var ast_json = null
var f = null
var bytecode = null
var has_errors = ast.errors != null && length(ast.errors) > 0
if (has_errors) {
print("PARSE ERRORS:")
while (i < length(ast.errors)) {
print(text(ast.errors[i].line) + ":" + text(ast.errors[i].column) + " " + ast.errors[i].message)
i = i + 1
}
} else {
print("Parse OK")
print(" statements: " + text(length(ast.statements)))
if (ast.functions != null) {
print(" functions: " + text(length(ast.functions)))
} else {
print(" functions: null")
}
folded = fold(ast)
ast_json = json.encode(folded)
f = fd.open("/tmp/bootstrap_ast.json", "w")
fd.write(f, ast_json)
fd.close(f)
print("Wrote AST to /tmp/bootstrap_ast.json")
bytecode = mach_compile_ast("bootstrap", ast_json)
print("Bytecode size: " + text(length(bytecode)))
f = fd.open("/tmp/bootstrap_test.mach", "w")
fd.write(f, bytecode)
fd.close(f)
print("Wrote bytecode to /tmp/bootstrap_test.mach")
}

Binary file not shown.

View File

@@ -1489,16 +1489,6 @@ run("zero div zero is null", function() {
if (nan != null) fail("0/0 should be null") if (nan != null) fail("0/0 should be null")
}) })
run("max safe integer", function() {
var max = 9007199254740991
if (max + 1 - 1 != max) fail("max safe integer precision lost")
})
run("min safe integer", function() {
var min = -9007199254740991
if (min - 1 + 1 != min) fail("min safe integer precision lost")
})
run("empty string falsy", function() { run("empty string falsy", function() {
if ("") fail("empty string should be falsy") if ("") fail("empty string should be falsy")
}) })