# Flaris Changelog

New features and fixed bugs in each release, newest first.

## 1.0.3.0 - 2026-09-15

### Added

- **Multiple isolated script environments per VM (embedding).** `libflaris`
  gained `FlarisCreateContext`/`FlarisDestroyContext`: each context is its own
  script's globals, capabilities, denied modules and budgets, sharing one VM,
  thread and object pool. Creating a context, running a script to completion
  and tearing it down costs tens of microseconds, so a host can give every
  request, tenant or plugin its own throwaway context instead of restarting
  the whole VM to reload one script. See the embedding guide.
- **String interpolation, `$"..."`.** Compiles to `String.Format`; `{expr}`,
  `{expr,align}` and `{expr:spec}` holes, `{{`/`}}` to escape a literal brace,
  up to 15 holes per literal. `$"""..."""` is not supported.
- **Null-conditional member and index access, `obj?.prop` and `arr?.[i]`.**
  Short-circuits to `nil` instead of raising when the receiver is `nil`, so
  `user?.address.city` and `cfg?.items?.[0] ?? 3` no longer need a manual
  nil-check first.
- **Labeled loops.** Any `while`, `do ... while`, `for`, `iter` or `foreach`
  can carry a `label:` prefix, and `break label;`/`continue label;` target it
  from inside a nested loop. Labels can't cross a function/lambda boundary or
  jump out of a `finally`; an unknown or duplicate label is error 1006.
- **Unsigned right shift `>>>` and `>>>=`.** A logical (zero-filling) shift of
  the full 64-bit `int`, with the count masked to 0..63 like `<<` and `>>`:
  `-8 >>> 1` is 9223372036854775804. Folded by the optimiser and compiled by
  both JIT back ends; a `float` operand is rejected at compile time.
- **`--allow-ffi`**, alongside `--unsafe`. Grants `Ffi.*` only, without also
  opening raw `Memory`, `Os.Kill` and plain-`http://` imports, so a script
  that just needs one native plugin no longer needs the whole unsafe surface.
  `--unsafe` still implies it.
- **`-Werror=<name>`** fails the compile on one named analyzer warning (e.g.
  `-Werror=no-effect`), instead of `-Werror`'s blanket treatment of every
  warning as an error.
- **FFI handles are now a tagged opaque pointer type**, not a bare `int`. A
  plugin returns a handle carrying a tag it chooses; a script can hold and
  pass it back but can no longer forge one by constructing an arbitrary
  integer.
- **`Os.Execute`/`Os.ExecuteAsync` can verify the binary before it runs.** An
  optional `sha256` argument hashes the command's leading binary and refuses
  to launch anything (returning `nil`) on a mismatch. Fails closed on a
  missing/unreadable file, a malformed digest, or a bare command name resolved
  through `PATH` - see "Verifying the binary before it runs" in the reference.
- **Three new exception codes**: `Exception.ModuleDenied` (25) for a builtin
  module an embedding host withheld, and the uncatchable
  `Exception.Cancelled` (26, from `Fiber.Cancel`) and `Exception.Interrupted`
  (27, from a host stopping the script) - both still run every enclosing
  `finally` on the way out.
- **Shebang scripts.** A file starting with `#!` runs its top level as a
  synthesized `Main()`.
- Multi-line raw strings (`"""`) strip the closing line's indentation from
  every line; `case A, B:` shares one switch body between comma-separated
  labels; identifiers and numeric literals longer than 1024 bytes are now a
  compile error; source containing a bidirectional Unicode control character
  (the "Trojan Source" attack) is rejected wherever it appears.

### Changed

- **The JIT is on by default.** `--jit-disable` turns it off; the old opt-in
  `--jit` flag is now a no-op. Run untrusted bytecode with `--jit-disable`.
- **`^^` (power) binds tighter than every other operator, including a unary
  minus on its left, and nests to the right**: `2 * 3 ^^ 2` is 18,
  `2 ^^ 3 ^^ 2` is 512 (was 64 under the old left-associative reading),
  `-2 ^^ 2` is -4. Integer `^^` is now exact and wraps on overflow like `*`
  instead of always truncating; a negative exponent still gives 0 except for
  the bases 1 and -1.
- **`int(x)` saturates** to `Math.Int64Max`/`Math.Int64Min` for an
  out-of-range float instead of undefined truncation (`NaN` still gives `0`),
  and a numeric string converts the same way.
- **A duplicate enum member name is now a compile error** instead of being
  accepted with the first value silently winning; enum members may now be
  negative literals (`A = -1`), previously rejected.
- **Closures can capture across any number of enclosing function levels**, not
  just the immediately enclosing one, and a bare reference to the owning
  class's own field or method inside a lambda now implicitly captures `this`
  the same way `this.field` does.
- **`Math.Mean`, `Median`, `Mode`, `Stddev`, `Variance` and `RandomNormal` are
  removed** from the core module. Statistics now lives in the `Numerics`
  library's `Stats` class alongside its distributions and regression support.
