7 Commits

Author SHA1 Message Date
John Alanbrook
a08ee50f84 serializable bytecode 2026-02-04 20:57:44 -06:00
John Alanbrook
ed7dd91c3f rm global 2026-02-04 18:57:45 -06:00
John Alanbrook
3abe20fee0 merge 2026-02-04 18:38:46 -06:00
John Alanbrook
a92a96118e remove eval parser; consolidate addintrinsic 2026-02-04 17:15:03 -06:00
John Alanbrook
ab74cdc173 merge warningfix 2026-02-04 16:17:52 -06:00
John Alanbrook
2c9d039271 massive cleanup 2026-02-04 14:26:17 -06:00
John Alanbrook
d4635f2a75 remove unused vars, fix warnings 2026-02-04 13:49:43 -06:00
5 changed files with 1946 additions and 1294 deletions

View File

@@ -125,12 +125,8 @@ JSValue js_wota_use(JSContext *js);
void script_startup(cell_rt *prt)
{
JSRuntime *rt = JS_NewRuntime();
JSContext *js = JS_NewContextRaw(rt);
JS_SetInterruptHandler(rt, (JSInterruptHandler *)actor_interrupt_cb, prt);
JS_AddIntrinsicBaseObjects(js);
JS_AddIntrinsicEval(js);
JS_AddIntrinsicRegExp(js);
JSContext *js = JS_NewContext(rt);
JS_SetContextOpaque(js, prt);
prt->context = js;
@@ -214,15 +210,13 @@ static int run_test_suite(size_t heap_size)
return 1;
}
JSContext *ctx = JS_NewContextRawWithHeapSize(rt, heap_size);
JSContext *ctx = JS_NewContextWithHeapSize(rt, heap_size);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
return 1;
}
JS_AddIntrinsicBaseObjects(ctx);
int result = run_c_test_suite(ctx);
JS_FreeContext(ctx);
@@ -272,7 +266,7 @@ static int run_eval(const char *script_or_file, int print_bytecode, int use_boot
return 1;
}
JSContext *ctx = JS_NewContextRaw(rt);
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
@@ -280,10 +274,6 @@ static int run_eval(const char *script_or_file, int print_bytecode, int use_boot
return 1;
}
JS_AddIntrinsicBaseObjects(ctx);
JS_AddIntrinsicEval(ctx);
JS_AddIntrinsicRegExp(ctx);
int result = 0;
JSGCRef bytecode_ref;

View File

@@ -205,6 +205,7 @@ DEF( set_up, 4, 1, 0, u8_u16) /* value, depth:u8, slot:u16 -> */
/* Name resolution with bytecode patching */
DEF( get_name, 5, 0, 1, const) /* cpool_idx -> value, patches itself */
DEF( get_env_slot, 3, 0, 1, u16) /* slot -> value (patched from get_name) */
DEF( set_env_slot, 3, 1, 0, u16) /* value -> slot (patched from put_var) */
DEF(get_global_slot, 3, 0, 1, u16) /* slot -> value (patched from get_var) */
DEF(set_global_slot, 3, 1, 0, u16) /* value -> slot (patched from put_var) */

File diff suppressed because it is too large Load Diff

View File

