ocaml style rooting macros

This commit is contained in:
2026-02-13 20:46:31 -06:00
parent e80e615634
commit 83263379bd
24 changed files with 418 additions and 220 deletions

View File

@@ -103,6 +103,7 @@ var v = a[] // pop: v is 3, a is [1, 2]
- Most files don't have headers; files in a package are not shared between packages
- No undefined in C API: use `JS_IsNull` and `JS_NULL` only
- A C file with correct macros (`CELL_USE_FUNCS` etc) is loaded as a module by its name (e.g., `png.c` in a package → `use('<package>/png')`)
- Use `JS_FRAME`/`JS_ROOT`/`JS_RETURN` macros for any C function that allocates multiple heap objects. Any `JS_New*`/`JS_SetProperty*` call can trigger GC.
## Project Layout

View File

@@ -381,19 +381,21 @@ static const JSCFunctionListEntry js_reader_funcs[] = {
JSValue js_miniz_use(JSContext *js)
{
JS_FRAME(js);
JS_NewClassID(&js_reader_class_id);
JS_NewClass(js, js_reader_class_id, &js_reader_class);
JSValue reader_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, reader_proto, js_reader_funcs, sizeof(js_reader_funcs) / sizeof(JSCFunctionListEntry));
JS_SetClassProto(js, js_reader_class_id, reader_proto);
JS_ROOT(reader_proto, JS_NewObject(js));
JS_SetPropertyFunctionList(js, reader_proto.val, js_reader_funcs, sizeof(js_reader_funcs) / sizeof(JSCFunctionListEntry));
JS_SetClassProto(js, js_reader_class_id, reader_proto.val);
JS_NewClassID(&js_writer_class_id);
JS_NewClass(js, js_writer_class_id, &js_writer_class);
JSValue writer_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, writer_proto, js_writer_funcs, sizeof(js_writer_funcs) / sizeof(JSCFunctionListEntry));
JS_SetClassProto(js, js_writer_class_id, writer_proto);
JSValue export = JS_NewObject(js);
JS_SetPropertyFunctionList(js, export, js_miniz_funcs, sizeof(js_miniz_funcs)/sizeof(JSCFunctionListEntry));
return export;
JS_ROOT(writer_proto, JS_NewObject(js));
JS_SetPropertyFunctionList(js, writer_proto.val, js_writer_funcs, sizeof(js_writer_funcs) / sizeof(JSCFunctionListEntry));
JS_SetClassProto(js, js_writer_class_id, writer_proto.val);
JS_ROOT(export, JS_NewObject(js));
JS_SetPropertyFunctionList(js, export.val, js_miniz_funcs, sizeof(js_miniz_funcs)/sizeof(JSCFunctionListEntry));
JS_RETURN(export.val);
}

View File

@@ -240,7 +240,8 @@ static const JSCFunctionListEntry js_crypto_funcs[] = {
JSValue js_crypto_use(JSContext *js)
{
JSValue obj = JS_NewObject(js);
JS_SetPropertyFunctionList(js, obj, js_crypto_funcs, sizeof(js_crypto_funcs)/sizeof(js_crypto_funcs[0]));
return obj;
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_crypto_funcs, sizeof(js_crypto_funcs)/sizeof(js_crypto_funcs[0]));
JS_RETURN(mod.val);
}

View File

@@ -22,7 +22,8 @@ static const JSCFunctionListEntry js_debug_funcs[] = {
};
JSValue js_debug_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_debug_funcs,countof(js_debug_funcs));
return mod;
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_debug_funcs, countof(js_debug_funcs));
JS_RETURN(mod.val);
}

View File

@@ -21,7 +21,8 @@ static const JSCFunctionListEntry js_js_funcs[] = {
};
JSValue js_js_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_js_funcs,countof(js_js_funcs));
return mod;
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_js_funcs, countof(js_js_funcs));
JS_RETURN(mod.val);
}

153
fd.c
View File

