764 lines
20 KiB
C
764 lines
20 KiB
C
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include "wota.h"
|
|
|
|
#define STB_DS_IMPLEMENTATION
|
|
#include "stb_ds.h"
|
|
|
|
#include "cell.h"
|
|
#include "cell_internal.h"
|
|
#include "cJSON.h"
|
|
|
|
#define ENGINE "internal/engine.cm"
|
|
#define CELL_SHOP_DIR ".cell"
|
|
#define CELL_CORE_DIR "packages/core"
|
|
|
|
#include <math.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
|
|
/* Test suite declarations */
|
|
int run_c_test_suite(JSContext *ctx);
|
|
static int run_test_suite(size_t heap_size);
|
|
|
|
cell_rt *root_cell = NULL;
|
|
static char *core_path = NULL;
|
|
static JSRuntime *g_runtime = NULL;
|
|
|
|
// Get the home directory
|
|
static const char* get_home_dir(void) {
|
|
const char *home = getenv("HOME");
|
|
if (!home) {
|
|
home = getenv("USERPROFILE"); // Windows fallback
|
|
}
|
|
return home;
|
|
}
|
|
|
|
// Find and verify the cell shop at ~/.cell
|
|
int find_cell_shop(void)
|
|
{
|
|
const char *home = get_home_dir();
|
|
if (!home) {
|
|
printf("ERROR: Could not determine home directory. Set HOME environment variable.\n");
|
|
return 0;
|
|
}
|
|
|
|
// Build path to ~/.cell/core
|
|
size_t path_len = strlen(home) + strlen("/" CELL_SHOP_DIR "/" CELL_CORE_DIR) + 1;
|
|
core_path = malloc(path_len);
|
|
if (!core_path) {
|
|
printf("ERROR: Could not allocate memory for core path\n");
|
|
return 0;
|
|
}
|
|
snprintf(core_path, path_len, "%s/" CELL_SHOP_DIR "/" CELL_CORE_DIR, home);
|
|
|
|
// Check if the core directory exists
|
|
struct stat st;
|
|
if (stat(core_path, &st) != 0 || !S_ISDIR(st.st_mode)) {
|
|
printf("ERROR: Cell shop not found at %s/" CELL_SHOP_DIR "\n", home);
|
|
printf("Run 'cell install' to set up the cell environment.\n");
|
|
free(core_path);
|
|
core_path = NULL;
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Load a file from the core directory
|
|
static char* load_core_file(const char *filename, size_t *out_size) {
|
|
if (!core_path) return NULL;
|
|
|
|
size_t path_len = strlen(core_path) + 1 + strlen(filename) + 1;
|
|
char *full_path = malloc(path_len);
|
|
if (!full_path) return NULL;
|
|
|
|
snprintf(full_path, path_len, "%s/%s", core_path, filename);
|
|
|
|
FILE *fh = fopen(full_path, "rb");
|
|
free(full_path);
|
|
|
|
if (!fh) return NULL;
|
|
|
|
fseek(fh, 0, SEEK_END);
|
|
long file_size = ftell(fh);
|
|
fseek(fh, 0, SEEK_SET);
|
|
|
|
char *data = malloc(file_size + 1);
|
|
if (!data) {
|
|
fclose(fh);
|
|
return NULL;
|
|
}
|
|
|
|
if (fread(data, 1, file_size, fh) != (size_t)file_size) {
|
|
free(data);
|
|
fclose(fh);
|
|
return NULL;
|
|
}
|
|
|
|
fclose(fh);
|
|
data[file_size] = 0;
|
|
|
|
if (out_size) *out_size = file_size;
|
|
return data;
|
|
}
|
|
|
|
static int print_tree_errors(cJSON *root) {
|
|
if (!root) return 0;
|
|
cJSON *errors = cJSON_GetObjectItemCaseSensitive(root, "errors");
|
|
if (!cJSON_IsArray(errors) || cJSON_GetArraySize(errors) == 0)
|
|
return 0;
|
|
const char *filename = "<unknown>";
|
|
cJSON *fname = cJSON_GetObjectItemCaseSensitive(root, "filename");
|
|
if (cJSON_IsString(fname))
|
|
filename = fname->valuestring;
|
|
int prev_line = -1;
|
|
const char *prev_msg = NULL;
|
|
cJSON *e;
|
|
cJSON_ArrayForEach(e, errors) {
|
|
const char *msg = cJSON_GetStringValue(
|
|
cJSON_GetObjectItemCaseSensitive(e, "message"));
|
|
cJSON *line = cJSON_GetObjectItemCaseSensitive(e, "line");
|
|
cJSON *col = cJSON_GetObjectItemCaseSensitive(e, "column");
|
|
int cur_line = cJSON_IsNumber(line) ? (int)line->valuedouble : -1;
|
|
if (prev_msg && msg && cur_line == prev_line && strcmp(msg, prev_msg) == 0)
|
|
continue;
|
|
prev_line = cur_line;
|
|
prev_msg = msg;
|
|
if (msg && cJSON_IsNumber(line) && cJSON_IsNumber(col))
|
|
fprintf(stderr, "%s:%d:%d: error: %s\n",
|
|
filename, (int)line->valuedouble, (int)col->valuedouble, msg);
|
|
else if (msg)
|
|
fprintf(stderr, "%s: error: %s\n", filename, msg);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int print_json_errors(const char *json) {
|
|
if (!json) return 0;
|
|
cJSON *root = cJSON_Parse(json);
|
|
if (!root) return 0;
|
|
int result = print_tree_errors(root);
|
|
cJSON_Delete(root);
|
|
return result;
|
|
}
|
|
|
|
// Get the core path for use by scripts
|
|
const char* cell_get_core_path(void) {
|
|
return core_path;
|
|
}
|
|
|
|
void actor_disrupt(cell_rt *crt)
|
|
{
|
|
crt->disrupt = 1;
|
|
if (crt->state != ACTOR_RUNNING)
|
|
actor_free(crt);
|
|
}
|
|
|
|
JSValue js_os_use(JSContext *js);
|
|
JSValue js_math_use(JSContext *js);
|
|
JSValue js_json_use(JSContext *js);
|
|
JSValue js_nota_use(JSContext *js);
|
|
JSValue js_wota_use(JSContext *js);
|
|
|
|
void script_startup(cell_rt *prt)
|
|
{
|
|
if (!g_runtime) {
|
|
g_runtime = JS_NewRuntime();
|
|
}
|
|
JSContext *js = JS_NewContext(g_runtime);
|
|
JS_SetInterruptHandler(js, (JSInterruptHandler *)actor_interrupt_cb, prt);
|
|
|
|
JS_SetContextOpaque(js, prt);
|
|
prt->context = js;
|
|
|
|
/* Register all GCRef fields so the Cheney GC can relocate them. */
|
|
JS_AddGCRef(js, &prt->idx_buffer_ref);
|
|
JS_AddGCRef(js, &prt->on_exception_ref);
|
|
JS_AddGCRef(js, &prt->message_handle_ref);
|
|
JS_AddGCRef(js, &prt->unneeded_ref);
|
|
JS_AddGCRef(js, &prt->actor_sym_ref);
|
|
prt->idx_buffer_ref.val = JS_NULL;
|
|
prt->on_exception_ref.val = JS_NULL;
|
|
prt->message_handle_ref.val = JS_NULL;
|
|
prt->unneeded_ref.val = JS_NULL;
|
|
prt->actor_sym_ref.val = JS_NULL;
|
|
|
|
cell_rt *crt = JS_GetContextOpaque(js);
|
|
JS_FreeValue(js, js_blob_use(js));
|
|
|
|
// Load and parse engine.cm to AST
|
|
size_t engine_size;
|
|
char *data = load_core_file(ENGINE, &engine_size);
|
|
if (!data) {
|
|
printf("ERROR: Could not load %s from %s!\n", ENGINE, core_path);
|
|
return;
|
|
}
|
|
|
|
cJSON *ast = JS_ASTTree(data, engine_size, ENGINE);
|
|
free(data);
|
|
if (!ast) {
|
|
printf("ERROR: Failed to parse %s\n", ENGINE);
|
|
return;
|
|
}
|
|
|
|
if (print_tree_errors(ast)) {
|
|
cJSON_Delete(ast);
|
|
return;
|
|
}
|
|
|
|
// Create hidden environment
|
|
JSValue hidden_env = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, hidden_env, "os", js_os_use(js));
|
|
JS_SetPropertyStr(js, hidden_env, "json", js_json_use(js));
|
|
JS_SetPropertyStr(js, hidden_env, "nota", js_nota_use(js));
|
|
JS_SetPropertyStr(js, hidden_env, "wota", js_wota_use(js));
|
|
|
|
crt->actor_sym_ref.val = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, hidden_env, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val));
|
|
|
|
// Always set init (even if null)
|
|
if (crt->init_wota) {
|
|
JS_SetPropertyStr(js, hidden_env, "init", wota2value(js, crt->init_wota));
|
|
free(crt->init_wota);
|
|
crt->init_wota = NULL;
|
|
} else {
|
|
JS_SetPropertyStr(js, hidden_env, "init", JS_NULL);
|
|
}
|
|
|
|
if (core_path) {
|
|
JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path));
|
|
}
|
|
|
|
// Stone the environment
|
|
hidden_env = JS_Stone(js, hidden_env);
|
|
|
|
// Run through MACH VM
|
|
crt->state = ACTOR_RUNNING;
|
|
JSValue v = JS_RunMachTree(js, ast, hidden_env);
|
|
cJSON_Delete(ast);
|
|
uncaught_exception(js, v);
|
|
crt->state = ACTOR_IDLE;
|
|
set_actor_state(crt);
|
|
}
|
|
|
|
static void signal_handler(int sig)
|
|
{
|
|
const char *str = NULL;
|
|
#ifndef TARGET_PLAYDATE
|
|
switch (sig) {
|
|
case SIGABRT: str = "SIGABRT"; break;
|
|
case SIGFPE: str = "SIGFPE"; break;
|
|
case SIGILL: str = "SIGILL"; break;
|
|
case SIGINT: str = "SIGINT"; break;
|
|
case SIGSEGV: str = "SIGSEGV"; break;
|
|
case SIGTERM: str = "SIGTERM"; break;
|
|
}
|
|
#endif
|
|
if (!str) return;
|
|
|
|
exit_handler();
|
|
}
|
|
|
|
/* Run the C test suite with minimal runtime setup */
|
|
static int run_test_suite(size_t heap_size)
|
|
{
|
|
JSRuntime *rt = JS_NewRuntime();
|
|
if (!rt) {
|
|
printf("Failed to create JS runtime\n");
|
|
return 1;
|
|
}
|
|
|
|
JSContext *ctx = JS_NewContextWithHeapSize(rt, heap_size);
|
|
if (!ctx) {
|
|
printf("Failed to create JS context\n");
|
|
JS_FreeRuntime(rt);
|
|
return 1;
|
|
}
|
|
|
|
int result = run_c_test_suite(ctx);
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Run an immediate script string */
|
|
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(" --ast <code|file> Output AST as JSON\n");
|
|
printf(" --tokenize <code|file> Output token array as JSON\n");
|
|
printf(" --mcode <script> [args] Run through mcode compilation pipeline\n");
|
|
printf(" --mcode-print <code|file> Compile to MCODE JSON and print it\n");
|
|
printf(" --mach <code|file> Output MACH bytecode\n");
|
|
printf(" --mach-run <code|file> Compile and run through MACH VM\n");
|
|
printf(" --test [heap_size] Run C test suite\n");
|
|
printf(" -h, --help Show this help message\n");
|
|
printf("\nRecompile after changes: make\n");
|
|
printf("Bootstrap from scratch: make bootstrap\n");
|
|
}
|
|
|
|
int cell_init(int argc, char **argv)
|
|
{
|
|
/* Check for --help flag */
|
|
if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
|
|
print_usage(argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
/* Check for --test flag to run C test suite */
|
|
if (argc >= 2 && strcmp(argv[1], "--test") == 0) {
|
|
size_t heap_size = 64 * 1024; /* 64KB default */
|
|
if (argc >= 3) {
|
|
heap_size = strtoull(argv[2], NULL, 0);
|
|
/* Round up to power of 2 for buddy allocator */
|
|
size_t p = 1;
|
|
while (p < heap_size) p <<= 1;
|
|
heap_size = p;
|
|
}
|
|
return run_test_suite(heap_size);
|
|
}
|
|
|
|
/* Check for --ast flag to output AST JSON */
|
|
if (argc >= 3 && strcmp(argv[1], "--ast") == 0) {
|
|
const char *script_or_file = argv[2];
|
|
char *script = NULL;
|
|
char *allocated_script = NULL;
|
|
const char *filename = "<eval>";
|
|
|
|
struct stat st;
|
|
if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) {
|
|
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 {
|
|
script = (char *)script_or_file;
|
|
}
|
|
|
|
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
|
|
if (ast) {
|
|
int has_errors = print_tree_errors(ast);
|
|
char *pretty = cJSON_Print(ast);
|
|
cJSON_Delete(ast);
|
|
printf("%s\n", pretty);
|
|
free(pretty);
|
|
free(allocated_script);
|
|
return has_errors ? 1 : 0;
|
|
} else {
|
|
printf("Failed to parse AST\n");
|
|
free(allocated_script);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Check for --tokenize flag to output token array JSON */
|
|
if (argc >= 3 && strcmp(argv[1], "--tokenize") == 0) {
|
|
const char *script_or_file = argv[2];
|
|
char *script = NULL;
|
|
char *allocated_script = NULL;
|
|
const char *filename = "<eval>";
|
|
|
|
struct stat st;
|
|
if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) {
|
|
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 {
|
|
script = (char *)script_or_file;
|
|
}
|
|
|
|
char *json = JS_Tokenize(script, strlen(script), filename);
|
|
if (json) {
|
|
int has_errors = print_json_errors(json);
|
|
cJSON *root = cJSON_Parse(json);
|
|
free(json);
|
|
if (root) {
|
|
char *pretty = cJSON_Print(root);
|
|
cJSON_Delete(root);
|
|
printf("%s\n", pretty);
|
|
free(pretty);
|
|
}
|
|
free(allocated_script);
|
|
return has_errors ? 1 : 0;
|
|
} else {
|
|
printf("Failed to tokenize\n");
|
|
free(allocated_script);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* Check for --mcode-print flag to compile a script to MCODE JSON and print it */
|
|
if (argc >= 3 && strcmp(argv[1], "--mcode-print") == 0) {
|
|
const char *script_or_file = argv[2];
|
|
char *script = NULL;
|
|
char *allocated_script = NULL;
|
|
const char *filename = "<eval>";
|
|
|
|
struct stat st;
|
|
if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) {
|
|
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 {
|
|
script = (char *)script_or_file;
|
|
}
|
|
|
|
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
|
|
if (!ast) {
|
|
printf("Failed to parse AST\n");
|
|
free(allocated_script);
|
|
return 1;
|
|
}
|
|
|
|
if (print_tree_errors(ast)) {
|
|
cJSON_Delete(ast);
|
|
free(allocated_script);
|
|
return 1;
|
|
}
|
|
|
|
cJSON *mcode = JS_McodeTree(ast);
|
|
cJSON_Delete(ast);
|
|
|
|
if (!mcode) {
|
|
printf("Failed to generate MCODE\n");
|
|
free(allocated_script);
|
|
return 1;
|
|
}
|
|
|
|
if (print_tree_errors(mcode)) {
|
|
cJSON_Delete(mcode);
|
|
free(allocated_script);
|
|
return 1;
|
|
}
|
|
|
|
char *pretty = cJSON_Print(mcode);
|
|
cJSON_Delete(mcode);
|
|
printf("%s\n", pretty);
|
|
free(pretty);
|
|
free(allocated_script);
|
|
return 0;
|
|
}
|
|
|
|
/* Check for --mach flag to dump MACH bytecode */
|
|
if (argc >= 3 && strcmp(argv[1], "--mach") == 0) {
|
|
const char *script_or_file = argv[2];
|
|
char *script = NULL;
|
|
char *allocated_script = NULL;
|
|
const char *filename = "<eval>";
|
|
|
|
struct stat st;
|
|
if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) {
|
|
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 {
|
|
script = (char *)script_or_file;
|
|
}
|
|
|
|
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
|
|
if (!ast) {
|
|
printf("Failed to parse AST\n");
|
|
free(allocated_script);
|
|
return 1;
|
|
}
|
|
|
|
if (print_tree_errors(ast)) {
|
|
cJSON_Delete(ast);
|
|
free(allocated_script);
|
|
return 1;
|
|
}
|
|
|
|
JSRuntime *rt = JS_NewRuntime();
|
|
if (!rt) {
|
|
printf("Failed to create JS runtime\n");
|
|
cJSON_Delete(ast); free(allocated_script);
|
|
return 1;
|
|
}
|
|
JSContext *ctx = JS_NewContext(rt);
|
|
if (!ctx) {
|
|
printf("Failed to create JS context\n");
|
|
cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script);
|
|
return 1;
|
|
}
|
|
|
|
JS_DumpMachTree(ctx, ast, JS_NULL);
|
|
cJSON_Delete(ast);
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
free(allocated_script);
|
|
return 0;
|
|
}
|
|
|
|
/* Check for --mach-run flag to compile and run through MACH VM */
|
|
if (argc >= 3 && strcmp(argv[1], "--mach-run") == 0) {
|
|
const char *script_name = argv[2];
|
|
char *script = NULL;
|
|
char *allocated_script = NULL;
|
|
const char *filename = script_name;
|
|
|
|
struct stat st;
|
|
if (stat(script_name, &st) == 0 && S_ISREG(st.st_mode)) {
|
|
/* Exact name found */
|
|
} else {
|
|
/* Try .ce then .cm extension */
|
|
static char pathbuf[4096];
|
|
snprintf(pathbuf, sizeof(pathbuf), "%s.ce", script_name);
|
|
if (stat(pathbuf, &st) == 0 && S_ISREG(st.st_mode)) {
|
|
script_name = pathbuf;
|
|
filename = pathbuf;
|
|
} else {
|
|
snprintf(pathbuf, sizeof(pathbuf), "%s.cm", script_name);
|
|
if (stat(pathbuf, &st) == 0 && S_ISREG(st.st_mode)) {
|
|
script_name = pathbuf;
|
|
filename = pathbuf;
|
|
} else {
|
|
printf("Failed to find file: %s\n", argv[2]);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
FILE *f = fopen(script_name, "r");
|
|
if (!f) {
|
|
printf("Failed to open file: %s\n", script_name);
|
|
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;
|
|
|
|
cJSON *ast = JS_ASTTree(script, read_size, filename);
|
|
free(allocated_script);
|
|
if (!ast) {
|
|
printf("Failed to parse %s\n", filename);
|
|
return 1;
|
|
}
|
|
|
|
if (print_tree_errors(ast)) {
|
|
cJSON_Delete(ast);
|
|
return 1;
|
|
}
|
|
|
|
JSRuntime *rt = JS_NewRuntime();
|
|
if (!rt) {
|
|
printf("Failed to create JS runtime\n");
|
|
cJSON_Delete(ast);
|
|
return 1;
|
|
}
|
|
JSContext *ctx = JS_NewContextWithHeapSize(rt, 16 * 1024 * 1024);
|
|
if (!ctx) {
|
|
printf("Failed to create JS context\n");
|
|
cJSON_Delete(ast); JS_FreeRuntime(rt);
|
|
return 1;
|
|
}
|
|
|
|
JS_FreeValue(ctx, js_blob_use(ctx));
|
|
|
|
JSValue result = JS_RunMachTree(ctx, ast, JS_NULL);
|
|
cJSON_Delete(ast);
|
|
|
|
int exit_code = 0;
|
|
if (JS_IsException(result)) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
return exit_code;
|
|
}
|
|
|
|
/* Default: run script through mach-run bootstrap pipeline */
|
|
int use_mcode = 0;
|
|
int arg_start = 1;
|
|
if (argc >= 3 && strcmp(argv[1], "--mcode") == 0) {
|
|
use_mcode = 1;
|
|
arg_start = 2;
|
|
}
|
|
|
|
if (!find_cell_shop()) return 1;
|
|
|
|
size_t boot_size;
|
|
char *boot_data = load_core_file("internal/bootstrap.cm", &boot_size);
|
|
if (!boot_data) {
|
|
printf("ERROR: Could not load internal/bootstrap.cm from %s\n", core_path);
|
|
return 1;
|
|
}
|
|
|
|
cJSON *boot_ast = JS_ASTTree(boot_data, boot_size, "internal/bootstrap.cm");
|
|
free(boot_data);
|
|
if (!boot_ast) {
|
|
printf("Failed to parse internal/bootstrap.cm\n");
|
|
return 1;
|
|
}
|
|
|
|
if (print_tree_errors(boot_ast)) {
|
|
cJSON_Delete(boot_ast);
|
|
return 1;
|
|
}
|
|
|
|
JSRuntime *rt = JS_NewRuntime();
|
|
if (!rt) {
|
|
printf("Failed to create JS runtime\n");
|
|
cJSON_Delete(boot_ast);
|
|
return 1;
|
|
}
|
|
JSContext *ctx = JS_NewContextWithHeapSize(rt, 16 * 1024 * 1024);
|
|
if (!ctx) {
|
|
printf("Failed to create JS context\n");
|
|
cJSON_Delete(boot_ast); JS_FreeRuntime(rt);
|
|
return 1;
|
|
}
|
|
|
|
JS_FreeValue(ctx, js_blob_use(ctx));
|
|
|
|
JSValue hidden_env = JS_NewObject(ctx);
|
|
JS_SetPropertyStr(ctx, hidden_env, "os", js_os_use(ctx));
|
|
JS_SetPropertyStr(ctx, hidden_env, "core_path", JS_NewString(ctx, core_path));
|
|
JS_SetPropertyStr(ctx, hidden_env, "use_mcode", JS_NewBool(ctx, use_mcode));
|
|
JSValue args_arr = JS_NewArray(ctx);
|
|
for (int i = arg_start; i < argc; i++) {
|
|
JSValue str = JS_NewString(ctx, argv[i]);
|
|
JS_ArrayPush(ctx, &args_arr, str);
|
|
}
|
|
JS_SetPropertyStr(ctx, hidden_env, "args", args_arr);
|
|
hidden_env = JS_Stone(ctx, hidden_env);
|
|
|
|
JSValue result = JS_RunMachTree(ctx, boot_ast, hidden_env);
|
|
cJSON_Delete(boot_ast);
|
|
|
|
int exit_code = 0;
|
|
if (JS_IsException(result)) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
JS_FreeContext(ctx);
|
|
JS_FreeRuntime(rt);
|
|
return exit_code;
|
|
}
|
|
|
|
int JS_ArrayLength(JSContext *js, JSValue a)
|
|
{
|
|
int64_t len;
|
|
JS_GetLength(js, a, &len);
|
|
return len;
|
|
}
|
|
|
|
int js2bool(JSContext *js, JSValue v) { return JS_ToBool(js,v); }
|
|
JSValue bool2js(JSContext *js, int b) { return JS_NewBool(js,b); }
|
|
|
|
JSValue number2js(JSContext *js, double g) { return JS_NewFloat64(js,g); }
|
|
double js2number(JSContext *js, JSValue v) {
|
|
double g;
|
|
JS_ToFloat64(js, &g, v);
|
|
if (isnan(g)) g = 0;
|
|
return g;
|
|
}
|
|
|
|
uint64_t cell_random_fit() {
|
|
uint64_t buf;
|
|
randombytes((uint8_t *)&buf, sizeof(buf));
|
|
return buf >> 11;
|
|
}
|
|
|
|
double cell_random() {
|
|
uint64_t buf = cell_random_fit();
|
|
return (double)buf / 9007199254740992.0;
|
|
}
|
|
|
|
void cell_trace_sethook(cell_hook)
|
|
{
|
|
|
|
}
|
|
|
|
int uncaught_exception(JSContext *js, JSValue v)
|
|
{
|
|
(void)v;
|
|
if (!JS_HasException(js))
|
|
return 1;
|
|
/* Error message and backtrace were already printed to stderr
|
|
by JS_ThrowError2 / print_backtrace. Just clear the flag. */
|
|
JS_GetException(js);
|
|
return 0;
|
|
}
|