@@ -92,6 +92,7 @@ static inline int objhdr_s (objhdr_t h) { return (h & OBJHDR_S_MASK) != 0; }
typedef struct JSRuntime JSRuntime; // the entire VM
typedef struct JSContext JSContext; // Each actor
typedef struct JSClass JSClass;
typedef struct JSFunctionBytecode JSFunctionBytecode;
typedef uint32_t JSClassID;
/* Forward declaration - JSGCRef moved after JSValue definition */
@@ -176,8 +177,6 @@ void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref);
Value Extraction
============================================================ */
#define JS_VALUE_GET_INT(v) ((int)(v) >> 1)
/* Get primary tag (low 2-3 bits) */
static inline int
JS_VALUE_GET_TAG (JSValue v) {
@@ -334,13 +333,8 @@ JS_IsShortFloat (JSValue v) {
#define JS_DEFAULT_STACK_SIZE (1024 * 1024)
#endif
/* Internal eval flags */
#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */
#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */
#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */
#define JS_EVAL_TYPE_MASK (3 << 0)
/* Internal compile flags */
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) /* internal use */
#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) /* internal use */
typedef JSValue JSCFunction (JSContext *ctx, JSValue this_val, int argc,
JSValue *argv);
@@ -395,10 +389,7 @@ JSRuntime *JS_GetRuntime (JSContext *ctx);
void JS_SetClassProto (JSContext *ctx, JSClassID class_id, JSValue obj);
JSValue JS_GetClassProto (JSContext *ctx, JSClassID class_id);
/* the following functions are used to select the intrinsic object to
save memory */
JSContext *JS_NewContextRaw (JSRuntime *rt);
JSContext *JS_NewContextRawWithHeapSize (JSRuntime *rt, size_t heap_size);
JSContext *JS_NewContextWithHeapSize (JSRuntime *rt, size_t heap_size);
typedef struct JSMemoryUsage {
int64_t malloc_size, malloc_limit, memory_used_size;
@@ -733,7 +724,6 @@ JSValue JS_Compile (JSContext *ctx, const char *input, size_t input_len,
env should be stoned record or null.
Variables resolve: env first, then global intrinsics. */
JSValue JS_Integrate (JSContext *ctx, JSValue bytecode, JSValue env);
JSValue JS_GetGlobalObject (JSContext *ctx);
void JS_SetOpaque (JSValue obj, void *opaque);
void *JS_GetOpaque (JSValue obj, JSClassID class_id);
void *JS_GetOpaque2 (JSContext *ctx, JSValue obj, JSClassID class_id);
@@ -793,7 +783,14 @@ typedef enum JSCFunctionEnum {
JS_CFUNC_1, /* JSValue f(ctx, this_val, arg0) */
JS_CFUNC_2, /* JSValue f(ctx, this_val, arg0, arg1) */
JS_CFUNC_3, /* JSValue f(ctx, this_val, arg0, arg1, arg2) */
JS_CFUNC_4
JS_CFUNC_4,
/* Pure functions (no this_val) - for global utility functions */
JS_CFUNC_PURE, /* JSValue f(ctx, argc, argv) - generic pure */
JS_CFUNC_PURE_0, /* JSValue f(ctx) */
JS_CFUNC_PURE_1, /* JSValue f(ctx, arg0) */
JS_CFUNC_PURE_2, /* JSValue f(ctx, arg0, arg1) */
JS_CFUNC_PURE_3, /* JSValue f(ctx, arg0, arg1, arg2) */
JS_CFUNC_PURE_4 /* JSValue f(ctx, arg0, arg1, arg2, arg3) */
} JSCFunctionEnum;
/* Fixed-arity C function types for fast paths */
@@ -809,6 +806,16 @@ typedef JSValue JSCFunction4 (JSContext *ctx, JSValue this_val,
JSValue arg0, JSValue arg1,
JSValue arg2, JSValue arg3);
/* Pure function types (no this_val) */
typedef JSValue JSCFunctionPure (JSContext *ctx, int argc, JSValue *argv);
typedef JSValue JSCFunctionPure0 (JSContext *ctx);
typedef JSValue JSCFunctionPure1 (JSContext *ctx, JSValue arg0);
typedef JSValue JSCFunctionPure2 (JSContext *ctx, JSValue arg0, JSValue arg1);
typedef JSValue JSCFunctionPure3 (JSContext *ctx, JSValue arg0, JSValue arg1,
JSValue arg2);
typedef JSValue JSCFunctionPure4 (JSContext *ctx, JSValue arg0, JSValue arg1,
JSValue arg2, JSValue arg3);
typedef union JSCFunctionType {
JSCFunction *generic;
JSValue (*generic_magic) (JSContext *ctx, JSValue this_val, int argc,
@@ -821,6 +828,13 @@ typedef union JSCFunctionType {
JSCFunction2 *f2;
JSCFunction3 *f3;
JSCFunction4 *f4;
/* Pure function pointers */
JSCFunctionPure *pure;
JSCFunctionPure0 *pure0;
JSCFunctionPure1 *pure1;
JSCFunctionPure2 *pure2;
JSCFunctionPure3 *pure3;
JSCFunctionPure4 *pure4;
} JSCFunctionType;
JSValue JS_NewCFunction2 (JSContext *ctx, JSCFunction *func, const char *name,
@@ -937,6 +951,43 @@ typedef struct JSCFunctionListEntry {
.u \
= {.func = { 3, JS_CFUNC_3, { .f3 = func1 } } } \
}
/* Pure function (no this_val) macros */
#define JS_CFUNC_PURE_DEF(name, length, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { length, JS_CFUNC_PURE, { .pure = func1 } } } \
}
#define JS_CFUNC_PURE0_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 0, JS_CFUNC_PURE_0, { .pure0 = func1 } } } \
}
#define JS_CFUNC_PURE1_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 1, JS_CFUNC_PURE_1, { .pure1 = func1 } } } \
}
#define JS_CFUNC_PURE2_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 2, JS_CFUNC_PURE_2, { .pure2 = func1 } } } \
}
#define JS_CFUNC_PURE3_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 3, JS_CFUNC_PURE_3, { .pure3 = func1 } } } \
}
#define JS_CFUNC_PURE4_DEF(name, func1) \
{ \
name, 0, JS_DEF_CFUNC, 0, \
.u \
= {.func = { 4, JS_CFUNC_PURE_4, { .pure4 = func1 } } } \
}
#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) \
{ \
name, 0, JS_DEF_CFUNC, magic, .u = { \
@@ -1049,11 +1100,127 @@ void *js_malloc_rt (size_t size);
void *js_mallocz_rt (size_t size);
void js_free_rt (void *ptr);
/* Intrinsic setup functions */
void JS_AddIntrinsicBaseObjects (JSContext *ctx);
void JS_AddIntrinsicBasicObjects (JSContext *ctx);
void JS_AddIntrinsicEval (JSContext *ctx);
void JS_AddIntrinsicRegExp (JSContext *ctx);
/* ============================================================================
Context-Neutral Module Format (CellModule)
============================================================================ */
/* Capture descriptor - what a nested function closes over */
typedef enum {
CAP_FROM_PARENT_LOCAL = 1, /* capture local from parent function */
CAP_FROM_PARENT_UPVALUE = 2 /* forward upvalue from parent's upvalues */
} CellCapKind;
typedef struct CellCapDesc {
uint8_t kind; /* CAP_FROM_PARENT_LOCAL or CAP_FROM_PARENT_UPVALUE */
uint16_t index; /* local index in parent, or upvalue index in parent */
} CellCapDesc;
/* External relocation - for integrate-time patching */
typedef enum {
EXT_GET = 1, /* OP_get_var -> OP_get_env_slot or OP_get_global_slot */
EXT_SET = 2 /* OP_put_var -> OP_set_env_slot or OP_set_global_slot */
} CellExtKind;
typedef struct CellExternalReloc {
uint32_t pc_offset; /* where operand lives in bytecode */
uint32_t name_sid; /* string id of the external name */
uint8_t kind; /* EXT_GET or EXT_SET */
} CellExternalReloc;
/* Constant types in cpool */
typedef enum {
CELL_CONST_NULL = 0,
CELL_CONST_INT = 1,
CELL_CONST_FLOAT = 2,
CELL_CONST_STRING = 3, /* string_sid into module string table */
CELL_CONST_UNIT = 4 /* unit_id for nested function */
} CellConstType;
typedef struct CellConst {
uint8_t type; /* CellConstType */
union {
int32_t i32;
double f64;
uint32_t string_sid;
uint32_t unit_id;
};
} CellConst;
/* Per-unit structure (context-neutral, flattened) */
typedef struct CellUnit {
/* Constant pool */
uint32_t const_count;
CellConst *constants;
/* Bytecode */
uint32_t bytecode_len;
uint8_t *bytecode;
/* Stack requirements */
uint16_t arg_count;
uint16_t var_count;
uint16_t stack_size;
/* Upvalue (capture) descriptors */
uint16_t upvalue_count;
CellCapDesc *upvalues;
/* External relocations */
uint32_t external_count;
CellExternalReloc *externals;
/* Debug info (optional) */
uint32_t pc2line_len;
uint8_t *pc2line;
uint32_t name_sid; /* unit name for stack traces */
} CellUnit;
/* Module-level structure (context-neutral) */
#define CELL_MODULE_MAGIC 0x4C4C4543 /* "CELL" */
#define CELL_MODULE_VERSION 1
typedef struct CellModule {
uint32_t magic; /* CELL_MODULE_MAGIC */
uint8_t version; /* CELL_MODULE_VERSION */
uint8_t flags;
/* Shared string table (module-global) */
uint32_t string_count;
uint32_t string_data_size;
uint8_t *string_data; /* concatenated UTF-8 strings */
uint32_t *string_offsets; /* offset for each string */
/* Unit table (entry 0 is the main/entry unit) */
uint32_t unit_count;
CellUnit *units;
/* Debug: source stored once at module level */
uint32_t source_len;
char *source;
} CellModule;
/* Free a CellModule and all its contents */
void cell_module_free (CellModule *mod);
/* Write a CellModule to a byte buffer.
Returns allocated buffer (caller must free with pjs_free), or NULL on error. */
uint8_t *cell_module_write (CellModule *mod, size_t *out_len);
/* Read a CellModule from a byte buffer.
Returns allocated CellModule (caller must free with cell_module_free), or NULL on error. */
CellModule *cell_module_read (const uint8_t *buf, size_t buf_len);
/* Convert compiled JSFunctionBytecode to CellModule.
Returns allocated CellModule (caller must free with cell_module_free), or NULL on error. */
CellModule *cell_module_from_bytecode (JSContext *ctx, JSFunctionBytecode *main_func);
/* Compile source code directly to CellModule.
Returns allocated CellModule (caller must free with cell_module_free), or NULL on error. */
CellModule *JS_CompileModule (JSContext *ctx, const char *input, size_t input_len, const char *filename);
/* Integrate a CellModule with an environment and execute.
Returns callable function value, or JS_EXCEPTION on error. */
JSValue cell_module_integrate (JSContext *ctx, CellModule *mod, JSValue env);
#undef js_unlikely
#undef inline

View File

@@ -954,24 +954,6 @@ TEST(array_foreach_basic) {
return 1;
}
/* ============================================================================
GLOBAL OBJECT TEST
============================================================================ */
TEST(global_object) {
JSGCRef global_ref;
JS_PushGCRef(ctx, &global_ref);
global_ref.val = JS_GetGlobalObject(ctx);
ASSERT(JS_IsRecord(global_ref.val));
/* Set something on global */
JS_SetPropertyStr(ctx, global_ref.val, "testGlobal", JS_NewInt32(ctx, 777));
JSValue val = JS_GetPropertyStr(ctx, global_ref.val, "testGlobal");
JS_PopGCRef(ctx, &global_ref);
ASSERT_INT(val, 777);
return 1;
}
/* ============================================================================
VALUE CONVERSION TESTS
============================================================================ */
@@ -1521,23 +1503,6 @@ TEST(new_cfunction_with_args) {
return 1;
}
TEST(call_function_on_global) {
JSGCRef func_ref, global_ref;
JS_PushGCRef(ctx, &global_ref);
JS_PushGCRef(ctx, &func_ref);
global_ref.val = JS_GetGlobalObject(ctx);
func_ref.val = JS_NewCFunction(ctx, cfunc_return_42, "testFunc", 0);
JS_SetPropertyStr(ctx, global_ref.val, "testFunc", func_ref.val);
JSValue got = JS_GetPropertyStr(ctx, global_ref.val, "testFunc");
int is_func = JS_IsFunction(got);
JSValue result = JS_Call(ctx, got, JS_NULL, 0, NULL);
JS_PopGCRef(ctx, &func_ref);
JS_PopGCRef(ctx, &global_ref);
ASSERT(is_func);
ASSERT_INT(result, 42);
return 1;
}
/* ============================================================================
PROPERTY ACCESS TESTS
============================================================================ */
@@ -2035,6 +2000,138 @@ TEST(wota_encode_blob) {
return 1;
}
/* ============================================================================
CELL MODULE TESTS - Serialize/Deserialize bytecode
============================================================================ */
TEST(cell_module_compile_basic) {
/* Compile simple source to CellModule */
const char *source = "1 + 2";
CellModule *mod = JS_CompileModule(ctx, source, strlen(source), "<test>");
ASSERT_MSG(mod != NULL, "JS_CompileModule returned NULL");
/* Check module has units */
ASSERT_MSG(mod->unit_count > 0, "Module has no units");
ASSERT_MSG(mod->units[0].bytecode_len > 0, "Unit has no bytecode");
cell_module_free(mod);
return 1;
}
TEST(cell_module_write_read) {
/* Compile, serialize, deserialize */
const char *source = "var x = 10; x * 2";
CellModule *mod = JS_CompileModule(ctx, source, strlen(source), "<test>");
ASSERT_MSG(mod != NULL, "JS_CompileModule returned NULL");
/* Serialize */
size_t len;
uint8_t *buf = cell_module_write(mod, &len);
ASSERT_MSG(buf != NULL, "cell_module_write returned NULL");
ASSERT_MSG(len > 0, "cell_module_write produced empty buffer");
/* Deserialize */
CellModule *mod2 = cell_module_read(buf, len);
free(buf);
ASSERT_MSG(mod2 != NULL, "cell_module_read returned NULL");
/* Verify structure matches */
ASSERT_MSG(mod2->unit_count == mod->unit_count, "unit_count mismatch");
ASSERT_MSG(mod2->string_count == mod->string_count, "string_count mismatch");
cell_module_free(mod);
cell_module_free(mod2);
return 1;
}
TEST(cell_module_integrate_basic) {
/* Compile, then integrate and execute */
const char *source = "3 + 4";
CellModule *mod = JS_CompileModule(ctx, source, strlen(source), "<test>");
ASSERT_MSG(mod != NULL, "JS_CompileModule returned NULL");
/* Integrate into context */
JSValue func = cell_module_integrate(ctx, mod, JS_NULL);
if (JS_IsException(func)) {
cell_module_free(mod);
ASSERT_MSG(0, "cell_module_integrate threw exception");
}
/* Execute */
JSValue result = JS_Call(ctx, func, JS_NULL, 0, NULL);
JS_FreeValue(ctx, func);
cell_module_free(mod);
if (JS_IsException(result)) {
ASSERT_MSG(0, "JS_Call threw exception");
}
ASSERT_INT(result, 7);
return 1;
}
TEST(cell_module_roundtrip_execute) {
/* Full round-trip: compile -> write -> read -> integrate -> execute */
const char *source = "var a = 5; var b = 3; a * b";
CellModule *mod = JS_CompileModule(ctx, source, strlen(source), "<test>");
ASSERT_MSG(mod != NULL, "JS_CompileModule returned NULL");
/* Serialize */
size_t len;
uint8_t *buf = cell_module_write(mod, &len);
cell_module_free(mod);
ASSERT_MSG(buf != NULL, "cell_module_write returned NULL");
/* Deserialize */
CellModule *mod2 = cell_module_read(buf, len);
free(buf);
ASSERT_MSG(mod2 != NULL, "cell_module_read returned NULL");
/* Integrate and execute */
JSValue func = cell_module_integrate(ctx, mod2, JS_NULL);
cell_module_free(mod2);
if (JS_IsException(func)) {
ASSERT_MSG(0, "cell_module_integrate threw exception");
}
JSValue result = JS_Call(ctx, func, JS_NULL, 0, NULL);
JS_FreeValue(ctx, func);
if (JS_IsException(result)) {
ASSERT_MSG(0, "JS_Call threw exception");
}
ASSERT_INT(result, 15);
return 1;
}
TEST(cell_module_string_constant) {
/* Test string constant handling */
const char *source = "'hello' + ' world'";
CellModule *mod = JS_CompileModule(ctx, source, strlen(source), "<test>");
ASSERT_MSG(mod != NULL, "JS_CompileModule returned NULL");
/* Verify string table has entries */
ASSERT_MSG(mod->string_count > 0, "Module has no strings");
/* Integrate and execute */
JSValue func = cell_module_integrate(ctx, mod, JS_NULL);
cell_module_free(mod);
if (JS_IsException(func)) {
ASSERT_MSG(0, "cell_module_integrate threw exception");
}
JSValue result = JS_Call(ctx, func, JS_NULL, 0, NULL);
JS_FreeValue(ctx, func);
if (JS_IsException(result)) {
ASSERT_MSG(0, "JS_Call threw exception");
}
ASSERT_STR(result, "hello world");
return 1;
}
/* ============================================================================
MAIN TEST RUNNER
============================================================================ */
@@ -2113,9 +2210,6 @@ int run_c_test_suite(JSContext *ctx)
RUN_TEST(strict_eq_null);
RUN_TEST(strict_eq_bool);
printf("\nGlobal Object:\n");
RUN_TEST(global_object);
printf("\nValue Conversions:\n");
RUN_TEST(to_bool_true_values);
RUN_TEST(to_bool_false_values);
@@ -2188,7 +2282,6 @@ int run_c_test_suite(JSContext *ctx)
printf("\nC Functions:\n");
RUN_TEST(new_cfunction_no_args);
RUN_TEST(new_cfunction_with_args);
RUN_TEST(call_function_on_global);
printf("\nProperty Access:\n");
RUN_TEST(get_property_with_jsvalue_key);
@@ -2248,6 +2341,13 @@ int run_c_test_suite(JSContext *ctx)
RUN_TEST(wota_encode_nested_array);
RUN_TEST(wota_encode_blob);
// CellModule tests
RUN_TEST(cell_module_compile_basic);
RUN_TEST(cell_module_write_read);
RUN_TEST(cell_module_integrate_basic);
RUN_TEST(cell_module_roundtrip_execute);
RUN_TEST(cell_module_string_constant);
printf("\n=================================\n");
printf("Results: %d passed, %d failed\n", tests_passed, tests_failed);
printf("=================================\n\n");