@@ -412,117 +412,117 @@ JSC_CCALL(fd_close,
JSC_CCALL(fd_fstat,
int fd = js2fd(js, argv[0]);
if (fd < 0) return JS_EXCEPTION;
struct stat st;
if (fstat(fd, &st) != 0)
return JS_ThrowInternalError(js, "fstat failed: %s", strerror(errno));
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size));
JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode));
JS_SetPropertyStr(js, obj, "uid", JS_NewInt32(js, st.st_uid));
JS_SetPropertyStr(js, obj, "gid", JS_NewInt32(js, st.st_gid));
JS_SetPropertyStr(js, obj, "atime", JS_NewInt64(js, st.st_atime));
JS_SetPropertyStr(js, obj, "mtime", JS_NewInt64(js, st.st_mtime));
JS_SetPropertyStr(js, obj, "ctime", JS_NewInt64(js, st.st_ctime));
JS_SetPropertyStr(js, obj, "nlink", JS_NewInt32(js, st.st_nlink));
JS_SetPropertyStr(js, obj, "ino", JS_NewInt64(js, st.st_ino));
JS_SetPropertyStr(js, obj, "dev", JS_NewInt32(js, st.st_dev));
JS_SetPropertyStr(js, obj, "rdev", JS_NewInt32(js, st.st_rdev));
JS_FRAME(js);
JS_ROOT(obj, JS_NewObject(js));
JS_SetPropertyStr(js, obj.val, "size", JS_NewInt64(js, st.st_size));
JS_SetPropertyStr(js, obj.val, "mode", JS_NewInt32(js, st.st_mode));
JS_SetPropertyStr(js, obj.val, "uid", JS_NewInt32(js, st.st_uid));
JS_SetPropertyStr(js, obj.val, "gid", JS_NewInt32(js, st.st_gid));
JS_SetPropertyStr(js, obj.val, "atime", JS_NewInt64(js, st.st_atime));
JS_SetPropertyStr(js, obj.val, "mtime", JS_NewInt64(js, st.st_mtime));
JS_SetPropertyStr(js, obj.val, "ctime", JS_NewInt64(js, st.st_ctime));
JS_SetPropertyStr(js, obj.val, "nlink", JS_NewInt32(js, st.st_nlink));
JS_SetPropertyStr(js, obj.val, "ino", JS_NewInt64(js, st.st_ino));
JS_SetPropertyStr(js, obj.val, "dev", JS_NewInt32(js, st.st_dev));
JS_SetPropertyStr(js, obj.val, "rdev", JS_NewInt32(js, st.st_rdev));
#ifndef _WIN32
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize));
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_blocks));
JS_SetPropertyStr(js, obj.val, "blksize", JS_NewInt32(js, st.st_blksize));
JS_SetPropertyStr(js, obj.val, "blocks", JS_NewInt64(js, st.st_blocks));
#else
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, 4096));
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_size / 512));
JS_SetPropertyStr(js, obj.val, "blksize", JS_NewInt32(js, 4096));
JS_SetPropertyStr(js, obj.val, "blocks", JS_NewInt64(js, st.st_size / 512));
#endif
// Add boolean properties for file type
JS_SetPropertyStr(js, obj, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
JS_SetPropertyStr(js, obj, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
JS_SetPropertyStr(js, obj, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
JS_SetPropertyStr(js, obj, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
JS_SetPropertyStr(js, obj, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
JS_SetPropertyStr(js, obj, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
return obj;
JS_SetPropertyStr(js, obj.val, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
JS_RETURN(obj.val);
)
JSC_CCALL(fd_stat,
const char *path = JS_ToCString(js, argv[0]);
if (!path) return JS_EXCEPTION;
struct stat st;
if (stat(path, &st) != 0) {
JS_FreeCString(js, path);
return JS_NewObject(js);
}
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size));
JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode));
JS_SetPropertyStr(js, obj, "uid", JS_NewInt32(js, st.st_uid));
JS_SetPropertyStr(js, obj, "gid", JS_NewInt32(js, st.st_gid));
JS_SetPropertyStr(js, obj, "atime", JS_NewInt64(js, st.st_atime));
JS_SetPropertyStr(js, obj, "mtime", JS_NewInt64(js, st.st_mtime));
JS_SetPropertyStr(js, obj, "ctime", JS_NewInt64(js, st.st_ctime));
JS_SetPropertyStr(js, obj, "nlink", JS_NewInt32(js, st.st_nlink));
JS_SetPropertyStr(js, obj, "ino", JS_NewInt64(js, st.st_ino));
JS_SetPropertyStr(js, obj, "dev", JS_NewInt32(js, st.st_dev));
JS_SetPropertyStr(js, obj, "rdev", JS_NewInt32(js, st.st_rdev));
JS_FRAME(js);
JS_ROOT(obj, JS_NewObject(js));
JS_SetPropertyStr(js, obj.val, "size", JS_NewInt64(js, st.st_size));
JS_SetPropertyStr(js, obj.val, "mode", JS_NewInt32(js, st.st_mode));
JS_SetPropertyStr(js, obj.val, "uid", JS_NewInt32(js, st.st_uid));
JS_SetPropertyStr(js, obj.val, "gid", JS_NewInt32(js, st.st_gid));
JS_SetPropertyStr(js, obj.val, "atime", JS_NewInt64(js, st.st_atime));
JS_SetPropertyStr(js, obj.val, "mtime", JS_NewInt64(js, st.st_mtime));
JS_SetPropertyStr(js, obj.val, "ctime", JS_NewInt64(js, st.st_ctime));
JS_SetPropertyStr(js, obj.val, "nlink", JS_NewInt32(js, st.st_nlink));
JS_SetPropertyStr(js, obj.val, "ino", JS_NewInt64(js, st.st_ino));
JS_SetPropertyStr(js, obj.val, "dev", JS_NewInt32(js, st.st_dev));
JS_SetPropertyStr(js, obj.val, "rdev", JS_NewInt32(js, st.st_rdev));
#ifndef _WIN32
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize));
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_blocks));
JS_SetPropertyStr(js, obj.val, "blksize", JS_NewInt32(js, st.st_blksize));
JS_SetPropertyStr(js, obj.val, "blocks", JS_NewInt64(js, st.st_blocks));
#else
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, 4096));
JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_size / 512));
JS_SetPropertyStr(js, obj.val, "blksize", JS_NewInt32(js, 4096));
JS_SetPropertyStr(js, obj.val, "blocks", JS_NewInt64(js, st.st_size / 512));
#endif
// Add boolean properties for file type
JS_SetPropertyStr(js, obj, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
JS_SetPropertyStr(js, obj, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
JS_SetPropertyStr(js, obj, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
JS_SetPropertyStr(js, obj, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
JS_SetPropertyStr(js, obj, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
JS_SetPropertyStr(js, obj, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isFile", JS_NewBool(js, S_ISREG(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode)));
JS_SetPropertyStr(js, obj.val, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode)));
JS_FreeCString(js, path);
return obj;
JS_RETURN(obj.val);
)
JSC_SCALL(fd_readdir,
JS_FRAME(js);
#ifdef _WIN32
WIN32_FIND_DATA ffd;
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s\\*", str);
HANDLE hFind = FindFirstFile(path, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
ret = JS_ThrowInternalError(js, "FindFirstFile failed for %s", path);
ret = JS_ThrowInternalError(js, "FindFirstFile failed for %s", path);
} else {
ret = JS_NewArray(js);
do {
if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue;
JS_ArrayPush(js, &ret, JS_NewString(js, ffd.cFileName));
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
JS_ROOT(arr, JS_NewArray(js));
do {
if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue;
JS_ArrayPush(js, &arr.val, JS_NewString(js, ffd.cFileName));
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
ret = arr.val;
}
#else
DIR *d;
struct dirent *dir;
d = opendir(str);
if (d) {
ret = JS_NewArray(js);
JS_ROOT(arr, JS_NewArray(js));
while ((dir = readdir(d)) != NULL) {
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue;
JS_ArrayPush(js, &ret, JS_NewString(js, dir->d_name));
JS_ArrayPush(js, &arr.val, JS_NewString(js, dir->d_name));
}
closedir(d);
ret = arr.val;
} else {
ret = JS_ThrowInternalError(js, "opendir failed for %s: %s", str, strerror(errno));
}
#endif
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame);
)
JSC_CCALL(fd_is_file,
@@ -585,9 +585,9 @@ JSC_CCALL(fd_slurpwrite,
)
// Helper function for recursive enumeration
static void visit_directory(JSContext *js, JSValue results, int *result_count, const char *curr_path, const char *rel_prefix, int recurse) {
static void visit_directory(JSContext *js, JSValue *results, int *result_count, const char *curr_path, const char *rel_prefix, int recurse) {
if (!curr_path) return;
#ifdef _WIN32
WIN32_FIND_DATA ffd;
char search_path[PATH_MAX];
@@ -602,7 +602,7 @@ static void visit_directory(JSContext *js, JSValue results, int *result_count, c
} else {
strcpy(item_rel, ffd.cFileName);
}
JS_SetPropertyNumber(js, results, (*result_count)++, JS_NewString(js, item_rel));
JS_SetPropertyNumber(js, *results, (*result_count)++, JS_NewString(js, item_rel));
if (recurse) {
struct stat st;
@@ -627,7 +627,7 @@ static void visit_directory(JSContext *js, JSValue results, int *result_count, c
} else {
strcpy(item_rel, dir->d_name);
}
JS_SetPropertyNumber(js, results, (*result_count)++, JS_NewString(js, item_rel));
JS_SetPropertyNumber(js, *results, (*result_count)++, JS_NewString(js, item_rel));
if (recurse) {
struct stat st;
@@ -651,14 +651,16 @@ JSC_SCALL(fd_enumerate,
if (argc > 1)
recurse = JS_ToBool(js, argv[1]);
JSValue results = JS_NewArray(js);
JS_FRAME(js);
JS_ROOT(arr, JS_NewArray(js));
int result_count = 0;
struct stat st;
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode))
visit_directory(js, results, &result_count, path, "", recurse);
visit_directory(js, &arr.val, &result_count, path, "", recurse);
ret = results;
ret = arr.val;
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame);
)
JSC_CCALL(fd_realpath,
@@ -753,7 +755,8 @@ static const JSCFunctionListEntry js_fd_funcs[] = {
};
JSValue js_fd_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_fd_funcs, countof(js_fd_funcs));
return mod;
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_fd_funcs, countof(js_fd_funcs));
JS_RETURN(mod.val);
}

7
fit.c
View File

@@ -250,7 +250,8 @@ static const JSCFunctionListEntry js_fit_funcs[] = {
JSValue js_fit_use(JSContext *js)
{
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_fit_funcs, countof(js_fit_funcs));
return mod;
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_fit_funcs, countof(js_fit_funcs));
JS_RETURN(mod.val);
}

View File

@@ -75,7 +75,8 @@ static const JSCFunctionListEntry js_kim_funcs[] = {
JSValue js_kim_use(JSContext *js)
{
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_kim_funcs, countof(js_kim_funcs));
return mod;
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_kim_funcs, countof(js_kim_funcs));
JS_RETURN(mod.val);
}

View File

@@ -277,29 +277,31 @@ JSC_CCALL(os_mallinfo,
)
static JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) {
JSValue ret = JS_NULL;
ret = JS_NewObject(js);
JS_FRAME(js);
JS_ROOT(ret, JS_NewObject(js));
#if defined(__linux__) || defined(__APPLE__)
struct rusage jsmem;
getrusage(RUSAGE_SELF, &jsmem);
JSJMEMRET(ru_maxrss);
JSJMEMRET(ru_ixrss);
JSJMEMRET(ru_idrss);
JSJMEMRET(ru_isrss);
JSJMEMRET(ru_minflt);
JSJMEMRET(ru_majflt);
JSJMEMRET(ru_nswap);
JSJMEMRET(ru_inblock);
JSJMEMRET(ru_oublock);
JSJMEMRET(ru_msgsnd);
JSJMEMRET(ru_msgrcv);
JSJMEMRET(ru_nsignals);
JSJMEMRET(ru_nvcsw);
JSJMEMRET(ru_nivcsw);
#define JSJMEMRET_GC(FIELD) JS_SetPropertyStr(js, ret.val, #FIELD, number2js(js, jsmem.FIELD));
JSJMEMRET_GC(ru_maxrss);
JSJMEMRET_GC(ru_ixrss);
JSJMEMRET_GC(ru_idrss);
JSJMEMRET_GC(ru_isrss);
JSJMEMRET_GC(ru_minflt);
JSJMEMRET_GC(ru_majflt);
JSJMEMRET_GC(ru_nswap);
JSJMEMRET_GC(ru_inblock);
JSJMEMRET_GC(ru_oublock);
JSJMEMRET_GC(ru_msgsnd);
JSJMEMRET_GC(ru_msgrcv);
JSJMEMRET_GC(ru_nsignals);
JSJMEMRET_GC(ru_nvcsw);
JSJMEMRET_GC(ru_nivcsw);
#undef JSJMEMRET_GC
#endif
return ret;
JS_RETURN(ret.val);
}
JSC_SCALL(os_system,
@@ -628,7 +630,8 @@ JSValue js_os_use(JSContext *js) {
JS_NewClassID(&js_dylib_class_id);
JS_NewClass(js, js_dylib_class_id, &js_dylib_class);
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs));
return mod;
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_os_funcs, countof(js_os_funcs));
JS_RETURN(mod.val);
}

