Release Notes
1.41.3
Added
reflect.parametersandreflect.constructorsentries gainclassandelementClass: the class a parameter's type names (Widget,?Widget,geo.Widget,Box<int>), and the element class oflist<T>,set<T>orT[], resolved in the module that declares the function. Import aliases,fromimports and unexported classes resolve even when the caller is another module, so code that binds or documents typed parameters no longer has to look a type up by its string.reflect.fieldsentries gain the sameclassandelementClass, andreflect.fields(cls, inherited: true)lists inherited fields after the class's own, nearest ancestor first, each tagged withdeclaringClass. The default result is unchanged apart from the two new keys.reflect.returnClass(fn)returns{class, elementClass}for a function's declared return type, resolved the same way.reflect.class("module.Class")finds a class by its module's canonical name, exported or not, even from a module that never imported it.reflect.parentClass(cls)returns the parent class itself, resolved in the child's module, ornullfor no parent or a built-in parent such asError.xml.parseAsmaps elements onto class fields: child elements and attributes fill same-named fields converted to their declared types, nested elements fill class fields and repeated elements fill lists.xml.stringifyaccepts a class instance and writes it in the same shape, so the two round-trip. Previouslyxml.parseAsleft every fieldnull.
Performance
- Building a string with repeated
s = s + x(or+=, or interpolation) was quadratic on both runtimes: every append copied the whole string. Appends now reuse spare buffer capacity, so such loops are linear; 50,000 appends went from 3.2s to 31ms. - Deleting dict keys no longer shifts the key order on every delete: removing 40,000 keys went from 527ms to 62ms. Iteration order is unchanged.
- Calling methods on strings, lists, dicts, sets and bytes is about 40% faster, and dict literals about 30% faster.
reflect.fieldscaches each class's field metadata: an inherited lookup is about 2.4x faster and allocates about 80% less. Each call still returns fresh dicts.- Smaller savings: field writes on classes without field decorators, checked casts, string-key dict lookups for keys over 32 bytes, startup (the HTML sanitiser policy is built on first use), and the memory held by idle cross-module worker VMs.
- Programs that call between modules or hand callbacks to native functions at a high rate, such as HTTP servers, do less work per call. A pooled worker reuses its copy of top-level values that have not changed instead of recopying them, per-request runtime state is created only when used, and decorator tables are shared instead of copied. On a small JSON HTTP endpoint this served 15 to 20% more requests per second, with about a third lower p99 latency and about 18% lower peak memory.
- The deployment guide now gives measured
GOGC/GOMEMLIMITtrade-offs for HTTP servers.
Fixes
re.compileandpcre.compilekept every compiled pattern for the life of the process, so a program compiling patterns repeatedly grew without bound (about 210 bytes per compile). A pattern is now freed with itsPatternvalue.json.parseAs,yaml.parseAsandtoml.parseAsleft every inherited fieldnullwhen the target was a subclass. Inherited fields, including those declared in another module, are now filled and type-checked like the class's own.http.jsonResponse,http.postJsonand structured log fields rejected class instances withjson.stringify does not support X. They now encode exactly asjson.stringifydoes: public fields, inherited included, or the__serialize()result. Output for other values is unchanged.json.stringifyescaped<,>and&in the string fields of class instances (\u003cb\u003e) but not in dicts, lists or plain strings. Instance fields are now written as-is too.- On the bytecode VM,
reflect.returnTypedropped type arguments (listforlist<int>,?Boxfor?Box<int>) on functions, lambdas, methods and static methods. It now reports the full type, as the evaluator always did. - On the bytecode VM,
reflect.classwith a name string whose prefix is an import alias (let n = "geo.Widget"; reflect.class(n)) returnednull; it now resolves to the aliased module's export, as on the evaluator. - A bare
reflect.class("Widget")that several modules could satisfy picked the last-declared class on the evaluator and an arbitrary one on the bytecode VM. Both now look in the calling module, then the entry script, then the other modules in alphabetical order. parseAsresolved a nested field's class by its bare name, so a field typedgeo.Widgetcould be filled with another module'sWidget. Nested fields now use the class the declaring module names.
1.41.2
Fixes
- A module-qualified type annotation naming a class, interface or enum that
its module declares without
export(lib.Boxin a parameter, a local or atypealias) was rejected withlib has no exported type Boxbygeblang check,geblang run,geblang testandgeblang build, a regression in 1.41.1. Such types are accepted again; calling or importing an unexported name (lib.Box(),from lib import Box) is still an error.
1.41.1
Added
secureRandom.randomInts(s, count, min, max)drawscountunbiased ints in[min, max]as one audited draw: one nonce and one audit-log entry whose output is the whole list, whichreplayreproduces. A round that needs many values (a reel feature placing 150 symbols, say) pays the per-draw cost of building and logging a message once instead of for every value.geblang run --go-profile <cpu.pprof>(orGEBLANG_GO_PROFILE) profiles the engine itself: it writes Go's CPU profile, and lock-contention and heap profiles beside it, when the program exits, forgo tool pprof. Where--profileshows which Geblang code is slow, this shows time going to garbage collection, memory or contention between workers.
Performance
- A call into another module's code (a method on an object from another module, a constructor, a module function) runs on a pooled VM, and handing it back cleared the VM's whole stack, locals and frame capacity, about 90 KB, however little the call used. It now clears only what the call wrote. Cross-module-heavy code runs faster, most of all on several workers at once, where the clearing saturated memory bandwidth: a slot simulation on 6 workers ran 67% faster.
Fixes
geblang check(and the language server) treated every top-level declaration as exported, solib.Helper()passed the check whenHelperlackedexport, and both runtimes then failed withmodule lib has no export Helper. The checker now counts onlyexportdeclarations, for members,fromimports and qualified types alike.- On the evaluator, a method overriding a parent class's method from another
module no longer becomes a second overload when it names a parameter type
through the import alias (
base.Statein the subclass,Statein the parent's module). Calls to it failed withambiguous overload; the bytecode VM already treated it as the override.
1.41.0
Added
- Opt-in project workspaces: list packages with
workspace.membersand reference them explicitly withworkspace: truedependencies. Existing package layouts, source paths, imports and path/Git dependencies remain supported. geblang install --workspaceinstalls member and transitive dependencies, retaining each package's vendor directory and lockfile.geblang resolvereports an import's source file or built-in provider.--explainshows candidate paths and manifests,--fromchooses the lookup context, and--strictopts into failure on competing providers.
Fixes
- Dependencies are traversed in alphabetical key order. Previously, an ambiguous import could select different providers on repeated lookups.
- Import-check diagnostics include the underlying resolution error, including workspace configuration problems.
- Database
Rowsno longer accumulate for the life of the process. Every query result stayed registered with its cached rows until the program exited, closed or not, so a long-running server's memory grew with each query (about 14 KB each for a small select). ARowsvalue that is closed, read to the end or no longer referenced is now freed, and one dropped part-way through is closed and returns its connection to the pool. - A failed checked collection cast now says what failed and where, on both
runtimes:
cannot cast dict to dict<string, any>: key 0 is int, not string, or: ["stats"][2] is string, not intfor a nested element. It previously said onlycannot cast dict to dict<string, any>.
1.40.4
Fixes
- On the VM, an interface that extends an interface from a module imported
under an alias (
import lib.base as basemod;theninterface Contract extends basemod.Contract {}) lost that parent when used from other modules. A class implementing it did not match the parent ininstanceof, and casting it to the parent failed withcannot cast. The evaluator was not affected. - On the VM,
reflect.interfaceParentsreturns bare parent names for a qualified parent, as the evaluator does. - On the VM,
reflect.interfaceParents,reflect.interfaceMethodsandreflect.docaccept an interface imported from another module instead of failing withexpects interface, got interface.
1.40.3
Performance
- Recovers most of the performance lost in 1.40.1. Releasing references held by popped values and returned call frames no longer clears memory in bulk on every instruction, and values that hold no reference skip the release, so call-heavy and arithmetic code is close to 1.40.0 speed again. Memory is still released as in 1.40.1.
- JSON encoding runs its cycle and nesting-depth checks only on lists, dictionaries and objects and compares ancestors by identity directly, recovering most of the encoding time added in 1.40.1. The checks themselves are unchanged.
- Casts to
string,int,decimal,floatandboolof values other than objects and errors take a direct path, removing the per-cast overhead added in 1.40.1. Results and errors are unchanged.
1.40.2
Fixes
- Regression in 1.40.1: on the evaluator, a qualified type in a cast (such as
value as cfg.Weights) resolved its module alias from the most recent import of that alias anywhere in the program instead of from its own module's import. When two modules imported different modules under the same alias, the cast failed withcannot cast. The VM was not affected.
1.40.1
Added
string.byteLength()counts stored UTF-8 bytes without a bytes conversion.cli.promptDecimalshares nullable typed-prompt validation, defaults and cancellation withpromptInt, using exact decimal parsing.money.sumtotals same-currency values in integer minor units; empty input requires an explicit currency.json.write(value, stream)writes incrementally, retries short writes and leaves the borrowed stream open and unflushed. Formatting matches stringify.- Optional
MemoryCacheStore(ttl, capacity)capacity enables LRU eviction and expiry reclamation while preserving shared state and JSON snapshot isolation. - LSP completion/signature help and VS Code snippets cover the added APIs; primitive method signature help now uses the shared method catalog.
- The bytecode VM runs non-literal parameter defaults, instance field defaults
and static initializers, which previously required the evaluator. Defaults
run on every call or new instance and see earlier parameters, captured
variables and
this(parameters) or the declaring module (fields).
Fixes
- Imported ancestor fields retain literal defaults even without constructors. Mutable collection defaults remain independent between instances.
- Computed parameter defaults are type-checked like supplied arguments; the evaluator previously accepted a value of the wrong type.
- Errors inside a parameter default are reported from the called function; errors inside a field default are reported under the class name followed by the line that created the instance.
- Traces after a call returns report the caller's line instead of the last line run inside the callee.
- Reading a global before its declaration runs reports
"name" is not declaredon the VM, matching the evaluator. geblang checktype-checks a parameter default against the names in scope at that point, not against the parameter itself.- An exception raised while importing a module keeps its class and message on
both backends and can be caught around the
import; traces show the module as<module NAME>. The evaluator previously replaced it with a generic import error and the VM wrapped runtime faults twice.sys.exitduring an import now exits on the evaluator too, and module rejections readmodule NAME: ...on both backends. - A class without a constructor runs the nearest ancestor constructor that takes no arguments, including through intermediate classes without constructors and across modules. Previously such classes skipped the parent constructor, and the VM skipped it for an empty child constructor whose parent was in another module.
- Traces for an automatic parent constructor call include the child constructor's frame on both backends.
- Release dead VM operand/local references after returns and exception unwinding, preserving escaped closure cells. Evaluator call/defer frames also release completed environments and arguments. Trim unusually large idle worker stacks.
- JSON encoding rejects mixed-key dictionaries and cyclic values with catchable errors. Parsing and encoding limit container nesting to 512 levels.
- Native router state is locked on the shared app rather than a callback evaluator, fixing races between async registration and dispatch.
- Unpaired JSON Unicode surrogates no longer swallow the next escape.
- Checked casts retain nested collection and reified class arguments, including nullable list shorthand, aliases, inherited generic bases and module identity. A same-named class from a different module no longer satisfies a checked cast.
- Explicit generic arguments survive imported and stored function calls, named and spread calls, async/generator workers and executable decorator forwarding. Overload selection uses each candidate's declared type parameters. Failed spread expansion cannot bind the next call accidentally.
- Isolated request snapshots preserve class/interface declaring-module identity and generic parent arguments. Raw native stream handles retain qualified casts consistently across both runtimes.
Performance
- Escaped JSON tokens grow their own buffers instead of reserving the remainder of the document. Encoder pools clear references and discard oversized buffers.
- Compact dictionaries allocate storage for their actual small-object size; spilled dictionaries release their compact storage. JSON builds small objects at their final size.
- Dictionary key checks use mutation-aware shared metadata;
anycollection elements need no repeated structural walks. Concrete element checks remain. - Native route dispatch captures append-only index/middleware prefixes under an app lock and uses precompiled path segments, preserving first-match order and the parameter dictionary's existing key order.
- Character indexing reuses immutable ASCII values and single-character Unicode metadata. Small JSON strings from large documents own their storage.
1.40.0
Added
- Source stdlib
moneysupplies immutable currency values backed by arbitrary-size integer minor units. Exact construction, currency-checked arithmetic, explicit rounding, total-preserving allocation and exact JSON serialization share the existing locale currency data. locale.currencyInfo(code)returns a canonical currency code and its standard fraction digits.geblang test --jobs Nruns files in isolated worker processes. Output remains in discovery order, failures are aggregated, and the default is one worker.
Performance
-
String-key dictionary reads avoid allocating an encoded key, including indexing, membership, primitive methods and native option lookups.
-
Test preparation validates the merged source and generated runner in one semantic pass. Files without selected tests still receive validation.
-
Embedded source stdlib resolution reuses the executable's content digest within a process, retaining version/content cache invalidation.
Fixes
- Unclosed declaration and control-flow bodies report a syntax error at EOF.
- Method values inherited through a local subclass of an imported ancestor resolve to the ancestor's module before magic property fallback.
- Bound method values accept named arguments and omitted defaults, including methods inherited from another module.
- Omitted collection defaults are fresh for each named call; explicitly supplied collections retain their identity even when equal to a declared default.
- Variadic method calls check each element's type, including the receiver's concrete generic bindings in direct and stored-method calls.
- Local descendants acquire concrete type bindings from imported ancestors even when the descendant has no type parameters of its own.
- Parallel workers use the divergence ledger selected for the parent invocation.
1.39.0
Added
- Nullable
cli.readLineandcli.promptIntdistinguish EOF and cancellation from valid input. Typed prompts retry invalid input and support defaults, bounds, choices and custom validation. - Shared validation rules for
cli.parseArgsand fluentcli.command.Optionbuilders. cli.withContextscopes input, output, prompt suppression and terminal detection, restoring the previous context after success or failure.cli.widgets.withProgressprovides throttled terminal progress, guaranteed display cleanup and cooperative cancellation throughCancelledError.- Table column descriptors support alignment, format specifications and typed record accessors. Strict tables reject missing keys.
- String
toIntOrNull,toDecimalOrNullandtoFloatOrNullmethods return null for invalid numeric text. - Session-free
secureRandom.bool()andsecureRandom.choice(items)use cryptographic randomness alongside the existing audited overloads.
Fixes
- CLI and IO readers share buffered stdin without losing piped input. Nested input scopes restore unread outer input after exceptions.
- Confirmation and choice prompts retry invalid answers and raise
IOErrorat EOF. Blank confirmation uses its default, false when omitted. - Progress rendering rejects invalid widths and handles very large task counts without integer overflow.
- Formatting preserves indentation for nested multi-line callbacks.
- Assigning a local subclass to an imported ancestor type follows the source module's parent chain without loading or executing it.
- Native named arguments select the matching overload, preserving audited RNG calls alongside their session-free forms.
1.38.1
Fixes
- Returning from
finallyoverrides a pending return without crashing the compiler or leaving extra values on the VM stack. Breaking or continuing an inner loop does not run an enclosing finalizer early. - Nested named functions retain captured locals after their enclosing call returns, including recursive functions and captures through another lambda. Decorated functions retain their declared argument names, defaults and overload types on both runtimes. Closure capture respects block and comprehension scope, including a loop binding that shadows its iterable.
- Async calls made through callbacks no longer share instruction storage that a reused worker can rewrite concurrently. Socket callbacks do not read temporary execution state from their running parent VM.
- Idle VM workers release completed call arguments and stale global snapshots instead of keeping them alive between calls.
- List loops observe element replacements, including after a list grows, and stop at the original length. Appending does not extend the loop.
- Explicit
nullrange starts and steps are rejected on both runtimes. Character ranges evaluate an explicitbyexpression once on both. - Debugger breakpoints in imported functions and inherited methods stop in their defining files, and caller frames keep their own source paths.
- Editor rename respects lexical bindings and leaves plain strings, comments and unrelated variables unchanged. Navigation, rename, completion and diagnostic positions account for UTF-16 text positions.
- The language server exits on an
exitnotification without waiting for its input to close, and reports the current toolchain version. - Sampling deduplicates workers running on the same goroutine. A profile
report ending in
.foldedkeeps a separate folded-stack output file. Starting a collection is safe while cancelled workers finish.
Performance
- HTTP callback contexts construct builtin function tables only when they need them, reducing request setup allocations while preserving isolation.
1.38.0
New
- The
browsermodule can capture more than the visible viewport.screenshot(path, opts)takes one element (selector), aclipregion or thefullPage, inpng,jpegorwebpwith aquality, and can make the background transparent.screenshotBytes(opts)returns the image as bytes.pdf(path, opts)takes page size, margins, orientation, scale and page ranges.startScreencast(dir, opts)andstopScreencast()record the page as numbered frames. - Visual regression checks:
browser.compareImages(a, b, opts)compares two PNG, JPEG or WebP images (paths or bytes) pixel by pixel with a per-pixelthreshold, and can write a highlighted difference image.page.matchScreenshot(baseline, opts)compares a screenshot with a stored baseline, creating or updating it as asked. - Viewport and device emulation: a
viewportoption onbrowser.launch, andsetViewport,setUserAgent,emulateMedia(print media, dark colour scheme, reduced motion),setTimezone,setLocale,setOffline,setExtraHeadersandemulateFocuson pages. - Waiting:
waitForFunction(js, opts)waits until a JavaScript expression is truthy and returns its value;waitFortakes astate(attached,visible,hidden,detached);goto,reload,goBack,goForwardand the newwaitForNavigationtakewaitUntil(load,domcontentloaded,networkidle);waitForRequestandwaitForResponse(with the response body) wait for network traffic, andrequests()lists it. Every wait takestimeoutMsand raisesTimeoutError. consoleMessages(),pageErrors()andclearConsole()expose what a page logged and threw.onDialoganddialogs()answer and recordalert,confirmandpromptdialogs.- Downloads:
setDownloadPath(dir)andwaitForDownload(), which names the file after the server's suggested name. - Input by coordinates for canvas apps and games:
mouseMove,mouseDown,mouseUp,mouseClick,dragandwheel, pluskeyDown,keyUp, key combinations such aspress("Control+A"),hover,dblclick,focus,check,uncheck,setInputFiles, andbutton,clickCountandpositionoptions onclick. - Element queries:
count,isVisible,isEnabled,texts,innerHTML,inputValueandboundingBox. browser.newContext(opts)returns aContextwith its own cookies and storage, so several users can run side by side in one browser. Itsviewport,userAgent,localeandtimezoneapply to each page it opens.addInitScript,setContentandbringToFronton pages.geblang run --profile <report>(orGEBLANG_PROFILE=<report>, which also works for built binaries and servers) samples a program's Geblang call stacks about once a millisecond, across modules andasynctasks, and on exit writes the functions and lines it spent its time in, plus folded stacks for flame-graph tools. Both backends support it.sys.cpuCount()returns how many CPUs the process can run code on at once, for sizing a pool of CPU-boundasynctasks.sys.setGcPercent(percent)sets the garbage collector's target (Go'sGOGC) and returns the previous one, so a CPU-bound batch job can trade memory for fewer collections.
Changes
gotoandreloadnow wait for the page'sloadevent by default, report a navigation the browser could not complete as an error, and raiseTimeoutErrorwhen the page does not load in time. They previously returned once the document was interactive, and silently after 30 seconds.waitForraisesTimeoutError(a subclass ofError) when the selector does not appear in time.- Dialogs no longer block a page:
alert,confirmandpromptare dismissed unlessonDialog("accept")was called, andbeforeunloadis accepted. presssends proper key codes for named keys and rejects unknown key names.- Less garbage for the collector: cross-module calls reuse their worker VMs
(they were rebuilt after every collection), a
secureRandomsession keys its HMAC once instead of on every draw (outputs and verification are unchanged), and aforloop over a range literal counts without building a range value. - Calls into another module no longer take one process-wide lock. Calls
that read a module's globals share a per-module read lock, and a call that
reassigned none takes no lock on return, so CPU-bound
asynctasks calling into the same modules scale better across cores (about 20% more throughput with 12 tasks in one benchmark).
Fixes
list.slice()returns a new list. It shared the original's storage, so assigning to an element of the slice changed the original, and pushing onto it could overwrite the original's next element.- A call to a function in another module can name a later argument and leave
defaulted parameters before it out (
lib.run(1, workers: 4)). The VM reported "missing argument" for the skipped parameter; the evaluator was already correct.
Older releases (1.0.0 through 1.37.8) are archived: see the release notes archive.