#include "cell.h"

#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <signal.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

#ifdef _WIN32
#include <windows.h>
#include <bcrypt.h>
#else
#include <unistd.h>
#include <sys/utsname.h>
#include <dlfcn.h>
#include <fcntl.h>
#ifdef __linux__
#include <sys/sysinfo.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <linux/sysctl.h>
#include <errno.h>
#endif
#endif

#ifdef __APPLE__
#include <sys/sysctl.h>
#endif

static JSClassID js_dylib_class_id;

static void js_dylib_finalizer(JSRuntime *rt, JSValue val) {
  void *handle = JS_GetOpaque(val, js_dylib_class_id);
  if (handle) {
#ifdef _WIN32
    FreeLibrary((HMODULE)handle);
#else
    dlclose(handle);
#endif
  }
}

static JSClassDef js_dylib_class = {
  "dylib",
  .finalizer = js_dylib_finalizer,
};

#ifndef _WIN32
#ifdef __APPLE__
#include <mach/mach_time.h>
#endif
#endif

uint64_t cell_ns()
{
#ifdef _WIN32
  LARGE_INTEGER frequency, counter;
  QueryPerformanceFrequency(&frequency);
  QueryPerformanceCounter(&counter);
  return (uint64_t)((double)counter.QuadPart / (double)frequency.QuadPart * 1e9);
#elif defined(__APPLE__)
  static mach_timebase_info_data_t timebase = {0, 0};
  if (timebase.denom == 0) {
    mach_timebase_info(&timebase);
  }
  uint64_t time = mach_absolute_time();
  return time * timebase.numer / timebase.denom;
#else
  struct timespec ts;
  clock_gettime(CLOCK_MONOTONIC, &ts);
  return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
#endif
}

JSC_CCALL(os_now, return number2js(js, cell_ns()); )

void cell_sleep(double seconds)
{
#ifdef _WIN32
  Sleep((DWORD)(seconds * 1000));
#else
  struct timespec ts;
  ts.tv_sec = (time_t)seconds;
  ts.tv_nsec = (long)((seconds - ts.tv_sec) * 1000000000);
  nanosleep(&ts, NULL);
#endif
}
JSC_CCALL(os_sleep,
  double secs = js2number(js,argv[0]);
  cell_sleep(secs);
)

static JSValue js_os_totalmem(JSContext *js, JSValue self, int argc, JSValue *argv) {
  JSValue ret = JS_NULL;
#ifdef _WIN32
  MEMORYSTATUSEX statex;
  statex.dwLength = sizeof(statex);
  if (!GlobalMemoryStatusEx(&statex)) return JS_ThrowInternalError(js,"GlobalMemoryStatusEx failed");
  return JS_NewInt64(js,(int64_t)(statex.ullTotalPhys / (1024 * 1024)));
#elif defined(__linux__)
  struct sysinfo info;
  if (sysinfo(&info) == 0)
    return JS_NewInt64(js,(int64_t)((info.totalram * info.mem_unit) / (1024 * 1024)));
  return JS_NewInt64(js,0);
#elif defined(__APPLE__)
  int mib[2] = {CTL_HW, HW_MEMSIZE};
  int64_t memsize;
  size_t len = sizeof(memsize);
  if (sysctl(mib, 2, &memsize, &len, NULL, 0) == 0)
    return JS_NewInt64(js, memsize / (1024 * 1024));
  return JS_NewInt64(js,0);
#else
  long pages = sysconf(_SC_PHYS_PAGES);
  long page_size = sysconf(_SC_PAGE_SIZE);
  if (pages < 0 || page_size < 0) return JS_NewInt64(js,0);
  return JS_NewInt64(js,(int64_t)((pages * page_size) / (1024 * 1024)));
#endif
  return ret;
}