View File

@@ -22,6 +22,10 @@ if get_option('validate_gc')
add_project_arguments('-DVALIDATE_GC', language: 'c')
endif
if get_option('force_gc')
add_project_arguments('-DFORCE_GC_AT_MALLOC', language: 'c')
endif
deps = []
if host_machine.system() == 'darwin'

View File

@@ -1,2 +1,4 @@
option('validate_gc', type: 'boolean', value: false,
description: 'Enable GC validation checks (stale pointer detection, pre-GC frame validation)')
option('force_gc', type: 'boolean', value: false,
description: 'Force GC on every allocation (makes stale pointer bugs deterministic)')

View File

@@ -570,19 +570,21 @@ static const JSCFunctionListEntry js_enet_peer_funcs[] = {
JSValue js_enet_use(JSContext *ctx)
{
JS_FRAME(ctx);
JS_NewClassID(&enet_host_id);
JS_NewClass(ctx, enet_host_id, &enet_host);
JSValue host_proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, host_proto, js_enet_host_funcs, countof(js_enet_host_funcs));
JS_SetClassProto(ctx, enet_host_id, host_proto);
JS_ROOT(host_proto, JS_NewObject(ctx));
JS_SetPropertyFunctionList(ctx, host_proto.val, js_enet_host_funcs, countof(js_enet_host_funcs));
JS_SetClassProto(ctx, enet_host_id, host_proto.val);
JS_NewClassID(&enet_peer_class_id);
JS_NewClass(ctx, enet_peer_class_id, &enet_peer_class);
JSValue peer_proto = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, peer_proto, js_enet_peer_funcs, countof(js_enet_peer_funcs));
JS_SetClassProto(ctx, enet_peer_class_id, peer_proto);
JS_ROOT(peer_proto, JS_NewObject(ctx));
JS_SetPropertyFunctionList(ctx, peer_proto.val, js_enet_peer_funcs, countof(js_enet_peer_funcs));
JS_SetClassProto(ctx, enet_peer_class_id, peer_proto.val);
JSValue export_obj = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, export_obj, js_enet_funcs, countof(js_enet_funcs));
return export_obj;
JS_ROOT(export_obj, JS_NewObject(ctx));
JS_SetPropertyFunctionList(ctx, export_obj.val, js_enet_funcs, countof(js_enet_funcs));
JS_RETURN(export_obj.val);
}

