Skip to content

Document standard library APIs and improve Lua 5.2 compatibility - #396

Merged
akeit0 merged 7 commits into
mainfrom
codex/fix-stdlib-api-compatibility
Oct 31, 2026
Merged

akeit0 merged 7 commits into
mainfrom
codex/fix-stdlib-api-compatibility

Conversation

@akeit0

@akeit0 akeit0 commented Oct 31, 2026

Copy link
Copy Markdown
Collaborator

Lua-facing standard-library behavior diverged from Lua 5.2 in date conversion, debug traces, iterator return counts, and several I/O and argument-handling paths. This PR fixes those contracts and adds a per-API reference linking every standard-library entry to the official Lua 5.2 documentation, with implementation differences and their reasons.

Compatibility changes

  • Dates and times: normalize os.time calendar overflow (for example, day 0 and month 13), truncate fractional fields, accept numeric strings, preserve optional-field defaults, and read fields through __index in Lua 5.2 order. Keep the input table unchanged. Use the configured TimeProvider consistently for explicit/current timestamps and honor isdst during repeated local hours, including negative DST adjustments. Return nil when date-table conversion cannot be represented.
  • Date formatting: implement week numbers, ISO week years, century padding, valid POSIX %E/%O modifiers, C-locale composite formats, and timezone offsets/names. Return pooled formatting buffers on invalid formats as well as success.
  • Light userdata: share one type metatable within a global state and its coroutines. debug.setmetatable now affects both host-object tokens and debug.upvalueid results; independent states remain isolated.
  • Loading: omitted/nil filenames in loadfile and dofile read host standard input. Preserve raw bytecode, BOM/shebang handling, source line numbers, =stdin chunk names, modes/environments, and input-stream ownership.
  • Debug/error contracts: accept numeric-string stack levels; correct traceback messages, skip levels, current-thread defaults, and Lua 5.2 deep-trace abbreviation. Keep host tracebacks complete. Convert numeric error objects to strings only at positive levels. Preserve Lua 5.2's active stripped-frame line 0 and empty stripped upvalue names.
  • Iterators and I/O: fix ipairs control conversion/exhaustion arity and line-iterator exhaustion arity/owned-file closing. Accept numeric-string seek offsets while retaining integral/range requirements. Return the I/O error tuple for temporary-file and removal permission failures. On POSIX, allow removal of empty directories without recursive deletion; retain Windows directory rejection.
  • Other contracts: truncate os.difftime timestamps and default its second argument to zero; coerce numeric os.exit codes; support fixed-C-locale queries/category validation; terminate package.config with a newline.

Add 11 standard-library reference pages, link them from both READMEs, and cover the changed behavior with regression tests.

Remaining limitations

These are documented rather than presented as full Lua compatibility:

  • collectgarbage remains deferred: recognized options still call managed collection and return no values. Lua weak-table and __gc protocols remain unsupported.
  • Date conversion is bounded by managed date/time ranges. For normalization, both the normalized month anchor and the final date must fit years 1–9999. An explicit isdst on an unambiguous date does not force C mktime's seasonal correction; unspecified DST during a repeated hour uses the managed standard-time default. C runtime ranges and platform-specific behavior are not fully emulated.
  • Only the fixed C locale is supported. Default UTF-16 strings, managed error codes/messages, timezone names, and host-object metadata retain the documented managed differences; byte strings remain selectable.
  • Reader-based load still buffers the whole input. An invalid hash key passed to next still returns nil instead of raising C Lua's invalid-key error.
  • Built-in file:setvbuf controls flushing policy rather than implementing C stdio buffering. Native C modules, io.popen, shell execution, and debug.debug remain unavailable; os.exit still ignores its close argument. Uservalue storage retains the host-defined API.
  • The individual API pages describe the complete remaining differences.

Validation

  • 3,502 runtime tests passed on each of .NET 8 and .NET 10; 73 source-generator tests passed.
  • All 29 latest regression cases fail against the pre-fix assembly and pass after the fixes; they also pass on Linux/.NET 10.
  • Native C Lua 5.2 comparisons cover date normalization/coercion, shared light-userdata metatables, and redirected stdin loading, environments, source names, empty input, and syntax errors.
  • The CI benchmark workload verifier passed, including bidirectional NuLua Lua 5.2 / Lua-CSharp bytecode interoperability.
  • All library target builds passed: net10.0, net8.0, net6.0, and netstandard2.1.
  • Repository-wide formatting and documentation coverage/link checks passed.

Performance versus main

Focused paired .NET 10 Release measurements on Windows, using the same compiled Lua workloads and host timezone for both versions. Each batch performs 1,000 operations; results use the median of nine alternating pairs after warmup, with the process pinned to one CPU. The baseline runtime sources match main at f6813c3.

Workload Runtime change Allocation change
Date fields (!%Y-%m-%dT%H:%M:%S) 4.4% faster 256 → 64 bytes/call
Date composite (!%c) 5.8% faster 232 → 72 bytes/call
Numeric-level debug.getinfo 7.6% faster Unchanged
os.time, full date table 2.5% slower, about 2 ns/call Unchanged
os.time, default time fields 8.4% slower, about 6 ns/call Unchanged
Small-file loading Effectively unchanged Unchanged
Userdata lookup / numeric metatable lookup Effectively unchanged Unchanged

Ordinary date tables stay synchronous; metatable-backed reads use the existing VM path with the Lua non-yieldable boundary preserved. These are focused microbenchmarks, not whole-application performance estimates.

Document every named Lua-facing standard-library API, table, and path
environment setting with links to the official Lua 5.2 manual. Explain
managed runtime differences, provider-dependent behavior, unavailable
features, and current compatibility limitations with their mechanisms.