static JSValue js_os_platform(JSContext *js, JSValue self, int argc, JSValue *argv) {
  JSValue ret = JS_NULL;
#ifdef _WIN32
  return JS_NewString(js,"Windows");
#elif defined(__linux__)
  return JS_NewString(js,"Linux");
#elif defined(__APPLE__)
  return JS_NewString(js,"macOS");
#elif defined(__FreeBSD__)
  return JS_NewString(js,"FreeBSD");
#elif defined(__OpenBSD__)
  return JS_NewString(js,"OpenBSD");
#elif defined(__NetBSD__)
  return JS_NewString(js,"NetBSD");
#else
  return JS_NewString(js,"Unknown");
#endif
  return ret;
}

static JSValue js_os_hostname(JSContext *js, JSValue self, int argc, JSValue *argv) {
  JSValue ret = JS_NULL;
#ifdef _WIN32
  TCHAR buffer[256] = TEXT("");
  DWORD size = sizeof(buffer) / sizeof(TCHAR);
  GetComputerName(buffer, &size);
  return JS_NewString(js, buffer);
#else
  struct utsname buffer;
  if (uname(&buffer) != 0) return JS_ThrowReferenceError(js,"Could not get hostname.");
  return JS_NewString(js, buffer.nodename);
#endif
  return ret;
}

static JSValue js_os_arch(JSContext *js, JSValue self, int argc, JSValue *argv) {
  JSValue ret = JS_NULL;
#if defined(__x86_64__) || defined(_M_X64)
  return JS_NewString(js,"x64");
#elif defined(__aarch64__) || defined(_M_ARM64)
  return JS_NewString(js,"arm64");
#elif defined(__arm__) || defined(_M_ARM)
  return JS_NewString(js,"arm");
#elif defined(__i386__) || defined(_M_IX86)
  return JS_NewString(js,"ia32");
#elif defined(__loongarch__) || defined(__loongarch32) || defined(__loongarch64)
  return JS_NewString(js,"loong64");
#elif defined(__mips__) || defined(__mips) || defined(_M_MIPS)
  // You might want to distinguish mips vs mipsel
  return JS_NewString(js,"mips");
#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_M_PPC)
  // You might want to distinguish ppc vs ppc64, big-endian vs little-endian
  return JS_NewString(js,"ppc64");
#elif defined(__riscv) && __riscv_xlen == 64
  return JS_NewString(js,"riscv64");
#elif defined(__s390x__)
  return JS_NewString(js,"s390x");
#else
  return JS_NewString(js,"unknown");
#endif
  return ret;
}

static JSValue js_os_freemem(JSContext *js, JSValue self, int argc, JSValue *argv) {
  JSValue ret = JS_NULL;
#ifdef _WIN32
  MEMORYSTATUSEX statex;
  statex.dwLength = sizeof(statex);
  if (!GlobalMemoryStatusEx(&statex)) return JS_ThrowInternalError(js,"GlobalMemoryStatusEx failed");
  return JS_NewInt64(js,(int64_t)statex.ullAvailPhys);
#elif defined(__linux__)
  struct sysinfo info;
  if (sysinfo(&info) == 0)
    return JS_NewInt64(js,(int64_t)info.freeram * info.mem_unit);
  return JS_NewInt64(js,0);
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
  // A very rough fallback using the same sysconf approach
  // (macOS or *BSD typically need specialized APIs to get free mem accurately)
  // This is often only "unused" pages, ignoring caches, etc.
  long pages = sysconf(_SC_AVPHYS_PAGES);
  long page_size = sysconf(_SC_PAGE_SIZE);
  if (pages < 0 || page_size < 0) return JS_NewInt64(js,0);
  return JS_NewInt64(js,(int64_t)pages * (int64_t)page_size);
#else
  // Fallback: unknown
  return JS_NewInt64(js,0);
#endif
  return ret;
}