- **`[int]` arrays are stored flat** - a raw 64-bit integer buffer, as `[float]`
  arrays already were. Every element holds the full 64-bit range, the JIT reads
  and writes elements without tagging, and `.flx` files record the layout.
  Bulk operations gain the most: `Array.Clone` is now a block copy and
  `Array.Contains` a linear scan, both roughly ten times faster on a large
  array, and an array of values beyond ±2^60 uses a quarter of the memory.
- **Float-heavy code is 11-18% faster.** Every `float` value is a heap box,
  since the immediate encoding has no spare tag and a double needs all 64 bits.
  Released `int` and `float` boxes are now parked in a small per-VM cache and
  handed straight back to the next one, instead of going the long way through
  the deferred-free list. Reading a wide `int` out of a flat array gains the
  same way.
- Tail calls (`return f(...)`) now enter a callee's compiled JIT code
  directly when it has one, instead of always going through the interpreter.

### Fixed

- **The JIT truncated ints outside ±2^60 stored into class fields**, and read
  garbage from a field the interpreter had boxed. Field loads and stores now
  detect the boxed form.
- **A call inside a `new { ... }` initializer block to a function sharing the
  constructor's own name re-entered the block** instead of making a plain
  call, corrupting the object under construction.
- An FFI callback re-entering from a worker thread is now dropped with a
  warning instead of relying on a bare thread check.
- An older FFI plugin ABI version is now accepted (fields append forward-
  compatibly) rather than only an exact or unrecognized version.

### Bytecode

- The opcode set grew from 174 to 198: `OP_IMM_F32` is removed; `OP_USHR` is
  inserted directly after `OP_SHR`; and new opcodes cover the flat `[int]`
  array form, host-module calls from embedding, and more self-patching
  fast paths for `this`-field/array access and `super` calls. Every opcode
  number after the insertion point shifted. The bytecode floor is now
  1.0.3.0: a `.flx` built by an earlier version must be recompiled.

## 1.0.2.1 - 2026-09-01

### Fixed

- **`Collections.PriorityQueue` and `MaxPriorityQueue` crashed the VM** on the
  first `Enqueue`. The min/max flag is a tagged value and was read as a pointer.
  Both are now covered by tests, as are `LinkedList` and `OrderedMap`, which had
  no coverage either.
- **`s += piece` was quadratic** unless the optimiser happened to fuse the
  concatenation: `s += a; s += b;`, `this.field += piece`, `obj.field += piece`
  and `a[i] += piece` all copied the whole accumulated string each step. All are
  linear now.
- **`Time.Parse` returned nil for `1969-12-31T23:59:59`** - the one instant whose
  timestamp, -1, is also the failure value the underlying C call reports. Dates
  before 1901 that the platform previously declined now parse too.
- **`--check` reported "reads a global variable"** for a method that returns
  `this`, which reads no global.

### Bytecode

- One opcode appended (`OP_OBJ_ARITH_L`, number 175). `.flx` files built with
  1.0.1.1 or later still load; files built with **1.0.2.1 do not load on an
  earlier VM**, which rejects them by version rather than misreading them.

## 1.0.2.0 - 2026-08-30

### New

- **Object initializer blocks** - a `new` expression can carry a trailing block
  that runs on the fresh instance right after the constructor:
  `let p = new Point(3) { y = scale(2); };`
- **Embedding the VM in a C application (beta)** - `libflaris` ships as a static
  and shared library with `flaris.h`. See the embedding guide.
- **`--bundle`** packs every module the program imports, transitively, into one
  self-contained `.flx`.
- **flarispm 0.5.0** - a `flarispm.lock` lockfile pins the exact commit and
  hashes for reproducible installs, plus `flarispm verify` for CI and
  `flarispm prune` for packages nothing references.
- **`Class.Define(name, fields)`** builds a real, sealed class at run time.
- **`Os.RunExTimeout` and `Os.WaitTimeout`** - run or wait for a command with a
  deadline.
- **`Hash.Crc8` and `Hash.Crc16`**.
- **Serial flow control** (`rtscts`, `xonxoff`) and the full baud range in
  `Stream.OpenSerial`.
- **Nil-narrowing** - error 1000 no longer fires after `if (x != nil)` or an
  early `return`, so code that already checked compiles.

### Fixed

- Printing a string no longer re-interprets escape sequences. A literal
  backslash arriving in data was expanded as though it had been typed in
  source; bytes are now written verbatim.
- `Stream.ReadBytes` keeps what it already read when a timeout fires part-way
  through, instead of discarding bytes that are already gone from the socket.
- `Stream.ReadString`, `Stream.ReadLine` and `Stream.WriteString` are
  binary-safe: an embedded NUL is data, not a terminator.
- The JIT no longer refuses functions whose only exit is a `return` inside
  `while (true)` or `for (;;)`. They ran interpreted, so decompression (zip,
  gzip, compressed HTTP) is substantially faster in this release.
- Faster FFI marshalling for arrays of uniform primitives.
- A JIT chunk that fails validation now reports why instead of quietly falling
  back to the interpreter.
- VS Code: the "Run current file (unsafe)" task passes `--unsafe` again.

### Compatibility

`.flx` bytecode built with 1.0.1.1 still loads - the instruction set did not
change in this release.
