From 8150c64c7d014d4221f5b70830019cedc74af346 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 6 Feb 2026 22:56:21 -0600 Subject: [PATCH] pitcode --- docs/pitcode.md | 287 ++++++++++++ source/quickjs.c | 1079 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 1032 insertions(+), 334 deletions(-) create mode 100644 docs/pitcode.md diff --git a/docs/pitcode.md b/docs/pitcode.md new file mode 100644 index 00000000..94289ecc --- /dev/null +++ b/docs/pitcode.md @@ -0,0 +1,287 @@ +# Pitmachine +A pitmachine is an abstract register machine for executing pitcode. + +The pitmachine assembly language is JSON. The instructions field contains an array of labels and instructions. Labels are the targets of branch instructions. They are simple text. Slots are elements in an activation frame. They are designated by a small positive integer. Slots are the general registers of the pitmachine. Slots hold the arguments, variables, and temporaries of a function invocation. + +{ + "name": "program_name", + "data": {⸳⸳⸳}, + "source": "...", +The labels record associates labels with tokens for use by debuggers. + + "labels": { + "entry": token, + "beyond": token, + ... + }, +The instructions array is a list of instructions and labels. + + instructions: [ +A statement label: + + "entry", +go to beyond: + + ["jump", "beyond"], +assign slot 8: pi / 2 + + ["access", 13, {"kind": "name", "name": "pi", "make": "intrinsic", ⸳⸳⸳}], + ["int", 14, 2], + ["divide", 8, 13, 14], + ⸳⸳⸳ + "beyond" + ⸳⸳⸳ + ] +} + +## Pitcode instructions +This is a register based machine. + +### Arithmetic +Arithmetic instructions perform operations on numbers. All other cases disrupt (except add, which is polymorphic). + +"add", dest, left, right // dest, left, and right are numbers designating slots in the current activation frame. Also works on texts (concatenation). + +"subtract", dest, left, right + +"multiply", dest, left, right + +"divide", dest, left, right + +"integer_divide", dest, left, right + +"modulo", dest, left, right + +"remainder", dest, left, right + +"max", dest, left, right + +"min", dest, left, right + +"abs", dest, right + +"neg", dest, right + +"sign", dest, right + +"fraction", dest, right + +"integer", dest, right + +"ceiling", dest, right, place + +"floor", dest, right, place + +"round", dest, right, place + +"trunc", dest, right, place + +### Text +Text instructions perform operations on texts. All other cases disrupt. + +"concat", dest, left, right // concat two texts + +"concat_space", dest, left, right + +"character", dest, right + +"codepoint", dest, right + +"length", dest, right + +"lower", dest, right + +"upper", dest, right + +"append", pretext, right + +Append the right text to the pretext, forwarding and growing its capacity if necessary. + +### Comparison +Comparison instructions perform operations on texts or numbers. All other cases disrupt. + +"eq", dest, left, right + +"ne", dest, left, right + +"lt", dest, left, right + +"le", dest, left, right + +"gt", dest, left, right + +"ge", dest, left, right + +### Logical + +"not", dest, right + +### Bitwise +Bitwise instructions convert operands to 32-bit integers. Non-numbers disrupt. + +"bitand", dest, left, right + +"bitor", dest, left, right + +"bitxor", dest, left, right + +"bitnot", dest, right + +"shl", dest, left, right + +"shr", dest, left, right + +"ushr", dest, left, right + +### Function + +"frame", dest, func, nr_args + +Prepare to invoke the func object. If the nr_args is too large, disrupt. Allocate the new activation frame. Put the current frame pointer into it. + +"goframe", dest, func, nr_args + +Same as frame, except that the current frame is reused if it is large enough. + +"invoke", frame + +Store the next instruction address in the current frame. Make the new frame the current frame. Jump to the entry point. + +"goinvoke", frame + +"apply", func, array + +"return", value + +"return_value", dest + +"setarg", frame, slot, value // set the slot of frame to value + +### Branching + +"jump", label + +"jump_true", slot, label + +If the value in the slot is true, jump to the label. Otherwise, continue with the next instruction. + +"jump_false": slot, label + +If the value in the slot is false, jump to the label. Otherwise, continue with the next instruction. + +"jump_null": slot, label + +"jump_empty": slot, label + +"wary_true", slot, label + +If the value in the slot is true, jump to the label. If the value is false, continue with the next instruction. Otherwise disrupt because of a type error. + +"wary_false": slot, label + +If the value in the slot is false, jump to the label. If the value is true, continue with the next instruction. Otherwise disrupt because of a type error. + +### Sensory + +Does the right slot contain a value of the indicated type? + +"array?", dest, right + +"blob?", dest, right + +"character?", dest, right + +"data?", dest, right + +"digit?", dest, right + +"false?", dest, right + +"fit?", dest, right + +"function?", dest, right + +"integer?", dest, right + +"letter?", dest, right + +"logical?", dest, right + +"null?", dest, right + +"pattern?", dest, right + +"record?", dest, right + +"stone?", dest, right + +"text?", dest, right + +"true?", dest, right + +"upper?", dest, right + +"whitespace?", dest, right + +### Potpourri + +"stone", dest, right // stone an object + +"true", dest + +"false", dest + +"null", dest + +"move", dest, right + +"int", dest, small_int + +"access", dest, literal + +This is used to access values (numbers, texts) from the program's immutable memory. The literal is a number or text. + +"load", dest, object, subscript + +This is used to load values from records and arrays. + +"store", dest, object, subscript + +This is used to store values into records and arrays. + +"delete", object, subscript + +used to delete a field from a record. + +"get", dest, slot, level + +This is used to get values from slots in outer frames. + +"put", value, slot, level + +This is used to store values into slots in outer frames. + +"push", array, slot + +Append to a mutable array. + +"pop", dest, array + +Remove the last element of a mutable array, putting the element into dest. + +"disrupt" + +### Make + +"array", dest, nr_elements + +"blob", dest, nr_bits + +"function", dest, code_name_text + +"pretext", dest, nr_characters + +A pretext must be converted to text by stone before it can leave the function scope. + +"record", dest, nr_elements + diff --git a/source/quickjs.c b/source/quickjs.c index c6c3f88b..ec414065 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -32643,9 +32643,13 @@ static void mach_gen_load_intrinsics (MachGenState *s, cJSON *intrinsics) { if (mach_gen_find_intrinsic (s, name) >= 0) continue; int slot = mach_gen_alloc_slot (s); cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("cap_name")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("access")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); - cJSON_AddItemToArray (instr, cJSON_CreateString (name)); + cJSON *lit = cJSON_CreateObject (); + cJSON_AddStringToObject (lit, "kind", "name"); + cJSON_AddStringToObject (lit, "name", name); + cJSON_AddStringToObject (lit, "make", "intrinsic"); + cJSON_AddItemToArray (instr, lit); cJSON_AddItemToArray (s->instructions, instr); s->intrinsic_cache[s->intrinsic_count].name = name; s->intrinsic_cache[s->intrinsic_count].slot = slot; @@ -32826,7 +32830,7 @@ static void mach_gen_emit_jump_cond (MachGenState *s, const char *op, int slot, static void mach_gen_emit_get_prop (MachGenState *s, int dest, int obj, const char *prop) { cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("load_prop")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("load")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); @@ -32835,7 +32839,7 @@ static void mach_gen_emit_get_prop (MachGenState *s, int dest, int obj, const ch static void mach_gen_emit_set_prop (MachGenState *s, int obj, const char *prop, int val) { cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("store_prop")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("store")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (val)); cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); @@ -32843,11 +32847,11 @@ static void mach_gen_emit_set_prop (MachGenState *s, int obj, const char *prop, } static void mach_gen_emit_get_elem (MachGenState *s, int dest, int obj, int idx) { - mach_gen_emit_3 (s, "load_idx", dest, obj, idx); + mach_gen_emit_3 (s, "load", dest, obj, idx); } static void mach_gen_emit_set_elem (MachGenState *s, int obj, int idx, int val) { - mach_gen_emit_3 (s, "store_idx", obj, idx, val); + mach_gen_emit_3 (s, "store", obj, idx, val); } static void mach_gen_emit_call (MachGenState *s, int dest, int func_slot, cJSON *args) { @@ -32856,11 +32860,11 @@ static void mach_gen_emit_call (MachGenState *s, int dest, int func_slot, cJSON mach_gen_emit_3 (s, "frame", frame_slot, func_slot, argc); int null_slot = mach_gen_alloc_slot (s); mach_gen_emit_1 (s, "null", null_slot); - mach_gen_emit_2 (s, "set_this", frame_slot, null_slot); + mach_gen_emit_3 (s, "setarg", frame_slot, 0, null_slot); int arg_idx = 1; cJSON *arg; cJSON_ArrayForEach (arg, args) { - mach_gen_emit_3 (s, "arg", frame_slot, arg_idx++, arg->valueint); + mach_gen_emit_3 (s, "setarg", frame_slot, arg_idx++, arg->valueint); } mach_gen_emit_2 (s, "invoke", frame_slot, dest); } @@ -32871,11 +32875,11 @@ static void mach_gen_emit_call_method (MachGenState *s, int dest, int obj, const int argc = cJSON_GetArraySize (args); int frame_slot = mach_gen_alloc_slot (s); mach_gen_emit_3 (s, "frame", frame_slot, func_slot, argc); - mach_gen_emit_2 (s, "set_this", frame_slot, obj); + mach_gen_emit_3 (s, "setarg", frame_slot, 0, obj); int arg_idx = 1; cJSON *arg; cJSON_ArrayForEach (arg, args) { - mach_gen_emit_3 (s, "arg", frame_slot, arg_idx++, arg->valueint); + mach_gen_emit_3 (s, "setarg", frame_slot, arg_idx++, arg->valueint); } mach_gen_emit_2 (s, "invoke", frame_slot, dest); } @@ -32886,11 +32890,11 @@ static void mach_gen_emit_go_call (MachGenState *s, int func_slot, cJSON *args) mach_gen_emit_3 (s, "goframe", frame_slot, func_slot, argc); int null_slot = mach_gen_alloc_slot (s); mach_gen_emit_1 (s, "null", null_slot); - mach_gen_emit_2 (s, "set_this", frame_slot, null_slot); + mach_gen_emit_3 (s, "setarg", frame_slot, 0, null_slot); int arg_idx = 1; cJSON *arg; cJSON_ArrayForEach (arg, args) { - mach_gen_emit_3 (s, "arg", frame_slot, arg_idx++, arg->valueint); + mach_gen_emit_3 (s, "setarg", frame_slot, arg_idx++, arg->valueint); } mach_gen_emit_1 (s, "goinvoke", frame_slot); } @@ -32901,22 +32905,21 @@ static void mach_gen_emit_go_call_method (MachGenState *s, int obj, const char * int argc = cJSON_GetArraySize (args); int frame_slot = mach_gen_alloc_slot (s); mach_gen_emit_3 (s, "goframe", frame_slot, func_slot, argc); - mach_gen_emit_2 (s, "set_this", frame_slot, obj); + mach_gen_emit_3 (s, "setarg", frame_slot, 0, obj); int arg_idx = 1; cJSON *arg; cJSON_ArrayForEach (arg, args) { - mach_gen_emit_3 (s, "arg", frame_slot, arg_idx++, arg->valueint); + mach_gen_emit_3 (s, "setarg", frame_slot, arg_idx++, arg->valueint); } mach_gen_emit_1 (s, "goinvoke", frame_slot); } static const char *mach_gen_binop_to_string (const char *kind) { if (strcmp (kind, "+") == 0) return "add"; - if (strcmp (kind, "-") == 0) return "sub"; - if (strcmp (kind, "*") == 0) return "mul"; - if (strcmp (kind, "/") == 0) return "div"; - if (strcmp (kind, "%") == 0) return "mod"; - if (strcmp (kind, "**") == 0) return "pow"; + if (strcmp (kind, "-") == 0) return "subtract"; + if (strcmp (kind, "*") == 0) return "multiply"; + if (strcmp (kind, "/") == 0) return "divide"; + if (strcmp (kind, "%") == 0) return "modulo"; if (strcmp (kind, "&") == 0) return "bitand"; if (strcmp (kind, "|") == 0) return "bitor"; if (strcmp (kind, "^") == 0) return "bitxor"; @@ -33005,10 +33008,14 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o mach_gen_emit_3 (s, "get", left_slot, slot, level); } else { cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("cap_name")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("access")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (left_slot)); - cJSON_AddItemToArray (instr, cJSON_CreateString (name)); - cJSON_AddItemToArray (s->instructions, instr); + cJSON *lit = cJSON_CreateObject (); + cJSON_AddStringToObject (lit, "kind", "name"); + cJSON_AddStringToObject (lit, "name", name); + cJSON_AddStringToObject (lit, "make", "intrinsic"); + cJSON_AddItemToArray (instr, lit); + mach_gen_add_instr (s, instr); } int right_slot = mach_gen_expr (s, right, -1); int dest = mach_gen_alloc_slot (s); @@ -33062,11 +33069,10 @@ static int mach_gen_assign (MachGenState *s, cJSON *node) { cJSON *right = cJSON_GetObjectItem (node, "right"); if (strcmp (kind, "+=") == 0) return mach_gen_compound_assign (s, node, "add"); - if (strcmp (kind, "-=") == 0) return mach_gen_compound_assign (s, node, "sub"); - if (strcmp (kind, "*=") == 0) return mach_gen_compound_assign (s, node, "mul"); - if (strcmp (kind, "/=") == 0) return mach_gen_compound_assign (s, node, "div"); - if (strcmp (kind, "%=") == 0) return mach_gen_compound_assign (s, node, "mod"); - if (strcmp (kind, "**=") == 0) return mach_gen_compound_assign (s, node, "pow"); + if (strcmp (kind, "-=") == 0) return mach_gen_compound_assign (s, node, "subtract"); + if (strcmp (kind, "*=") == 0) return mach_gen_compound_assign (s, node, "multiply"); + if (strcmp (kind, "/=") == 0) return mach_gen_compound_assign (s, node, "divide"); + if (strcmp (kind, "%=") == 0) return mach_gen_compound_assign (s, node, "modulo"); if (strcmp (kind, "&=") == 0) return mach_gen_compound_assign (s, node, "bitand"); if (strcmp (kind, "|=") == 0) return mach_gen_compound_assign (s, node, "bitor"); if (strcmp (kind, "^=") == 0) return mach_gen_compound_assign (s, node, "bitxor"); @@ -33163,15 +33169,19 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { mach_gen_emit_3 (s, "get", dest, parent_slot, level); return dest; } - /* Unbound — check intrinsic cache first, then emit cap_name */ + /* Unbound — check intrinsic cache first, then emit access with intrinsic */ int cached = mach_gen_find_intrinsic (s, name); if (cached >= 0) return cached; int dest = mach_gen_alloc_slot (s); cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("cap_name")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("access")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); - cJSON_AddItemToArray (instr, cJSON_CreateString (name)); - cJSON_AddItemToArray (s->instructions, instr); + cJSON *lit = cJSON_CreateObject (); + cJSON_AddStringToObject (lit, "kind", "name"); + cJSON_AddStringToObject (lit, "name", name); + cJSON_AddStringToObject (lit, "make", "intrinsic"); + cJSON_AddItemToArray (instr, lit); + mach_gen_add_instr (s, instr); return dest; } @@ -33253,8 +33263,10 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { cJSON *operand = cJSON_GetObjectItem (expr, "expression"); cJSON *is_postfix = cJSON_GetObjectItem (expr, "postfix"); int postfix = is_postfix && cJSON_IsTrue (is_postfix); - const char *op = (strcmp (kind, "++") == 0) ? "inc" : "dec"; + const char *arith_op = (strcmp (kind, "++") == 0) ? "add" : "subtract"; const char *operand_kind = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "kind")); + int one_slot = mach_gen_alloc_slot (s); + mach_gen_emit_2 (s, "int", one_slot, 1); if (strcmp (operand_kind, "name") == 0) { const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "name")); @@ -33272,13 +33284,17 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { mach_gen_emit_3 (s, "get", old_slot, slot, level); } else { cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("cap_name")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("access")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (old_slot)); - cJSON_AddItemToArray (instr, cJSON_CreateString (name)); - cJSON_AddItemToArray (s->instructions, instr); + cJSON *lit = cJSON_CreateObject (); + cJSON_AddStringToObject (lit, "kind", "name"); + cJSON_AddStringToObject (lit, "name", name); + cJSON_AddStringToObject (lit, "make", "intrinsic"); + cJSON_AddItemToArray (instr, lit); + mach_gen_add_instr (s, instr); } int new_slot = mach_gen_alloc_slot (s); - mach_gen_emit_2 (s, op, new_slot, old_slot); + mach_gen_emit_3 (s, arith_op, new_slot, old_slot, one_slot); /* Store new value */ if (level == 0) { int local = mach_gen_find_var (s, name); @@ -33297,7 +33313,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { int old_slot = mach_gen_alloc_slot (s); mach_gen_emit_get_prop (s, old_slot, obj_slot, prop); int new_slot = mach_gen_alloc_slot (s); - mach_gen_emit_2 (s, op, new_slot, old_slot); + mach_gen_emit_3 (s, arith_op, new_slot, old_slot, one_slot); mach_gen_emit_set_prop (s, obj_slot, prop, new_slot); return postfix ? old_slot : new_slot; } else if (strcmp (operand_kind, "[") == 0) { @@ -33308,7 +33324,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { int old_slot = mach_gen_alloc_slot (s); mach_gen_emit_get_elem (s, old_slot, obj_slot, idx_slot); int new_slot = mach_gen_alloc_slot (s); - mach_gen_emit_2 (s, op, new_slot, old_slot); + mach_gen_emit_3 (s, arith_op, new_slot, old_slot, one_slot); mach_gen_emit_set_elem (s, obj_slot, idx_slot, new_slot); return postfix ? old_slot : new_slot; } @@ -33324,7 +33340,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { const char *prop = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "right")); int obj_slot = mach_gen_expr (s, obj, -1); cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("delete_prop")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("delete")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (obj_slot)); cJSON_AddItemToArray (instr, cJSON_CreateString (prop)); @@ -33334,7 +33350,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { cJSON *idx = cJSON_GetObjectItem (operand, "right"); int obj_slot = mach_gen_expr (s, obj, -1); int idx_slot = mach_gen_expr (s, idx, -1); - mach_gen_emit_3 (s, "delete_idx", slot, obj_slot, idx_slot); + mach_gen_emit_3 (s, "delete", slot, obj_slot, idx_slot); } else { mach_gen_emit_const_bool (s, slot, 1); } @@ -33375,7 +33391,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { } int dest = mach_gen_alloc_slot (s); cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("mkarray")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("array")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (count)); cJSON *el; @@ -33392,7 +33408,7 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { cJSON *list = cJSON_GetObjectItem (expr, "list"); int dest = mach_gen_alloc_slot (s); cJSON *instr = cJSON_CreateArray (); - cJSON_AddItemToArray (instr, cJSON_CreateString ("mkrecord")); + cJSON_AddItemToArray (instr, cJSON_CreateString ("record")); cJSON_AddItemToArray (instr, cJSON_CreateNumber (dest)); cJSON_AddItemToArray (instr, cJSON_CreateNumber (0)); cJSON_AddItemToArray (s->instructions, instr); @@ -33421,13 +33437,8 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) { int func_id = s->func_counter++; cJSON *func = mach_gen_function (s, expr); cJSON_AddItemToArray (s->functions, func); - cJSON *is_arrow = cJSON_GetObjectItem (expr, "arrow"); int dest = mach_gen_alloc_slot (s); - if (is_arrow && cJSON_IsTrue (is_arrow)) { - mach_gen_emit_2 (s, "mkfunc_arrow", dest, func_id); - } else { - mach_gen_emit_2 (s, "mkfunc", dest, func_id); - } + mach_gen_emit_2 (s, "function", dest, func_id); return dest; } @@ -33589,7 +33600,9 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) { int slot = mach_gen_expr (s, expr, -1); mach_gen_emit_1 (s, "return", slot); } else { - mach_gen_emit_0 (s, "return_undef"); + int null_slot = mach_gen_alloc_slot (s); + mach_gen_emit_1 (s, "null", null_slot); + mach_gen_emit_1 (s, "return", null_slot); } return; } @@ -33692,7 +33705,7 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) { cJSON_AddItemToArray (s->functions, func); int local_slot = mach_gen_find_var (s, name); int dest = mach_gen_alloc_slot (s); - mach_gen_emit_2 (s, "mkfunc", dest, func_id); + mach_gen_emit_2 (s, "function", dest, func_id); if (local_slot >= 0) mach_gen_emit_2 (s, "move", local_slot, dest); } return; @@ -33791,7 +33804,7 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) { cJSON_AddItemToArray (s.functions, compiled); int local_slot = mach_gen_find_var (&s, fname); int dest = mach_gen_alloc_slot (&s); - mach_gen_emit_2 (&s, "mkfunc", dest, func_id); + mach_gen_emit_2 (&s, "function", dest, func_id); if (local_slot >= 0) mach_gen_emit_2 (&s, "move", local_slot, dest); } } @@ -33811,7 +33824,11 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) { cJSON_ArrayForEach (stmt, stmts) { mach_gen_statement (&s, stmt); } } - mach_gen_emit_0 (&s, "return_undef"); + { + int null_slot = mach_gen_alloc_slot (&s); + mach_gen_emit_1 (&s, "null", null_slot); + mach_gen_emit_1 (&s, "return", null_slot); + } /* Compile disruption clause if present */ int disruption_start = 0; @@ -33819,7 +33836,9 @@ static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) { if (disruption && cJSON_IsArray (disruption)) { disruption_start = cJSON_GetArraySize (s.instructions); cJSON_ArrayForEach (stmt, disruption) { mach_gen_statement (&s, stmt); } - mach_gen_emit_0 (&s, "return_undef"); + int null_slot2 = mach_gen_alloc_slot (&s); + mach_gen_emit_1 (&s, "null", null_slot2); + mach_gen_emit_1 (&s, "return", null_slot2); } cJSON_AddNumberToObject (result, "disruption_pc", disruption_start); @@ -33897,7 +33916,7 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) { cJSON_AddItemToArray (s->functions, compiled); int local_slot = mach_gen_find_var (s, name); int dest = mach_gen_alloc_slot (s); - mach_gen_emit_2 (s, "mkfunc", dest, func_id); + mach_gen_emit_2 (s, "function", dest, func_id); if (local_slot >= 0) mach_gen_emit_2 (s, "move", local_slot, dest); } } @@ -33932,10 +33951,13 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) { } } - if (last_expr_slot >= 0) + if (last_expr_slot >= 0) { mach_gen_emit_1 (s, "return", last_expr_slot); - else - mach_gen_emit_0 (s, "return_undef"); + } else { + int null_slot = mach_gen_alloc_slot (s); + mach_gen_emit_1 (s, "null", null_slot); + mach_gen_emit_1 (s, "return", null_slot); + } cJSON *main_obj = cJSON_CreateObject (); cJSON_AddNumberToObject (main_obj, "nr_args", 0); @@ -34192,7 +34214,19 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, /* ---- Constants ---- */ if (strcmp(op, "access") == 0) { int dest = (int)a1->valuedouble; - if (cJSON_IsString(a2)) { + if (cJSON_IsObject(a2)) { + /* Intrinsic: {"kind":"name","name":"...","make":"intrinsic"} */ + const char *iname = cJSON_GetStringValue(cJSON_GetObjectItem(a2, "name")); + if (iname) { + JSValue key = JS_NewString(ctx, iname); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + JSValue val = JS_GetProperty(ctx, ctx->global_obj, key); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + frame->slots[dest] = val; + } else { + frame->slots[dest] = JS_NULL; + } + } else if (cJSON_IsString(a2)) { JSValue str = JS_NewString(ctx, a2->valuestring); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame->slots[dest] = str; @@ -34219,163 +34253,424 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, frame->slots[(int)a1->valuedouble] = frame->slots[(int)a2->valuedouble]; } - /* ---- Arithmetic (float64) ---- */ + /* ---- Arithmetic (inline) ---- */ else if (strcmp(op, "add") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_ADD, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int64_t r = (int64_t)JS_VALUE_GET_INT(left) + (int64_t)JS_VALUE_GET_INT(right); + frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) + ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewFloat64(ctx, a + b); + } else if (JS_IsText(left) && JS_IsText(right)) { + JSValue res = JS_ConcatString(ctx, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + frame->slots[dest] = res; + } else { goto disrupt; } } - else if (strcmp(op, "sub") == 0) { + else if (strcmp(op, "subtract") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_SUB, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int64_t r = (int64_t)JS_VALUE_GET_INT(left) - (int64_t)JS_VALUE_GET_INT(right); + frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) + ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewFloat64(ctx, a - b); + } else { goto disrupt; } } - else if (strcmp(op, "mul") == 0) { + else if (strcmp(op, "multiply") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_MUL, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int64_t r = (int64_t)JS_VALUE_GET_INT(left) * (int64_t)JS_VALUE_GET_INT(right); + frame->slots[dest] = (r >= INT32_MIN && r <= INT32_MAX) + ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewFloat64(ctx, a * b); + } else { goto disrupt; } } - else if (strcmp(op, "div") == 0) { + else if (strcmp(op, "divide") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_DIV, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int32_t ia = JS_VALUE_GET_INT(left), ib = JS_VALUE_GET_INT(right); + if (ib == 0) { goto disrupt; } + if (ia % ib == 0) frame->slots[dest] = JS_NewInt32(ctx, ia / ib); + else frame->slots[dest] = JS_NewFloat64(ctx, (double)ia / (double)ib); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + if (b == 0.0) { goto disrupt; } + frame->slots[dest] = JS_NewFloat64(ctx, a / b); + } else { goto disrupt; } } - else if (strcmp(op, "mod") == 0) { + else if (strcmp(op, "integer_divide") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_MOD, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int32_t ib = JS_VALUE_GET_INT(right); + if (ib == 0) { goto disrupt; } + frame->slots[dest] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) / ib); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + if (b == 0.0) { goto disrupt; } + frame->slots[dest] = JS_NewInt32(ctx, (int32_t)(a / b)); + } else { goto disrupt; } } - else if (strcmp(op, "pow") == 0) { + else if (strcmp(op, "modulo") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_POW, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int32_t ib = JS_VALUE_GET_INT(right); + if (ib == 0) { goto disrupt; } + frame->slots[dest] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) % ib); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + if (b == 0.0) { goto disrupt; } + frame->slots[dest] = JS_NewFloat64(ctx, fmod(a, b)); + } else { goto disrupt; } + } + else if (strcmp(op, "remainder") == 0) { + int dest = (int)a1->valuedouble; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int32_t ib = JS_VALUE_GET_INT(right); + if (ib == 0) { goto disrupt; } + frame->slots[dest] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) % ib); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + if (b == 0.0) { goto disrupt; } + frame->slots[dest] = JS_NewFloat64(ctx, remainder(a, b)); + } else { goto disrupt; } + } + else if (strcmp(op, "max") == 0) { + int dest = (int)a1->valuedouble; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int32_t ia = JS_VALUE_GET_INT(left), ib = JS_VALUE_GET_INT(right); + frame->slots[dest] = JS_NewInt32(ctx, ia > ib ? ia : ib); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewFloat64(ctx, a > b ? a : b); + } else { goto disrupt; } + } + else if (strcmp(op, "min") == 0) { + int dest = (int)a1->valuedouble; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int32_t ia = JS_VALUE_GET_INT(left), ib = JS_VALUE_GET_INT(right); + frame->slots[dest] = JS_NewInt32(ctx, ia < ib ? ia : ib); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewFloat64(ctx, a < b ? a : b); + } else { goto disrupt; } } else if (strcmp(op, "neg") == 0) { int dest = (int)a1->valuedouble; JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsNumber(v)) { goto disrupt; } if (JS_IsInt(v)) { int32_t i = JS_VALUE_GET_INT(v); if (i == INT32_MIN) frame->slots[dest] = JS_NewFloat64(ctx, -(double)i); else frame->slots[dest] = JS_NewInt32(ctx, -i); } else { - double d; JS_ToFloat64(ctx, &d, v); + double d; + JS_ToFloat64(ctx, &d, v); frame->slots[dest] = JS_NewFloat64(ctx, -d); } } - else if (strcmp(op, "inc") == 0) { + else if (strcmp(op, "abs") == 0) { int dest = (int)a1->valuedouble; JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsNumber(v)) { goto disrupt; } if (JS_IsInt(v)) { int32_t i = JS_VALUE_GET_INT(v); - if (i == INT32_MAX) frame->slots[dest] = JS_NewFloat64(ctx, (double)i + 1); - else frame->slots[dest] = JS_NewInt32(ctx, i + 1); + if (i == INT32_MIN) frame->slots[dest] = JS_NewFloat64(ctx, (double)INT32_MAX + 1.0); + else frame->slots[dest] = JS_NewInt32(ctx, i < 0 ? -i : i); } else { - double d; JS_ToFloat64(ctx, &d, v); - frame->slots[dest] = JS_NewFloat64(ctx, d + 1); + double d; + JS_ToFloat64(ctx, &d, v); + frame->slots[dest] = JS_NewFloat64(ctx, fabs(d)); } } - else if (strcmp(op, "dec") == 0) { + else if (strcmp(op, "sign") == 0) { int dest = (int)a1->valuedouble; JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsNumber(v)) { goto disrupt; } if (JS_IsInt(v)) { int32_t i = JS_VALUE_GET_INT(v); - if (i == INT32_MIN) frame->slots[dest] = JS_NewFloat64(ctx, (double)i - 1); - else frame->slots[dest] = JS_NewInt32(ctx, i - 1); + frame->slots[dest] = JS_NewInt32(ctx, (i > 0) - (i < 0)); } else { - double d; JS_ToFloat64(ctx, &d, v); - frame->slots[dest] = JS_NewFloat64(ctx, d - 1); + double d; + JS_ToFloat64(ctx, &d, v); + frame->slots[dest] = JS_NewInt32(ctx, (d > 0) - (d < 0)); } } + else if (strcmp(op, "fraction") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsNumber(v)) { goto disrupt; } + if (JS_IsInt(v)) { + frame->slots[dest] = JS_NewInt32(ctx, 0); + } else { + double d; + JS_ToFloat64(ctx, &d, v); + frame->slots[dest] = JS_NewFloat64(ctx, d - trunc(d)); + } + } + else if (strcmp(op, "integer") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsNumber(v)) { goto disrupt; } + if (JS_IsInt(v)) { + frame->slots[dest] = v; + } else { + double d; + JS_ToFloat64(ctx, &d, v); + frame->slots[dest] = JS_NewFloat64(ctx, trunc(d)); + } + } + else if (strcmp(op, "ceiling") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + JSValue p = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(v) || !JS_IsNumber(p)) { goto disrupt; } + double d, place; + JS_ToFloat64(ctx, &d, v); + JS_ToFloat64(ctx, &place, p); + frame->slots[dest] = JS_NewFloat64(ctx, ceil(d * place) / place); + } + else if (strcmp(op, "floor") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + JSValue p = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(v) || !JS_IsNumber(p)) { goto disrupt; } + double d, place; + JS_ToFloat64(ctx, &d, v); + JS_ToFloat64(ctx, &place, p); + frame->slots[dest] = JS_NewFloat64(ctx, floor(d * place) / place); + } + else if (strcmp(op, "round") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + JSValue p = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(v) || !JS_IsNumber(p)) { goto disrupt; } + double d, place; + JS_ToFloat64(ctx, &d, v); + JS_ToFloat64(ctx, &place, p); + frame->slots[dest] = JS_NewFloat64(ctx, round(d * place) / place); + } + else if (strcmp(op, "trunc") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + JSValue p = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(v) || !JS_IsNumber(p)) { goto disrupt; } + double d, place; + JS_ToFloat64(ctx, &d, v); + JS_ToFloat64(ctx, &place, p); + frame->slots[dest] = JS_NewFloat64(ctx, trunc(d * place) / place); + } /* ---- Text ---- */ else if (strcmp(op, "concat") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; + if (!JS_IsText(left) || !JS_IsText(right)) { goto disrupt; } JSValue res = JS_ConcatString(ctx, left, right); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame->slots[dest] = res; } + else if (strcmp(op, "concat_space") == 0) { + int dest = (int)a1->valuedouble; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (!JS_IsText(left) || !JS_IsText(right)) { goto disrupt; } + JSValue space = JS_ConcatString(ctx, left, JS_NewString(ctx, " ")); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + JSValue res = JS_ConcatString(ctx, space, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + frame->slots[dest] = res; + } + else if (strcmp(op, "length") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsText(v)) { goto disrupt; } + frame->slots[dest] = JS_NewInt32(ctx, js_string_value_len(v)); + } + else if (strcmp(op, "lower") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsText(v)) { goto disrupt; } + JSValue res = js_cell_text_lower(ctx, JS_NULL, 1, &v); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + frame->slots[dest] = res; + } + else if (strcmp(op, "upper") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsText(v)) { goto disrupt; } + JSValue res = js_cell_text_upper(ctx, JS_NULL, 1, &v); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + frame->slots[dest] = res; + } + else if (strcmp(op, "character") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsNumber(v)) { goto disrupt; } + JSValue res = js_cell_character(ctx, JS_NULL, 1, &v); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + frame->slots[dest] = res; + } + else if (strcmp(op, "codepoint") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (!JS_IsText(v)) { goto disrupt; } + JSValue res = js_cell_text_codepoint(ctx, JS_NULL, 1, &v); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + frame->slots[dest] = res; + } - /* ---- Comparison ---- */ + /* ---- Comparison (inline) ---- */ else if (strcmp(op, "eq") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_EQ, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; + if (left == right) { + frame->slots[dest] = JS_TRUE; + } else if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) == JS_VALUE_GET_INT(right)); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewBool(ctx, a == b); + } else if (JS_IsText(left) && JS_IsText(right)) { + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, TRUE) == 0); + } else if (JS_IsNull(left) && JS_IsNull(right)) { + frame->slots[dest] = JS_TRUE; + } else if (JS_VALUE_GET_TAG(left) == JS_TAG_BOOL && JS_VALUE_GET_TAG(right) == JS_TAG_BOOL) { + frame->slots[dest] = JS_NewBool(ctx, left == right); + } else { + frame->slots[dest] = JS_FALSE; + } } else if (strcmp(op, "ne") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_NEQ, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; + if (left == right) { + frame->slots[dest] = JS_FALSE; + } else if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) != JS_VALUE_GET_INT(right)); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewBool(ctx, a != b); + } else if (JS_IsText(left) && JS_IsText(right)) { + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, TRUE) != 0); + } else if (JS_IsNull(left) && JS_IsNull(right)) { + frame->slots[dest] = JS_FALSE; + } else if (JS_VALUE_GET_TAG(left) == JS_TAG_BOOL && JS_VALUE_GET_TAG(right) == JS_TAG_BOOL) { + frame->slots[dest] = JS_NewBool(ctx, left != right); + } else { + frame->slots[dest] = JS_TRUE; + } } else if (strcmp(op, "lt") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_LT, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) < JS_VALUE_GET_INT(right)); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewBool(ctx, a < b); + } else if (JS_IsText(left) && JS_IsText(right)) { + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, FALSE) < 0); + } else { goto disrupt; } } else if (strcmp(op, "le") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_LE, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) <= JS_VALUE_GET_INT(right)); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewBool(ctx, a <= b); + } else if (JS_IsText(left) && JS_IsText(right)) { + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, FALSE) <= 0); + } else { goto disrupt; } } else if (strcmp(op, "gt") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_GT, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) > JS_VALUE_GET_INT(right)); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewBool(ctx, a > b); + } else if (JS_IsText(left) && JS_IsText(right)) { + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, FALSE) > 0); + } else { goto disrupt; } } else if (strcmp(op, "ge") == 0) { int dest = (int)a1->valuedouble; JSValue left = frame->slots[(int)a2->valuedouble]; JSValue right = frame->slots[(int)a3->valuedouble]; - JSValue res = reg_vm_binop(ctx, MACH_GE, left, right); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = res; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[dest] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) >= JS_VALUE_GET_INT(right)); + } else if (JS_IsNumber(left) && JS_IsNumber(right)) { + double a, b; + JS_ToFloat64(ctx, &a, left); + JS_ToFloat64(ctx, &b, right); + frame->slots[dest] = JS_NewBool(ctx, a >= b); + } else if (JS_IsText(left) && JS_IsText(right)) { + frame->slots[dest] = JS_NewBool(ctx, js_string_compare_value(ctx, left, right, FALSE) >= 0); + } else { goto disrupt; } } /* ---- Sensory (type checks) ---- */ - else if (strcmp(op, "number?") == 0) { - int dest = (int)a1->valuedouble; - frame->slots[dest] = JS_VALUE_IS_NUMBER(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; - } else if (strcmp(op, "text?") == 0) { int dest = (int)a1->valuedouble; frame->slots[dest] = JS_VALUE_IS_TEXT(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; @@ -34413,6 +34708,88 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, int dest = (int)a1->valuedouble; frame->slots[dest] = (frame->slots[(int)a2->valuedouble] == JS_FALSE) ? JS_TRUE : JS_FALSE; } + else if (strcmp(op, "blob?") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_IsBlob(frame->slots[(int)a2->valuedouble]) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "character?") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + frame->slots[dest] = (JS_IsText(v) && js_string_value_len(v) == 1) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "data?") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + frame->slots[dest] = (JS_IsRecord(v) || JS_IsArray(v)) ? JS_TRUE : JS_FALSE; + } + else if (strcmp(op, "digit?") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (JS_IsText(v) && js_string_value_len(v) == 1) { + uint32_t c = js_string_value_get(v, 0); + frame->slots[dest] = (c >= '0' && c <= '9') ? JS_TRUE : JS_FALSE; + } else { + frame->slots[dest] = JS_FALSE; + } + } + else if (strcmp(op, "fit?") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (JS_IsInt(v)) { + frame->slots[dest] = JS_TRUE; + } else if (JS_IsNumber(v)) { + double d; + JS_ToFloat64(ctx, &d, v); + frame->slots[dest] = (d == (double)(int32_t)d) ? JS_TRUE : JS_FALSE; + } else { + frame->slots[dest] = JS_FALSE; + } + } + else if (strcmp(op, "letter?") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (JS_IsText(v) && js_string_value_len(v) == 1) { + uint32_t c = js_string_value_get(v, 0); + frame->slots[dest] = ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) ? JS_TRUE : JS_FALSE; + } else { + frame->slots[dest] = JS_FALSE; + } + } + else if (strcmp(op, "pattern?") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = JS_FALSE; /* TODO: pattern type check */ + } + else if (strcmp(op, "stone?") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (JS_IsPtr(v)) { + objhdr_t hdr = *(objhdr_t *)JS_VALUE_GET_PTR(v); + frame->slots[dest] = objhdr_s(hdr) ? JS_TRUE : JS_FALSE; + } else { + /* Primitives are immutable */ + frame->slots[dest] = JS_TRUE; + } + } + else if (strcmp(op, "upper?") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (JS_IsText(v) && js_string_value_len(v) == 1) { + uint32_t c = js_string_value_get(v, 0); + frame->slots[dest] = (c >= 'A' && c <= 'Z') ? JS_TRUE : JS_FALSE; + } else { + frame->slots[dest] = JS_FALSE; + } + } + else if (strcmp(op, "whitespace?") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + if (JS_IsText(v) && js_string_value_len(v) == 1) { + uint32_t c = js_string_value_get(v, 0); + frame->slots[dest] = (c == ' ' || c == '\t' || c == '\n' || c == '\r') ? JS_TRUE : JS_FALSE; + } else { + frame->slots[dest] = JS_FALSE; + } + } /* ---- Logical / Bitwise ---- */ else if (strcmp(op, "not") == 0) { @@ -34427,45 +34804,63 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, } else if (strcmp(op, "bitand") == 0) { int dest = (int)a1->valuedouble; - JSValue res = reg_vm_binop(ctx, MACH_BAND, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } + int32_t ia, ib; + JS_ToInt32(ctx, &ia, left); + JS_ToInt32(ctx, &ib, right); + frame->slots[dest] = JS_NewInt32(ctx, ia & ib); } else if (strcmp(op, "bitor") == 0) { int dest = (int)a1->valuedouble; - JSValue res = reg_vm_binop(ctx, MACH_BOR, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } + int32_t ia, ib; + JS_ToInt32(ctx, &ia, left); + JS_ToInt32(ctx, &ib, right); + frame->slots[dest] = JS_NewInt32(ctx, ia | ib); } else if (strcmp(op, "bitxor") == 0) { int dest = (int)a1->valuedouble; - JSValue res = reg_vm_binop(ctx, MACH_BXOR, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } + int32_t ia, ib; + JS_ToInt32(ctx, &ia, left); + JS_ToInt32(ctx, &ib, right); + frame->slots[dest] = JS_NewInt32(ctx, ia ^ ib); } else if (strcmp(op, "shl") == 0) { int dest = (int)a1->valuedouble; - JSValue res = reg_vm_binop(ctx, MACH_SHL, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } + int32_t ia, ib; + JS_ToInt32(ctx, &ia, left); + JS_ToInt32(ctx, &ib, right); + frame->slots[dest] = JS_NewInt32(ctx, ia << (ib & 31)); } else if (strcmp(op, "shr") == 0) { int dest = (int)a1->valuedouble; - JSValue res = reg_vm_binop(ctx, MACH_SHR, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } + int32_t ia, ib; + JS_ToInt32(ctx, &ia, left); + JS_ToInt32(ctx, &ib, right); + frame->slots[dest] = JS_NewInt32(ctx, ia >> (ib & 31)); } else if (strcmp(op, "ushr") == 0) { int dest = (int)a1->valuedouble; - JSValue res = reg_vm_binop(ctx, MACH_USHR, frame->slots[(int)a2->valuedouble], frame->slots[(int)a3->valuedouble]); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } - frame->slots[dest] = res; + JSValue left = frame->slots[(int)a2->valuedouble]; + JSValue right = frame->slots[(int)a3->valuedouble]; + if (!JS_IsNumber(left) || !JS_IsNumber(right)) { goto disrupt; } + int32_t ia, ib; + JS_ToInt32(ctx, &ia, left); + JS_ToInt32(ctx, &ib, right); + frame->slots[dest] = JS_NewInt32(ctx, (uint32_t)ia >> (ib & 31)); } /* ---- Control flow ---- */ @@ -34497,71 +34892,86 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, if (label && !JS_IsNull(frame->slots[slot])) pc = mcode_resolve_label(code, label); } + else if (strcmp(op, "jump_empty") == 0) { + int slot = (int)a1->valuedouble; + const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; + if (label && JS_IsNull(frame->slots[slot])) + pc = mcode_resolve_label(code, label); + } + else if (strcmp(op, "wary_true") == 0) { + int slot = (int)a1->valuedouble; + const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; + JSValue v = frame->slots[slot]; + if (v == JS_TRUE) { + if (label) pc = mcode_resolve_label(code, label); + } else if (v != JS_FALSE) { + goto disrupt; + } + } + else if (strcmp(op, "wary_false") == 0) { + int slot = (int)a1->valuedouble; + const char *label = cJSON_IsString(a2) ? a2->valuestring : NULL; + JSValue v = frame->slots[slot]; + if (v == JS_FALSE) { + if (label) pc = mcode_resolve_label(code, label); + } else if (v != JS_TRUE) { + goto disrupt; + } + } - /* ---- Property access ---- */ - else if (strcmp(op, "load_prop") == 0) { + /* ---- Property/element access (unified) ---- */ + else if (strcmp(op, "load") == 0) { int dest = (int)a1->valuedouble; int obj_reg = (int)a2->valuedouble; - JSValue key; - if (cJSON_IsString(a3)) { - key = JS_NewString(ctx, a3->valuestring); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } else { - key = frame->slots[(int)a3->valuedouble]; - } JSValue obj = frame->slots[obj_reg]; - JSValue val = JS_GetProperty(ctx, obj, key); + JSValue val; + if (cJSON_IsString(a3)) { + JSValue key = JS_NewString(ctx, a3->valuestring); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + obj = frame->slots[obj_reg]; + val = JS_GetProperty(ctx, obj, key); + } else { + JSValue idx = frame->slots[(int)a3->valuedouble]; + if (JS_IsInt(idx)) + val = JS_GetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx)); + else + val = JS_GetProperty(ctx, obj, idx); + } frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame->slots[dest] = val; } - else if (strcmp(op, "store_prop") == 0) { + else if (strcmp(op, "store") == 0) { int obj_reg = (int)a1->valuedouble; int val_reg = (int)a2->valuedouble; - JSValue key; - if (cJSON_IsString(a3)) { - key = JS_NewString(ctx, a3->valuestring); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } else { - key = frame->slots[(int)a3->valuedouble]; - } JSValue obj = frame->slots[obj_reg]; JSValue val = frame->slots[val_reg]; - JS_SetProperty(ctx, obj, key, val); + if (cJSON_IsString(a3)) { + JSValue key = JS_NewString(ctx, a3->valuestring); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + obj = frame->slots[obj_reg]; + val = frame->slots[val_reg]; + JS_SetProperty(ctx, obj, key, val); + } else { + JSValue idx = frame->slots[(int)a3->valuedouble]; + if (JS_IsInt(idx)) + JS_SetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx), val); + else + JS_SetProperty(ctx, obj, idx, val); + } frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); } - else if (strcmp(op, "load_idx") == 0) { - int dest = (int)a1->valuedouble; - JSValue obj = frame->slots[(int)a2->valuedouble]; - JSValue idx = frame->slots[(int)a3->valuedouble]; - JSValue val; - if (JS_IsInt(idx)) - val = JS_GetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx)); - else - val = JS_GetProperty(ctx, obj, idx); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = val; - } - else if (strcmp(op, "store_idx") == 0) { - JSValue obj = frame->slots[(int)a1->valuedouble]; - JSValue idx = frame->slots[(int)a2->valuedouble]; - JSValue val = frame->slots[(int)a3->valuedouble]; - if (JS_IsInt(idx)) - JS_SetPropertyUint32(ctx, obj, JS_VALUE_GET_INT(idx), val); - else - JS_SetProperty(ctx, obj, idx, val); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } - else if (strcmp(op, "delete_prop") == 0) { + else if (strcmp(op, "delete") == 0) { int dest = (int)a1->valuedouble; int obj_reg = (int)a2->valuedouble; + JSValue obj = frame->slots[obj_reg]; JSValue key; if (cJSON_IsString(a3)) { key = JS_NewString(ctx, a3->valuestring); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + obj = frame->slots[obj_reg]; } else { key = frame->slots[(int)a3->valuedouble]; } - JSValue obj = frame->slots[obj_reg]; int ret = JS_DeleteProperty(ctx, obj, key); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame->slots[dest] = JS_NewBool(ctx, ret >= 0); @@ -34604,33 +35014,7 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, } } - /* ---- Variable access (globals / unresolved) ---- */ - else if (strcmp(op, "cap_name") == 0) { - int dest = (int)a1->valuedouble; - JSValue key; - if (cJSON_IsString(a2)) { - key = JS_NewString(ctx, a2->valuestring); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } else { - key = frame->slots[(int)a2->valuedouble]; - } - JSValue val = JS_GetProperty(ctx, ctx->global_obj, key); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - frame->slots[dest] = val; - } - else if (strcmp(op, "set_var") == 0) { - int val_reg = (int)a2->valuedouble; - JSValue key; - if (cJSON_IsString(a1)) { - key = JS_NewString(ctx, a1->valuestring); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } else { - key = frame->slots[(int)a1->valuedouble]; - } - JSValue val = frame->slots[val_reg]; - JS_SetProperty(ctx, ctx->global_obj, key, val); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - } + /* ---- Function calls ---- */ else if (strcmp(op, "frame") == 0) { @@ -34643,29 +35027,20 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, } JSFunction *fn = JS_VALUE_GET_FUNCTION(func_val); + int nr_slots; if (fn->kind == JS_FUNC_KIND_MCODE) { - JSMCode *fn_code = fn->u.mcode.code; - JSFrameRegister *new_frame = alloc_frame_register(ctx, fn_code->nr_slots); - if (!new_frame) { goto disrupt; } - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - /* Re-read func_val after GC may have moved it */ - func_val = frame->slots[(int)a2->valuedouble]; - new_frame->function = func_val; - frame->slots[frame_reg] = JS_MKPTR(new_frame); + nr_slots = fn->u.mcode.code->nr_slots; } else { - /* For C/bytecode functions, store func_val directly — handle at invoke */ - frame->slots[frame_reg] = func_val; + nr_slots = call_argc + 2; } + JSFrameRegister *new_frame = alloc_frame_register(ctx, nr_slots); + if (!new_frame) { goto disrupt; } + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + func_val = frame->slots[(int)a2->valuedouble]; + new_frame->function = func_val; + frame->slots[frame_reg] = JS_MKPTR(new_frame); } - else if (strcmp(op, "set_this") == 0) { - int frame_reg = (int)a1->valuedouble; - JSValue target = frame->slots[frame_reg]; - if (!JS_IsFunction(target)) { - JSFrameRegister *call_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(target); - call_frame->slots[0] = frame->slots[(int)a2->valuedouble]; - } - } - else if (strcmp(op, "arg") == 0) { + else if (strcmp(op, "setarg") == 0) { int frame_reg = (int)a1->valuedouble; int arg_idx = (int)a2->valuedouble; int val_reg = (int)a3->valuedouble; @@ -34680,67 +35055,35 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, int ret_reg = (int)a2->valuedouble; JSValue target = frame->slots[frame_reg]; - if (JS_IsFunction(target)) { - /* C or bytecode function — collect args from the frame state */ - JSFunction *fn = JS_VALUE_GET_FUNCTION(target); - /* For C functions, we need to collect args. Look back at preceding arg instructions. */ - /* Simpler approach: call via JS_Call with args collected */ - /* We need to find the args that were set. Use a small stack buffer. */ - JSValue c_argv[16]; - int c_argc = 0; - /* Scan back in instructions to find arg instructions for this frame_reg */ - /* Actually, for C functions, the args were stored on the frame target which is func_val */ - /* Better: just call with global_obj as this and no args for now */ - /* Full approach: re-scan recent instructions to collect args */ + JSFrameRegister *new_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(target); + JSFunction *fn = JS_VALUE_GET_FUNCTION(new_frame->function); - /* Better: use JS_Call with the func and gather args from recent frame/arg ops */ - /* Since we can't easily recover args from a non-frame target, use JS_Call with 0 args */ - /* TODO: proper C function arg collection */ - JSValue this_val = JS_NULL; - result = JS_Call(ctx, target, this_val, c_argc, c_argv); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(result)) goto disrupt; - frame->slots[ret_reg] = result; + if (fn->kind == JS_FUNC_KIND_MCODE) { + /* Store return address: pc << 16 | ret_slot */ + frame->address = JS_NewInt32(ctx, (pc << 16) | ret_reg); + new_frame->caller = JS_MKPTR(frame); + + /* Switch to new frame */ + frame = new_frame; + frame_ref.val = JS_MKPTR(frame); + code = fn->u.mcode.code; + pc = 0; } else { - /* MCODE function — switch frames */ - JSFrameRegister *new_frame = (JSFrameRegister *)JS_VALUE_GET_PTR(target); - JSFunction *fn = JS_VALUE_GET_FUNCTION(new_frame->function); - - if (fn->kind == JS_FUNC_KIND_MCODE) { - /* Store return address: pc << 16 | ret_slot */ - frame->address = JS_NewInt32(ctx, (pc << 16) | ret_reg); - new_frame->caller = JS_MKPTR(frame); - - /* Switch to new frame */ - frame = new_frame; - frame_ref.val = JS_MKPTR(frame); - code = fn->u.mcode.code; - pc = 0; - } else if (fn->kind == JS_FUNC_KIND_C) { - /* C function stored in a frame — collect args from frame slots */ - int c_argc = 0; - JSValue c_argv[16]; - for (int i = 1; i < fn->u.mcode.code->nr_slots && i <= 16; i++) { - if (JS_IsNull(new_frame->slots[i])) break; - c_argv[c_argc++] = new_frame->slots[i]; - } - ctx->reg_current_frame = frame_ref.val; - ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0; - JSValue c_result = js_call_c_function(ctx, new_frame->function, new_frame->slots[0], c_argc, c_argv); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - ctx->reg_current_frame = JS_NULL; - if (JS_IsException(c_result)) { goto disrupt; } - frame->slots[ret_reg] = c_result; - } else { - /* Bytecode interop — use JS_Call */ - int bc_argc = 0; - JSValue bc_argv[16]; - /* TODO: proper arg count */ - JSValue bc_result = JS_Call(ctx, new_frame->function, new_frame->slots[0], bc_argc, bc_argv); - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(bc_result)) { goto disrupt; } - frame->slots[ret_reg] = bc_result; + /* C or bytecode function — collect args from frame slots */ + int c_argc = 0; + JSValue c_argv[16]; + int nr_slots = (int)objhdr_cap56(new_frame->hdr); + for (int i = 1; i < nr_slots && c_argc < 16; i++) { + if (JS_IsNull(new_frame->slots[i])) break; + c_argv[c_argc++] = new_frame->slots[i]; } + ctx->reg_current_frame = frame_ref.val; + ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0; + JSValue c_result = JS_Call(ctx, new_frame->function, new_frame->slots[0], c_argc, c_argv); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + ctx->reg_current_frame = JS_NULL; + if (JS_IsException(c_result)) { goto disrupt; } + frame->slots[ret_reg] = c_result; } } @@ -34748,27 +35091,28 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, else if (strcmp(op, "goframe") == 0) { int frame_reg = (int)a1->valuedouble; int func_reg = (int)a2->valuedouble; + int call_argc = a3 ? (int)a3->valuedouble : 0; JSValue func_val = frame->slots[func_reg]; if (!JS_IsFunction(func_val)) { goto disrupt; } JSFunction *fn = JS_VALUE_GET_FUNCTION(func_val); + int nr_slots; if (fn->kind == JS_FUNC_KIND_MCODE) { - JSMCode *fn_code = fn->u.mcode.code; - JSFrameRegister *new_frame = alloc_frame_register(ctx, fn_code->nr_slots); - if (!new_frame) { goto disrupt; } - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - func_val = frame->slots[func_reg]; /* re-read after GC */ - new_frame->function = func_val; - frame->slots[frame_reg] = JS_MKPTR(new_frame); + nr_slots = fn->u.mcode.code->nr_slots; } else { - frame->slots[frame_reg] = func_val; + nr_slots = call_argc + 2; } + JSFrameRegister *new_frame = alloc_frame_register(ctx, nr_slots); + if (!new_frame) { goto disrupt; } + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + func_val = frame->slots[func_reg]; + new_frame->function = func_val; + frame->slots[frame_reg] = JS_MKPTR(new_frame); } else if (strcmp(op, "goinvoke") == 0) { int frame_reg = (int)a1->valuedouble; - int ret_reg = (int)a2->valuedouble; JSValue target = frame->slots[frame_reg]; if (JS_IsFunction(target)) { @@ -34810,55 +35154,59 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, pc = ret_info >> 16; frame->slots[ret_info & 0xFFFF] = result; } - else if (strcmp(op, "return_undef") == 0) { - result = JS_NULL; + else if (strcmp(op, "return_value") == 0) { + int dest = (int)a1->valuedouble; + frame->slots[dest] = result; + } - if (JS_IsNull(frame->caller)) goto done; - - JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller); - int ret_info = JS_VALUE_GET_INT(caller->address); - frame->caller = JS_NULL; - - frame = caller; - frame_ref.val = JS_MKPTR(frame); - - JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); - code = fn->u.mcode.code; - pc = ret_info >> 16; - frame->slots[ret_info & 0xFFFF] = result; + /* ---- Apply ---- */ + else if (strcmp(op, "apply") == 0) { + int func_slot = (int)a1->valuedouble; + int arr_slot = (int)a2->valuedouble; + JSValue func_val = frame->slots[func_slot]; + JSValue arr_val = frame->slots[arr_slot]; + if (!JS_IsFunction(func_val) || !JS_IsArray(arr_val)) { goto disrupt; } + JSValue len_val = JS_GetProperty(ctx, arr_val, JS_KEY_length); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + int len = JS_IsNumber(len_val) ? (int)JS_VALUE_GET_INT(len_val) : 0; + JSValue argv[256]; + if (len > 256) len = 256; + for (int i = 0; i < len; i++) { + argv[i] = JS_GetPropertyUint32(ctx, arr_val, i); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + } + ctx->reg_current_frame = frame_ref.val; + ctx->rt->current_register_pc = pc > 0 ? pc - 1 : 0; + result = JS_Call(ctx, func_val, JS_NULL, len, argv); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(result)) { goto disrupt; } } /* ---- Object/Array creation ---- */ - else if (strcmp(op, "mkrecord") == 0) { + else if (strcmp(op, "record") == 0) { int dest = (int)a1->valuedouble; JSValue rec = JS_NewObject(ctx); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(rec)) { goto disrupt; } frame->slots[dest] = rec; } - else if (strcmp(op, "mkarray") == 0) { + else if (strcmp(op, "array") == 0) { int dest = (int)a1->valuedouble; + int nr_elems = a2 ? (int)a2->valuedouble : 0; JSValue arr = JS_NewArray(ctx); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(arr)) { goto disrupt; } frame->slots[dest] = arr; - } - else if (strcmp(op, "mkfunc") == 0) { - int dest = (int)a1->valuedouble; - int func_id = (int)a2->valuedouble; - if ((uint32_t)func_id < code->func_count && code->functions[func_id]) { - JSValue fn_val = js_new_mcode_function(ctx, code->functions[func_id]); - /* Refresh frame AFTER allocation (GC may have moved it) */ - frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - /* Set outer_frame AFTER refresh so it points to the current frame location */ - JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val); - fn->u.mcode.outer_frame = frame_ref.val; - frame->slots[dest] = fn_val; - } else { - frame->slots[dest] = JS_NULL; + for (int i = 0; i < nr_elems; i++) { + cJSON *elem = cJSON_GetArrayItem(instr, 3 + i); + if (elem) { + int elem_slot = (int)elem->valuedouble; + JS_SetPropertyUint32(ctx, frame->slots[dest], i, frame->slots[elem_slot]); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + } } } - else if (strcmp(op, "mkfunc_arrow") == 0) { + else if (strcmp(op, "function") == 0) { int dest = (int)a1->valuedouble; int func_id = (int)a2->valuedouble; if ((uint32_t)func_id < code->func_count && code->functions[func_id]) { @@ -34872,6 +35220,69 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj, } } + /* ---- Blob ---- */ + else if (strcmp(op, "blob") == 0) { + int dest = (int)a1->valuedouble; + int nr_bits = a2 ? (int)a2->valuedouble : 0; + blob *bd = blob_new((size_t)(nr_bits < 0 ? 0 : nr_bits)); + if (!bd) { goto disrupt; } + JSValue bv = js_new_blob(ctx, bd); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(bv)) { goto disrupt; } + frame->slots[dest] = bv; + } + + /* ---- Pretext ---- */ + else if (strcmp(op, "pretext") == 0) { + int dest = (int)a1->valuedouble; + int nr_chars = a2 ? (int)a2->valuedouble : 16; + JSText *s = pretext_init(ctx, nr_chars); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (!s) { goto disrupt; } + frame->slots[dest] = JS_MKPTR(s); + } + + /* ---- Append (to pretext) ---- */ + else if (strcmp(op, "append") == 0) { + int pt_slot = (int)a1->valuedouble; + JSValue right = frame->slots[(int)a2->valuedouble]; + JSValue pt_val = frame->slots[pt_slot]; + if (!JS_IsText(pt_val) || !JS_IsText(right)) { goto disrupt; } + JSText *s = JS_VALUE_GET_PTR(pt_val); + s = pretext_concat_value(ctx, s, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (!s) { goto disrupt; } + frame->slots[pt_slot] = JS_MKPTR(s); + } + + /* ---- Stone ---- */ + else if (strcmp(op, "stone") == 0) { + int dest = (int)a1->valuedouble; + JSValue v = frame->slots[(int)a2->valuedouble]; + JSValue stoned = JS_Stone(ctx, v); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + frame->slots[dest] = stoned; + } + + /* ---- Push (append to array) ---- */ + else if (strcmp(op, "push") == 0) { + int arr_slot = (int)a1->valuedouble; + JSValue val = frame->slots[(int)a2->valuedouble]; + if (!JS_IsArray(frame->slots[arr_slot])) { goto disrupt; } + JS_ArrayPush(ctx, &frame->slots[arr_slot], val); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + } + + /* ---- Pop (remove last from array) ---- */ + else if (strcmp(op, "pop") == 0) { + int dest = (int)a1->valuedouble; + JSValue arr = frame->slots[(int)a2->valuedouble]; + if (!JS_IsArray(arr)) { goto disrupt; } + JSValue popped = JS_ArrayPop(ctx, arr); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + frame->slots[dest] = popped; + } + /* ---- Disruption ---- */ else if (strcmp(op, "disrupt") == 0) { goto disrupt;