rm quickjs vm
This commit is contained in:
51
debug/js.c
51
debug/js.c
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ src += [ # core
|
||||
'qjs_actor.c',
|
||||
'miniz.c',
|
||||
'runtime.c',
|
||||
'cell_js.c',
|
||||
'tokenize.c',
|
||||
'parse.c',
|
||||
'mach.c',
|
||||
|
||||
190
source/cell.c
190
source/cell.c
@@ -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, ¬a_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, ¬a_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;
|
||||
}
|
||||
12740
source/cell_js.c
12740
source/cell_js.c
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
157
source/quickjs.h
157
source/quickjs.h
@@ -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
|
||||
|
||||
|
||||
896
source/runtime.c
896
source/runtime.c
File diff suppressed because it is too large
Load Diff
149
source/suite.c
149
source/suite.c
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user