rm quickjs vm

This commit is contained in:
2026-02-09 12:54:55 -06:00
parent 790293d915
commit 3d1fd37979
10 changed files with 244 additions and 14003 deletions

View File

@@ -1,4 +1,5 @@
#include "cell.h"
#include "cJSON.h"
JSC_CCALL(os_mem_limit, JS_SetMemoryLimit(JS_GetRuntime(js), js2number(js,argv[0])))
JSC_CCALL(os_max_stacksize, JS_SetMaxStackSize(JS_GetRuntime(js), js2number(js,argv[0])))
@@ -13,7 +14,6 @@ JSC_CCALL(os_calc_mem,
JS_SetPropertyStr(js,ret,"memory_used_size",number2js(js,mu.memory_used_size));
JS_SetPropertyStr(js,ret,"malloc_count",number2js(js,mu.malloc_count));
JS_SetPropertyStr(js,ret,"memory_used_count",number2js(js,mu.memory_used_count));
/* atom_count and atom_size removed - atoms are now just strings */
JS_SetPropertyStr(js,ret,"str_count",number2js(js,mu.str_count));
JS_SetPropertyStr(js,ret,"str_size",number2js(js,mu.str_size));
JS_SetPropertyStr(js,ret,"obj_count",number2js(js,mu.obj_count));
@@ -35,47 +35,14 @@ JSC_CCALL(os_calc_mem,
JS_SetPropertyStr(js,ret,"binary_object_size",number2js(js,mu.binary_object_size));
)
// Evaluate a string of JavaScript code in the current QuickJS context.
// Evaluate a string via MACH VM
JSC_SSCALL(os_eval,
if (!str2) return JS_ThrowReferenceError(js, "Second argument should be the script.");
if (!str) return JS_ThrowReferenceError(js, "First argument should be the name of the script.");
JSValue bytecode = JS_Compile(js, str2, strlen(str2), str);
if (JS_IsException(bytecode)) return bytecode;
ret = JS_Integrate(js, bytecode, JS_NULL);
)
// Compile a string of JavaScript code into a function object.
JSC_SSCALL(js_compile,
if (!str2) return JS_ThrowReferenceError(js, "Second argument should be the script.");
if (!str) return JS_ThrowReferenceError(js, "First argument should be the name of the script.");
ret = JS_Compile(js, str2, strlen(str2), str);
)
// Link compiled bytecode with environment and execute.
JSC_CCALL(js_integrate,
JSValue env = (argc > 1 && !JS_IsNull(argv[1])) ? argv[1] : JS_NULL;
ret = JS_Integrate(js, argv[0], env);
)
// Compile a function object into a bytecode blob.
JSC_CCALL(js_compile_blob,
size_t size;
uint8_t *data = JS_WriteObject(js, &size, argv[0], JS_WRITE_OBJ_BYTECODE);
if (!data) {
return JS_ThrowInternalError(js, "Failed to serialize bytecode");
}
ret = js_new_blob_stoned_copy(js, data, size);
js_free(js, data);
)
// Compile a bytecode blob into a function object.
JSC_CCALL(js_compile_unblob,
size_t size;
void *data = js_get_blob_data(js, &size, argv[0]);
if (data == -1) return JS_EXCEPTION;
if (!data) return JS_ThrowReferenceError(js, "No data present in blob.");
return JS_ReadObject(js, data, size, JS_READ_OBJ_BYTECODE);
cJSON *ast = JS_ASTTree(str2, strlen(str2), str);
if (!ast) return JS_ThrowSyntaxError(js, "eval: failed to parse");
ret = JS_RunMachTree(js, ast, JS_NULL);
cJSON_Delete(ast);
)
// Disassemble a function object into a string.
@@ -93,10 +60,6 @@ static const JSCFunctionListEntry js_js_funcs[] = {
MIST_FUNC_DEF(os, mem_limit, 1),
MIST_FUNC_DEF(os, max_stacksize, 1),
MIST_FUNC_DEF(os, eval, 2),
MIST_FUNC_DEF(js, compile, 2),
MIST_FUNC_DEF(js, integrate, 2),
MIST_FUNC_DEF(js, compile_blob, 1),
MIST_FUNC_DEF(js, compile_unblob, 1),
MIST_FUNC_DEF(js, disassemble, 1),
MIST_FUNC_DEF(js, fn_info, 1),
};
@@ -105,4 +68,4 @@ JSValue js_js_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_js_funcs,countof(js_js_funcs));
return mod;
}
}

