ocaml style rooting macros
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
7
crypto.c
7
crypto.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
153
fd.c
@@ -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
7
fit.c
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)')
|
||||
|
||||
20
net/enet.c
20
net/enet.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
43
net/socket.c
43
net/socket.c
@@ -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
20
qop.c
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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); \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
7
time.c
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user