View File

@@ -319,9 +319,10 @@ static const JSCFunctionListEntry js_http_funcs[] = {
};
JSValue js_http_use(JSContext *js) {
JS_FRAME(js);
par_easycurl_init(0); // Initialize platform HTTP backend
JSValue obj = JS_NewObject(js);
JS_SetPropertyFunctionList(js, obj, js_http_funcs,
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_http_funcs,
sizeof(js_http_funcs)/sizeof(js_http_funcs[0]));
return obj;
JS_RETURN(mod.val);
}

View File

@@ -595,26 +595,27 @@ static const JSCFunctionListEntry js_socket_funcs[] = {
};
JSValue js_socket_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_socket_funcs, countof(js_socket_funcs));
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_socket_funcs, countof(js_socket_funcs));
// Add constants
JS_SetPropertyStr(js, mod, "AF_UNSPEC", JS_NewInt32(js, AF_UNSPEC));
JS_SetPropertyStr(js, mod, "AF_INET", JS_NewInt32(js, AF_INET));
JS_SetPropertyStr(js, mod, "AF_INET6", JS_NewInt32(js, AF_INET6));
JS_SetPropertyStr(js, mod, "AF_UNIX", JS_NewInt32(js, AF_UNIX));
JS_SetPropertyStr(js, mod, "SOCK_STREAM", JS_NewInt32(js, SOCK_STREAM));
JS_SetPropertyStr(js, mod, "SOCK_DGRAM", JS_NewInt32(js, SOCK_DGRAM));
JS_SetPropertyStr(js, mod, "AI_PASSIVE", JS_NewInt32(js, AI_PASSIVE));
JS_SetPropertyStr(js, mod, "SHUT_RD", JS_NewInt32(js, SHUT_RD));
JS_SetPropertyStr(js, mod, "SHUT_WR", JS_NewInt32(js, SHUT_WR));
JS_SetPropertyStr(js, mod, "SHUT_RDWR", JS_NewInt32(js, SHUT_RDWR));
JS_SetPropertyStr(js, mod, "SOL_SOCKET", JS_NewInt32(js, SOL_SOCKET));
JS_SetPropertyStr(js, mod, "SO_REUSEADDR", JS_NewInt32(js, SO_REUSEADDR));
return mod;
JS_SetPropertyStr(js, mod.val, "AF_UNSPEC", JS_NewInt32(js, AF_UNSPEC));
JS_SetPropertyStr(js, mod.val, "AF_INET", JS_NewInt32(js, AF_INET));
JS_SetPropertyStr(js, mod.val, "AF_INET6", JS_NewInt32(js, AF_INET6));
JS_SetPropertyStr(js, mod.val, "AF_UNIX", JS_NewInt32(js, AF_UNIX));
JS_SetPropertyStr(js, mod.val, "SOCK_STREAM", JS_NewInt32(js, SOCK_STREAM));
JS_SetPropertyStr(js, mod.val, "SOCK_DGRAM", JS_NewInt32(js, SOCK_DGRAM));
JS_SetPropertyStr(js, mod.val, "AI_PASSIVE", JS_NewInt32(js, AI_PASSIVE));
JS_SetPropertyStr(js, mod.val, "SHUT_RD", JS_NewInt32(js, SHUT_RD));
JS_SetPropertyStr(js, mod.val, "SHUT_WR", JS_NewInt32(js, SHUT_WR));
JS_SetPropertyStr(js, mod.val, "SHUT_RDWR", JS_NewInt32(js, SHUT_RDWR));
JS_SetPropertyStr(js, mod.val, "SOL_SOCKET", JS_NewInt32(js, SOL_SOCKET));
JS_SetPropertyStr(js, mod.val, "SO_REUSEADDR", JS_NewInt32(js, SO_REUSEADDR));
JS_RETURN(mod.val);
}

