Skip to content

Releases: nicy-luau/Runtime

Nicy Runtime (v1.1.2)

Choose a tag to compare

@yanlvl99 yanlvl99 released this 18 Apr 15:09
v1.1.2
2dc80ec
Release v1.1.2

Nicy Runtime (v1.1.1)

Choose a tag to compare

@yanlvl99 yanlvl99 released this 18 Apr 14:18
v1.1.1
2cdf006
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)

Choose a tag to compare

@yanlvl99 yanlvl99 released this 15 Apr 15:49
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)

Choose a tag to compare

@yanlvl99 yanlvl99 released this 12 Apr 16:39
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...
Read more