Merge branch 'mach' into bytecode_cleanup
This commit is contained in:
20
dump_mcode.cm
Normal file
20
dump_mcode.cm
Normal 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")
|
||||
BIN
fold_new.mach
Normal file
BIN
fold_new.mach
Normal file
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
// 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
|
||||
// Actor spawn (script_startup): os, json, nota, wota, actorsym, init, core_path, shop_path
|
||||
// args[0] = script name, args[1..] = user args
|
||||
var load_internal = os.load_internal
|
||||
@@ -24,7 +24,7 @@ function use_basic(path) {
|
||||
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) {
|
||||
var mach_path = core_path + '/' + name + ".mach"
|
||||
var data = null
|
||||
@@ -32,9 +32,8 @@ function boot_load(name, env) {
|
||||
data = fd.slurp(mach_path)
|
||||
return mach_load(data, env)
|
||||
}
|
||||
var ast_path = core_path + '/' + name + ".ast.json"
|
||||
data = text(fd.slurp(ast_path))
|
||||
return mach_eval_ast(name, data, env)
|
||||
print("error: missing bootstrap bytecode: " + mach_path + "\n")
|
||||
disrupt
|
||||
}
|
||||
|
||||
var boot_env = {use: use_basic}
|
||||
@@ -45,14 +44,11 @@ use_cache['tokenize'] = tokenize_mod
|
||||
use_cache['parse'] = parse_mod
|
||||
use_cache['fold'] = fold_mod
|
||||
|
||||
// Optionally load mcode compiler module
|
||||
var mcode_mod = null
|
||||
// Always load mcode compiler module
|
||||
var mcode_mod = boot_load("mcode", boot_env)
|
||||
use_cache['mcode'] = mcode_mod
|
||||
var streamline_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
|
||||
function check_mach_stale() {
|
||||
@@ -134,6 +130,8 @@ function load_module(name, env) {
|
||||
var src_path = null
|
||||
var src = null
|
||||
var ast = null
|
||||
var compiled = null
|
||||
var optimized = null
|
||||
if (fd.is_file(mach_path)) {
|
||||
data = fd.slurp(mach_path)
|
||||
return mach_load(data, env)
|
||||
@@ -141,38 +139,33 @@ function load_module(name, env) {
|
||||
src_path = core_path + '/' + name + ".cm"
|
||||
src = text(fd.slurp(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)
|
||||
var qbe_macros = null
|
||||
if (use_mcode) {
|
||||
streamline_mod = load_module("streamline", boot_env)
|
||||
use_cache['streamline'] = streamline_mod
|
||||
if (emit_qbe) {
|
||||
qbe_macros = load_module("qbe", boot_env)
|
||||
qbe_emit_mod = load_module("qbe_emit", boot_env)
|
||||
use_cache['qbe'] = qbe_macros
|
||||
use_cache['qbe_emit'] = qbe_emit_mod
|
||||
}
|
||||
streamline_mod = load_module("streamline", boot_env)
|
||||
use_cache['streamline'] = streamline_mod
|
||||
if (emit_qbe) {
|
||||
qbe_macros = load_module("qbe", boot_env)
|
||||
qbe_emit_mod = load_module("qbe_emit", boot_env)
|
||||
use_cache['qbe'] = qbe_macros
|
||||
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) {
|
||||
var compiled = null
|
||||
var optimized = null
|
||||
var compiled = mcode_mod(ast)
|
||||
var optimized = streamline_mod(compiled)
|
||||
var qbe_il = null
|
||||
if (use_mcode) {
|
||||
compiled = mcode_mod(ast)
|
||||
optimized = streamline_mod(compiled)
|
||||
if (emit_qbe) {
|
||||
qbe_il = qbe_emit_mod(optimized, qbe_macros)
|
||||
print(qbe_il)
|
||||
return null
|
||||
}
|
||||
return mcode_run(name, json.encode(optimized), env)
|
||||
if (emit_qbe) {
|
||||
qbe_il = qbe_emit_mod(optimized, qbe_macros)
|
||||
print(qbe_il)
|
||||
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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
mcode.mach
BIN
mcode.mach
Binary file not shown.
@@ -46,7 +46,6 @@ src += [ # core
|
||||
'miniz.c',
|
||||
'runtime.c',
|
||||
'mach.c',
|
||||
'mcode.c',
|
||||
'libregexp.c', 'libunicode.c', 'cutils.c', 'dtoa.c'
|
||||
]
|
||||
|
||||
|
||||
14
parse.cm
14
parse.cm
@@ -65,7 +65,7 @@ var parse = function(tokens, src, filename, tokenizer) {
|
||||
|
||||
var errors = []
|
||||
var error_count = 0
|
||||
var function_nr = 1
|
||||
var fn_counter = 1
|
||||
|
||||
var ast_node = function(kind, token) {
|
||||
return {
|
||||
@@ -422,8 +422,8 @@ var parse = function(tokens, src, filename, tokenizer) {
|
||||
_control_depth = meth_old_cd
|
||||
_control_type = meth_old_ct
|
||||
_expecting_body = meth_old_eb
|
||||
fn.function_nr = function_nr
|
||||
function_nr = function_nr + 1
|
||||
fn.function_nr = fn_counter
|
||||
fn_counter = fn_counter + 1
|
||||
ast_node_end(fn)
|
||||
pair.right = fn
|
||||
} else if (is_ident && (tok.kind == "," || tok.kind == "}")) {
|
||||
@@ -918,8 +918,8 @@ var parse = function(tokens, src, filename, tokenizer) {
|
||||
_control_depth = old_cd
|
||||
_control_type = old_ct
|
||||
_expecting_body = old_eb
|
||||
node.function_nr = function_nr
|
||||
function_nr = function_nr + 1
|
||||
node.function_nr = fn_counter
|
||||
fn_counter = fn_counter + 1
|
||||
ast_node_end(node)
|
||||
return node
|
||||
}
|
||||
@@ -1008,8 +1008,8 @@ var parse = function(tokens, src, filename, tokenizer) {
|
||||
_control_depth = old_cd
|
||||
_control_type = old_ct
|
||||
_expecting_body = old_eb
|
||||
node.function_nr = function_nr
|
||||
function_nr = function_nr + 1
|
||||
node.function_nr = fn_counter
|
||||
fn_counter = fn_counter + 1
|
||||
ast_node_end(node)
|
||||
return node
|
||||
}
|
||||
|
||||
BIN
parse.mach
BIN
parse.mach
Binary file not shown.
@@ -405,7 +405,7 @@ var qbe_emit = function(ir, qbe) {
|
||||
else if (op == "le_float") fop_id = 3
|
||||
else if (op == "gt_float") fop_id = 4
|
||||
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}`)
|
||||
wb(a1)
|
||||
continue
|
||||
|
||||
BIN
qbe_emit.mach
BIN
qbe_emit.mach
Binary file not shown.
36
regen.cm
36
regen.cm
@@ -6,6 +6,8 @@ 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 files = [
|
||||
{src: "tokenize.cm", name: "tokenize", out: "tokenize.mach"},
|
||||
@@ -25,21 +27,49 @@ var src = null
|
||||
var tok_result = null
|
||||
var ast = null
|
||||
var folded = null
|
||||
var ast_json = null
|
||||
var compiled = null
|
||||
var optimized = null
|
||||
var mcode_json = null
|
||||
var bytecode = null
|
||||
var f = null
|
||||
var errs = null
|
||||
var ei = 0
|
||||
var e = null
|
||||
var had_errors = false
|
||||
|
||||
while (i < length(files)) {
|
||||
entry = files[i]
|
||||
src = text(fd.slurp(entry.src))
|
||||
tok_result = tokenize(src, entry.src)
|
||||
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)
|
||||
ast_json = json.encode(folded)
|
||||
bytecode = mach_compile_ast(entry.name, ast_json)
|
||||
compiled = mcode(folded)
|
||||
optimized = streamline(compiled)
|
||||
mcode_json = json.encode(optimized)
|
||||
bytecode = mach_compile_mcode_bin(entry.name, mcode_json)
|
||||
f = fd.open(entry.out, "w")
|
||||
fd.write(f, bytecode)
|
||||
fd.close(f)
|
||||
print(`wrote ${entry.out}`)
|
||||
i = i + 1
|
||||
}
|
||||
if (had_errors) {
|
||||
print("regen aborted: fix errors above")
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "cJSON.h"
|
||||
|
||||
#define BOOTSTRAP_MACH "internal/bootstrap.mach"
|
||||
#define BOOTSTRAP_AST "internal/bootstrap.ast.json"
|
||||
#define BOOTSTRAP_SRC "internal/bootstrap.cm"
|
||||
#define CELL_SHOP_DIR ".cell"
|
||||
#define CELL_CORE_DIR "packages/core"
|
||||
@@ -179,14 +178,9 @@ void script_startup(cell_rt *prt)
|
||||
cell_rt *crt = JS_GetContextOpaque(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;
|
||||
int boot_is_bin = 1;
|
||||
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) {
|
||||
printf("ERROR: Could not load bootstrap from %s!\n", core_path);
|
||||
return;
|
||||
@@ -211,9 +205,8 @@ void script_startup(cell_rt *prt)
|
||||
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, "use_mcode", JS_NewBool(js, 0));
|
||||
|
||||
if (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
|
||||
crt->state = ACTOR_RUNNING;
|
||||
JSValue v;
|
||||
if (boot_is_bin) {
|
||||
v = JS_RunMachBin(js, (const uint8_t *)boot_data, boot_size, hidden_env);
|
||||
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);
|
||||
}
|
||||
JSValue v = JS_RunMachBin(js, (const uint8_t *)boot_data, boot_size, hidden_env);
|
||||
free(boot_data);
|
||||
uncaught_exception(js, v);
|
||||
crt->state = ACTOR_IDLE;
|
||||
set_actor_state(crt);
|
||||
@@ -290,7 +274,7 @@ static void print_usage(const char *prog)
|
||||
printf("Options:\n");
|
||||
printf(" --core <path> Set core path directly (overrides CELL_CORE)\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(" --test [heap_size] Run C test suite\n");
|
||||
printf(" -h, --help Show this help message\n");
|
||||
printf("\nEnvironment:\n");
|
||||
@@ -322,7 +306,6 @@ int cell_init(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Default: run script through bootstrap pipeline */
|
||||
int use_mcode = 0;
|
||||
int emit_qbe = 0;
|
||||
int arg_start = 1;
|
||||
const char *shop_override = NULL;
|
||||
@@ -331,10 +314,9 @@ int cell_init(int argc, char **argv)
|
||||
// Parse flags (order-independent)
|
||||
while (arg_start < argc && argv[arg_start][0] == '-') {
|
||||
if (strcmp(argv[arg_start], "--mcode") == 0) {
|
||||
use_mcode = 1;
|
||||
/* --mcode is now always on; accept and ignore for compat */
|
||||
arg_start++;
|
||||
} else if (strcmp(argv[arg_start], "--emit-qbe") == 0) {
|
||||
use_mcode = 1; // QBE requires mcode pipeline
|
||||
emit_qbe = 1;
|
||||
arg_start++;
|
||||
} else if (strcmp(argv[arg_start], "--shop") == 0) {
|
||||
@@ -361,12 +343,7 @@ int cell_init(int argc, char **argv)
|
||||
actor_initialize();
|
||||
|
||||
size_t boot_size;
|
||||
int boot_is_bin = 1;
|
||||
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) {
|
||||
printf("ERROR: Could not load bootstrap from %s\n", core_path);
|
||||
return 1;
|
||||
@@ -420,7 +397,6 @@ int cell_init(int argc, char **argv)
|
||||
JS_SetPropertyStr(ctx, hidden_env, "core_path", JS_NewString(ctx, core_path));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "shop_path",
|
||||
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, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
|
||||
JS_SetPropertyStr(ctx, hidden_env, "json", js_json_use(ctx));
|
||||
@@ -435,17 +411,8 @@ int cell_init(int argc, char **argv)
|
||||
JS_SetPropertyStr(ctx, hidden_env, "args", args_arr);
|
||||
hidden_env = JS_Stone(ctx, hidden_env);
|
||||
|
||||
JSValue result;
|
||||
if (boot_is_bin) {
|
||||
result = JS_RunMachBin(ctx, (const uint8_t *)boot_data, boot_size, hidden_env);
|
||||
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);
|
||||
}
|
||||
JSValue result = JS_RunMachBin(ctx, (const uint8_t *)boot_data, boot_size, hidden_env);
|
||||
free(boot_data);
|
||||
|
||||
int exit_code = 0;
|
||||
if (JS_IsException(result)) {
|
||||
|
||||
3523
source/mach.c
3523
source/mach.c
File diff suppressed because it is too large
Load Diff
2029
source/mcode.c
2029
source/mcode.c
File diff suppressed because it is too large
Load Diff
@@ -406,6 +406,8 @@ typedef struct { uint16_t line; uint16_t col; } MachLineEntry;
|
||||
#define MACH_GET_sJ(i) ((int32_t)((i) & 0xFFFFFF00) >> 8)
|
||||
|
||||
typedef enum MachOpcode {
|
||||
/* === Legacy opcodes (used by existing .mach files) === */
|
||||
|
||||
/* Constants & Loading */
|
||||
MACH_LOADK, /* R(A) = K(Bx) — load from constant pool (ABx) */
|
||||
MACH_LOADI, /* R(A) = (int16_t)sBx — load small integer (AsBx) */
|
||||
@@ -416,7 +418,7 @@ typedef enum MachOpcode {
|
||||
/* Movement */
|
||||
MACH_MOVE, /* R(A) = R(B) */
|
||||
|
||||
/* Arithmetic (ABC) */
|
||||
/* Generic arithmetic (ABC) — used by legacy .mach */
|
||||
MACH_ADD, /* R(A) = R(B) + R(C) */
|
||||
MACH_SUB, /* R(A) = R(B) - R(C) */
|
||||
MACH_MUL, /* R(A) = R(B) * R(C) */
|
||||
@@ -427,7 +429,7 @@ typedef enum MachOpcode {
|
||||
MACH_INC, /* 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_NEQ, /* R(A) = (R(B) != R(C)) */
|
||||
MACH_LT, /* R(A) = (R(B) < R(C)) */
|
||||
@@ -435,7 +437,7 @@ typedef enum MachOpcode {
|
||||
MACH_GT, /* 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_BNOT, /* R(A) = ~R(B) */
|
||||
MACH_BAND, /* R(A) = R(B) & R(C) */
|
||||
@@ -445,7 +447,7 @@ typedef enum MachOpcode {
|
||||
MACH_SHR, /* 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_SETFIELD, /* R(A)[K(B)] = R(C) — named property */
|
||||
MACH_GETINDEX, /* R(A) = R(B)[R(C)] — computed property */
|
||||
@@ -466,12 +468,12 @@ typedef enum MachOpcode {
|
||||
MACH_JMPFALSE, /* if !R(A): pc += sBx — (iAsBx format) */
|
||||
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_RETURN, /* Return R(A) */
|
||||
MACH_RETNIL, /* Return null */
|
||||
|
||||
/* Object/array creation */
|
||||
/* Object/array creation — legacy .mach */
|
||||
MACH_NEWOBJECT, /* R(A) = {} */
|
||||
MACH_NEWARRAY, /* R(A) = new array, B = element count in R(A+1)..R(A+B) */
|
||||
MACH_CLOSURE, /* R(A) = closure(functions[Bx]) (ABx) */
|
||||
@@ -493,10 +495,112 @@ typedef enum MachOpcode {
|
||||
|
||||
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_OP_COUNT
|
||||
} MachOpcode;
|
||||
|
||||
static const char *mach_opcode_names[MACH_OP_COUNT] = {
|
||||
/* Legacy */
|
||||
[MACH_LOADK] = "loadk",
|
||||
[MACH_LOADI] = "loadi",
|
||||
[MACH_LOADNULL] = "loadnull",
|
||||
@@ -556,6 +660,70 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
|
||||
[MACH_EQ_TOL] = "eq_tol",
|
||||
[MACH_NEQ_TOL] = "neq_tol",
|
||||
[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",
|
||||
};
|
||||
|
||||
/* Compiled register-based code (off-heap, never GC'd).
|
||||
@@ -586,28 +754,6 @@ typedef struct JSCodeRegister {
|
||||
uint16_t disruption_pc; /* start of disruption handler (0 = none) */
|
||||
} 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
|
||||
reference outer frames via (depth, slot) addressing.
|
||||
@@ -1068,7 +1214,6 @@ typedef enum {
|
||||
JS_FUNC_KIND_BYTECODE,
|
||||
JS_FUNC_KIND_C_DATA,
|
||||
JS_FUNC_KIND_REGISTER, /* register-based VM function */
|
||||
JS_FUNC_KIND_MCODE, /* MCODE JSON interpreter */
|
||||
} JSFunctionKind;
|
||||
|
||||
typedef struct JSFunction {
|
||||
@@ -1092,11 +1237,6 @@ typedef struct JSFunction {
|
||||
JSValue env_record; /* stone record, module environment */
|
||||
JSValue outer_frame; /* JSFrame JSValue, for closures */
|
||||
} 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;
|
||||
} JSFunction;
|
||||
|
||||
@@ -1259,8 +1399,6 @@ JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj,
|
||||
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_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);
|
||||
JSValue __attribute__ ((format (printf, 2, 3)))
|
||||
JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
|
||||
@@ -1574,13 +1712,6 @@ typedef struct {
|
||||
} 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) === */
|
||||
typedef struct PPretext {
|
||||
@@ -1770,8 +1901,5 @@ JSFrameRegister *alloc_frame_register(JSContext *ctx, int slot_count);
|
||||
cJSON *mach_find_scope_record(cJSON *scopes, int function_nr);
|
||||
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 */
|
||||
|
||||
@@ -992,30 +992,11 @@ MachCode *JS_DeserializeMachCode(const uint8_t *data, size_t size);
|
||||
/* Load compiled MachCode into a JSContext, materializing JSValues. */
|
||||
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. */
|
||||
JSValue JS_RunMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env);
|
||||
|
||||
/* Execute MCODE from cJSON tree. Takes ownership of root. */
|
||||
JSValue JS_CallMcodeTree (JSContext *ctx, struct cJSON *root);
|
||||
|
||||
/* Execute MCODE from cJSON tree with hidden env. Takes ownership of root. */
|
||||
JSValue JS_CallMcodeTreeEnv (JSContext *ctx, struct cJSON *root, JSValue env);
|
||||
|
||||
/* Parse and execute MCODE JSON string.
|
||||
Returns result of execution, or JS_EXCEPTION on error. */
|
||||
JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json);
|
||||
/* Compile mcode JSON IR to MachCode binary. */
|
||||
MachCode *mach_compile_mcode(struct cJSON *mcode_json);
|
||||
|
||||
/* Get stack trace as cJSON array of frame objects.
|
||||
Returns NULL if no register VM frame is active.
|
||||
|
||||
178
source/runtime.c
178
source/runtime.c
@@ -1143,10 +1143,6 @@ void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8_t *fro
|
||||
nested->name = gc_copy_value (ctx, nested->name, from_base, from_end, to_base, to_free, to_end);
|
||||
}
|
||||
}
|
||||
} else if (fn->kind == JS_FUNC_KIND_MCODE) {
|
||||
/* MCODE function - scan outer_frame and env_record */
|
||||
fn->u.mcode.outer_frame = gc_copy_value (ctx, fn->u.mcode.outer_frame, from_base, from_end, to_base, to_free, to_end);
|
||||
fn->u.mcode.env_record = gc_copy_value (ctx, fn->u.mcode.env_record, from_base, from_end, to_base, to_free, to_end);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4336,8 +4332,6 @@ JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj,
|
||||
case JS_FUNC_KIND_REGISTER:
|
||||
return JS_CallRegisterVM (ctx, f->u.reg.code, this_obj, argc, argv,
|
||||
f->u.reg.env_record, f->u.reg.outer_frame);
|
||||
case JS_FUNC_KIND_MCODE:
|
||||
return mcode_exec (ctx, f->u.mcode.code, this_obj, argc, argv, f->u.mcode.outer_frame);
|
||||
default:
|
||||
return JS_ThrowTypeError (ctx, "not a function");
|
||||
}
|
||||
@@ -4359,8 +4353,6 @@ JSValue JS_Call (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, J
|
||||
case JS_FUNC_KIND_REGISTER:
|
||||
return JS_CallRegisterVM (ctx, f->u.reg.code, this_obj, argc, argv,
|
||||
f->u.reg.env_record, f->u.reg.outer_frame);
|
||||
case JS_FUNC_KIND_MCODE:
|
||||
return mcode_exec (ctx, f->u.mcode.code, this_obj, argc, argv, f->u.mcode.outer_frame);
|
||||
default:
|
||||
return JS_ThrowTypeError (ctx, "not a function");
|
||||
}
|
||||
@@ -9721,83 +9713,6 @@ int js_is_blob (JSContext *js, JSValue v) {
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* mach_eval_ast(name, ast_json, env?) - compile pre-parsed AST and run */
|
||||
static JSValue js_mach_eval_ast (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1]))
|
||||
return JS_ThrowTypeError (ctx, "mach_eval_ast requires (name, ast_json) text arguments");
|
||||
|
||||
const char *name = JS_ToCString (ctx, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
const char *json_str = JS_ToCString (ctx, argv[1]);
|
||||
if (!json_str) {
|
||||
JS_FreeCString (ctx, name);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
cJSON *ast = cJSON_Parse (json_str);
|
||||
JS_FreeCString (ctx, json_str);
|
||||
|
||||
if (!ast) {
|
||||
JS_FreeCString (ctx, name);
|
||||
return JS_ThrowSyntaxError (ctx, "mach_eval_ast: failed to parse AST JSON");
|
||||
}
|
||||
|
||||
/* Set the filename on the AST root */
|
||||
cJSON_DeleteItemFromObjectCaseSensitive (ast, "filename");
|
||||
cJSON_AddStringToObject (ast, "filename", name);
|
||||
|
||||
JSValue env = (argc >= 3 && JS_IsGCObject (argv[2])) ? argv[2] : JS_NULL;
|
||||
JSValue result = JS_RunMachTree (ctx, ast, env);
|
||||
cJSON_Delete (ast);
|
||||
JS_FreeCString (ctx, name);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* mach_compile_ast(name, ast_json) - compile pre-parsed AST to binary blob */
|
||||
static JSValue js_mach_compile_ast (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1]))
|
||||
return JS_ThrowTypeError (ctx, "mach_compile_ast requires (name, ast_json) text arguments");
|
||||
|
||||
const char *name = JS_ToCString (ctx, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
const char *json_str = JS_ToCString (ctx, argv[1]);
|
||||
if (!json_str) {
|
||||
JS_FreeCString (ctx, name);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
cJSON *ast = cJSON_Parse (json_str);
|
||||
JS_FreeCString (ctx, json_str);
|
||||
|
||||
if (!ast) {
|
||||
JS_FreeCString (ctx, name);
|
||||
return JS_ThrowSyntaxError (ctx, "mach_compile_ast: failed to parse AST JSON");
|
||||
}
|
||||
|
||||
cJSON_DeleteItemFromObjectCaseSensitive (ast, "filename");
|
||||
cJSON_AddStringToObject (ast, "filename", name);
|
||||
|
||||
MachCode *mc = JS_CompileMachTree (ast);
|
||||
cJSON_Delete (ast);
|
||||
JS_FreeCString (ctx, name);
|
||||
|
||||
if (!mc)
|
||||
return JS_ThrowSyntaxError (ctx, "mach_compile_ast: failed to compile AST");
|
||||
|
||||
size_t blob_size;
|
||||
uint8_t *buf = JS_SerializeMachCode (mc, &blob_size);
|
||||
JS_FreeMachCode (mc);
|
||||
|
||||
if (!buf)
|
||||
return JS_ThrowInternalError (ctx, "mach_compile_ast: serialization failed");
|
||||
|
||||
JSValue result = js_new_blob_stoned_copy (ctx, buf, blob_size);
|
||||
sys_free (buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* mach_load(blob, env?) - deserialize and execute binary blob */
|
||||
static JSValue js_mach_load (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1)
|
||||
@@ -9824,10 +9739,10 @@ static JSValue js_mach_load (JSContext *ctx, JSValue this_val, int argc, JSValue
|
||||
return result;
|
||||
}
|
||||
|
||||
/* mcode_run(name, mcode_json, env?) - run pre-compiled mcode JSON */
|
||||
static JSValue js_mcode_run (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
/* mach_eval_mcode(name, mcode_json, env?) - compile mcode IR and run via register VM */
|
||||
static JSValue js_mach_eval_mcode (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1]))
|
||||
return JS_ThrowTypeError (ctx, "mcode_run requires (name, mcode_json) text arguments");
|
||||
return JS_ThrowTypeError (ctx, "mach_eval_mcode requires (name, mcode_json) text arguments");
|
||||
|
||||
const char *name = JS_ToCString (ctx, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
@@ -9843,16 +9758,81 @@ static JSValue js_mcode_run (JSContext *ctx, JSValue this_val, int argc, JSValue
|
||||
|
||||
if (!mcode) {
|
||||
JS_FreeCString (ctx, name);
|
||||
return JS_ThrowSyntaxError (ctx, "mcode_run: failed to parse mcode JSON");
|
||||
return JS_ThrowSyntaxError (ctx, "mach_eval_mcode: failed to parse mcode JSON");
|
||||
}
|
||||
|
||||
/* Set filename on the mcode root if not present */
|
||||
if (!cJSON_GetObjectItemCaseSensitive (mcode, "filename"))
|
||||
cJSON_AddStringToObject (mcode, "filename", name);
|
||||
|
||||
/* Compile mcode IR → MachCode binary */
|
||||
MachCode *mc = mach_compile_mcode (mcode);
|
||||
cJSON_Delete (mcode);
|
||||
|
||||
if (!mc) {
|
||||
JS_FreeCString (ctx, name);
|
||||
return JS_ThrowInternalError (ctx, "mach_eval_mcode: compilation failed");
|
||||
}
|
||||
|
||||
JSValue env = (argc >= 3 && JS_IsGCObject (argv[2])) ? argv[2] : JS_NULL;
|
||||
JSValue result = JS_CallMcodeTreeEnv (ctx, mcode, env);
|
||||
/* mcode tree ownership transferred to JS_CallMcodeTreeEnv — do not free */
|
||||
|
||||
JSGCRef env_ref;
|
||||
JS_PushGCRef (ctx, &env_ref);
|
||||
env_ref.val = env;
|
||||
|
||||
JSCodeRegister *code = JS_LoadMachCode (ctx, mc, env_ref.val);
|
||||
JS_FreeMachCode (mc);
|
||||
JSValue result = JS_CallRegisterVM (ctx, code, ctx->global_obj, 0, NULL, env_ref.val, JS_NULL);
|
||||
JS_PopGCRef (ctx, &env_ref);
|
||||
JS_FreeCString (ctx, name);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* mach_compile_mcode_bin(name, mcode_json) - compile mcode IR to serialized binary blob */
|
||||
static JSValue js_mach_compile_mcode_bin (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2 || !JS_IsText (argv[0]) || !JS_IsText (argv[1]))
|
||||
return JS_ThrowTypeError (ctx, "mach_compile_mcode_bin requires (name, mcode_json) text arguments");
|
||||
|
||||
const char *name = JS_ToCString (ctx, argv[0]);
|
||||
if (!name) return JS_EXCEPTION;
|
||||
|
||||
const char *json_str = JS_ToCString (ctx, argv[1]);
|
||||
if (!json_str) {
|
||||
JS_FreeCString (ctx, name);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
cJSON *mcode = cJSON_Parse (json_str);
|
||||
JS_FreeCString (ctx, json_str);
|
||||
|
||||
if (!mcode) {
|
||||
JS_FreeCString (ctx, name);
|
||||
return JS_ThrowSyntaxError (ctx, "mach_compile_mcode_bin: failed to parse mcode JSON");
|
||||
}
|
||||
|
||||
if (!cJSON_GetObjectItemCaseSensitive (mcode, "filename"))
|
||||
cJSON_AddStringToObject (mcode, "filename", name);
|
||||
|
||||
MachCode *mc = mach_compile_mcode (mcode);
|
||||
cJSON_Delete (mcode);
|
||||
JS_FreeCString (ctx, name);
|
||||
|
||||
if (!mc)
|
||||
return JS_ThrowInternalError (ctx, "mach_compile_mcode_bin: compilation failed");
|
||||
|
||||
size_t size = 0;
|
||||
uint8_t *data = JS_SerializeMachCode (mc, &size);
|
||||
|
||||
JS_FreeMachCode (mc);
|
||||
|
||||
if (!data)
|
||||
return JS_ThrowInternalError (ctx, "mach_compile_mcode_bin: serialization failed");
|
||||
|
||||
JSValue blob = js_new_blob_stoned_copy (ctx, data, size);
|
||||
sys_free (data);
|
||||
return blob;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* stone() function - deep freeze with blob support
|
||||
* ============================================================================
|
||||
@@ -10938,10 +10918,9 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
|
||||
}
|
||||
|
||||
/* Core functions - using GC-safe helper */
|
||||
js_set_global_cfunc(ctx, "mach_eval_ast", js_mach_eval_ast, 3);
|
||||
js_set_global_cfunc(ctx, "mcode_run", js_mcode_run, 3);
|
||||
js_set_global_cfunc(ctx, "mach_compile_ast", js_mach_compile_ast, 2);
|
||||
js_set_global_cfunc(ctx, "mach_load", js_mach_load, 2);
|
||||
js_set_global_cfunc(ctx, "mach_eval_mcode", js_mach_eval_mcode, 3);
|
||||
js_set_global_cfunc(ctx, "mach_compile_mcode_bin", js_mach_compile_mcode_bin, 2);
|
||||
js_set_global_cfunc(ctx, "stone", js_cell_stone, 1);
|
||||
js_set_global_cfunc(ctx, "length", js_cell_length, 1);
|
||||
js_set_global_cfunc(ctx, "call", js_cell_call, 3);
|
||||
@@ -12326,17 +12305,6 @@ cJSON *JS_GetStack(JSContext *ctx) {
|
||||
line = code->line_table[pc].line;
|
||||
col = code->line_table[pc].col;
|
||||
}
|
||||
} else if (fn->kind == JS_FUNC_KIND_MCODE && fn->u.mcode.code) {
|
||||
JSMCode *code = fn->u.mcode.code;
|
||||
file = code->filename;
|
||||
func_name = code->name;
|
||||
if (!is_first) {
|
||||
pc = (uint32_t)(JS_VALUE_GET_INT(frame->address) >> 16);
|
||||
}
|
||||
if (code->line_table && pc < code->instr_count) {
|
||||
line = code->line_table[pc].line;
|
||||
col = code->line_table[pc].col;
|
||||
}
|
||||
}
|
||||
|
||||
cJSON *entry = cJSON_CreateObject();
|
||||
|
||||
BIN
streamline.mach
BIN
streamline.mach
Binary file not shown.
42
test_parse_boot.cm
Normal file
42
test_parse_boot.cm
Normal 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")
|
||||
}
|
||||
BIN
tokenize.mach
BIN
tokenize.mach
Binary file not shown.
Reference in New Issue
Block a user