View File

@@ -45,7 +45,6 @@ src += [ # core
'qjs_actor.c',
'miniz.c',
'runtime.c',
'cell_js.c',
'tokenize.c',
'parse.c',
'mach.c',

View File

@@ -288,113 +288,11 @@ static int run_test_suite(size_t heap_size)
}
/* Run an immediate script string */
static int run_eval(const char *script_or_file, int print_bytecode, int use_bootstrap_env)
{
if (!find_cell_shop()) return 1;
/* Check if argument is a file path */
struct stat st;
char *script = NULL;
char *allocated_script = NULL;
const char *filename = "<eval>";
if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) {
/* It's a file, read its contents */
FILE *f = fopen(script_or_file, "r");
if (!f) {
printf("Failed to open file: %s\n", script_or_file);
return 1;
}
allocated_script = malloc(st.st_size + 1);
if (!allocated_script) {
fclose(f);
printf("Failed to allocate memory for script\n");
return 1;
}
size_t read_size = fread(allocated_script, 1, st.st_size, f);
fclose(f);
allocated_script[read_size] = '\0';
script = allocated_script;
filename = script_or_file;
} else {
/* Treat as inline script */
script = (char *)script_or_file;
}
JSRuntime *rt = JS_NewRuntime();
if (!rt) {
printf("Failed to create JS runtime\n");
free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
int result = 0;
JSGCRef bytecode_ref;
JS_PushGCRef(ctx, &bytecode_ref);
bytecode_ref.val = JS_Compile(ctx, script, strlen(script), filename);
if (JS_IsException(bytecode_ref.val)) {
uncaught_exception(ctx, bytecode_ref.val);
JS_PopGCRef(ctx, &bytecode_ref);
result = 1;
} else {
if (print_bytecode) {
printf("=== Compiled Bytecode ===\n");
JS_DumpFunctionBytecode(ctx, bytecode_ref.val);
}
JSValue env = JS_NULL;
if (use_bootstrap_env) {
JSGCRef env_ref, json_ref, nota_ref, wota_ref;
JS_PushGCRef(ctx, &env_ref);
JS_PushGCRef(ctx, &json_ref);
JS_PushGCRef(ctx, &nota_ref);
JS_PushGCRef(ctx, &wota_ref);
env_ref.val = JS_NewObject(ctx);
/* Create modules with GC rooting, then stone them */
json_ref.val = js_json_use(ctx);
nota_ref.val = js_nota_use(ctx);
wota_ref.val = js_wota_use(ctx);
JS_SetPropertyStr(ctx, env_ref.val, "json", JS_Stone(ctx, json_ref.val));
JS_SetPropertyStr(ctx, env_ref.val, "nota", JS_Stone(ctx, nota_ref.val));
JS_SetPropertyStr(ctx, env_ref.val, "wota", JS_Stone(ctx, wota_ref.val));
env = JS_Stone(ctx, env_ref.val);
JS_PopGCRef(ctx, &wota_ref);
JS_PopGCRef(ctx, &nota_ref);
JS_PopGCRef(ctx, &json_ref);
JS_PopGCRef(ctx, &env_ref);
}
JSValue v = JS_Integrate(ctx, bytecode_ref.val, env);
JS_PopGCRef(ctx, &bytecode_ref);
if (JS_IsException(v)) {
uncaught_exception(ctx, v);
result = 1;
} else {
JS_FreeValue(ctx, v);
}
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return result;
}
static void print_usage(const char *prog)
{
printf("Usage: %s [options] <script> [args...]\n\n", prog);
printf("Run a cell script (.ce actor or .cm module).\n\n");
printf("Options:\n");
printf(" -e, --eval <code|file> Evaluate code or run a file directly\n");
printf(" -p, --print-bytecode <code|file> Compile and print bytecode\n");
printf(" -s, --serializers <code|file> Run with json/nota/wota in env\n");
printf(" --ast <code|file> Output AST as JSON\n");
printf(" --tokenize <code|file> Output token array as JSON\n");
printf(" --mcode <code|file> Output MCODE IR as JSON\n");
@@ -633,24 +531,11 @@ int cell_init(int argc, char **argv)
JSValue result = JS_CallMcodeTreeEnv(ctx, mcode, hidden_env);
int exit_code = 0;
if (JS_IsException(result)) {
JSValue exc = JS_GetException(ctx);
const char *str = JS_ToCString(ctx, exc);
if (str) { printf("Error: %s\n", str); JS_FreeCString(ctx, str); }
cJSON *stack = JS_GetStack(ctx);
if (stack) {
int n = cJSON_GetArraySize(stack);
for (int i = 0; i < n; i++) {
cJSON *fr = cJSON_GetArrayItem(stack, i);
const char *fn = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "function"));
const char *file = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "file"));
int line = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "line"));
int col = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "column"));
printf(" at %s (%s:%d:%d)\n", fn ? fn : "<anonymous>", file ? file : "<unknown>", line, col);
}
cJSON_Delete(stack);
}
JS_FreeValue(ctx, exc);
/* Error already printed to stderr by JS_Throw* */
JS_GetException(ctx);
exit_code = 1;
} else if (!JS_IsNull(result)) {
const char *str = JS_ToCString(ctx, result);
if (str) { printf("%s\n", str); JS_FreeCString(ctx, str); }
@@ -658,7 +543,7 @@ int cell_init(int argc, char **argv)
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
return JS_IsException(result) ? 1 : 0;
return exit_code;
}
/* Check for --mach flag to dump MACH bytecode */
@@ -774,26 +659,8 @@ int cell_init(int argc, char **argv)
int exit_code = 0;
if (JS_IsException(result)) {
JSValue exc = JS_GetException(ctx);
const char *err_str = JS_ToCString(ctx, exc);
if (err_str) {
printf("Error: %s\n", err_str);
JS_FreeCString(ctx, err_str);
}
cJSON *stack = JS_GetStack(ctx);
if (stack) {
int n = cJSON_GetArraySize(stack);
for (int i = 0; i < n; i++) {
cJSON *fr = cJSON_GetArrayItem(stack, i);
const char *fn = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "function"));
const char *file = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "file"));
int line = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "line"));
int col = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "column"));
printf(" at %s (%s:%d:%d)\n", fn ? fn : "<anonymous>", file ? file : "<unknown>", line, col);
}
cJSON_Delete(stack);
}
JS_FreeValue(ctx, exc);
/* Error already printed to stderr by JS_Throw* */
JS_GetException(ctx);
exit_code = 1;
} else if (!JS_IsNull(result)) {
const char *str = JS_ToCString(ctx, result);
@@ -808,19 +675,6 @@ int cell_init(int argc, char **argv)
return exit_code;
}
/* Check for -e or --eval flag to run immediate script */
/* Also check for -p flag to print bytecode */
/* -s / --serializers flag provides json, nota, wota in env */
if (argc >= 3 && (strcmp(argv[1], "-e") == 0 || strcmp(argv[1], "--eval") == 0)) {
return run_eval(argv[2], 0, 0);
}
if (argc >= 3 && (strcmp(argv[1], "-p") == 0 || strcmp(argv[1], "--print-bytecode") == 0)) {
return run_eval(argv[2], 1, 0);
}
if (argc >= 3 && (strcmp(argv[1], "-s") == 0 || strcmp(argv[1], "--serializers") == 0)) {
return run_eval(argv[2], 0, 1);
}
int script_start = 1;
/* Find the cell shop at ~/.cell */
@@ -894,31 +748,11 @@ void cell_trace_sethook(cell_hook)
int uncaught_exception(JSContext *js, JSValue v)
{
cell_rt *rt = JS_GetContextOpaque(js);
if (!JS_HasException(js)) {
JS_FreeValue(js,v);
(void)v;
if (!JS_HasException(js))
return 1;
}
JSValue exp = JS_GetException(js);
JSValue message = JS_GetPropertyStr(js, exp, "message");
const char *msg_str = JS_ToCString(js, message);
if (msg_str) {
printf("Exception: %s\n", msg_str);
JS_FreeCString(js, msg_str);
}
JS_FreeValue(js, message);
JSValue stack = JS_GetPropertyStr(js, exp, "stack");
const char *stack_str = JS_ToCString(js, stack);
if (stack_str) {
printf("Stack:\n%s\n", stack_str);
JS_FreeCString(js, stack_str);
}
JS_FreeValue(js, stack);
JS_FreeValue(js, exp);
JS_FreeValue(js, v);
/* Error message and backtrace were already printed to stderr
by JS_ThrowError2 / print_backtrace. Just clear the flag. */
JS_GetException(js);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -2919,7 +2919,8 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
break;
}
if (JS_IsNull(frame->caller)) {
result = JS_Throw(ctx, JS_NewString(ctx, "unhandled disruption"));
fprintf(stderr, "unhandled disruption\n");
result = JS_Throw(ctx, JS_NULL);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto done;
}

