no more special case for core C
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
// Hidden vars come from env:
|
||||
// CLI mode (cell_init): os, args, core_path, shop_path
|
||||
// Actor spawn (script_startup): os, json, nota, wota, actorsym, init, core_path, shop_path
|
||||
// Actor spawn (script_startup): os, json, actorsym, init, core_path, shop_path
|
||||
// args[0] = script name, args[1..] = user args
|
||||
var load_internal = os.load_internal
|
||||
function use_embed(name) {
|
||||
return load_internal("js_" + name + "_use")
|
||||
return load_internal("js_core_" + name + "_use")
|
||||
}
|
||||
|
||||
var fd = use_embed('fd')
|
||||
var fd = use_embed('internal_fd')
|
||||
var json = use_embed('json')
|
||||
var crypto = use_embed('crypto')
|
||||
|
||||
@@ -23,7 +23,7 @@ function content_hash(content) {
|
||||
|
||||
function cache_path(hash) {
|
||||
if (!shop_path) return null
|
||||
return shop_path + '/build/' + hash + '.mach'
|
||||
return shop_path + '/build/' + hash
|
||||
}
|
||||
|
||||
function ensure_build_dir() {
|
||||
@@ -267,7 +267,7 @@ if (args != null) {
|
||||
// Actor spawn mode — load engine.cm with full actor env
|
||||
load_engine({
|
||||
os: os, actorsym: actorsym, init: init,
|
||||
core_path: core_path, shop_path: shop_path, json: json, nota: nota, wota: wota,
|
||||
core_path: core_path, shop_path: shop_path, json: json,
|
||||
analyze: analyze, run_ast_fn: run_ast, run_ast_noopt_fn: run_ast_noopt,
|
||||
use_cache: use_cache,
|
||||
content_hash: content_hash, cache_path: cache_path,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// Hidden vars (os, actorsym, init, core_path, shop_path, analyze, run_ast_fn, run_ast_noopt_fn, json, use_cache, content_hash, cache_path, ensure_build_dir, compile_to_blob_fn) come from env
|
||||
// In actor spawn mode, also: nota, wota
|
||||
var ACTORDATA = actorsym
|
||||
var SYSYM = '__SYSTEM__'
|
||||
|
||||
@@ -19,7 +18,7 @@ var ACTOR_EXT = '.ce'
|
||||
|
||||
var load_internal = os.load_internal
|
||||
function use_embed(name) {
|
||||
return load_internal("js_" + name + "_use")
|
||||
return load_internal("js_core_" + name + "_use")
|
||||
}
|
||||
|
||||
function logical(val1) {
|
||||
@@ -46,7 +45,7 @@ function ends_with(str, suffix) {
|
||||
return search(str, suffix, -length(suffix)) != null
|
||||
}
|
||||
|
||||
var fd = use_embed('fd')
|
||||
var fd = use_embed('internal_fd')
|
||||
var js = use_embed('js')
|
||||
|
||||
// core_path and shop_path come from env (bootstrap.cm passes them through)
|
||||
@@ -69,53 +68,17 @@ function use_core(path) {
|
||||
var result = null
|
||||
var script = null
|
||||
var ast = null
|
||||
var c_cache_key = null
|
||||
|
||||
// If C embed exists, register it so .cm modules can use('internal/<name>_c')
|
||||
if (sym) {
|
||||
c_cache_key = 'core/internal/' + path + '_c'
|
||||
if (!use_cache[c_cache_key])
|
||||
use_cache[c_cache_key] = sym
|
||||
}
|
||||
|
||||
// Build env: merge core_extras
|
||||
env = {use: use_core}
|
||||
arrfor(array(core_extras), function(k) { env[k] = core_extras[k] })
|
||||
|
||||
// Check for pre-compiled .cm.mach file first
|
||||
var mach_path = core_path + '/' + path + '.cm.mach'
|
||||
if (fd.is_file(mach_path)) {
|
||||
result = mach_load(fd.slurp(mach_path), env)
|
||||
use_cache[cache_key] = result
|
||||
return result
|
||||
}
|
||||
|
||||
// Check for .cm.mcode JSON IR
|
||||
var mcode_path = core_path + '/' + path + '.cm.mcode'
|
||||
var mcode_blob = null
|
||||
var hash = null
|
||||
var cached_path = null
|
||||
var mach_blob = null
|
||||
var source_blob = null
|
||||
if (fd.is_file(mcode_path)) {
|
||||
mcode_blob = fd.slurp(mcode_path)
|
||||
hash = content_hash(mcode_blob)
|
||||
cached_path = cache_path(hash)
|
||||
if (cached_path && fd.is_file(cached_path)) {
|
||||
result = mach_load(fd.slurp(cached_path), env)
|
||||
} else {
|
||||
mach_blob = mach_compile_mcode_bin('core:' + path, text(mcode_blob))
|
||||
if (cached_path) {
|
||||
ensure_build_dir()
|
||||
fd.slurpwrite(cached_path, mach_blob)
|
||||
}
|
||||
result = mach_load(mach_blob, env)
|
||||
}
|
||||
use_cache[cache_key] = result
|
||||
return result
|
||||
}
|
||||
|
||||
// Fall back to source .cm file — compile at runtime
|
||||
// Compile from source .cm file
|
||||
var file_path = core_path + '/' + path + MOD_EXT
|
||||
if (fd.is_file(file_path)) {
|
||||
source_blob = fd.slurp(file_path)
|
||||
|
||||
759
internal/fd.c
Normal file
759
internal/fd.c
Normal file
@@ -0,0 +1,759 @@
|
||||
#include "cell.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#define mkdir(path, mode) _mkdir(path)
|
||||
#define rmdir _rmdir
|
||||
#define unlink _unlink
|
||||
#define getcwd _getcwd
|
||||
#define PATH_MAX _MAX_PATH
|
||||
#define fsync(fd) _commit(fd)
|
||||
#define S_ISLNK(m) 0
|
||||
#define S_ISSOCK(m) 0
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
// Helper to convert JS value to file descriptor
|
||||
static int js2fd(JSContext *ctx, JSValueConst val)
|
||||
{
|
||||
int fd;
|
||||
if (JS_ToInt32(ctx, &fd, val) < 0) {
|
||||
JS_ThrowTypeError(ctx, "Expected file descriptor number");
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Helper function for writing
|
||||
static ssize_t js_fd_write_helper(JSContext *js, int fd, JSValue val)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// POSIX FILE DESCRIPTOR FUNCTIONS
|
||||
JSC_SCALL(fd_open,
|
||||
int flags = O_RDWR | O_CREAT;
|
||||
mode_t mode = 0644;
|
||||
|
||||
// Parse optional flags argument
|
||||
if (argc > 1 && JS_IsText(argv[1])) {
|
||||
const char *flag_str = JS_ToCString(js, argv[1]);
|
||||
flags = 0;
|
||||
|
||||
if (strchr(flag_str, 'r')) flags |= O_RDONLY;
|
||||
if (strchr(flag_str, 'w')) flags |= O_WRONLY | O_CREAT | O_TRUNC;
|
||||
if (strchr(flag_str, 'a')) flags |= O_WRONLY | O_CREAT | O_APPEND;
|
||||
if (strchr(flag_str, '+')) {
|
||||
flags &= ~(O_RDONLY | O_WRONLY);
|
||||
flags |= O_RDWR;
|
||||
}
|
||||
|
||||
JS_FreeCString(js, flag_str);
|
||||
}
|
||||
|
||||
int fd = open(str, flags, mode);
|
||||
if (fd < 0)
|
||||
ret = JS_ThrowInternalError(js, "open failed: %s", strerror(errno));
|
||||
else
|
||||
ret = JS_NewInt32(js, fd);
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_write,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
size_t len;
|
||||
ssize_t wrote;
|
||||
if (JS_IsText(argv[1])) {
|
||||
const char *data = JS_ToCStringLen(js, &len, argv[1]);
|
||||
if (!data) return JS_EXCEPTION;
|
||||
wrote = write(fd, data, len);
|
||||
JS_FreeCString(js, data);
|
||||
} else {
|
||||
void *data = js_get_blob_data(js, &len, argv[1]);
|
||||
if (data == -1)
|
||||
return JS_EXCEPTION;
|
||||
wrote = write(fd, data, len);
|
||||
}
|
||||
|
||||
return JS_NewInt64(js, wrote);
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_read,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
size_t size = 4096;
|
||||
if (argc > 1)
|
||||
size = js2number(js, argv[1]);
|
||||
|
||||
void *buf = malloc(size);
|
||||
if (!buf)
|
||||
return JS_ThrowInternalError(js, "malloc failed");
|
||||
|
||||
ssize_t bytes_read = read(fd, buf, size);
|
||||
if (bytes_read < 0) {
|
||||
free(buf);
|
||||
return JS_ThrowInternalError(js, "read failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
ret = js_new_blob_stoned_copy(js, buf, bytes_read);
|
||||
free(buf);
|
||||
return ret;
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_slurp,
|
||||
struct stat st;
|
||||
if (stat(str, &st) != 0)
|
||||
return JS_ThrowInternalError(js, "stat failed: %s", strerror(errno));
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
return JS_ThrowTypeError(js, "path is not a regular file");
|
||||
|
||||
size_t size = st.st_size;
|
||||
if (size == 0)
|
||||
return js_new_blob_stoned_copy(js, NULL, 0);
|
||||
|
||||
#ifndef _WIN32
|
||||
int fd = open(str, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return JS_ThrowInternalError(js, "open failed: %s", strerror(errno));
|
||||
|
||||
void *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
close(fd);
|
||||
return JS_ThrowInternalError(js, "mmap failed: %s", strerror(errno));
|
||||
}
|
||||
ret = js_new_blob_stoned_copy(js, data, size);
|
||||
munmap(data, size);
|
||||
close(fd);
|
||||
#else
|
||||
// Windows: use memory mapping for optimal performance
|
||||
HANDLE hFile = CreateFileA(str, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return JS_ThrowInternalError(js, "CreateFile failed: %lu", GetLastError());
|
||||
|
||||
HANDLE hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
if (hMapping == NULL) {
|
||||
CloseHandle(hFile);
|
||||
return JS_ThrowInternalError(js, "CreateFileMapping failed: %lu", GetLastError());
|
||||
}
|
||||
|
||||
void *data = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
|
||||
if (data == NULL) {
|
||||
CloseHandle(hMapping);
|
||||
CloseHandle(hFile);
|
||||
return JS_ThrowInternalError(js, "MapViewOfFile failed: %lu", GetLastError());
|
||||
}
|
||||
|
||||
ret = js_new_blob_stoned_copy(js, data, size);
|
||||
UnmapViewOfFile(data);
|
||||
CloseHandle(hMapping);
|
||||
CloseHandle(hFile);
|
||||
#endif
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_lseek,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
off_t offset = js2number(js, argv[1]);
|
||||
int whence = SEEK_SET;
|
||||
|
||||
if (argc > 2) {
|
||||
const char *whence_str = JS_ToCString(js, argv[2]);
|
||||
if (strcmp(whence_str, "cur") == 0) whence = SEEK_CUR;
|
||||
else if (strcmp(whence_str, "end") == 0) whence = SEEK_END;
|
||||
JS_FreeCString(js, whence_str);
|
||||
}
|
||||
|
||||
off_t new_pos = lseek(fd, offset, whence);
|
||||
if (new_pos < 0)
|
||||
return JS_ThrowInternalError(js, "lseek failed: %s", strerror(errno));
|
||||
|
||||
return JS_NewInt64(js, new_pos);
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_getcwd,
|
||||
char buf[PATH_MAX];
|
||||
if (getcwd(buf, sizeof(buf)) == NULL)
|
||||
return JS_ThrowInternalError(js, "getcwd failed: %s", strerror(errno));
|
||||
return JS_NewString(js, buf);
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_rmdir,
|
||||
int recursive = 0;
|
||||
if (argc > 1)
|
||||
recursive = JS_ToBool(js, argv[1]);
|
||||
|
||||
if (recursive) {
|
||||
// Recursively remove directory contents first
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATAA ffd;
|
||||
char search_path[PATH_MAX];
|
||||
snprintf(search_path, sizeof(search_path), "%s\\*", str);
|
||||
HANDLE hFind = FindFirstFileA(search_path, &ffd);
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0)
|
||||
continue;
|
||||
char full_path[PATH_MAX];
|
||||
snprintf(full_path, sizeof(full_path), "%s\\%s", str, ffd.cFileName);
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
JSValue args[2] = { JS_NewString(js, full_path), JS_TRUE };
|
||||
JSValue result = js_fd_rmdir(js, JS_NULL, 2, args);
|
||||
JS_FreeValue(js, args[0]);
|
||||
if (JS_IsException(result)) {
|
||||
FindClose(hFind);
|
||||
return result;
|
||||
}
|
||||
JS_FreeValue(js, result);
|
||||
} else {
|
||||
if (unlink(full_path) != 0) {
|
||||
FindClose(hFind);
|
||||
ret = JS_ThrowInternalError(js, "could not remove file %s: %s", full_path, strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} while (FindNextFileA(hFind, &ffd));
|
||||
FindClose(hFind);
|
||||
}
|
||||
#else
|
||||
DIR *dir = opendir(str);
|
||||
if (dir) {
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
char full_path[PATH_MAX];
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", str, entry->d_name);
|
||||
struct stat st;
|
||||
if (lstat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
JSValue args[2] = { JS_NewString(js, full_path), JS_TRUE };
|
||||
JSValue result = js_fd_rmdir(js, JS_NULL, 2, args);
|
||||
JS_FreeValue(js, args[0]);
|
||||
if (JS_IsException(result)) {
|
||||
closedir(dir);
|
||||
return result;
|
||||
}
|
||||
JS_FreeValue(js, result);
|
||||
} else {
|
||||
if (unlink(full_path) != 0) {
|
||||
closedir(dir);
|
||||
ret = JS_ThrowInternalError(js, "could not remove file %s: %s", full_path, strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (rmdir(str) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not remove directory %s: %s", str, strerror(errno));
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_mkdir,
|
||||
if (mkdir(str, 0755) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not make directory %s: %s", str, strerror(errno));
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_mv,
|
||||
if (argc < 2)
|
||||
ret = JS_ThrowTypeError(js, "fd.mv requires 2 arguments: old path and new path");
|
||||
else if (!JS_IsText(argv[1]))
|
||||
ret = JS_ThrowTypeError(js, "second argument must be a string (new path)");
|
||||
else {
|
||||
const char *new_path = JS_ToCString(js, argv[1]);
|
||||
if (rename(str, new_path) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not rename %s to %s: %s", str, new_path, strerror(errno));
|
||||
JS_FreeCString(js, new_path);
|
||||
}
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_symlink,
|
||||
if (argc < 2)
|
||||
ret = JS_ThrowTypeError(js, "fd.symlink requires 2 arguments: target and link path");
|
||||
else if (!JS_IsText(argv[1]))
|
||||
ret = JS_ThrowTypeError(js, "second argument must be a string (link path)");
|
||||
else {
|
||||
const char *link_path = JS_ToCString(js, argv[1]);
|
||||
#ifdef _WIN32
|
||||
if (!CreateSymbolicLinkA(link_path, str, SYMBOLIC_LINK_FLAG_DIRECTORY)) {
|
||||
// Try file
|
||||
if (!CreateSymbolicLinkA(link_path, str, 0)) {
|
||||
ret = JS_ThrowInternalError(js, "could not create symlink %s -> %s: %lu", link_path, str, GetLastError());
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (symlink(str, link_path) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not create symlink %s -> %s: %s", link_path, str, strerror(errno));
|
||||
#endif
|
||||
JS_FreeCString(js, link_path);
|
||||
}
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_unlink,
|
||||
#ifdef _WIN32
|
||||
// On Windows, try to remove read-only attribute first
|
||||
DWORD attrs = GetFileAttributesA(str);
|
||||
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_READONLY)) {
|
||||
SetFileAttributesA(str, attrs & ~FILE_ATTRIBUTE_READONLY);
|
||||
}
|
||||
if (!DeleteFileA(str)) {
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_ACCESS_DENIED) {
|
||||
// Might be a directory, try RemoveDirectory
|
||||
if (!RemoveDirectoryA(str))
|
||||
ret = JS_ThrowInternalError(js, "could not remove %s: error %lu", str, GetLastError());
|
||||
} else {
|
||||
ret = JS_ThrowInternalError(js, "could not remove file %s: error %lu", str, err);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (unlink(str) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not remove file %s: %s", str, strerror(errno));
|
||||
#endif
|
||||
)
|
||||
|
||||
// Helper function for recursive removal
|
||||
static int remove_recursive(const char *path) {
|
||||
struct stat st;
|
||||
#ifdef _WIN32
|
||||
if (stat(path, &st) != 0)
|
||||
#else
|
||||
if (lstat(path, &st) != 0)
|
||||
#endif
|
||||
return -1;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
// Directory: remove contents first
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA ffd;
|
||||
char search_path[PATH_MAX];
|
||||
snprintf(search_path, sizeof(search_path), "%s\\*", path);
|
||||
HANDLE hFind = FindFirstFile(search_path, &ffd);
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue;
|
||||
char child_path[PATH_MAX];
|
||||
snprintf(child_path, sizeof(child_path), "%s\\%s", path, ffd.cFileName);
|
||||
if (remove_recursive(child_path) != 0) {
|
||||
FindClose(hFind);
|
||||
return -1;
|
||||
}
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
FindClose(hFind);
|
||||
}
|
||||
#else
|
||||
DIR *d = opendir(path);
|
||||
if (d) {
|
||||
struct dirent *dir;
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue;
|
||||
char child_path[PATH_MAX];
|
||||
snprintf(child_path, sizeof(child_path), "%s/%s", path, dir->d_name);
|
||||
if (remove_recursive(child_path) != 0) {
|
||||
closedir(d);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
#endif
|
||||
// Remove the now-empty directory
|
||||
return rmdir(path);
|
||||
} else {
|
||||
// File: just unlink
|
||||
return unlink(path);
|
||||
}
|
||||
}
|
||||
|
||||
JSC_SCALL(fd_rm,
|
||||
if (remove_recursive(str) != 0)
|
||||
ret = JS_ThrowInternalError(js, "could not remove %s: %s", str, strerror(errno));
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_fsync,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
if (fsync(fd) != 0)
|
||||
return JS_ThrowInternalError(js, "fsync failed: %s", strerror(errno));
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_close,
|
||||
int fd = js2fd(js, argv[0]);
|
||||
if (fd < 0) return JS_EXCEPTION;
|
||||
|
||||
if (close(fd) != 0)
|
||||
return JS_ThrowInternalError(js, "close failed: %s", strerror(errno));
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
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));
|
||||
#ifndef _WIN32
|
||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize));
|
||||
JS_SetPropertyStr(js, obj, "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));
|
||||
#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;
|
||||
)
|
||||
|
||||
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));
|
||||
#ifndef _WIN32
|
||||
JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize));
|
||||
JS_SetPropertyStr(js, obj, "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));
|
||||
#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_FreeCString(js, path);
|
||||
return obj;
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_readdir,
|
||||
#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);
|
||||
} 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);
|
||||
}
|
||||
#else
|
||||
DIR *d;
|
||||
struct dirent *dir;
|
||||
d = opendir(str);
|
||||
if (d) {
|
||||
ret = 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));
|
||||
}
|
||||
closedir(d);
|
||||
} else {
|
||||
ret = JS_ThrowInternalError(js, "opendir failed for %s: %s", str, strerror(errno));
|
||||
}
|
||||
#endif
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_is_file,
|
||||
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_NewBool(js, 0);
|
||||
}
|
||||
|
||||
JS_FreeCString(js, path);
|
||||
return JS_NewBool(js, S_ISREG(st.st_mode));
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_is_dir,
|
||||
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_NewBool(js, 0);
|
||||
}
|
||||
|
||||
JS_FreeCString(js, path);
|
||||
return JS_NewBool(js, S_ISDIR(st.st_mode));
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_slurpwrite,
|
||||
size_t len;
|
||||
const char *data = js_get_blob_data(js, &len, argv[1]);
|
||||
|
||||
if (!data && len > 0)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
|
||||
if (!str) return JS_EXCEPTION;
|
||||
int fd = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd < 0) {
|
||||
ret = JS_ThrowInternalError(js, "open failed for %s: %s", str, strerror(errno));
|
||||
JS_FreeCString(js, str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t written = write(fd, data, len);
|
||||
close(fd);
|
||||
|
||||
if (written != (ssize_t)len) {
|
||||
ret = JS_ThrowInternalError(js, "write failed for %s: %s", str, strerror(errno));
|
||||
JS_FreeCString(js, str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
JS_FreeCString(js, str);
|
||||
|
||||
return JS_NULL;
|
||||
)
|
||||
|
||||
// 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) {
|
||||
if (!curr_path) return;
|
||||
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA ffd;
|
||||
char search_path[PATH_MAX];
|
||||
snprintf(search_path, sizeof(search_path), "%s\\*", curr_path);
|
||||
HANDLE hFind = FindFirstFile(search_path, &ffd);
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue;
|
||||
char item_rel[PATH_MAX];
|
||||
if (rel_prefix && strlen(rel_prefix) > 0) {
|
||||
snprintf(item_rel, sizeof(item_rel), "%s/%s", rel_prefix, ffd.cFileName);
|
||||
} else {
|
||||
strcpy(item_rel, ffd.cFileName);
|
||||
}
|
||||
JS_SetPropertyNumber(js, results, (*result_count)++, JS_NewString(js, item_rel));
|
||||
|
||||
if (recurse) {
|
||||
struct stat st;
|
||||
char child_path[PATH_MAX];
|
||||
snprintf(child_path, sizeof(child_path), "%s\\%s", curr_path, ffd.cFileName);
|
||||
if (stat(child_path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
visit_directory(js, results, result_count, child_path, item_rel, recurse);
|
||||
}
|
||||
}
|
||||
} while (FindNextFile(hFind, &ffd) != 0);
|
||||
FindClose(hFind);
|
||||
}
|
||||
#else
|
||||
DIR *d = opendir(curr_path);
|
||||
if (d) {
|
||||
struct dirent *dir;
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue;
|
||||
char item_rel[PATH_MAX];
|
||||
if (rel_prefix && strlen(rel_prefix) > 0) {
|
||||
snprintf(item_rel, sizeof(item_rel), "%s/%s", rel_prefix, dir->d_name);
|
||||
} else {
|
||||
strcpy(item_rel, dir->d_name);
|
||||
}
|
||||
JS_SetPropertyNumber(js, results, (*result_count)++, JS_NewString(js, item_rel));
|
||||
|
||||
if (recurse) {
|
||||
struct stat st;
|
||||
char child_path[PATH_MAX];
|
||||
snprintf(child_path, sizeof(child_path), "%s/%s", curr_path, dir->d_name);
|
||||
if (stat(child_path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
visit_directory(js, results, result_count, child_path, item_rel, recurse);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JSC_SCALL(fd_enumerate,
|
||||
const char *path = str;
|
||||
if (!path) path = ".";
|
||||
int recurse = 0;
|
||||
|
||||
if (argc > 1)
|
||||
recurse = JS_ToBool(js, argv[1]);
|
||||
|
||||
JSValue results = 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);
|
||||
|
||||
ret = results;
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_realpath,
|
||||
const char *path = JS_ToCString(js, argv[0]);
|
||||
if (!path) return JS_EXCEPTION;
|
||||
|
||||
#ifdef _WIN32
|
||||
char resolved[PATH_MAX];
|
||||
DWORD len = GetFullPathNameA(path, PATH_MAX, resolved, NULL);
|
||||
if (len == 0 || len >= PATH_MAX) {
|
||||
JSValue err = JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno));
|
||||
JS_FreeCString(js, path);
|
||||
return err;
|
||||
}
|
||||
JS_FreeCString(js, path);
|
||||
return JS_NewString(js, resolved);
|
||||
#else
|
||||
char *resolved = realpath(path, NULL);
|
||||
if (!resolved) {
|
||||
JSValue err = JS_ThrowInternalError(js, "realpath failed for %s: %s", path, strerror(errno));
|
||||
JS_FreeCString(js, path);
|
||||
return err;
|
||||
}
|
||||
JS_FreeCString(js, path);
|
||||
JSValue result = JS_NewString(js, resolved);
|
||||
free(resolved);
|
||||
return result;
|
||||
#endif
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_is_link,
|
||||
const char *path = JS_ToCString(js, argv[0]);
|
||||
if (!path) return JS_EXCEPTION;
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD attrs = GetFileAttributesA(path);
|
||||
JS_FreeCString(js, path);
|
||||
int is_link = (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_REPARSE_POINT));
|
||||
return JS_NewBool(js, is_link);
|
||||
#else
|
||||
struct stat st;
|
||||
int is_link = (lstat(path, &st) == 0 && S_ISLNK(st.st_mode));
|
||||
JS_FreeCString(js, path);
|
||||
return JS_NewBool(js, is_link);
|
||||
#endif
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_readlink,
|
||||
const char *path = JS_ToCString(js, argv[0]);
|
||||
if (!path) return JS_EXCEPTION;
|
||||
|
||||
#ifdef _WIN32
|
||||
JS_FreeCString(js, path);
|
||||
return JS_ThrowInternalError(js, "readlink not supported on Windows");
|
||||
#else
|
||||
char buf[PATH_MAX];
|
||||
ssize_t len = readlink(path, buf, sizeof(buf) - 1);
|
||||
JS_FreeCString(js, path);
|
||||
if (len < 0) {
|
||||
return JS_ThrowInternalError(js, "readlink failed: %s", strerror(errno));
|
||||
}
|
||||
buf[len] = '\0';
|
||||
return JS_NewString(js, buf);
|
||||
#endif
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_fd_funcs[] = {
|
||||
MIST_FUNC_DEF(fd, open, 2),
|
||||
MIST_FUNC_DEF(fd, write, 2),
|
||||
MIST_FUNC_DEF(fd, read, 2),
|
||||
MIST_FUNC_DEF(fd, slurp, 1),
|
||||
MIST_FUNC_DEF(fd, slurpwrite, 2),
|
||||
MIST_FUNC_DEF(fd, lseek, 3),
|
||||
MIST_FUNC_DEF(fd, getcwd, 0),
|
||||
MIST_FUNC_DEF(fd, rmdir, 2),
|
||||
MIST_FUNC_DEF(fd, unlink, 1),
|
||||
MIST_FUNC_DEF(fd, mkdir, 1),
|
||||
MIST_FUNC_DEF(fd, mv, 2),
|
||||
MIST_FUNC_DEF(fd, rm, 1),
|
||||
MIST_FUNC_DEF(fd, fsync, 1),
|
||||
MIST_FUNC_DEF(fd, close, 1),
|
||||
MIST_FUNC_DEF(fd, stat, 1),
|
||||
MIST_FUNC_DEF(fd, fstat, 1),
|
||||
MIST_FUNC_DEF(fd, readdir, 1),
|
||||
MIST_FUNC_DEF(fd, is_file, 1),
|
||||
MIST_FUNC_DEF(fd, is_dir, 1),
|
||||
MIST_FUNC_DEF(fd, is_link, 1),
|
||||
MIST_FUNC_DEF(fd, enumerate, 2),
|
||||
MIST_FUNC_DEF(fd, symlink, 2),
|
||||
MIST_FUNC_DEF(fd, realpath, 1),
|
||||
MIST_FUNC_DEF(fd, readlink, 1),
|
||||
};
|
||||
|
||||
JSValue js_core_internal_fd_use(JSContext *js) {
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_fd_funcs, countof(js_fd_funcs));
|
||||
return mod;
|
||||
}
|
||||
@@ -73,7 +73,7 @@ static const JSCFunctionListEntry js_kim_funcs[] = {
|
||||
MIST_FUNC_DEF(kim, decode, 1),
|
||||
};
|
||||
|
||||
JSValue js_kim_use(JSContext *js)
|
||||
JSValue js_core_kim_use(JSContext *js)
|
||||
{
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_kim_funcs, countof(js_kim_funcs));
|
||||
|
||||
@@ -641,7 +641,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
|
||||
MIST_FUNC_DEF(os, getenv, 1),
|
||||
};
|
||||
|
||||
JSValue js_os_use(JSContext *js) {
|
||||
JSValue js_core_os_use(JSContext *js) {
|
||||
JS_NewClassID(&js_dylib_class_id);
|
||||
JS_NewClass(js, js_dylib_class_id, &js_dylib_class);
|
||||
|
||||
|
||||
@@ -479,8 +479,8 @@ function resolve_mod_fn(path, pkg) {
|
||||
return cached
|
||||
}
|
||||
|
||||
// Check for cached mcode in content-addressed store
|
||||
cached_mcode_path = hash_path(content_key) + '.mcode'
|
||||
// Check for cached mcode in content-addressed store (salted hash to distinguish from mach)
|
||||
cached_mcode_path = global_shop_path + '/build/' + content_hash(stone(blob(text(content_key) + "\nmcode")))
|
||||
if (fd.is_file(cached_mcode_path)) {
|
||||
mcode_json = text(fd.slurp(cached_mcode_path))
|
||||
compiled = mach_compile_mcode_bin(path, mcode_json)
|
||||
@@ -488,22 +488,6 @@ function resolve_mod_fn(path, pkg) {
|
||||
return compiled
|
||||
}
|
||||
|
||||
// Check for pre-compiled .mach or .mcode file alongside .cm source
|
||||
if (ends_with(path, '.cm')) {
|
||||
mach_path = text(path, 0, length(path) - 3) + '.mach'
|
||||
if (fd.is_file(mach_path)) {
|
||||
mach_blob = fd.slurp(mach_path)
|
||||
put_into_cache(content_key, mach_blob)
|
||||
return mach_blob
|
||||
}
|
||||
mcode_path = path + '.mcode'
|
||||
if (fd.is_file(mcode_path)) {
|
||||
compiled = mach_compile_mcode_bin(path, text(fd.slurp(mcode_path)))
|
||||
put_into_cache(content_key, compiled)
|
||||
return compiled
|
||||
}
|
||||
}
|
||||
|
||||
// Compile via full pipeline: analyze → mcode → streamline → serialize
|
||||
if (!_mcode_mod) _mcode_mod = Shop.use("mcode", null)
|
||||
if (!_streamline_mod) _streamline_mod = Shop.use("streamline", null)
|
||||
@@ -684,8 +668,7 @@ function resolve_c_symbol(path, package_context) {
|
||||
|
||||
// If no package context, only check core
|
||||
if (!package_context || package_context == 'core') {
|
||||
_path = replace(path, '/', '_')
|
||||
core_sym = `js_${_path}_use`
|
||||
core_sym = make_c_symbol('core', path)
|
||||
|
||||
// Check lib/ dylib first for core
|
||||
loader = try_dylib_symbol(core_sym, 'core', path)
|
||||
@@ -760,7 +743,7 @@ function resolve_c_symbol(path, package_context) {
|
||||
}
|
||||
|
||||
// 3. Check core (dylib first, then internal)
|
||||
core_sym = `js_${replace(path, '/', '_')}_use`
|
||||
core_sym = make_c_symbol('core', path)
|
||||
|
||||
loader = try_dylib_symbol(core_sym, 'core', path)
|
||||
if (loader) {
|
||||
|
||||
80
internal/time.c
Normal file
80
internal/time.c
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "cell.h"
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
/* ---------------------------------------------------------------- *\
|
||||
Helpers
|
||||
\* ---------------------------------------------------------------- */
|
||||
|
||||
/* Seconds from Misty epoch (year-0) so JS “number()” stays consistent.
|
||||
62167219200 = seconds between 0000-01-01 00:00 UTC and 1970-01-01 00:00 UTC. */
|
||||
static inline double misty_now(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (double)tv.tv_sec + tv.tv_usec / 1000000.0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- *\
|
||||
JS bindings
|
||||
\* ---------------------------------------------------------------- */
|
||||
|
||||
static JSValue
|
||||
js_time_now(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return JS_NewFloat64(ctx, misty_now());
|
||||
}
|
||||
|
||||
static JSValue
|
||||
js_time_computer_dst(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
struct tm *lt = localtime(&t);
|
||||
return JS_NewBool(ctx, lt && lt->tm_isdst > 0);
|
||||
}
|
||||
|
||||
static JSValue
|
||||
js_time_computer_zone(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
|
||||
#ifdef __USE_BSD /* tm_gmtoff / tm_zone available */
|
||||
struct tm lt = *localtime(&t);
|
||||
long offset_sec = lt.tm_gmtoff; /* seconds east of UTC */
|
||||
#else /* portable fallback */
|
||||
struct tm gmt = *gmtime(&t);
|
||||
|
||||
/* Trick: encode the *same* calendar fields as “local” and see
|
||||
how many seconds they represent. */
|
||||
gmt.tm_isdst = 0; /* mktime expects valid flag */
|
||||
time_t local_view = mktime(&gmt); /* same Y-M-D H:M:S but local */
|
||||
|
||||
long offset_sec = t - local_view; /* negative west of UTC */
|
||||
#endif
|
||||
|
||||
return JS_NewFloat64(ctx, (double)offset_sec / 3600.0);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- *\
|
||||
registration
|
||||
\* ---------------------------------------------------------------- */
|
||||
|
||||
static const JSCFunctionListEntry js_time_funcs[] = {
|
||||
JS_CFUNC_DEF("now", 0, js_time_now),
|
||||
JS_CFUNC_DEF("computer_dst", 0, js_time_computer_dst),
|
||||
JS_CFUNC_DEF("computer_zone", 0, js_time_computer_zone),
|
||||
};
|
||||
|
||||
JSValue
|
||||
js_core_internal_time_use(JSContext *ctx)
|
||||
{
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyFunctionList(ctx, obj,
|
||||
js_time_funcs,
|
||||
sizeof(js_time_funcs) /
|
||||
sizeof(js_time_funcs[0]));
|
||||
return obj;
|
||||
}
|
||||
Reference in New Issue
Block a user