Releases: nicy-luau/Runtime
Releases · nicy-luau/Runtime
Release list
Nicy Runtime (v1.1.2)
Nicy Runtime (v1.1.1)
v1.1.1 — patch release Fix two stack imbalance bugs in runtime.loadlib: - Cache miss path left cache_table on stack before init_fn call - get_or_create_extension_cache_table else branch incorrectly popped the cache table, causing loadlib to return the path string instead of the module table
Nicy Runtime (v1.1.0)
chore: bump version to 1.1.0 — comprehensive changelog === Runtime === New: - ffi_exports: add null_guard! macro to every FFI function, preventing undefined behavior when external code passes null lua_State* pointers - ffi_exports: add nicy_error_name() — converts error codes to human-readable names (e.g. 103 → "NICY_ERR_CYCLIC_REQUIRE") - ffi_exports: add nicy_is_nicy_error() — distinguishes Nicy-specific error codes (>= 100) from standard Luau codes Refactor: - lib.rs: remove the api module entirely (~100 lines of unnecessary indirection around mlua_sys), all call sites now use mlua_sys directly - lib.rs: replace all b"string\0".as_ptr() patterns with C-string literals (c"string"), eliminating manual null termination - lib.rs: simplify --!optimize and --!typeinfo directive parsers to use a single rest.trim().parse() call, handling both "--!optimize 2" and "--!optimize2" uniformly - require_resolver: replace ~150-line hand-rolled JSON parser with serde_json for .luaurc alias parsing, including JSONC fallback that strips // and # comment lines before parsing - error.rs: collapse multiple nested if-let chains into Rust let-chain syntax in extract_location, stack trace formatter, and auto_init_logging - error.rs: replace crate::api calls with direct lua:: usage after api module removal - task_scheduler: remove duplicate panic_payload_to_string, import the shared version from lib.rs instead - task_scheduler: replace ms.max(1).min(...) with ms.clamp(1, ...) Fix: - task_scheduler: fix undefined behavior from calling lua_gettop() on a closed Lua state — added CURRENT_L_VALID AtomicBool flag with mark_current_state_valid/mark_current_state_invalid functions that guard all unref operations before any FFI call is made - task_scheduler: task_cancel now returns (nil, error_message) for IDs above 2^53 instead of luaL_error which would longjmp through Rust's catch_unwind - task_scheduler: duration_from_seconds returns Duration::ZERO for NaN and infinity inputs instead of producing a clamped duration - ffi_exports: fix nicy_luaL_len return type from int to nicy_Integer - ffi_exports: fix raise_panic_as_lua_error CStr cast to *const c_char - lib.rs: fix double string conversion in os_tmpname where to_string_lossy() was called twice, potentially producing different results for non-UTF-8 paths - require_resolver: fix Windows canonicalize_existing — strip \?\ extended- length prefix before passing to LoadLibraryW, which otherwise returns ACCESS_DENIED when loading .ndyn native modules - require_resolver: canonicalize_existing now falls back to the original path on error, handling PermissionDenied on junctions and AppData paths - require_resolver: deduplicate resolve_loadlib_base and resolve_spec_base by collapsing identical is_init_module branches into a single expression Safety: - lib.rs: mark nicy_start, nicy_eval, nicy_compile as unsafe extern "C" with /// # Safety documentation documenting pointer validity requirements - lib.rs: change push_nicy_table parameter from &PathBuf to &Path Leak fixes: - lib.rs: explicitly remove nicy_ext_cache table from the Lua registry before lua_close in both nicy_start and nicy_eval, fixing LEAK-2 where repeated runtime invocations accumulated orphaned registry tables Tests: - lib.rs: add unit tests for parse_native_directive, parse_compiler_directives (native, optimize, multiple directives, unknown directive skipping, optimize clamping), and panic_payload_string - task_scheduler: add unit tests for duration_from_seconds (positive, zero, negative, NaN, infinity, minimum 1ms) and panic_payload_to_string - require_resolver: add unit tests for parse_aliases_from_luaurc (valid JSON, @ prefix auto-addition, empty value skipping, invalid JSON, JSONC with comments, missing aliases key) - Task/cancel.luau: update "rejects ID above 2^53" test from 2^53+1 to 2^54 (f64 precision loss made them equal), assert nil+error return instead of boolean false; add "accepts ID at exact 2^53 boundary" test === Nicy === Refactor: - main.rs: change main() return type to Result<(), Box<dyn std::error::Error>>, eliminating all process::exit(1) calls in favor of the ? operator - main.rs: refactor execute_file, execute_eval, and execute_compile from unsafe fn returning () to fn returning Result, with only specific FFI calls wrapped in unsafe blocks instead of the entire match command - main.rs: remove to_cstring_or_exit and exit_with_library_error helper functions, handling CString errors inline with .map_err() and ? - main.rs: silently try each candidate library in preload_android_libcxx and load_nicy_lib, only reporting the final aggregate error if all candidates fail - main.rs: remove unused std::path::Path and std::process imports
Nicy Runtime (1.0.0-alpha)
feat: initial commit - NicyRuntime v1.0.0-alpha
A blazing-fast Luau runtime environment with modular architecture, built
in Rust.
PROJECT OVERVIEW
================
Complete workspace setup with a dual-crate architecture:
- Nicy CLI: Terminal wrapper for executing Luau scripts
- Runtime (cdylib): Core dynamic library with full Luau implementation
ARCHITECTURE
============
Workspace (Cargo workspace, resolver v2):
├── Nicy/ # CLI executable (361 lines)
│ └── src/main.rs # Dynamic library loading & command
routing
├── Runtime/ # cdylib core library (~5,756 lines)
│ └── src/
│ ├── lib.rs # Main entry point, Luau state
initialization, FFI
│ ├── require_resolver.rs # Custom module resolver with caching
& aliases
│ ├── task_scheduler.rs # Async task scheduler with
coroutines
│ ├── ffi_exports.rs # 70+ C-ABI Lua API wrappers
│ └── error.rs # Error reporting system
(concise/verbose)
├── build.ps1 # PowerShell multi-platform build
script
├── .github/workflows/release.yml # CI/CD pipeline for automated
releases
└── tests/ # 32 Luau test files covering all
features
RUNTIME CORE (Runtime/src/lib.rs - 1,250 lines)
================================================
- Luau state initialization with full environment setup
- FFI entry points: nicy_start(), nicy_eval(), nicy_compile(),
nicy_version()
- Compiler directives parser: --!native, --!optimize N, --!coverage,
--!profile, --!typeinfo N
- runtime.loadlib() with SEH crash protection (Windows), dynamic loading
with caching
- Native directive stripping (--!native removal)
- OS extensions: os_clock, os_time, os_date, os_difftime, os_exit,
os_getenv, os_remove, os_rename, os_sleep, os_tmpname
- Monotonic counter for temp file names (race condition prevention)
- Windows high-resolution timer via timeBeginPeriod/timeEndPeriod
(opt-in via NICY_HIRES_TIMER)
- Safe pcall wrapper with state tracking
- Bytecode compilation with optional Luau CodeGen/JIT
- Graceful shutdown with library cleanup (FIX C-1, C-5 memory leak
prevention)
- runtime table: version, loadlib, hasJIT, entry_file, entry_dir
CUSTOM MODULE RESOLVER (require_resolver.rs - 1,205 lines)
============================================================
- Custom require() implementation with complete module resolution
- File fingerprint caching (mtime + size, not content hash - faster)
- Circular dependency detection with clear error reporting
- Module cache invalidation on file changes
- .luaurc alias support with directory tree inheritance
- JSON parser for .luaurc (no external dependencies)
- Support for: relative paths, absolute paths and self alias
- Bytecode priority (.luauc > .luau > .lua > init.*)
- Concurrent loading detection with cooperative yielding
- Require chain tracking for error diagnostics
- Runtime state cleanup (shutdown_runtime, shutdown_all_globals)
- Thread-safe coroutine-to-main-state mapping
ASYNC TASK SCHEDULER (task_scheduler.rs - 782 lines)
=====================================================
- Cooperative multitasking with Luau coroutines
- Task types: spawn, defer, delay, wait, cancel
- Ready queue (VecDeque) + yielded queue + timer heap (BinaryHeap)
- task.spawn(): Creates coroutine with arguments, registers in scheduler
- task.defer(): Adds to end of yielded queue for deferred execution
- task.delay(): Schedules execution after delay in seconds, returns
cancellable ID
- task.wait(): Pauses coroutine (synchronous for main thread, async for
others)
- task.cancel(): Cancels thread or delay by ID with safe integer
validation (2^53)
- run_until_idle(): Main loop - processes ready, yielded, and timers
- run_one_iteration(): Single scheduler iteration (used by task.wait on
main thread)
- Memory fences (compiler_fence SeqCst) for cross-thread write
visibility
- Elapsed time tracking for task.wait return values
- Complete scheduler shutdown with registry ref cleanup
FFI EXPORTS (ffi_exports.rs - 522 lines)
=========================================
Complete Lua C API with stable C-ABI (extern "C-unwind", no_mangle):
- Stack manipulation: gettop, settop, pushvalue, remove, insert,
absindex, checkstack
- Push operations: nil, boolean, number, integer, string, lstring,
cfunction, cclosure, lightuserdata, newuserdata, newthread
- Type checking: type, typename, isnil, isboolean, isnumber, isstring,
istable, isfunction, isuserdata, isthread, iscfunction, isinteger
- Get/conversion: tostring, tolstring, toboolean, tonumber, tointeger,
touserdata
- Table access: getfield, getglobal, gettable, rawget, rawgeti,
getmetatable, next
- Table set: setfield, setglobal, settable, rawset, rawseti,
setmetatable
- Table creation: createtable
- Call/execution: call, pcall, error, resume, yield
- Comparison: equal, lessthan, rawequal
- Other: concat, gc, rawlen
- Lua 5.1 compatibility: getfenv, setfenv
- lauxlib: checkstring, checklstring, checknumber, checkboolean,
checkinteger, checktype, checkany, optstring, optinteger, optnumber,
optboolean
- lauxlib errors: error, argerror, where, traceback
- lauxlib refs: ref, unref
- lauxlib metatables: len, newmetatable, getmetatable
- Total: 70+ exported functions with nicy_lua_* / nicy_luaL_* prefix
ERROR REPORTING SYSTEM (error.rs - 1,997 lines)
=================================================
- Concise error reporting by default, verbose mode via
NICY_VERBOSE_ERRORS=1
- ANSI color support (red, yellow, cyan, green, bold, dim, reset)
- Color disabling via NICY_NO_COLOR=1
- Extended error codes: 0-6 (Luau standard) + 100+ (Nicy custom)
MODULE_NOT_FOUND, MODULE_LOAD_FAILED, MODULE_INIT_FAILED,
CYCLIC_REQUIRE,
TASK_CRASH, NATIVE_CRASH, TIMEOUT, PERMISSION_DENIED
- Error levels: Error, Warning, Info
- RequireChain: Hierarchical require tracking with frames (file, line,
spec, searched_paths)
- NicyError enum: LoadError, RequireError, RuntimeError, TaskError,
PanicError, FileError, RuntimeErrorGeneric
- ErrorReporter: Thread-local pcall state tracking (suppresses errors
inside pcall)
- Verbose mode: Hierarchical output with Exception, ErrorRecord,
TargetObject,
CategoryInfo, FullyQualifiedErrorId, RequireChain, InvocationInfo,
ScriptStackTrace
- Windows path cleaning (\\?\ prefix removal)
CLI (Nicy/src/main.rs - 361 lines)
====================================
- Dynamic loading of nicyruntime library via libloading
- Command routing: run, eval, compile
- Version display with Luau version
- File execution with path resolution
- Inline code evaluation
- Bytecode compilation to .luauc
- Minimal dependencies: only libloading 0.9
TESTING SUITE (32 Luau test files)
====================================
Core API (11 files):
api.luau, bit32.luau, buffers.luau, edge_cases.luau, gc.luau,
io_files.luau, luaurc.luau, metatables.luau, os_ext.luau,
stdlib.luau, vectors.luau
Require System (6 files + fixtures):
aliases.luau, bytecode.luau, circular.luau, concurrent.luau,
relative.luau, resolution.luau
fixtures/: nested.luau, simple.luau
Runtime (6 files):
debug.luau, error_handler.luau, globals.luau,
loadlib_errors.luau, shutdown.luau, traceback.luau
Task Scheduler (7 files):
cancel.luau, defer.luau, delay.luau, precision.luau,
spawn.luau, stress.luau, stress_extreme.luau, wait.luau
Test Helpers:
init.luau, expect.luau, report.luau
Test runner: run_all.luau (executes all tests and generates report)
BUILD & CI/CD
=============
build.ps1:
- 10 target combinations: win-x64, win-x86, win-arm, mac-arm, mac-x64,
linux-arm, linux-x64, linux-x86, android-arm, android-v7
- cargo zigbuild for Linux/macOS, cargo pure for Windows/MSVC, cargo ndk
for Android
- Auto-detection of host target via rustc or RuntimeInformation
- Force rebuild flag support
.github/workflows/release.yml:
- Trigger: release published event
- Build matrix: all 10 targets (win-arm with allow_failure)
- Tools: Rust stable, zig 0.14.0, Android NDK r26d, cargo-zigbuild,
cargo-ndk
- Separate builds for CLI and Runtime
- Artifact packaging: nicy-{target}.zip
- Upload to GitHub Release via softprops/action-gh-release@v2
DEPENDENCIES
============
Runtime:
- mlua-sys 0.10.0 (luau, luau-codegen, luau-vector4, vendored)
- Conditional: no codegen on Android for stability
- libloading 0.9
Nicy CLI:
- libloading 0.9
Build profile (release):
- strip = "symbols"
- lto = true
- codegen-units = 1
- opt-level = "z" (minimum size)
FEATURES
========
✓ Dynamic Library Architecture (cdylib with runtime loading)
✓ Native Code Integration (Luau CodeGen/JIT)
✓ Custom Module Resolver with caching and aliases
✓ Asynchronous Task Scheduler (cooperative coroutines)
✓ Cross-Platform Support (Windows, macOS, Linux, Android)
✓ Luau CodeGen/JIT (enabled on non-Android platforms)
✓ FFI C-ABI (70+ Lua C API functions exposed)
✓ Bytecode Compilation (.luauc format)
✓ Error Reporting (concise/verbose modes)
✓ High-Resolution Timer (Windows, opt-in)
✓ SEH Crash Protection (Windows loadlib)
✓ Circular Dependency Detection
✓ Module Cache Invalidation
✓ Require Chain Tracking
✓ Memory Safety (static state cleanup between calls)
LICENSE
=======
Mozilla Public License Version 2.0 (MPL 2.0)
- Root LICENSE
- Nicy/LICENSE
- Runtime/LICENSE
fix(runtime): disable luau-vector4 on x86 32-bit Linux
Resolve static assertion failure 'sizeof(lua_TValue) == 24' when
compiling for i686-unknown-linux-gnu via cargo-zigbuild.
Root cause: MSVC on Windows aligns TValue to 24 bytes even on 32-bit,
but the System V i386 ABI (Linux/Zig) results in a 20-byte TValue
when LUA_VECTOR_SIZE=4 is active.
Solution: Remove luau-vector4 feature conditionally for target_arch=x86
on Linux, kee...