20
qop.c
View File

@@ -457,19 +457,21 @@ static const JSCFunctionListEntry js_qop_funcs[] = {
};
JSValue js_qop_use(JSContext *js) {
JS_FRAME(js);
JS_NewClassID(&js_qop_archive_class_id);
JS_NewClass(js, js_qop_archive_class_id, &js_qop_archive_class);
JSValue archive_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, archive_proto, js_qop_archive_funcs, countof(js_qop_archive_funcs));
JS_SetClassProto(js, js_qop_archive_class_id, archive_proto);
JS_ROOT(archive_proto, JS_NewObject(js));
JS_SetPropertyFunctionList(js, archive_proto.val, js_qop_archive_funcs, countof(js_qop_archive_funcs));
JS_SetClassProto(js, js_qop_archive_class_id, archive_proto.val);
JS_NewClassID(&js_qop_writer_class_id);
JS_NewClass(js, js_qop_writer_class_id, &js_qop_writer_class);
JSValue writer_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, writer_proto, js_qop_writer_funcs, countof(js_qop_writer_funcs));
JS_SetClassProto(js, js_qop_writer_class_id, writer_proto);
JS_ROOT(writer_proto, JS_NewObject(js));
JS_SetPropertyFunctionList(js, writer_proto.val, js_qop_writer_funcs, countof(js_qop_writer_funcs));
JS_SetClassProto(js, js_qop_writer_class_id, writer_proto.val);
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_qop_funcs, countof(js_qop_funcs));
return mod;
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_qop_funcs, countof(js_qop_funcs));
JS_RETURN(mod.val);
}

View File

@@ -275,34 +275,47 @@ void script_startup(cell_rt *prt)
}
// 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));
// Note: evaluate allocating calls into temporaries before passing to
// JS_SetPropertyStr, so env_ref.val is read AFTER GC may have moved it.
JSGCRef env_ref;
JS_AddGCRef(js, &env_ref);
env_ref.val = JS_NewObject(js);
JSValue tmp;
tmp = js_os_use(js);
JS_SetPropertyStr(js, env_ref.val, "os", tmp);
tmp = js_json_use(js);
JS_SetPropertyStr(js, env_ref.val, "json", tmp);
tmp = js_nota_use(js);
JS_SetPropertyStr(js, env_ref.val, "nota", tmp);
tmp = js_wota_use(js);
JS_SetPropertyStr(js, env_ref.val, "wota", tmp);
crt->actor_sym_ref.val = JS_NewObject(js);
JS_SetPropertyStr(js, hidden_env, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val));
JS_SetPropertyStr(js, env_ref.val, "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));
tmp = wota2value(js, crt->init_wota);
JS_SetPropertyStr(js, env_ref.val, "init", tmp);
free(crt->init_wota);
crt->init_wota = NULL;
} else {
JS_SetPropertyStr(js, hidden_env, "init", JS_NULL);
JS_SetPropertyStr(js, env_ref.val, "init", JS_NULL);
}
// Set args to null for actor spawn (not CLI mode)
JS_SetPropertyStr(js, hidden_env, "args", JS_NULL);
JS_SetPropertyStr(js, env_ref.val, "args", JS_NULL);
if (core_path)
JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path));
JS_SetPropertyStr(js, hidden_env, "shop_path",
shop_path ? JS_NewString(js, shop_path) : JS_NULL);
if (core_path) {
tmp = JS_NewString(js, core_path);
JS_SetPropertyStr(js, env_ref.val, "core_path", tmp);
}
tmp = shop_path ? JS_NewString(js, shop_path) : JS_NULL;
JS_SetPropertyStr(js, env_ref.val, "shop_path", tmp);
// Stone the environment
hidden_env = JS_Stone(js, hidden_env);
JSValue hidden_env = JS_Stone(js, env_ref.val);
JS_DeleteGCRef(js, &env_ref);
// Run from binary
crt->state = ACTOR_RUNNING;
@@ -504,23 +517,35 @@ int cell_init(int argc, char **argv)
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, "shop_path",
shop_path ? JS_NewString(ctx, shop_path) : JS_NULL);
JS_SetPropertyStr(ctx, hidden_env, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
JS_SetPropertyStr(ctx, hidden_env, "json", js_json_use(ctx));
JS_SetPropertyStr(ctx, hidden_env, "nota", js_nota_use(ctx));
JS_SetPropertyStr(ctx, hidden_env, "wota", js_wota_use(ctx));
JS_SetPropertyStr(ctx, hidden_env, "init", JS_NULL);
JSValue args_arr = JS_NewArray(ctx);
JSGCRef env_ref;
JS_AddGCRef(ctx, &env_ref);
env_ref.val = JS_NewObject(ctx);
JSValue tmp;
tmp = js_os_use(ctx);
JS_SetPropertyStr(ctx, env_ref.val, "os", tmp);
tmp = JS_NewString(ctx, core_path);
JS_SetPropertyStr(ctx, env_ref.val, "core_path", tmp);
tmp = shop_path ? JS_NewString(ctx, shop_path) : JS_NULL;
JS_SetPropertyStr(ctx, env_ref.val, "shop_path", tmp);
JS_SetPropertyStr(ctx, env_ref.val, "actorsym", JS_DupValue(ctx, cli_rt->actor_sym_ref.val));
tmp = js_json_use(ctx);
JS_SetPropertyStr(ctx, env_ref.val, "json", tmp);
tmp = js_nota_use(ctx);
JS_SetPropertyStr(ctx, env_ref.val, "nota", tmp);
tmp = js_wota_use(ctx);
JS_SetPropertyStr(ctx, env_ref.val, "wota", tmp);
JS_SetPropertyStr(ctx, env_ref.val, "init", JS_NULL);
JSGCRef args_ref;
JS_AddGCRef(ctx, &args_ref);
args_ref.val = 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_ArrayPush(ctx, &args_ref.val, str);
}
JS_SetPropertyStr(ctx, hidden_env, "args", args_arr);
hidden_env = JS_Stone(ctx, hidden_env);
JS_SetPropertyStr(ctx, env_ref.val, "args", args_ref.val);
JS_DeleteGCRef(ctx, &args_ref);
JSValue hidden_env = JS_Stone(ctx, env_ref.val);
JS_DeleteGCRef(ctx, &env_ref);
JSValue result = JS_RunMachBin(ctx, (const uint8_t *)bin_data, bin_size, hidden_env);
free(bin_data);