static JSValue js_os_version(JSContext *js, JSValue self, int argc, JSValue *argv) {
  JSValue ret = JS_NULL;
#ifdef _WIN32
  typedef LONG (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
  HMODULE h = GetModuleHandleA("ntdll.dll");
  if (h) {
    RtlGetVersionPtr fx = (RtlGetVersionPtr)GetProcAddress(h, "RtlGetVersion");
    if (fx) {
      RTL_OSVERSIONINFOW ver;
      memset(&ver, 0, sizeof(ver));
      ver.dwOSVersionInfoSize = sizeof(ver);
      if (!fx(&ver)) {
        char buf[128];
        sprintf(buf, "%u.%u.%u",
          (unsigned)ver.dwMajorVersion,
          (unsigned)ver.dwMinorVersion,
          (unsigned)ver.dwBuildNumber
        );
        return JS_NewString(js, buf);
      }
    }
  }
  OSVERSIONINFOW wver;
  memset(&wver, 0, sizeof(wver));
  wver.dwOSVersionInfoSize = sizeof(wver);
  if (GetVersionExW(&wver)) {
    char buf[128];
    sprintf(buf, "%u.%u.%u",
      (unsigned)wver.dwMajorVersion,
      (unsigned)wver.dwMinorVersion,
      (unsigned)wver.dwBuildNumber
    );
    return JS_NewString(js, buf);
  }
  return JS_NewString(js, "Windows_Unknown");
#else
  struct utsname info;
  if (!uname(&info)) return JS_NewString(js, info.release);
  return JS_NewString(js, "");
#endif
  return ret;
}


#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \
JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\

#define JSJMEMRET(FIELD) JSOBJ_ADD_FIELD(ret, jsmem, FIELD, number)

JSC_CCALL(os_mallinfo,
  ret = JS_NULL;
  /*struct mallinfo jsmem = mallinfo();
  ret = JS_NewObject(js);
  JSJMEMRET(arena);
  JSJMEMRET(ordblks);
  JSJMEMRET(smblks);
  JSJMEMRET(hblks);
  JSJMEMRET(hblkhd);
  JSJMEMRET(usmblks);
  JSJMEMRET(fsmblks);
  JSJMEMRET(uordblks);
  JSJMEMRET(fordblks);
  JSJMEMRET(keepcost);*/
)

static JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) {
  JS_FRAME(js);
  JS_ROOT(ret, JS_NewObject(js));

#if defined(__linux__) || defined(__APPLE__)
  struct rusage jsmem;
  getrusage(RUSAGE_SELF, &jsmem);
#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

  JS_RETURN(ret.val);
}

JSC_SCALL(os_system,
  int err = system(str);
  ret = number2js(js,err);
)

JSC_CCALL(os_exit,
  exit(0);
)

static JSValue js_os_dylib_open(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  if (argc < 1) {
    return JS_ThrowTypeError(js, "dylib_open requires a path argument");
  }

  const char *path = JS_ToCString(js, argv[0]);
  if (!path) {
    return JS_ThrowTypeError(js, "path must be a string");
  }

  void *handle;
#ifdef _WIN32
  handle = LoadLibraryA(path);
#else
  handle = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
#endif

  JS_FreeCString(js, path);

  if (!handle) {
    const char *error_msg = "Could not load library";
#ifndef _WIN32
    const char *dl_error = dlerror();
    if (dl_error) {
      error_msg = dl_error;
    }
#endif
    return JS_ThrowReferenceError(js, "Failed to load library: %s", error_msg);
  }

  JSValue dylib_obj = JS_NewObjectClass(js, js_dylib_class_id);
  if (JS_IsException(dylib_obj)) {
#ifdef _WIN32
    FreeLibrary((HMODULE)handle);
#else
    dlclose(handle);
#endif
    return dylib_obj;
  }

  JS_SetOpaque(dylib_obj, handle);
  return dylib_obj;
}

static JSValue js_os_dylib_preload(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  if (argc < 1)
    return JS_NULL;

  const char *path = JS_ToCString(js, argv[0]);
  if (!path)
    return JS_NULL;

  void *handle;
#ifdef _WIN32
  handle = LoadLibraryA(path);
#else
  handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
#endif

  JS_FreeCString(js, path);

  if (!handle)
    return JS_NULL;

  JSValue dylib_obj = JS_NewObjectClass(js, js_dylib_class_id);
  if (JS_IsException(dylib_obj)) {
#ifdef _WIN32
    FreeLibrary((HMODULE)handle);
#else
    dlclose(handle);
#endif
    return JS_NULL;
  }

  JS_SetOpaque(dylib_obj, handle);
  return dylib_obj;
}