Link the reference from both READMEs and correct the stale string.dump
support note in the string-mode guide.

Validate official anchors, registered API coverage, local links, and
representative behavior against the implementation and native Lua 5.2.
Audit the per-API reference against Lua 5.2.4 source and the managed implementation. Remove incorrect stripped-name and os.time mutation differences, clarify shared behavior, and document verified conversion, formatting, iteration, and traceback limitations.

Validated all 161 official Lua-facing entries and 132 registered functions/file methods, local links and table structure, plus 16 focused comparisons in native Lua 5.2 and both managed string modes. Runtime code is unchanged.
Coerce numeric error objects at positive levels and accept Lua numeric
conversions in seek offsets and exit codes. Match iterator termination
result counts and newline-terminate all package.config markers.

Correct traceback message handling, default levels, frame skipping, and
numeric formatting. Complete date week/ISO-year and C-locale conversions,
including valid E/O modifiers. Preserve timezone offsets and use the
configured TimeProvider timezone consistently for os.date/os.time.

Add focused regression coverage and update the API reference, retaining
explicit documentation of C runtime and managed timezone-name differences.

Validation: 3,389 runtime tests pass on both net8.0 and net10.0; all 73
source-generator tests pass. All four library targets build without
warnings. CSharpier, documentation coverage/links, and native Lua 5.2
comparisons pass. Representative regressions fail against the baseline.

Paired net10.0 benchmarks against 7faa7f5 show date-field formatting
8-10% faster with 75% fewer allocated bytes; composite date formatting
allocates 69% less. Seek/line iteration remain comparable and ipairs is
within about 1%, with no additional allocations in these loops.
Use isdst to resolve repeated local times, including negative daylight
adjustments, and truncate explicit timestamps toward zero before date
conversion. Dispose the date formatter buffer on both success and failure.

Apply Lua 5.2 traceback abbreviation before formatting omitted frames,
including its absolute skip-level marker behavior. Keep public C# traces
complete and simplify the debug-library dispatch to one formatter call.

Add regressions for DST folds, fractional timestamps, all date-format
error exits, and deep live/suspended/failed tracebacks. Observe pool returns
synchronously without GC assertions, sleeps, retries, or production hooks.
Update the API reference and retain the remaining os.time limitations.

Validation: 3,431 runtime tests pass on both net8.0 and net10.0, with all
73 generator tests passing. The final no-DST optimization passes all 57
date tests on both targets. All four library targets build without warnings;
formatting and documentation coverage/link checks pass. Native Lua 5.2
matches all nine traceback boundary/skip cases and three fractional-time
cases in both string modes. All four regression groups fail on aa7c5c2.

Paired net10.0 benchmarks against aa7c5c2: a 1,000-call Lua traceback is
about 80% faster and allocates 1,584 rather than 66,320 bytes (97.6% less).
Ordinary date/time-table workloads remain within about 1.3% of baseline
with unchanged allocations. Avoid the isdst lookup in zones without DST.
Return the normal I/O error tuple for temporary-file creation and remove
permission failures, while preserving cancellation. Accept numeric-string
debug.getinfo levels and reuse the captured call-stack span. Keep stripped
active-frame currentline at 0: native Lua 5.2 confirms this existing behavior
is correct, so remove the incorrect documentation limitation.

Convert difftime inputs to 64-bit whole seconds before double subtraction,
including the omitted/nil zero base and positive zero results. Support fixed
C-locale queries and validate the Lua 5.2 category names without changing
host culture. Allow nonrecursive empty-directory removal on POSIX while
preserving Windows C-runtime behavior and the existing file fast path.

Add regressions for synchronous/asynchronous provider failures, cancellation,
metadata coercion, fractional/default time differences, locale validation,
and directory removal. Document Windows negative-timestamp restrictions.
Leave collectgarbage unchanged.

Validation: all 3,473 runtime tests pass on both net8.0 and net10.0; all
73 generator tests pass. All four library targets build without warnings.
All 42 focused cases also pass on Ubuntu net10.0, including POSIX directory
removal. Native Lua 5.2 comparisons pass for the shared cases; POSIX libc
comparisons verify negative difftime inputs and directory behavior. Formatter
and API-reference coverage/link checks pass; regressions fail on 655da93.

Paired net10.0 benchmarks against 655da93 show numeric-level getinfo about
7% faster and function queries comparable, with unchanged allocations.
Required difftime conversions add about 1-2 ns per call (3.5-4.2%) and
remain allocation-free.
Normalize os.time calendar fields, accept Lua numeric coercion and optional
field defaults, and read metatable-backed fields in Lua 5.2 order. Keep
ordinary tables synchronous and preserve the non-yieldable Lua boundary.
Return nil when the managed date range cannot represent a conversion.

Store the light-userdata type metatable in the shared global state.
Load omitted/nil filenames from host standard input through the file loader,
preserving raw bytecode, chunk names, environments and stream ownership.

Add 29 regression cases and update per-API documentation, including the
remaining managed date range and DST differences. Leave collectgarbage
unchanged.

Validation: 3,502 runtime tests per net8.0/net10.0, 73 generator tests,
29 focused Linux cases, all library target builds and formatting/docs checks.
Compared behavior with native Lua 5.2, including redirected standard input.
Paired .NET 10 benchmarks keep allocations unchanged: full date tables are
flat, defaulted date tables add about 7 ns/call, and small-file loading and
userdata lookup stay close to baseline.
@akeit0
akeit0 merged commit e5d83c6 into main Oct 31, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant