#include "qjs_os.h" #include "jsffi.h" #include "qjs_macros.h" #include "qjs_wota.h" #include "prosperon.h" #include "transform.h" #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #include #ifdef __linux__ #include #endif #endif // External variables extern int trace; extern prosperon_rt *io_actor; // External function declarations extern JSClassID js_transform_id; JSValue transform2js(JSContext *js, transform *t); transform *js2transform(JSContext *js, JSValue v); // OS FUNCTIONS JSC_SCALL(os_openurl, if (SDL_OpenURL(str) < 0) ret = JS_ThrowInternalError(js, "%s", SDL_GetError()); ) JSC_SCALL(os_env, char *env = getenv(str); if (env) ret = JS_NewString(js,env); ) JSC_CCALL(os_exit, exit(js2number(js,argv[0]));) JSC_CCALL(os_now, return number2js(js, (double)SDL_GetTicksNS()/1000000000.0)) JSC_SCALL(os_system, ret = number2js(js,system(str)); ) JSC_SCALL(os_kill, pid_t pid = js2number(js,argv[0]); if (kill(pid, SIGTERM) < 0) ret = JS_ThrowReferenceError(js,"could not kill process"); ) JSC_CCALL(os_sleep, double secs = js2number(js,argv[0]); int ms = secs*1000; SDL_Delay(ms); ) JSC_CCALL(os_battery_pct, int pct; SDL_PowerState state = SDL_GetPowerInfo(&pct, NULL); return number2js(js,pct); ) JSC_CCALL(os_battery_voltage, return number2js(js,0); ) JSC_CCALL(os_battery_seconds, int secs; SDL_PowerState state = SDL_GetPowerInfo(NULL, &secs); return number2js(js, secs); ) JSC_CCALL(os_power_state, int pct, secs; SDL_PowerState state = SDL_GetPowerInfo(&pct, &secs); const char *statestr = "unknown"; switch(state) { case SDL_POWERSTATE_ON_BATTERY: statestr = "battery"; break; case SDL_POWERSTATE_NO_BATTERY: statestr = "no battery"; break; case SDL_POWERSTATE_CHARGING: statestr = "charging"; break; case SDL_POWERSTATE_CHARGED: statestr = "charged"; break; } ret = JS_NewObject(js); JS_SetPropertyStr(js,ret,"state",JS_NewString(js,statestr)); JS_SetPropertyStr(js,ret,"percent",number2js(js,pct)); JS_SetPropertyStr(js,ret,"seconds",number2js(js,secs)); ) JSC_CCALL(os_totalmem, return number2js(js, SDL_GetSystemRAM())) JSC_CCALL(os_platform, return JS_NewString(js,SDL_GetPlatform())) JSC_CCALL(os_hostname, #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 ) JSC_CCALL(os_make_transform, transform *t = make_transform(); ret = transform2js(js,t); // t->self = JS_DupValue(js,ret); t->self = ret; ) JSC_CCALL(os_clean_transforms, clean_all(js); ) JSC_CCALL(os_arch, #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 ) JSC_CCALL(os_freemem, #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 ) JSC_CCALL(os_version, #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 ) JSValue js_os_get_trace(JSContext *js, JSValue self) { return JS_NewBool(js, trace); } JSValue js_os_set_trace(JSContext *js, JSValue self, JSValue value) { trace = JS_ToBool(js, value); return JS_UNDEFINED; } JSC_CCALL(os_on, prosperon_rt *rt = JS_GetContextOpaque(js); JS_FreeValue(js, rt->on_exception); rt->on_exception = JS_DupValue(js,argv[1]); ) #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_UNDEFINED; /*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);*/ ) JSC_CCALL(os_rusage, ret = JS_NewObject(js); #ifndef _WIN32 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); #endif return ret; ) JSC_CCALL(os_createprocess, int ac = JS_ArrayLength(js,argv[0]); const char *args[ac+1]; for (int i = 0; i < ac; i++) { JSValue astr = JS_GetPropertyUint32(js,argv[0],i); args[i] = JS_ToCString(js,astr); JS_FreeValue(js,astr); } args[ac] = NULL; SDL_Process *actor = SDL_CreateProcess(args, 0); for (int i = 0; i < ac; i++) JS_FreeCString(js,args[i]); if (!actor) return JS_ThrowReferenceError(js, "Unable to create process: %s\n", SDL_GetError()); ) JSC_CCALL(os_createactor, int margc = JS_ArrayLength(js, argv[0]); char **margv = malloc(margc*sizeof(char*)); for (int i = 0; i < margc; i++) { JSValue val = JS_GetPropertyUint32(js, argv[0], i); const char *cstr = JS_ToCString(js,val); margv[i] = strdup(cstr); JS_FreeCString(js,cstr); JS_FreeValue(js,val); } create_actor(margc, margv); ) JSC_CCALL(os_mailbox_push, if (argc < 2) return JS_ThrowInternalError(js, "Need an actor and a message"); if (!JS_IsObject(argv[1])) return JS_ThrowInternalError(js, "Object to push must be an object."); const char *id = JS_ToCString(js, argv[0]); int exist = actor_exists(id); JS_FreeCString(js,id); if (!exist) return JS_ThrowInternalError(js, "No mailbox found for given ID"); void *data = value2wota(js, argv[1], JS_UNDEFINED); const char *err = send_message(id, data); if (err) { free(data); return JS_ThrowInternalError(js, "Could not send message: %s", err); } ) JSC_CCALL(os_register_actor, prosperon_rt *rt = JS_GetContextOpaque(js); const char *id = JS_ToCString(js, argv[0]); const char *err = register_actor(id, rt, JS_ToBool(js, argv[2])); if (err) return JS_ThrowInternalError(js, "Could not register actor: %s", err); rt->message_handle = JS_DupValue(js, argv[1]); rt->context = js; JS_FreeCString(js, id); ) JSC_CCALL(os_mailbox_exist, const char *id = JS_ToCString(js, argv[0]); int exist = actor_exists(id); JS_FreeCString(js, id); return JS_NewBool(js, exist); ) JSC_CCALL(os_unneeded, prosperon_rt *actor = JS_GetContextOpaque(js); SDL_LockMutex(actor->msg_mutex); JS_FreeValue(js, actor->unneeded); if (!JS_IsFunction(js, argv[0])) { actor->unneeded = JS_UNDEFINED; goto END; } actor->unneeded = JS_DupValue(js, argv[0]); JS_ToFloat64(js, &actor->unneeded_secs, argv[1]); END: if (actor->ar) { SDL_RemoveTimer(actor->ar); actor->ar = 0; } set_actor_state(actor); SDL_UnlockMutex(actor->msg_mutex); ) JSC_CCALL(os_destroy, prosperon_rt *rt = JS_GetContextOpaque(js); rt->need_stop = 1; ) static const JSCFunctionListEntry js_os_funcs[] = { MIST_FUNC_DEF(os, make_transform, 0), MIST_FUNC_DEF(os, clean_transforms, 0), 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), JS_CGETSET_DEF("trace", js_os_get_trace, js_os_set_trace), MIST_FUNC_DEF(os, kill, 1), MIST_FUNC_DEF(os, exit, 1), MIST_FUNC_DEF(os, now, 0), MIST_FUNC_DEF(os, openurl, 1), MIST_FUNC_DEF(os, sleep, 1), MIST_FUNC_DEF(os, battery_pct, 0), MIST_FUNC_DEF(os, battery_voltage, 0), MIST_FUNC_DEF(os, battery_seconds, 0), MIST_FUNC_DEF(os, power_state, 0), MIST_FUNC_DEF(os, on, 2), MIST_FUNC_DEF(os, rusage, 0), MIST_FUNC_DEF(os, mallinfo, 0), // dangerous ones that need disabled for shipping MIST_FUNC_DEF(os, env, 1), MIST_FUNC_DEF(os, system, 1), MIST_FUNC_DEF(os, createprocess, 0), MIST_FUNC_DEF(os, createactor, 1), MIST_FUNC_DEF(os, mailbox_push, 2), MIST_FUNC_DEF(os, mailbox_exist, 1), MIST_FUNC_DEF(actor, delay, 2), MIST_FUNC_DEF(actor, removetimer, 1), MIST_FUNC_DEF(os, register_actor, 2), MIST_FUNC_DEF(os, unneeded, 2), MIST_FUNC_DEF(os, destroy, 0), MIST_FUNC_DEF(os, ioactor, 0), }; JSValue js_os_use(JSContext *js) { JSValue mod = JS_NewObject(js); JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs)); return mod; }