static JSValue js_os_dylib_symbol(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  if (argc < 2) {
    return JS_ThrowTypeError(js, "dylib_symbol requires dylib object and symbol name");
  }

  void *handle = JS_GetOpaque(argv[0], js_dylib_class_id);
  if (!handle) {
    return JS_ThrowTypeError(js, "First argument must be a dylib object");
  }

  const char *symbol_name = JS_ToCString(js, argv[1]);
  if (!symbol_name) {
    return JS_ThrowTypeError(js, "symbol name must be a string");
  }

  JSValue (*symbol)(JSContext *js);
#ifdef _WIN32
  symbol = GetProcAddress((HMODULE)handle, symbol_name);
#else
  symbol = dlsym(handle, symbol_name);
#endif

  JS_FreeCString(js, symbol_name);

  if (!symbol) {
    const char *error_msg = "Symbol not found";
#ifndef _WIN32
    const char *dl_error = dlerror();
    if (dl_error) {
      error_msg = dl_error;
    }
#endif
    return JS_ThrowReferenceError(js, "Failed to load symbol: %s", error_msg);
    return JS_NULL;
  }

  // Return the symbol as a pointer value
  return symbol(js);
}

static JSValue js_os_dylib_has_symbol(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  if (argc < 2) {
    return JS_ThrowTypeError(js, "dylib_has_symbol requires dylib object and symbol name");
  }

  void *handle = JS_GetOpaque(argv[0], js_dylib_class_id);
  if (!handle) {
    return JS_ThrowTypeError(js, "First argument must be a dylib object");
  }

  const char *symbol_name = JS_ToCString(js, argv[1]);
  if (!symbol_name) {
    return JS_ThrowTypeError(js, "symbol name must be a string");
  }

  void *symbol;
#ifdef _WIN32
  symbol = (void*)GetProcAddress((HMODULE)handle, symbol_name);
#else
  symbol = dlsym(handle, symbol_name);
#endif

  JS_FreeCString(js, symbol_name);

  return JS_NewBool(js, symbol != NULL);
}

static JSValue js_os_dylib_close(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  if (argc < 1)
    return JS_ThrowTypeError(js, "dylib_close requires a dylib object");
  void *handle = JS_GetOpaque(argv[0], js_dylib_class_id);
  if (handle) {
#ifdef _WIN32
    FreeLibrary((HMODULE)handle);
#else
    dlclose(handle);
#endif
    JS_SetOpaque(argv[0], NULL);
  }
  return JS_NULL;
}

/* Load a native .cm module from a dylib handle.
   Uses cell_rt_native_module_load from qbe_helpers.c */
extern JSValue cell_rt_native_module_load(JSContext *ctx, void *dl_handle, JSValue env);
extern JSValue cell_rt_native_module_load_named(JSContext *ctx, void *dl_handle, const char *sym_name, JSValue env);
extern JSValue js_os_qbe(JSContext *, JSValue, int, JSValue *);

static JSValue js_os_native_module_load(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  if (argc < 1)
    return JS_ThrowTypeError(js, "native_module_load requires a dylib object");

  void *handle = JS_GetOpaque(argv[0], js_dylib_class_id);
  if (!handle)
    return JS_ThrowTypeError(js, "First argument must be a dylib object");

  JSValue env = (argc >= 2) ? argv[1] : JS_NULL;
  return cell_rt_native_module_load(js, handle, env);
}

static JSValue js_os_native_module_load_named(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  if (argc < 2)
    return JS_ThrowTypeError(js, "native_module_load_named requires (dylib, sym_name)");

  void *handle = JS_GetOpaque(argv[0], js_dylib_class_id);
  if (!handle)
    return JS_ThrowTypeError(js, "First argument must be a dylib object");

  const char *sym_name = JS_ToCString(js, argv[1]);
  if (!sym_name)
    return JS_EXCEPTION;

  JSValue env = (argc >= 3) ? argv[2] : JS_NULL;
  JSValue result = cell_rt_native_module_load_named(js, handle, sym_name, env);
  JS_FreeCString(js, sym_name);
  return result;
}

JSC_CCALL(os_print,
    size_t len;
    const char *str = JS_ToCStringLen(js, &len, argv[0]);
    printf("%.*s", (int)len, str);
    JS_FreeCString(js, str);
)

static JSValue js_os_load_internal(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  void *handle;
#ifdef _WIN32
  handle = GetModuleHandle(NULL);
#else
  handle = dlopen(NULL, RTLD_LAZY);
#endif
  if (argc < 1)
    return JS_ThrowTypeError(js, "load_internal requires a symbol name");

  const char *symbol_name = JS_ToCString(js, argv[0]);
  if (!symbol_name)
    return JS_ThrowTypeError(js, "symbol name must be a string");

  JSValue (*symbol)(JSContext *js);
#if defined(_WIN32)
  symbol = GetProcAddress((HMODULE)handle, symbol_name);
#else
  symbol = dlsym(handle, symbol_name);
#endif

  JS_FreeCString(js, symbol_name);

  if (!symbol)
    return JS_NULL;

  return symbol(js);
}

static JSValue js_os_internal_exists(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  void *handle;
#ifdef _WIN32
  handle = GetModuleHandle(NULL);
#else
  handle = dlopen(NULL, RTLD_LAZY);
#endif
  if (argc < 1)
    return JS_ThrowTypeError(js, "internal_exists requires a symbol name");

  const char *symbol_name = JS_ToCString(js, argv[0]);
  if (!symbol_name)
    return JS_ThrowTypeError(js, "symbol name must be a string");

  void *symbol;
#if defined(_WIN32)
  symbol = (void*)GetProcAddress((HMODULE)handle, symbol_name);
#else
  symbol = dlsym(handle, symbol_name);
#endif

  JS_FreeCString(js, symbol_name);

  return JS_NewBool(js, symbol != NULL);
}

#if defined(_WIN32)
// ------- Windows: use BCryptGenRandom -------
int randombytes(void *buf, size_t n) {
    NTSTATUS status = BCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)n, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
    return (status == 0) ? 0 : -1;
}

#elif defined(__linux__)
// ------- Linux: try getrandom, fall back to /dev/urandom -------
static int randombytes_fallback(void *buf, size_t n) {
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd < 0) return -1;
    ssize_t r = read(fd, buf, n);
    close(fd);
    return (r == (ssize_t)n) ? 0 : -1;
}

int randombytes(void *buf, size_t n) {
#ifdef SYS_getrandom
    // Try getrandom(2) if available
    ssize_t ret = syscall(SYS_getrandom, buf, n, 0);
    if (ret < 0) {
        // If getrandom is not supported or fails, fall back
        if (errno == ENOSYS) {
            return randombytes_fallback(buf, n);
        }
        return -1;
    }
    return (ret == (ssize_t)n) ? 0 : -1;
#else
    // getrandom not available, just fallback
    return randombytes_fallback(buf, n);
#endif
}

#else
// ------- Other Unix: read from /dev/urandom -------
static int rand_fd = -1;
int randombytes(void *buf, size_t n) {
    if (rand_fd < 0) {
        rand_fd = open("/dev/urandom", O_RDONLY);
        if (rand_fd < 0) return -1;
    }
    ssize_t r = read(rand_fd, buf, n);
    return (r == (ssize_t)n) ? 0 : -1;
}
#endif

JSC_CCALL(os_random,
  return JS_NewFloat64(js, cell_random_fit());
)

JSC_CCALL(os_getenv,
  const char *name = JS_ToCString(js, argv[0]);
  if (!name) return JS_EXCEPTION;
  const char *value = getenv(name);
  JS_FreeCString(js, name);
  if (value)
    ret = JS_NewString(js, value);
  else
    ret = JS_NULL;
)