View File

@@ -156,6 +156,43 @@ JS_SetClassProto(js, js_##TYPE##_id, TYPE##_proto); \
#define countof(x) (sizeof(x)/sizeof((x)[0]))
/* GC safety macros for C functions that allocate multiple heap objects.
Any allocation call (JS_NewObject, JS_SetPropertyStr, etc.) can trigger GC.
JS_ROOT style: explicit, use .val to access the rooted value.
JS_LOCAL style: transparent, GC updates the C local through a pointer. */
#define JS_FRAME(ctx) \
JSContext *_js_ctx = (ctx); \
JSGCRef *_js_gc_frame = JS_GetGCFrame(_js_ctx); \
JSLocalRef *_js_local_frame = JS_GetLocalFrame(_js_ctx)
#define JS_ROOT(name, init) \
JSGCRef name; \
JS_PushGCRef(_js_ctx, &name); \
name.val = (init)
#define JS_LOCAL(name, init) \
JSValue name = (init); \
JSLocalRef name##__lr; \
name##__lr.ptr = &name; \
JS_PushLocalRef(_js_ctx, &name##__lr)
#define JS_RETURN(val) do { \
JSValue _js_ret = (val); \
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame); \
return _js_ret; \
} while (0)
#define JS_RETURN_NULL() do { \
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame); \
return JS_NULL; \
} while (0)
#define JS_RETURN_EX() do { \
JS_RestoreFrame(_js_ctx, _js_gc_frame, _js_local_frame); \
return JS_EXCEPTION; \
} while (0)
// Common macros for property access
#define JS_GETPROP(JS, TARGET, VALUE, PROP, TYPE) {\
JSValue __##PROP##__v = JS_GetPropertyStr(JS,VALUE,#PROP); \

View File

@@ -1127,6 +1127,17 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
result = frame->slots[a];
if (JS_IsNull(frame->caller)) goto done;
{
#ifdef VALIDATE_GC
const char *callee_name = "?";
const char *callee_file = "?";
{
JSFunction *callee_fn = JS_VALUE_GET_FUNCTION(frame->function);
if (callee_fn->kind == JS_FUNC_KIND_REGISTER && callee_fn->u.reg.code) {
if (callee_fn->u.reg.code->name_cstr) callee_name = callee_fn->u.reg.code->name_cstr;
if (callee_fn->u.reg.code->filename_cstr) callee_file = callee_fn->u.reg.code->filename_cstr;
}
}
#endif
JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
frame->caller = JS_NULL;
frame = caller;
@@ -1143,8 +1154,11 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
void *rp = JS_VALUE_GET_PTR(result);
if ((uint8_t *)rp < ctx->heap_base || (uint8_t *)rp >= ctx->heap_free) {
if (!is_ct_ptr(ctx, rp))
fprintf(stderr, "VALIDATE_GC: stale RETURN into slot %d, ptr=%p heap=[%p,%p) fn_slots=%d pc=%u\n",
ret_slot, rp, (void*)ctx->heap_base, (void*)ctx->heap_free, code->nr_slots, pc);
fprintf(stderr, "VALIDATE_GC: stale RETURN into slot %d, ptr=%p heap=[%p,%p) fn_slots=%d pc=%u callee=%s (%s) caller=%s (%s)\n",
ret_slot, rp, (void*)ctx->heap_base, (void*)ctx->heap_free, code->nr_slots, pc,
callee_name, callee_file,
code->name_cstr ? code->name_cstr : "?",
code->filename_cstr ? code->filename_cstr : "?");
}
}
#endif
@@ -1617,9 +1631,15 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
if (JS_IsPtr(ret)) {
void *rp = JS_VALUE_GET_PTR(ret);
if ((uint8_t *)rp < ctx->heap_base || (uint8_t *)rp >= ctx->heap_free) {
if (!is_ct_ptr(ctx, rp))
fprintf(stderr, "VALIDATE_GC: stale INVOKE result into slot %d, ptr=%p heap=[%p,%p) fn_slots=%d pc=%u kind=%d\n",
b, rp, (void*)ctx->heap_base, (void*)ctx->heap_free, code->nr_slots, pc - 1, fn->kind);
if (!is_ct_ptr(ctx, rp)) {
int magic = (fn->kind == JS_FUNC_KIND_C) ? fn->u.cfunc.magic : -1;
void *cfp = (fn->kind == JS_FUNC_KIND_C) ? (void *)fn->u.cfunc.c_function.generic : NULL;
fprintf(stderr, "VALIDATE_GC: stale INVOKE result into slot %d, ptr=%p heap=[%p,%p) fn_slots=%d pc=%u kind=%d magic=%d cfunc=%p caller=%s (%s)\n",
b, rp, (void*)ctx->heap_base, (void*)ctx->heap_free, code->nr_slots, pc - 1, fn->kind,
magic, cfp,
code->name_cstr ? code->name_cstr : "?",
code->filename_cstr ? code->filename_cstr : "?");
}
}
}
#endif

View File

@@ -156,7 +156,8 @@ static const JSCFunctionListEntry js_actor_funcs[] = {
};
JSValue js_actor_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_actor_funcs,countof(js_actor_funcs));
return mod;
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_actor_funcs, countof(js_actor_funcs));
JS_RETURN(mod.val);
}

View File

@@ -1092,6 +1092,7 @@ struct JSContext {
JSGCRef *top_gc_ref; /* used to reference temporary GC roots (stack top) */
JSGCRef *last_gc_ref; /* used to reference temporary GC roots (list) */
JSLocalRef *top_local_ref; /* for JS_LOCAL macro - GC updates C locals through pointers */
CCallRoot *c_call_root; /* stack of auto-rooted C call argv arrays */
int class_count; /* size of class_array and class_proto */
@@ -1185,7 +1186,15 @@ static int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size);
/* Helper to check if a pointer is in constant text pool memory */
static inline int is_ct_ptr (JSContext *ctx, void *ptr) {
return (uint8_t *)ptr >= ctx->ct_base && (uint8_t *)ptr < ctx->ct_end;
uint8_t *p = (uint8_t *)ptr;
if (p >= ctx->ct_base && p < ctx->ct_end) return 1;
/* Also check overflow pages */
CTPage *page = (CTPage *)ctx->ct_pages;
while (page) {
if (p >= page->data && p < page->data + page->size) return 1;
page = page->next;
}
return 0;
}
#ifdef HEAP_CHECK

View File

@@ -146,10 +146,22 @@ typedef struct JSGCRef {
struct JSGCRef *prev;
} JSGCRef;
/* JSLocalRef - GC updates C locals through pointers (OCaml-style) */
typedef struct JSLocalRef {
JSValue *ptr;
struct JSLocalRef *prev;
} JSLocalRef;
/* stack of JSGCRef */
JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref);
JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref);
/* JS_FRAME/JS_ROOT/JS_LOCAL helpers (for use from cell.h macros) */
JSGCRef *JS_GetGCFrame(JSContext *ctx);
JSLocalRef *JS_GetLocalFrame(JSContext *ctx);
void JS_PushLocalRef(JSContext *ctx, JSLocalRef *ref);
void JS_RestoreFrame(JSContext *ctx, JSGCRef *gc_frame, JSLocalRef *local_frame);
#define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0)
#define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref)

View File

@@ -183,6 +183,25 @@ void JS_DeleteGCRef (JSContext *ctx, JSGCRef *ref) {
}
}
/* JS_FRAME/JS_ROOT/JS_LOCAL helper functions */
JSGCRef *JS_GetGCFrame (JSContext *ctx) {
return ctx->top_gc_ref;
}
JSLocalRef *JS_GetLocalFrame (JSContext *ctx) {
return ctx->top_local_ref;
}
void JS_PushLocalRef (JSContext *ctx, JSLocalRef *ref) {
ref->prev = ctx->top_local_ref;
ctx->top_local_ref = ref;
}
void JS_RestoreFrame (JSContext *ctx, JSGCRef *gc_frame, JSLocalRef *local_frame) {
ctx->top_gc_ref = gc_frame;
ctx->top_local_ref = local_frame;
}
void *ct_alloc (JSContext *ctx, size_t bytes, size_t align) {
/* Align the request */
bytes = (bytes + align - 1) & ~(align - 1);
@@ -1608,6 +1627,14 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
ref->val = gc_copy_value (ctx, ref->val, from_base, from_end, to_base, &to_free, to_end);
}
/* Copy JS_LOCAL roots (update C locals through pointers) */
#ifdef DUMP_GC_DETAIL
printf(" roots: top_local_ref\n"); fflush(stdout);
#endif
for (JSLocalRef *ref = ctx->top_local_ref; ref != NULL; ref = ref->prev) {
*ref->ptr = gc_copy_value (ctx, *ref->ptr, from_base, from_end, to_base, &to_free, to_end);
}
/* Copy JS_AddGCRef/JS_DeleteGCRef roots */
#ifdef DUMP_GC_DETAIL
printf(" roots: last_gc_ref\n"); fflush(stdout);
@@ -4383,6 +4410,10 @@ JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj,
ctx->trace_hook (ctx, JS_HOOK_CALL, &dbg, ctx->trace_data);
}
#ifdef VALIDATE_GC
uint8_t *pre_heap_base = ctx->heap_base;
#endif
switch (cproto) {
case JS_CFUNC_generic:
ret_val = func.generic (ctx, this_obj, argc, arg_copy);
@@ -4449,6 +4480,21 @@ JSValue js_call_c_function (JSContext *ctx, JSValue func_obj, JSValue this_obj,
abort ();
}
#ifdef VALIDATE_GC
if (ctx->heap_base != pre_heap_base && JS_IsPtr (ret_val)) {
void *rp = JS_VALUE_GET_PTR (ret_val);
if (!is_ct_ptr (ctx, rp) &&
((uint8_t *)rp < ctx->heap_base || (uint8_t *)rp >= ctx->heap_free)) {
/* Note: f is stale after GC (func_obj was passed by value), so we
cannot read f->name here. Just report the pointer. */
fprintf (stderr, "VALIDATE_GC: C function returned stale ptr=%p "
"heap=[%p,%p) after GC\n", rp,
(void *)ctx->heap_base, (void *)ctx->heap_free);
fflush (stderr);
}
}
#endif
ctx->c_call_root = root.prev;
if (unlikely (ctx->trace_hook) && (ctx->trace_type & JS_HOOK_RET))
@@ -5356,19 +5402,27 @@ static JSValue cjson_to_jsvalue (JSContext *ctx, const cJSON *item) {
if (cJSON_IsString (item)) return JS_NewString (ctx, item->valuestring);
if (cJSON_IsArray (item)) {
int n = cJSON_GetArraySize (item);
JSValue arr = JS_NewArrayLen (ctx,n);
JSGCRef arr_ref;
JS_AddGCRef (ctx, &arr_ref);
arr_ref.val = JS_NewArrayLen (ctx, n);
for (int i = 0; i < n; i++) {
cJSON *child = cJSON_GetArrayItem (item, i);
JS_SetPropertyNumber (ctx, arr, i, cjson_to_jsvalue (ctx, child));
JS_SetPropertyNumber (ctx, arr_ref.val, i, cjson_to_jsvalue (ctx, child));
}
return arr;
JSValue result = arr_ref.val;
JS_DeleteGCRef (ctx, &arr_ref);
return result;
}
if (cJSON_IsObject (item)) {
JSValue obj = JS_NewObject (ctx);
JSGCRef obj_ref;
JS_AddGCRef (ctx, &obj_ref);
obj_ref.val = JS_NewObject (ctx);
for (cJSON *child = item->child; child; child = child->next) {
JS_SetPropertyStr (ctx, obj, child->string, cjson_to_jsvalue (ctx, child));
JS_SetPropertyStr (ctx, obj_ref.val, child->string, cjson_to_jsvalue (ctx, child));
}
return obj;
JSValue result = obj_ref.val;
JS_DeleteGCRef (ctx, &obj_ref);
return result;
}
return JS_NULL;
}
@@ -11879,9 +11933,13 @@ static const JSCFunctionListEntry js_math_radians_funcs[]
JS_CFUNC_DEF ("e", 1, js_math_e) };
JSValue js_math_radians_use (JSContext *ctx) {
JSValue obj = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj, js_math_radians_funcs, countof (js_math_radians_funcs));
return obj;
JSGCRef obj_ref;
JS_PushGCRef (ctx, &obj_ref);
obj_ref.val = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_radians_funcs, countof (js_math_radians_funcs));
JSValue result = obj_ref.val;
JS_PopGCRef (ctx, &obj_ref);
return result;
}
/* ============================================================================
@@ -11945,9 +12003,13 @@ static const JSCFunctionListEntry js_math_degrees_funcs[]
JS_CFUNC_DEF ("e", 1, js_math_e) };
JSValue js_math_degrees_use (JSContext *ctx) {
JSValue obj = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj, js_math_degrees_funcs, countof (js_math_degrees_funcs));
return obj;
JSGCRef obj_ref;
JS_PushGCRef (ctx, &obj_ref);
obj_ref.val = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_degrees_funcs, countof (js_math_degrees_funcs));
JSValue result = obj_ref.val;
JS_PopGCRef (ctx, &obj_ref);
return result;
}
/* ============================================================================
@@ -12010,9 +12072,13 @@ static const JSCFunctionListEntry js_math_cycles_funcs[]
JS_CFUNC_DEF ("e", 1, js_math_e) };
JSValue js_math_cycles_use (JSContext *ctx) {
JSValue obj = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj, js_math_cycles_funcs, countof (js_math_cycles_funcs));
return obj;
JSGCRef obj_ref;
JS_PushGCRef (ctx, &obj_ref);
obj_ref.val = JS_NewObject (ctx);
JS_SetPropertyFunctionList (ctx, obj_ref.val, js_math_cycles_funcs, countof (js_math_cycles_funcs));
JSValue result = obj_ref.val;
JS_PopGCRef (ctx, &obj_ref);
return result;
}
/* Public API: get stack trace as cJSON array */
cJSON *JS_GetStack(JSContext *ctx) {

7
time.c
View File

@@ -71,12 +71,13 @@ static const JSCFunctionListEntry js_time_funcs[] = {
JSValue
js_internal_time_use(JSContext *ctx)
{
JSValue obj = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, obj,
JS_FRAME(ctx);
JS_ROOT(mod, JS_NewObject(ctx));
JS_SetPropertyFunctionList(ctx, mod.val,
js_time_funcs,
sizeof(js_time_funcs) /
sizeof(js_time_funcs[0]));
return obj;
JS_RETURN(mod.val);
}
JSValue

View File

@@ -29,7 +29,8 @@ static const JSCFunctionListEntry js_wildstar_funcs[] = {
};
JSValue js_wildstar_use(JSContext *js) {
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mod, js_wildstar_funcs, countof(js_wildstar_funcs));
return mod;
JS_FRAME(js);
JS_ROOT(mod, JS_NewObject(js));
JS_SetPropertyFunctionList(js, mod.val, js_wildstar_funcs, countof(js_wildstar_funcs));
JS_RETURN(mod.val);
}