Add a bounds check in `forStatement` to ensure there is space for the two hidden local variables (sequence slot and iterator) before calling `addLocal`. Without this check, deeply nested for-loops could overflow the `locals` array, causing a buffer overrun. The fix emits a clear compiler error when `numLocals + 2` exceeds `MAX_LOCALS`, and includes a regression test with 85 nested for-loops that previously triggered the overflow.
When compiling a single expression, the parser now explicitly consumes the TOKEN_EOF token after parsing the expression. This ensures that any trailing garbage or unexpected tokens after the expression are caught and reported as errors, rather than being silently ignored. The comment for the non-expression path is also updated to clarify that it checks for end of file rather than end of block.
Replace strncpy with memcpy in readBuiltInModule to avoid potential string termination issues
when copying module source code, and increase ERROR_MESSAGE_SIZE from 60 to 80 to provide
more room for compiler error messages with long variable names.
Migrate the internal `String` struct to use the heap-allocated `ObjString*` throughout the compiler, debugger, VM, and value system. This unifies string representation, enables proper garbage collection of symbol table entries via new `wrenBlackenSymbolTable` function, and removes manual memory management for symbol names.
Add handling for UTF-8 byte order mark (BOM) at the start of source code
in wrenCompile function by checking for the three-byte sequence \xEF\xBB\xBF
and advancing the source pointer past it. Include a regression test file
(test/regression/520.wren) that contains a BOM to verify the fix works
correctly.
Add a MAX_PARAMETERS bound to the loop in signatureParameterList to prevent
buffer overflow when a method signature has an excessive number of parameters.
A compile error is already reported in such cases, but the unbounded loop could
still write past the end of the name buffer. Also add regression test 494.wren
to cover this scenario.
The compiler now always reserves slot zero for either the closure or method
receiver, eliminating a corner case where top-level REPL code lacked the
pre-allocated slot that function/method bodies expect. This ensures local
variables declared in REPL loops (e.g. `for (i in 1..2)`) get correct slot
indexes.
Additionally, inlines `wrenResetFiber` into `wrenNewFiber` and stores the
imported module's closure on the stack before invoking it, preventing a GC
from collecting the closure during import.
Introduce two new inline functions in wren_value.h: wrenStringEqualStrLength
compares an ObjString against a raw C string with known length, while
wrenStringsEqual compares two ObjString instances using hash pre-check and
delegating to wrenStringEqualStrLength. Refactor wrenSymbolTableFind in
wren_utils.c and wrenValuesEqual in wren_value.c to use these new helpers,
replacing inline memcmp calls for improved code clarity and reuse.
Unify string representation across the codebase by switching SymbolTable and method name storage from a manually-managed `String` struct (char* buffer + length) to `ObjString*` pointers. This eliminates duplicate string allocations in the meta API's `metaGetModuleVariables`, reduces memory management overhead by leveraging the existing GC for symbol strings, and adds `wrenBlackenSymbolTable` to properly trace these GC-roots during collection. The change touches the compiler, debug dumper, VM runtime error formatting, and module variable initialization to use `->value` and `->length` fields directly from `ObjString`.
Consolidate all Obj* type forward declarations from wren_value.h into wren_common.h
to centralize type definitions and reduce header dependencies. Convert anonymous
typedef structs to named structs with sObj* tags for consistent naming convention
across all object types.
The assertion for the `name` parameter in `wrenGetVariable` was incorrectly checking `module != NULL` instead of `name != NULL`. This fix ensures the variable name parameter is properly validated, preventing potential null pointer dereferences when retrieving variables.
Additionally, adds Marshall Bowers to the AUTHORS file.
The getNumArguments function had CODE_IMPORT_MODULE incorrectly returning 1 instead of 2, and CODE_IMPORT_VARIABLE returning 2 instead of 4. Additionally, wrenBindMethodCode had two bugs: it was incrementing ip before reading the instruction, causing incorrect opcode dispatch, and it was using ip++ instead of ip+1 for field offset adjustment, corrupting subsequent bytecode. The super instruction handling also incorrectly skipped over the symbol bytes before reading the constant index.
The compileInModule function now returns a closure directly instead of wrapping it in a fiber, simplifying the compilation pipeline and removing unnecessary fiber overhead for module imports. The wrenImportModule function is moved into wren_vm.c and rewritten to execute the imported module's closure immediately rather than returning a fiber for deferred execution. This change also adds a test for yielding from an imported module to verify correct behavior with the new execution model.
Remove redundant NULL-check branches for module parameter by merging the
wrenPopRoot call into a single conditional after compileInModule, eliminating
the early return path and simplifying control flow in the compilation entry point.
The assertion in `wrenGetVariable` was incorrectly checking `module != NULL` twice,
instead of checking `name != NULL` for the variable name parameter. This fix
ensures the second assertion validates the correct parameter, preventing
potential null pointer dereference when a NULL variable name is passed.
The getNumArguments function had CODE_IMPORT_MODULE incorrectly listed as a 1-argument
opcode and CODE_IMPORT_VARIABLE as a 2-argument opcode. This swap caused incorrect
bytecode parsing during method binding. Additionally, wrenBindMethodCode had two bugs:
the instruction fetch was incrementing ip before reading the opcode, and field offset
patching was using ip++ instead of ip+1, corrupting subsequent bytecode. The super
instruction handling also had an incorrect ip+=2 skip that removed the symbol offset
before reading the constant index.
The switch statement in getNumArguments covers all opcode cases, but the compiler
cannot prove exhaustiveness. Adding UNREACHABLE() followed by return 0 eliminates
the "control reaches end of non-void function" warning on strict compiler settings.
Replace the previous approach of desugaring import statements into calls to System.importModule() and System.getModuleVariable() with two new bytecode instructions: CODE_IMPORT_MODULE and CODE_IMPORT_VARIABLE. This eliminates the need for the corresponding primitive methods in the core library, adds proper debug dump support for the new opcodes, and updates the compiler to emit these instructions directly. The getNumArguments function is also updated to handle the new opcodes and the unreachable default case is removed.
When a fiber is started for the first time via call() or transfer(), the value argument is now bound to the fiber's function parameter if it takes one. Previously, the first value was always ignored. Also adds runtime error for fibers created with functions taking more than one parameter, and updates documentation and tests accordingly.
Replace `Meta.eval()` to compile source into a fiber instead of a function, fixing issue #456 where top-level variable declarations failed in the REPL. Add `Stdout` import and flush stdout before reading stdin since `System.print()` no longer auto-flushes. Refactor token detection to use `lexFirst()` instead of full `lex()`, and add new `Meta.compile()` method returning a fiber. Internally rename `wrenNewString` to `wrenNewStringLength` with explicit length parameter, updating all call sites across compiler, core, and meta modules.
Rename the internal `ClassCompiler` struct typedef to `ClassInfo` to better reflect its purpose as a descriptor for the enclosing class context during compilation. Update all variable declarations, function signatures, and references throughout `wren_compiler.c` to use the new type name, including in `getEnclosingClass`, `field`, `super_`, and `declareMethod` functions. This resolves issue #469 by eliminating the misleading "Compiler" suffix from a type that tracks class metadata rather than compilation state.
The compiler previously relied on `match(TOKEN_RIGHT_BRACE)` in the while loop condition, which consumed the brace and made it impossible to detect a missing one. Changed to peek-based loop termination and added explicit `consume` call for the closing brace, ensuring proper error reporting when '}' is absent. Added regression test for issue #429.
Add parentheses around the `count` parameter in both `ALLOCATE_FLEX` and
`ALLOCATE_ARRAY` macro definitions to prevent operator precedence bugs when
the argument is an expression. Without this fix, expressions like
`sizeof(type) * n + 1` would be evaluated incorrectly due to `*` having
higher precedence than `+`.
When the fiber's stack is reallocated, pointer subtraction between old and new stack
addresses is undefined behavior. Instead of computing an offset and adding it to each
pointer, recalculate each pointer relative to the new stack base using well-defined
arithmetic within the single array. This affects the apiStack, call frame stackStart
pointers, open upvalue values, and the stackTop pointer.
Implement the `round` method on the Num class in the Wren VM, which rounds a number to the nearest integer using standard rounding rules (away from zero for .5). The implementation adds a new `DEF_NUM_FN(round, round)` macro invocation in `wren_core.c`, registers the corresponding primitive `num_round`, and includes comprehensive documentation in the core module reference. A new test file `test/core/number/round.wren` validates behavior for positive, negative, zero, and fractional values. Also adds Kyle Charters to the AUTHORS file.
In runtimeError, walk the entire fiber call chain and set each fiber's error field to the same error value, unhooking callers as we go. Previously only the immediate caller was considered, leaving intermediate fibers in an inconsistent state. This ensures that when a fiber aborts, every fiber in the chain up to (but not including) the one that catches the error via try() is properly marked as errored.
Also remove the old nested fiber tests from try.wren and add a new comprehensive test (try_through_call.wren) that verifies the percolation behavior through multiple levels of fiber calls and confirms that intermediate fibers carry the error while the catching fiber and its callers remain clean.
Move the aborted fiber check in runFiber to execute before the "already called" check, ensuring a clear error message for aborted fibers. In runtimeError, replace the single caller unhook with a loop that walks the fiber chain, preserving the callerIsTrying flag and only unhooking fibers until a try-catch boundary is found, enabling proper error propagation through nested fiber calls. Add test cases for one and two levels of nested fiber try calls.
Implement the `round` method on the Num class that rounds a number to the nearest integer using standard rounding rules (away from zero for .5). The implementation adds a new `num_round` primitive function in the VM core, registers it as a method on the Num class, includes documentation in the core module reference, and adds a comprehensive test file covering positive, negative, zero, and fractional cases.
When a module is loaded via the host's loadModuleFn callback, the source string is dynamically allocated and ownership is transferred to the VM. Previously, this memory was never freed after compilation, causing a leak. This change introduces an `allocatedSource` flag that tracks whether the source was dynamically allocated (host-provided) or a static constant (built-in modules like "random"). After `loadModule` completes, the source is freed if it was dynamically allocated, resolving the memory leak described in issue #430.
Move the aborted fiber check before the already-called check in runFiber to catch errors earlier. In runtimeError, walk the caller chain to find the nearest fiber with callerIsTrying set, properly propagating errors through nested fiber calls. Add test cases for two and three levels of nested fiber try blocks.
When the lexer encounters an invalid character, it now explicitly sets the
current token's type to TOKEN_ERROR and its length to 0. Previously, the
token retained the previous token's type, which could cause the compiler
to get stuck in an infinite loop in certain code paths.
Also improves the error message for non-ASCII invalid bytes by showing
the raw hex value instead of attempting to display the character, since
the lexer operates on raw bytes without UTF-8 decoding.
Adds regression test for issue #428 that was crashing the compiler with
an out-of-bounds memory access due to this loop.
Add a new test suite for user data get/set operations on WrenVM, including
a Wren script that validates default NULL userData, retrieval after VM
creation, and mutation via wrenSetUserData. Also reorder the vm parameter
in WrenErrorFn callback signature and all its call sites to match the
convention used by other callback types, updating the typedef, the
reportError implementation, and all invocations in the compiler and
debug modules. Include the new source and header files in the Xcode
project build configuration.
Extend WrenConfiguration with a void* userData field for attaching arbitrary state to a VM instance. Add wrenGetUserData and wrenSetUserData accessor functions. Modify the WrenErrorFn typedef and all call sites (compiler, debug, VM) to pass the WrenVM pointer as the final argument, enabling error handlers to access VM context.
Add type and range checks to Sequence.skip() and Sequence.take() methods, aborting with "Count must be a non-negative integer." when count is not a non-negative integer. Rename split() parameter from 'delim' to 'delimiter' and update its error message from "Argument must be a non-empty string." to "Delimiter must be a non-empty string." for consistency. Remove old behavior allowing negative counts and add new test files for skip/take validation errors.
Implement `skip(count)` and `take(count)` on the Sequence class, backed by new SkipSequence and TakeSequence wrapper classes that lazily skip or limit iteration. Includes documentation in the sequence module markdown, core source definitions, and unit tests for edge cases (zero, negative, and overflow counts).
Adds `log` method to Num class via DEF_NUM_FN macro, and `pow(_)` method via a new DEF_PRIMITIVE that calls C's `pow()`. Registers both primitives in wrenInitializeCore. Includes new test files `test/core/number/log.wren` and `test/core/number/pow.wren` covering basic cases and edge values (negative input for log, zero exponent for pow). Also adds Sven Bergström to AUTHORS.
Add a `userData` void pointer field to `WrenConfiguration` and expose `wrenGetUserData`/`wrenSetUserData` functions for associating arbitrary state with a VM instance. Also update the `WrenErrorFn` signature to include the `WrenVM*` parameter, and propagate the VM pointer through all error callback invocations in the compiler, debug, and runtime paths.
The previous implementation used strtol() for parsing hex literals, which only supports up to 32-bit values on 32-bit architectures. This caused hex literals larger than 0x7FFFFFFF to overflow or fail. Changed to strtoll() which guarantees 64-bit parsing regardless of platform word size.
Also updated the error message to include the actual sizeof(long int) for debugging, uncommented the previously disabled 64-bit hex literal examples in syntax.wren, and added a test case for 0xdeadbeef to verify the fix works correctly.
The fpclassify() macro is not available in C++98, causing build failures on older compilers. This commit replaces the switch on fpclassify with explicit isnan() and isinf() checks, handling NaN and infinity edge cases directly. The logic for signed infinity is preserved by comparing value > 0.0 instead of using signbit(), which is also not guaranteed in C++98. This aligns with the existing usage of isnan/isinf elsewhere in the Wren codebase.
The bitwise operator tests used decimal literals exceeding u32 range, causing undefined behavior when converting double to u32 on some compilers. This replaces those values with hex equivalents that fit within u32, removes tests for values past max u32, and adds TODO comments for future handling. Also simplifies the `num_bitwiseNot` primitive by removing the intermediate `wideVal` variable, and fixes minor formatting in `util/test.py`.