/* --- Embedded module table (generated for static builds) ---
   Uses dlsym to check if cell_embedded_module_lookup exists at runtime.
   When linking a static build with a generated module_table.c, the symbol
   will be found; in dynamic builds it returns NULL gracefully. */

struct cell_embedded_entry {
  const char *name;
  const unsigned char *data;
  size_t size;
};

typedef const struct cell_embedded_entry *(*cell_embed_lookup_fn)(const char *);

static JSValue js_os_embedded_module(JSContext *js, JSValue self, int argc, JSValue *argv)
{
  cell_embed_lookup_fn lookup = (cell_embed_lookup_fn)dlsym(RTLD_DEFAULT, "cell_embedded_module_lookup");
  if (!lookup)
    return JS_NULL;

  const char *name = JS_ToCString(js, argv[0]);
  if (!name) return JS_NULL;

  const struct cell_embedded_entry *entry = lookup(name);
  JS_FreeCString(js, name);

  if (!entry) return JS_NULL;

  /* Return the mach blob as a stoned blob */
  return js_new_blob_stoned_copy(js, (void *)entry->data, entry->size);
}

static JSValue js_os_stack(JSContext *js, JSValue self, int argc, JSValue *argv) {
  (void)self;
  int skip = 0;
  if (argc > 0) JS_ToInt32(js, &skip, argv[0]);

  JSValue full = JS_GetStack(js);
  if (skip <= 0) return full;

  int64_t n = 0;
  JS_GetLength(js, full, &n);
  if (skip >= (int)n) return JS_NewArray(js);

  JS_FRAME(js);
  JS_ROOT(arr, JS_NewArray(js));
  for (int i = skip; i < (int)n; i++) {
    JSValue item = JS_GetPropertyNumber(js, full, i);
    JS_SetPropertyNumber(js, arr.val, i - skip, item);
  }
  JS_RETURN(arr.val);
}

static const JSCFunctionListEntry js_os_funcs[] = {
  MIST_FUNC_DEF(os, platform, 0),
  MIST_FUNC_DEF(os, arch, 0),
  MIST_FUNC_DEF(os, totalmem, 0),
  MIST_FUNC_DEF(os, freemem, 0),
  MIST_FUNC_DEF(os, hostname, 0),
  MIST_FUNC_DEF(os, version, 0),
  MIST_FUNC_DEF(os, now, 0),
  MIST_FUNC_DEF(os, rusage, 0),
  MIST_FUNC_DEF(os, mallinfo, 0),
  MIST_FUNC_DEF(os, system, 1),
  MIST_FUNC_DEF(os, exit, 0),
  MIST_FUNC_DEF(os, sleep, 1),
  MIST_FUNC_DEF(os, dylib_open, 1),
  MIST_FUNC_DEF(os, dylib_preload, 1),
  MIST_FUNC_DEF(os, dylib_close, 1),
  MIST_FUNC_DEF(os, dylib_symbol, 2),
  MIST_FUNC_DEF(os, dylib_has_symbol, 2),
  MIST_FUNC_DEF(os, native_module_load, 2),
  MIST_FUNC_DEF(os, native_module_load_named, 3),
  MIST_FUNC_DEF(os, embedded_module, 1),
  MIST_FUNC_DEF(os, load_internal, 1),
  MIST_FUNC_DEF(os, internal_exists, 1),
  MIST_FUNC_DEF(os, print, 1),
  MIST_FUNC_DEF(os, random, 0),
  MIST_FUNC_DEF(os, getenv, 1),
  MIST_FUNC_DEF(os, qbe, 1),
  MIST_FUNC_DEF(os, stack, 1),
};

JSValue js_core_os_use(JSContext *js) {
  JS_NewClassID(&js_dylib_class_id);
  JS_NewClass(js, js_dylib_class_id, &js_dylib_class);

  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);
}