View File

@@ -3393,7 +3393,8 @@ JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
break;
}
if (JS_IsNull(frame->caller)) {
result = JS_Throw(ctx, JS_NewString(ctx, "unhandled disruption"));
fprintf(stderr, "unhandled disruption\n");
result = JS_Throw(ctx, JS_NULL);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
goto done;
}

View File

@@ -274,8 +274,7 @@ static JSValue JS_Invoke (JSContext *ctx, JSValue this_val, JSValue method, int
enum {
/* classid tag */ /* union usage | properties */
JS_CLASS_OBJECT = 1, /* must be first */
JS_CLASS_ERROR,
JS_CLASS_REGEXP, /* u.regexp */
JS_CLASS_REGEXP = 3, /* u.regexp */
JS_CLASS_BLOB, /* u.opaque (blob *) */
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
@@ -291,7 +290,7 @@ typedef enum JSErrorEnum {
JS_INTERNAL_ERROR,
JS_AGGREGATE_ERROR,
JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
JS_NATIVE_ERROR_COUNT, /* number of different error name strings */
} JSErrorEnum;
/* the variable and scope indexes must fit on 16 bits. The (-1) and
@@ -941,7 +940,6 @@ struct JSContext {
JSClass *class_array;
JSValue *class_proto;
JSValue regexp_ctor;
JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
JSValue throw_type_error;
JSValue global_obj; /* global object (immutable intrinsics) */
@@ -1439,7 +1437,7 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
cJSON *JS_GetStack(JSContext *ctx);
JSValue JS_ThrowOutOfMemory (JSContext *ctx);
JSValue JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx);
JSValue JS_ToNumber (JSContext *ctx, JSValue val);
int JS_SetPropertyValue (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val);
@@ -1454,7 +1452,6 @@ void free_arg_list (JSContext *ctx, JSValue *tab, uint32_t len);
JSValue *build_arg_list (JSContext *ctx, uint32_t *plen, JSValue *parray_arg);
JSValue js_regexp_toString (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
JSValue js_error_toString (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
/* === Inline utility functions (used across modules) === */
@@ -1694,43 +1691,6 @@ typedef struct {
const uint8_t *buf_start;
} GetLineColCache;
/* === JSOpCode (needed by debugger in runtime.c and bytecode in cell_js.c) === */
typedef struct JSOpCode {
#ifdef DUMP_BYTECODE
const char *name;
#endif
uint8_t size; /* in bytes */
/* the opcodes remove n_pop items from the top of the stack, then
pushes n_push items */
uint8_t n_pop;
uint8_t n_push;
uint8_t fmt;
} JSOpCode;
static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
#define FMT(f)
#ifdef DUMP_BYTECODE
#define DEF(id, size, n_pop, n_push, f) \
{ #id, size, n_pop, n_push, OP_FMT_##f },
#else
#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_##f },
#endif
#include "quickjs-opcode.h"
#undef DEF
#undef FMT
};
#if SHORT_OPCODES
/* After the final compilation pass, short opcodes are used. Their
opcodes overlap with the temporary opcodes which cannot appear in
the final bytecode. Their description is after the temporary
opcodes in opcode_info[]. */
#define short_opcode_info(op) \
opcode_info[(op) >= OP_TEMP_START ? (op) + (OP_TEMP_END - OP_TEMP_START) \
: (op)]
#else
#define short_opcode_info(op) opcode_info[op]
#endif
/* === MachVarInfo (shared by mach.c and mcode.c) === */
typedef struct MachVarInfo {
@@ -1792,11 +1752,7 @@ extern JSClassID js_class_id_alloc;
/* === Forward declarations for functions split across modules === */
/* cell_js.c exports */
void gc_scan_parser_cpool (JSContext *ctx, uint8_t *from_base, uint8_t *from_end,
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end);
void gc_scan_bytecode_cpool (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *from_end,
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end);
/* runtime.c — line/column, GC, and VM dispatch */
int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size);
JSValue JS_CallInternal (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv, int flags);
int get_line_col (int *pcol_num, const uint8_t *buf, size_t len);
@@ -1806,12 +1762,10 @@ int get_line_col_cached (GetLineColCache *s, int *pcol_num, const uint8_t *ptr);
JSValue JS_ThrowStackOverflow (JSContext *ctx);
JSValue JS_ThrowSyntaxErrorVarRedeclaration (JSContext *ctx, JSValue prop);
JSValue JS_ThrowReferenceErrorUninitialized (JSContext *ctx, JSValue name);
JSValue JS_ThrowReferenceErrorUninitialized2 (JSContext *ctx, JSFunctionBytecode *b, int idx, BOOL is_ref);
int JS_DefineObjectName (JSContext *ctx, JSValue obj, JSValue name);
int JS_DefineObjectNameComputed (JSContext *ctx, JSValue obj, JSValue str);
int js_method_set_properties (JSContext *ctx, JSValue func_obj, JSValue name, int flags, JSValue home_obj);
JSValue JS_GetPropertyValue (JSContext *ctx, JSValue this_obj, JSValue prop);
JSValue js_closure (JSContext *ctx, JSValue bfunc, JSStackFrame *sf);
__exception int JS_CopyDataProperties (JSContext *ctx, JSValue target, JSValue source, JSValue excluded, BOOL setprop);
int js_string_compare_value (JSContext *ctx, JSValue op1, JSValue op2, BOOL eq_only);
int js_string_compare_value_nocase (JSContext *ctx, JSValue op1, JSValue op2);
@@ -1867,8 +1821,7 @@ static inline JSValue *get_upvalue_ptr (JSValue frame_val, int depth, int slot)
return &frame->slots[slot];
}
void build_backtrace (JSContext *ctx, JSValue error_obj, const char *filename, int line_num, int col_num, int backtrace_flags);
BOOL is_backtrace_needed (JSContext *ctx, JSValue obj);
void print_backtrace (JSContext *ctx, const char *filename, int line_num, int col_num);
JSValue JS_ThrowError2 (JSContext *ctx, JSErrorEnum error_num, const char *fmt, va_list ap, BOOL add_backtrace);
JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *from_end, uint8_t *to_base, uint8_t **to_free, uint8_t *to_end);
PPretext *ppretext_init (int capacity);

View File

@@ -577,11 +577,8 @@ static JS_BOOL JS_IsStone(JSValue v);
int JS_GetLength (JSContext *ctx, JSValue obj, int64_t *pres);
JSValue JS_Throw (JSContext *ctx, JSValue obj);
void JS_SetUncatchableException (JSContext *ctx, JS_BOOL flag);
JSValue JS_GetException (JSContext *ctx);
JS_BOOL JS_HasException (JSContext *ctx);
JS_BOOL JS_IsError (JSContext *ctx, JSValue val);
JSValue JS_NewError (JSContext *ctx);
JSValue __js_printf_like (2, 3)
JS_ThrowSyntaxError (JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like (2, 3)
@@ -714,16 +711,6 @@ JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj);
JSValue JS_Call (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
/* Compile source code to bytecode without executing.
'input' must be zero terminated i.e. input[input_len] = '\0'.
Returns unlinked bytecode on success, JS_EXCEPTION on error. */
JSValue JS_Compile (JSContext *ctx, const char *input, size_t input_len,
const char *filename);
/* Link compiled bytecode with environment and execute.
env should be stoned record or null.
Variables resolve: env first, then global intrinsics. */
JSValue JS_Integrate (JSContext *ctx, JSValue bytecode, JSValue env);
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);
@@ -749,28 +736,6 @@ void JS_SetInterruptHandler (JSContext *ctx, JSInterruptHandler *cb,
void JS_SetStripInfo (JSRuntime *rt, int flags);
int JS_GetStripInfo (JSRuntime *rt);
/* Object Writer/Reader (currently only used to handle precompiled code) */
#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */
#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
#define JS_WRITE_OBJ_REFERENCE \
(1 << 3) /* allow object references to \
encode arbitrary object \
graph */
uint8_t *JS_WriteObject (JSContext *ctx, size_t *psize, JSValue obj,
int flags);
uint8_t *JS_WriteObject2 (JSContext *ctx, size_t *psize, JSValue obj,
int flags, uint8_t ***psab_tab,
size_t *psab_tab_len);
#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */
#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */
#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */
JSValue JS_ReadObject (JSContext *ctx, const uint8_t *buf, size_t buf_len,
int flags);
/* Dump bytecode of a compiled function (for debugging) */
void JS_DumpFunctionBytecode (JSContext *ctx, JSValue func_val);
/* C function definition */
typedef enum JSCFunctionEnum {
@@ -1100,124 +1065,6 @@ void *js_malloc_rt (size_t size);
void *js_mallocz_rt (size_t size);
void js_free_rt (void *ptr);
/* ============================================================================
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);
/* Parse source code and return AST as cJSON tree.
Caller must call cJSON_Delete() on result. */
struct cJSON *JS_ASTTree (const char *source, size_t len, const char *filename);
@@ -1280,10 +1127,6 @@ JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json);
Caller must call cJSON_Delete() on the result. */
struct cJSON *JS_GetStack (JSContext *ctx);
/* 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

File diff suppressed because it is too large Load Diff

View File

@@ -1742,9 +1742,12 @@ TEST(has_exception_initially_false) {
return 1;
}
TEST(new_error) {
JSValue err = JS_NewError(ctx);
ASSERT(JS_IsError(ctx, err));
TEST(throw_and_check_exception) {
ASSERT(!JS_HasException(ctx));
JS_ThrowTypeError(ctx, "test error");
ASSERT(JS_HasException(ctx));
JS_GetException(ctx); /* clear it */
ASSERT(!JS_HasException(ctx));
return 1;
}
@@ -2001,138 +2004,6 @@ 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;
}
/* ============================================================================
ERROR RECOVERY TESTS - Helper macros
============================================================================ */
@@ -2590,7 +2461,7 @@ int run_c_test_suite(JSContext *ctx)
printf("\nExceptions/Errors:\n");
RUN_TEST(has_exception_initially_false);
RUN_TEST(new_error);
RUN_TEST(throw_and_check_exception);
printf("\nFloat Edge Cases:\n");
RUN_TEST(float_small_positive);
@@ -2622,12 +2493,6 @@ 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("\nTokenizer Errors:\n");
RUN_TEST(tokenize_unterminated_string);