Standard Library
`empl_runtime.h` — runtime utilities
empl_runtime.h — runtime utilities
The empl_runtime.h header (164 lines, MPL_Standard/System/empl_runtime.h) provides the runtime infrastructure that all language frontends use through their adapters. It declares:
mpl_os— OS / platform infompl_crypto_random_uuid()— RFC 4122 v4 UUIDmpl_crypto_random_bytes(n)— hex-encoded random bytesmpl_iso8601_to_time_t(s)— ISO 8601 → Unix timempl_process_exit(code),mpl_process_cwd(),mpl_process_chdir(path),mpl_process_nextTick(fn)— process helpers
It is intentionally language-agnostic and free of mpl_value / JS interop dependencies (so it can be included by empl_core.h and also by standalone tests).
Includes
// empl_runtime.h:18
#include <cstdlib>
#include <functional>
#include <string>
#include <chrono>
#include <cstdio>
#include <random>
#include <sstream>
#include <iomanip>
Plus platform-conditional includes:
// empl_runtime.h:27
#if defined(_WIN32)
#ifndef MPL_PLATFORM_WINDOWS
#define MPL_PLATFORM_WINDOWS 1
#endif
#else
#ifndef MPL_PLATFORM_POSIX
#define MPL_PLATFORM_POSIX 1
#endif
#include <unistd.h>
#if defined(__APPLE__)
#ifndef MPL_PLATFORM_DARWIN
#define MPL_PLATFORM_DARWIN 1
#endif
#endif
#endif
The MPL_PLATFORM_* macros can be used in downstream code for conditional compilation.
mpl_os — OS / platform info
// empl_runtime.h:44
struct mpl_os {
static std::string platform();
static std::string arch();
static std::string hostname();
static std::string homedir();
};
All four methods are static. The struct is a namespace-like container; do not instantiate.
platform()
// empl_runtime.h:45
static std::string platform() {
#if defined(MPL_PLATFORM_WINDOWS)
return "win32";
#elif defined(MPL_PLATFORM_DARWIN)
return "darwin";
#else
return "linux";
#endif
}
Returns one of "win32", "darwin", "linux". Matches the Environment.GetPlatform() stdlib function (which lives in mpl::System::Environment).
Source mapping:
| Source | Call |
|---|---|
JS process.platform |
mpl_os::platform() |
Python sys.platform |
mpl_os::platform() |
Ruby RUBY_PLATFORM |
mpl_os::platform() |
arch()
// empl_runtime.h:54
static std::string arch() {
#if defined(__aarch64__) || defined(_M_ARM64)
return "arm64";
#elif defined(__x86_64) || defined(_M_X64)
return "x64";
#else
return std::string(sizeof(void*) == 8 ? "x64" : "x86");
#endif
}
Returns "arm64", "x64", or "x86" (32-bit fallback).
Source mapping:
| Source | Call |
|---|---|
JS process.arch |
mpl_os::arch() |
Python platform.machine() |
mpl_os::arch() |
Go runtime.GOARCH |
mpl_os::arch() |
hostname()
// empl_runtime.h:63
static std::string hostname() {
char buf[256] = {0};
#if defined(MPL_PLATFORM_WINDOWS)
// Minimal fallback: not as feature-complete as gethostname
return std::string("windows-host");
#else
if (gethostname(buf, sizeof(buf) - 1) == 0) {
return std::string(buf);
}
return std::string("unknown");
#endif
}
Returns the system hostname. POSIX uses gethostname(2). Windows fallback returns the placeholder "windows-host" (Windows gethostname requires winsock linkage which is avoided here for header simplicity).
Source mapping:
| Source | Call |
|---|---|
JS os.hostname() |
mpl_os::hostname() |
Python socket.gethostname() |
mpl_os::hostname() |
Go os.Hostname() |
mpl_os::hostname() |
homedir()
// empl_runtime.h:75
static std::string homedir() {
#if defined(MPL_PLATFORM_WINDOWS)
const char* drive = std::getenv("HOMEDRIVE");
const char* path = std::getenv("HOMEPATH");
if (drive && path) return std::string(drive) + path;
return std::string("C:/Users/Default");
#else
const char* home = std::getenv("HOME");
if (home && *home) return std::string(home);
return std::string("/tmp");
#endif
}
Returns the user's home directory. Windows combines HOMEDRIVE + HOMEPATH. POSIX uses HOME.
Source mapping:
| Source | Call |
|---|---|
JS os.homedir() |
mpl_os::homedir() |
Python os.path.expanduser("~") |
mpl_os::homedir() |
Go os.UserHomeDir() |
mpl_os::homedir() |
Crypto helpers
mpl_crypto_random_uuid()
// empl_runtime.h:90
inline std::string mpl_crypto_random_uuid();
Returns a 36-character RFC 4122 v4 UUID string (lowercase hex, hyphenated), e.g. "f47ac10b-58cc-4372-a567-0e02b2c3d479".
Implementation uses std::mt19937_64 seeded from std::random_device, then sets the version (4) and variant (10xx) bits per RFC 4122.
Source mapping:
| Source | Call |
|---|---|
JS crypto.randomUUID() |
mpl_crypto_random_uuid() |
Python uuid.uuid4() |
mpl_crypto_random_uuid() |
Ruby SecureRandom.uuid |
mpl_crypto_random_uuid() |
Security note. This uses
std::mt19937_64(Mersenne Twister), which is not a cryptographic PRNG. It is adequate for IDs but should NOT be used for session tokens, key material, or other security-sensitive purposes. For security-sensitive randomness, useOpenSSL RAND_bytes(available viaempl_pqc.hinfrastructure or direct OpenSSL include).
mpl_crypto_random_bytes(n)
// empl_runtime.h:113
inline std::string mpl_crypto_random_bytes(long long n);
Returns n random bytes as a hex-encoded string of length 2n. Negative n returns empty string.
Implementation uses std::mt19937_64 (same caveat as above — non-cryptographic).
Source mapping:
| Source | Call |
|---|---|
JS crypto.getRandomValues(buf) (with hex encoding) |
mpl_crypto_random_bytes(n) |
Python secrets.token_hex(n) |
mpl_crypto_random_bytes(n) |
Go crypto/rand (with hex encoding) |
mpl_crypto_random_bytes(n) |
ISO 8601 parser
mpl_iso8601_to_time_t(s)
// empl_runtime.h:130
inline long long mpl_iso8601_to_time_t(const std::string& s);
// empl_runtime.h:155 (overload for mpl_value)
inline long long mpl_iso8601_to_time_t(const mpl_value& v);
Best-effort parse of an ISO 8601 date string into Unix seconds (time_t). Returns -1 on parse failure.
Accepted formats:
| Format | Example |
|---|---|
YYYY-MM-DD |
"2026-06-23" |
YYYY-MM-DDTHH:MM:SSZ |
"2026-06-23T12:34:56Z" (the T…Z suffix is parsed but only the date portion is used for the time_t) |
Validation rules:
- Year must be in
[1970, 9999]. - Month must be in
[1, 12]. - Day must be in
[1, 31](no month-aware validation). - Returns
-1for empty input or unparseable strings.
Platform handling:
- POSIX: uses
timegm(UTC time). - Windows: uses
_mkgmtime(the Windows equivalent).
Source mapping:
| Source | Call |
|---|---|
JS Date.parse(s).getTime() / 1000 |
mpl_iso8601_to_time_t(s) |
Python datetime.fromisoformat(s).timestamp() |
mpl_iso8601_to_time_t(s) |
Go time.Parse(time.RFC3339, s) |
mpl_iso8601_to_time_t(s) |
Process helpers (Phase 3)
mpl_process_exit(code)
// empl_runtime.h:160
inline void mpl_process_exit(int code) { std::exit(code); }
Terminates the process with the given exit code. Equivalent to Process.Exit(code) in the stdlib namespace.
mpl_process_cwd()
// empl_runtime.h:161
inline std::string mpl_process_cwd() { return "/"; }
Stub. Returns "/" always. The real implementation requires hooking main(int argc, char** argv) to capture the initial CWD. Use Environment.GetCurrentDirectory() (which wraps std::filesystem::current_path) instead.
mpl_process_chdir(path)
// empl_runtime.h:162
inline void mpl_process_chdir(const std::string&) {}
Stub. Empty body. Use Environment.SetCurrentDirectory(path) instead.
mpl_process_nextTick(fn)
// empl_runtime.h:163
inline void mpl_process_nextTick(std::function<void()> fn) { mpl_queue_microtask(std::move(fn)); }
Queues fn onto the EMPL microtask queue (defined in empl.h). Equivalent to JS process.nextTick(fn) — runs fn after the current operation completes but before the event loop continues.
Threading / async model
empl_runtime.h itself does not introduce threading primitives — those are inherited from empl_core.h (microtask queue, mutex, condition variable) and empl.h (event loop, microtask CV).
The mpl_crypto_random_uuid() and mpl_crypto_random_bytes() functions use thread_local std::mt19937_64 for thread-safety.
mpl_value overload
// empl_runtime.h:155
inline long long mpl_iso8601_to_time_t(const mpl_value& v) {
return mpl_iso8601_to_time_t(mpl_to_string(v));
}
The mpl_value overload exists because generated code passes mpl_value (a std::variant) to stdlib functions. The overload stringifies via mpl_to_string and delegates to the std::string version. This is the canonical pattern for any mpl_runtime function that accepts a string — always provide BOTH overloads.
See also
pqc.md— post-quantum crypto (separate header, more advanced)cpp-impl.md— C++ stdlib implementationmodules.md— full module listheaders.md— header hierarchyempl.hlines 86-189 —mpl_throw_*,mpl_strict_mode_flag,mpl_microtask_queue,mpl_run_event_loop(the upstream infrastructure thatmpl_process_nextTickbuilds on)