tom_d4rt 1.22.0
tom_d4rt: ^1.22.0 copied to clipboard
D4rt - A Dart interpreter and runtime with bridging, security sandboxing, and dynamic code execution. Fork of D4rt with extended features.
1.22.0 #
Changed — filesystem permission scopes are symlink-aware (DGUB5) #
FilesystemPermission now compares the grant and the requested path on
their REAL paths, with symlinks resolved, instead of on their literal
spellings. Both halves of the old behaviour are corrected:
-
A grant on a resolved path now admits an unresolved spelling of the same location. This was a routine annoyance on macOS, where
Directory.systemTemphands back/var/folders/...— itself a symlink to/private/var/folders/...— so granting a resolved path and then reading through the unresolved one was denied for no visible reason. -
A symlink inside a granted directory no longer reaches outside it. This is the security-relevant half:
<sandbox>/link_to_elsewhere/xused to satisfy a<sandbox>grant because it was lexically in scope, while actually reading from wherever the link pointed.
This is a tightening, so it can deny operations that previously succeeded — specifically, any access that relied on a symlink to leave its granted directory. Grants that name the same location the operation really touches are unaffected, whichever way either side is spelled.
Paths that do not exist yet are still matched: resolution walks up to the
deepest existing ancestor and re-appends the remainder, so a writePath
grant consulted before the file is created behaves as it always did — and
still notices a symlinked ancestor. Resolution failures (broken links,
racing deletions) fall back to the literal spelling rather than throwing.
1.21.0 #
Added — JsonUtf8Encoder and ClosableStringSink (SC9) #
Completes the P2 row of the stdlib SDK gap audit.
-
JsonUtf8Encoder(dart:convert) — object to UTF-8 JSON bytes in one pass, withconvert,startChunkedConversion,fuse,bindandcast, and all three optional constructor arguments (indent,toEncodable,bufferSize) read by position so that anullindent keeps its meaning.This repairs a live dead end rather than merely widening coverage. The SDK specialises
JsonEncoder.fuse, soJsonEncoder().fuse( Utf8Encoder())has always returned a nativeJsonUtf8Encoderthrough the long-shippedfuseadapter — and every call on the result then failed withUndefined property or method 'convert' on JsonUtf8Encoder. -
ClosableStringSink(dart:convert) —fromStringSink,close, and the fullStringSinksurface (write,writeln,writeCharCode,writeAll) declared explicitly, since bridge dispatch is per-bridge.
Fixed — two dart:convert bridges were unreachable #
StringConversionConvert and ChunkedConversionConvert were fully
written and exported from convert.dart but never passed to
defineBridge, so no script could name either. That left
Converter.startChunkedConversion uncallable across the whole library —
nothing could construct the sink argument it requires — and made
asStringSink(), the idiomatic route to a ClosableStringSink,
unreachable. Both are now registered, and StringConversionSink gains an
asStringSink adapter.
Fixed — sink dispatch after registering the hierarchy root #
Giving ChunkedConversionSink an isAssignable predicate makes it match
every sink in the library, and because each is handed back as a private
class the resolver always lands in the isAssignable pass. The root
therefore swallowed its own subtypes. Following the
QueueHierarchyCollection precedent, the edges are now declared via
BridgedClass.registerSupertypes in convert/convert_hierarchy.dart, and
ByteConversionSink carries its own predicate and nativeNames so the
most-specific filter has a candidate to keep.
Tests #
23 script-level tests (F-SC9-1 … F-SC9-23) under
test/stdlib/convert/, mirrored by 15 registration-level tests
(F-SC9-AST-*) in tom_d4rt_ast.
1.20.0 #
Added — BytesBuilder (SC8) #
BytesBuilder from dart:typed_data is now bridged, closing the last
typed-data entry on the P2 gap list. Scripts can accumulate bytes
incrementally instead of rebuilding a list on every append:
import 'dart:typed_data';
main() {
final b = BytesBuilder();
b.addByte(1);
b.add([2, 3]);
return b.takeBytes(); // Uint8List [1, 2, 3]
}
The full surface is available: the constructor with its copy: flag,
addByte, add, takeBytes, toBytes, clear, length, isEmpty and
isNotEmpty. toBytes and takeBytes return objects that route to the
existing Uint8List bridge, so indexing and sublist work on the result
without further registration.
Both private implementations are routed. BytesBuilder is abstract and
its only constructor is a factory returning _CopyingBytesBuilder by default
or _BytesBuilder under copy: false. This is the first bridge where a
constructor argument decides which private class comes back, so both names
are listed on nativeNames — with only the default, BytesBuilder(copy: false) would construct successfully and then fail on its first addByte.
Argument mistakes surface as catchable script errors rather than host type
errors: a positional argument to the constructor (the natural misreading of
BytesBuilder({bool copy})), a non-int to addByte, and a list carrying a
non-int element to add — the last naming the offending element.
1.19.0 #
Added — DoubleLinkedQueue and its entry cursor (SC7) #
DoubleLinkedQueue is now bridged, together with the DoubleLinkedQueueEntry
cursor that is the type's entire reason to exist. Both are mirrored into
tom_d4rt_ast.
- The entry type is not optional.
DoubleLinkedQueuediffers from the already-bridgedListQueuein exactly one way:firstEntry/lastEntry/forEachEntryhand out cursors that splice in place. Bridging the queue without the cursor would have shipped a slowerListQueuewith no reason to exist. nativeNames: ['_DoubleLinkedQueueElement']on the entry bridge.firstEntry()returns that private SDK subclass, not aDoubleLinkedQueueEntry— without the routing the entry API would hand back objects that reach no bridge at all. Same pattern as_TypeErrorin 1.17.0.
Fixed — queues could not reach their inherited Iterable surface #
Pre-existing breakage, not introduced here. Bridges are registered flat and
dispatch is per-bridge, so the shipped ListQueue bridge could not reach the
~30 Iterable members it inherits: .where, .map, .join and even
.contains failed with "has no instance method named", and q is Iterable
was false. (contains is the sharp case — the Queue bridge has always
declared it, but a native ListQueue dispatches to the ListQueue bridge,
which did not.)
QueueHierarchyCollection declares DoubleLinkedQueue/ListQueue -> Queue
and Queue -> Iterable to BridgedClass.registerSupertypes. One block both
answers is correctly and lets the bridged-supertype walk find the inherited
members, for every queue type at once, instead of copying thirty adapters onto
each bridge.
The edges are deliberately not expressed by widening any isAssignable —
that predicate decides which bridge owns a native object in
Environment.toBridgedInstance, where every hand-written stdlib bridge ties at
hierarchyDepth == 0. Feeding the registry instead lets the resolver's
most-specific filter use the hierarchy to drop supertype matches, so this
makes dispatch more exact rather than less: a deque is not mistaken for a
ListQueue.
17 script-level tests under
test/stdlib/collection/double_linked_queue_test.dart, mirrored by 15
registration-level tests in tom_d4rt_ast.
1.18.0 #
Added — the P2 dart:async types (SC6) #
StreamView, AsyncError and StreamTransformerBase are now bridged and
mirrored into tom_d4rt_ast. Each needed a different registration shape, and
the differences are load-bearing:
StreamViewisclass StreamView<T> extends Stream<T>— a wrapper whose entire value is the ~60-memberStreamsurface it inherits. Bridge dispatch is per-bridge rather than hierarchical, so aStreamViewbridge declaring only its constructor would have left every inherited member unreachable. Instead of duplicating that surface,'StreamView'is listed on theStreambridge'snativeNames(so instances dispatch there) and theStreamView -> Streamedge goes in the supertype registry. The bridge deliberately declares noisAssignable, which would have contestedStream's ownership of every stream-shaped object.AsyncErroris concrete, so it can carry anisAssignablewithout shadowing a more specific bridge — the onedart:asyncbridge that does. It accepts the SDK-idiomatic one-argument form, andimplements Erroris registered soon Error catchsees it.StreamTransformerBaseexists purely to be extended, so it gets a null-returning default constructor (only sosuper()resolves) and theStreamTransformerBase -> StreamTransformeredge.
Stream.transform was broadened to accept a script transformer. An interpreted
transformer has no native object at all — its bind lives only in the
interpreter — so the adapter wraps it in StreamTransformer.fromBind. Both
extends StreamTransformerBase and implements StreamTransformer reach it.
Fixed — three generic interpreter gaps surfaced by the above #
None of these are dart:async-specific; all three affect every bridge.
is BridgedXwas hard-false for any interpreted operand.visitIsExpressionshort-circuited everyInterpretedInstancetofalseand never consultedInterpretedClass.isSubtypeOf, which exists precisely to answer that question (RC-7). A script class failed theistest against its own declared bridged superclass —class Doubler extends StreamTransformerBasemadeDoubler() is StreamTransformerBasefalse.implements SomeBridgewas not a subtype edge.InterpretedClass.isSubtypeOfwalkedbridgedSuperclassandbridgedMixinsbut skippedbridgedInterfacesentirely.- Method invocation never walked the bridged supertype chain. The Cluster-12
lookupOnBridgedSupertypeswalk was wired into the three property-access paths but not into invocation, sov.map(...)on a bridge inheritingmapfrom a registered supertype failed with "has no instance method named" even though thev.maptear-off resolved.
1.17.0 #
Added — the catchable dart:core error types (SC5) #
Seven SDK error classes had no BridgedClass, so their names did not resolve
at all: on NoSuchMethodError catch (e) fell through to the bare clause, and
AssertionError('boom') failed with Undefined variable: AssertionError.
All seven are now bridged and mirrored into tom_d4rt_ast:
NoSuchMethodError, ConcurrentModificationError, IndexError, TypeError,
AssertionError, StackOverflowError, OutOfMemoryError.
TypeError and AssertionError also claim the private VM subclasses
(_TypeError, _AssertionError) through nativeNames — those are what a
failing cast and a failing assert actually raise, so without the alias the
value would reach no bridge and on TypeError could never see it.
The dart:core error inheritance chain is declared through
BridgedClass.registerSupertypes (ErrorHierarchyCore), not by widening any
isAssignable closure. isAssignable decides which bridge owns a native
object and every hand-written stdlib bridge has hierarchyDepth == 0, so ties
break on registration order — a supertype that claimed assignability for its
subtypes could quietly steal dispatch. The registry feeds isSubtypeOf only,
so indexError is RangeError answers correctly while dispatch stays exact.
Fixed — on <BridgedType> catch could not see subtypes, or its own throws #
Two independent defects in visitTryStatement, both surfaced while building
the tests above and both affecting every bridge, not just these seven:
- A script-thrown bridged error was never matched.
throw StateError('x')produces aBridgedInstance, but every type test in the catch matcher — the hardcoded fast-path switch and the bridge comparison alike — asks about the native type. Soon StateErrorfailed to catch aStateErrorthe same script had just thrown. Matching now runs against an unwrapped native view; the catch variable is still bound to theBridgedInstance, so member access in the handler is unchanged. - Bridged matching was exact-identity only. It compared the thrown value's
own bridge against the catch type, which cannot see that
_TypeErroris aTypeErroror that anIndexErroris aRangeError. The matcher now asks the catch type's ownisAssignablepredicate first — a real Dartis— so the match is subtype-correct for every bridge.
Known gaps (not addressed here, tracked separately) #
The audit recorded that the interpreter already throws SDK-shaped errors and
only the bridge was missing. Probing showed that is true for
ConcurrentModificationError and StackOverflowError but not for list[9],
a failing cast, a missing method on dynamic, or a failing assert — those
still surface as RuntimeD4rtException. Symbol literals (#foo) also
evaluate to null; use Symbol('foo') until that is fixed.
1.16.0 #
Added — StreamConsumer bridge and working controller sinks (SC4) #
StreamConsumer (dart:async) is now bridged, mirrored into tom_d4rt_ast. It
was the last P1 async gap in the SDK audit: the name did not resolve, so
StreamConsumer annotations and x is StreamConsumer tests were both unusable.
Interface only — no constructor. Scripts never build one; they receive one (a
StreamController, its .sink, or an IOSink) and either annotate against it
or type-test it. The bridge exposes the two members the interface declares,
addStream(Stream) and close().
Fixed — StreamController.sink was inert #
Registering the interface alone would have made the type nameable while leaving
every value of it unusable. StreamController.sink returns a private
_StreamSinkWrapper, which resolved to no bridge at all — add, addError,
close, done and addStream all failed as "Undefined property or method on
_StreamSinkWrapper<dynamic>". The StreamSink bridge now claims that native
name, and gains the addStream it inherits from StreamConsumer in the SDK
(bridge dispatch is per-bridge rather than hierarchical, so an inherited member
has to be repeated on the concrete bridge or it is unreachable). Both controller
flavours — single-subscription and broadcast — hand out the same wrapper, so one
entry covers both.
Fixed — is against a bridge with no isAssignable was always false #
An is test against a bridged type returned a hard false for an unwrapped
native operand whenever the target bridge declared no isAssignable closure —
even when the operand's own bridge and the registered supertype chain both said
yes. StreamController.sink hit exactly that: it resolves to the StreamSink
bridge for member dispatch, so sink.close() worked while sink is StreamSink
was false. The is path now falls back to resolving the operand's bridge the
same way dispatch does and re-running the subtype walk. Purely additive — it
runs only where the answer was already a hard false, and adds no
isAssignable closure, so bridge selection is untouched.
Why the hierarchy is declared, not claimed #
The sink edges (StreamSink/StreamController -> StreamConsumer, and
StreamSink -> EventSink) are registered with
BridgedClass.registerSupertypes rather than by giving the new bridge an
isAssignable predicate. That closure is what Environment.toBridgedInstance
consults when deciding which bridge owns a native object, and StreamConsumer
is a supertype of both StreamController and StreamSink. Claiming
assignability would have entered the new bridge into that contest as an
equally-ranked match — every hand-written stdlib bridge has
hierarchyDepth == 0, so the tie breaks on registration order — and could have
silently stolen dispatch from the two concrete bridges. The supertype registry
feeds isSubtypeOf only, so is answers truthfully while dispatch is untouched.
1.15.0 #
Added — UnmodifiableMapView and UnmodifiableSetView bridges (SC3) #
The two read-only dart:collection views are now bridged, mirrored
file-for-file into tom_d4rt_ast. Both were P1 gaps in the SDK audit and
completed the set alongside the already-bridged UnmodifiableListView.
UnmodifiableMapView— wrapping constructor plus the read-onlyMapsurface ([],containsKey,containsValue,forEach,map,cast,length,isEmpty,isNotEmpty,keys,values,entries).UnmodifiableSetView— wrapping constructor plus the read-onlySet/Iterablesurface, including the set algebra (union,intersection,difference,lookup,containsAll).
Both are views, not copies: a later change to the backing collection is visible through the wrapper, and the tests pin that rather than only checking the initial contents.
Why the mutators delegate instead of throwing #
Map.unmodifiable(...) and Set.unmodifiable(...) already returned these exact
runtime types, and the core Map/Set bridges claimed them by name — so reads
worked and a mutation attempt surfaced the SDK UnsupportedError, catchable
from script with on UnsupportedError. The new bridges therefore delegate
every mutating member to the native view rather than raising a
RuntimeD4rtException of their own. Intercepting would have silently broken
scripts that catch the SDK error type today.
This differs from the older UnmodifiableListView bridge, which does intercept
and throw RuntimeD4rtException. Realigning it is tracked separately, since it
is a behaviour change to a shipped bridge with its own test impact.
Known gap (pre-existing, not introduced here) #
x is Map is false for every bridged dart:collection map — HashMap,
SplayTreeMap and now the map view alike — and likewise x is List for
UnmodifiableListView. Supertype is checks only work where the core bridge's
nativeNames happens to enumerate the concrete runtime type, which is the case
for Set but not for Map/List. Characterized by F-SC3-21 so the day it is
fixed shows up as a red test rather than going unnoticed.
1.14.0 #
Added — LinkedHashSet and SplayTreeSet collection bridges (SC2) #
The two ordered Set implementations from dart:collection are now bridged,
mirrored file-for-file into tom_d4rt_ast. Both were P1 gaps in the SDK audit:
scripts could already build a Set, but had no way to state which ordering
contract they depended on.
LinkedHashSet— iteration in insertion order. Constructors(),.fromand.of, plus the fullSet/Iterablesurface shared with the existingHashSetbridge.SplayTreeSet— iteration in sorted order. Same member surface; the constructors additionally accept the optionalcomparefunction (SplayTreeSet(compare),.from(elements, [compare]),.of(elements, [compare])), adapted from an interpreted function into a nativeComparator.
Both are registered by CollectionStdlib, so they resolve on
import 'dart:collection' like their siblings. LinkedHashSet.isAssignable
matches plain set literals — the same overlap the shipping LinkedHashMap
bridge already has with map literals — and the full suite confirms it does not
change how a bare {...} literal dispatches.
1.13.0 #
The first release after 1.12.1, carrying two batches: the fork-update
realignment against upstream kodjodevf/d4rt (DFUB1–DFUB13, below) and the
first stdlib gap-audit bridges (SC1/SC10/SC11, further down). Several of the
DFUB entries are behaviour tightenings — read the DFUB7 and DFUB11 notes
before upgrading.
DFUB12 is absent by design, not by omission: it split the analyzer-free tree's
library barrel so a web target can avoid dart:io, which is meaningless here —
this package depends on analyzer and never targeted the web.
Added — relative filesystem imports actually resolve (DFUB1) #
execute() / executeAsync() have long accepted basePath and
allowFileSystemImports, and both were dead no-ops: an interpreted script could
not import a sibling .dart file from disk no matter how they were set. They are
now wired through to ModuleLoader, which resolves a relative import against
basePath and reads the file when the flag is enabled.
Nested relative imports resolve without shared state, because loadModule
already resolves per module and saves/restores the current library around each
load. Upstream's accompanying 453-line mutable→immutable currentLibrary
refactor is deliberately not ported — it is not needed for the fix.
Ports upstream 973feab.
Security — filesystem module reads are gated on FilesystemPermission (DFUB2) #
DFUB1's on-disk reads are checked before the read, so enabling
allowFileSystemImports is not by itself permission to read the filesystem: an
ungranted import now throws RuntimeD4rtException ... requires FilesystemPermission.
The single terminal "module not found" throw is also replaced by a diagnostic
that distinguishes the four real causes: filesystem imports disabled, an enabled
filesystem import whose file is missing (naming the resolved path), a missing
package: import, and a URI that was simply never preloaded.
Separately, visitImportDirective now self-resolves any already-absolute URI
rather than only dart: and package:, so an absolute file: import reaches
the loader without a base — matching upstream's resolveModuleUri.
Ports the read gate and error shapes of upstream 973feab.
Fixed — one file imported two ways was loaded twice (DFUB3) #
A filesystem module now has ONE identity regardless of how it is spelled. The
URI that flows through the loader is canonicalized to an absolute file: form
for reads and for the DFUB2 permission gate, and the module cache is keyed by the
symlink-resolved real path. Relative, absolute, ..-containing, symlinked-file
and symlinked-directory spellings of the same file therefore share one module
instance — previously each spelling produced its own, so top-level state was
duplicated and identity comparisons across the two copies failed.
Ports upstream 3b8b8ca.
Added — instance-method and setter dispatch on extension-type instances (DFUB4) #
InterpretedExtensionType gained a setters map, so assigning to a member of an
extension-type instance binds and invokes the matching setter instead of failing;
and InterpretedExtensionTypeInstance now resolves instance methods — not
only getters — at the method-invocation, implicit-this identifier, and
property-access sites.
Ports upstream 2f519cd (Extension Type Support 0.2.2).
Added — runtime type checks for function types and record types (DFUB5) #
is / as against a function type or a record type annotation no longer throws
"not implemented", and function/record return-type validation is now actually
enforced.
Two new structural runtime types in runtime_interfaces.dart:
FunctionRuntimeType (covariant return, contravariant parameters, arity and
named-parameter shape) and RecordRuntimeType (positional arity, named keys,
per-field compatibility), alongside the shared NamedRuntimeType contract.
InterpretedFunction exposes a cached callableRuntimeType.
Ports upstream 848f03d.
Added — applied generic type arguments preserved at runtime (DFUB6) #
New AppliedRuntimeType (a base type plus its applied arguments, with
element-wise subtyping and dynamic / Object / void treated as wildcards),
so is Box<int> honours the type argument instead of matching any Box.
Generic and typed native-collection returns are validated element-wise: the
applied return type is captured at declaration time onto
InterpretedFunction.declaredReturnTypeApplied and checked in
visitReturnStatement. Async and generator functions are exempt, because their
Future<T> / Stream<T> / Iterable<T> return type wraps the inner value
rather than describing it.
Ports the applied-runtime-types half of upstream 1042fff.
Fixed — BridgedClass / TypeParameter subtype checks were too permissive (DFUB7) #
Both halves are tightenings and can turn a previously-true is into false.
BridgedClass.isSubtypeOf: thenumearly block answered true fornum <: intandnum <: double, makingnuma subtype of its own subtypes. Onlynum <: numis kept; the downwardint/double <: numdirection is unaffected.TypeParameter.isSubtypeOf: the unconditionalreturn trueis replaced by real rules — anotherTypeParameteris a subtype; a boundedT extends Xdefers to its bound, soT extends numis not a subtype ofString; an unboundedTis a subtype only of the top types (Object/dynamic/void).
Ports the subtype half of upstream 28ca517.
Fixed — omitted optional super parameters clobbered the parent's default (DFUB8) #
An optional super parameter ([super.x] / {super.x}) that the caller omits,
and that carries no default of its own in the child constructor, is no longer
forwarded to the parent as an explicit null. Skipping the forward lets the
parent apply its declared default, e.g. Parent(this.name, [this.value = 0]).
Required super parameters are unaffected.
Ports the two failing super-parameter cases from upstream class_test.
Added — operator and call() dispatch on extension-type instances (DFUB9) #
Operator methods declared on an extension type were already stored on
InterpretedExtensionType.methods, keyed by the operator lexeme, but no dispatch
site recognised an InterpretedExtensionTypeInstance receiver. Binary operators
reported Unsupported operator (PLUS) for types InterpretedExtensionTypeInstance …, unary - reported Operand for unary '-' must be a number …, and invoking
an instance silently returned the instance itself instead of running its call
method.
Seven dispatch sites now resolve the operator on the extension type, bind this,
and invoke it:
visitBinaryExpression—+,*,>,==and friends. The lookup runs before the native comparison/arithmetic switch, because a comparison such as>would otherwise reachleft as dynamic > rightand throw aNoSuchMethodErroron the instance.- compound assignment (
+=,*=, …) — dispatches with the wrapped instance as the receiver, not the unwrapped representation value. visitPrefixExpression— unary-and~, bound with an empty argument list. A zero-argoperator -()and a one-arg binaryoperator -share the-key, so only the prefix site may bind it with no arguments.- index get
[]and index set[]=. - both invocation paths —
visitMethodInvocation(calc(5)) and function expression invocation ((calc)(5)) — route to thecallmethod, forwarding positional, named and type arguments.
Fixed — circular module imports and exports blew the stack (DFUB10) #
ModuleLoader.loadModule only published a module to its cache at the very END,
after recursing through every import and export directive. A cycle A -> B -> A
therefore re-entered the load of A while A was still in progress, the cache
guard missed, and the recursion never bottomed out. Circular imports and circular
exports are both legal Dart and run correctly, so this rejected valid
programs.
The loader now publishes a partial module under an in-flight map before walking
any directive, and a cyclic re-entry receives that partial instead of recursing.
The partial carries the very Environment instance that later receives the
module's own declarations, so importers hold a live reference.
Because Environment.importEnvironment copies bindings at call time rather
than aliasing the source environment, a merge taken from a still-incomplete
module would otherwise capture an empty export set and never self-heal. Each such
merge is recorded and replayed once the in-flight module finishes. Replays are
idempotent — importEnvironment skips names already bound to the identical value
— so they cost nothing and cannot raise a spurious conflict. A failed load drops
its in-flight registration, so an abandoned partial is never handed out on a
later execute.
DELIBERATE DIVERGENCE FROM UPSTREAM: kodjodevf/d4rt f6e1257 fixes the same
crash by detecting the cycle and throwing "Circular module dependency
detected". That rejects valid Dart, so it is not adopted here.
Security — scoped FilesystemPermission grants are now actually enforced (DFUB11) #
This is a behavioural tightening. Scripts that relied on the previous, laxer matching will now be denied.
Two independent sandbox holes are closed (ported from upstream 861117a).
1. No per-operation enforcement. The dart:io bridges in
stdlib/io/{file,directory,file_system_entity}.dart carried zero permission
checks. The only gate was at dart:io IMPORT time, and it merely required that
some FilesystemPermission had been granted. A grant scoped to one directory
was therefore indistinguishable from FilesystemPermission.any once the import
succeeded — every bridged file and directory operation ran unchecked.
Every read/write entry point now calls
checkFilesystemRead/WritePermission before the native operation, so a
denial cannot leave a side effect behind. Operations are classified by what they
actually do: rename requires write on both the old and the new path, copy
requires read on the source and write on the target, and
File.open/openSync follow the requested FileMode (only FileMode.read
counts as a read). FileStat.stat/statSync are gated too — they take a raw
path and would otherwise sidestep every File/Directory gate.
2. Naive scope matching. FilesystemPermission.allows compared with a raw
opPath.startsWith(_path). Two consequences: .. traversal escaped the scope
(/allowed/../etc/passwd was "inside" /allowed), and a sibling directory whose
name merely shares the string prefix (/allowed_sneaky against a grant on
/allowed) was treated as inside it.
Matching is now canonical and on a path-segment boundary: both sides are
absolutized, normalized to / separators, lowercased on a Windows drive letter,
and reduced by resolving . and .. away; the request must then either equal the
scope or start with scope + '/'. Symlinks are not resolved at this stage —
see the 1.22.0 entry, which revisits exactly that decision and makes the matcher
symlink-aware.
Pathless operations. Some checks have no meaningful path — the dart:io
import gate asks only "is any filesystem access granted?". Those now pass
'pathAgnostic': true, which waives the PATH check only, never the
read/write/execute flags. Conversely, a scoped grant asked about an operation
with no path and no pathAgnostic flag now denies, rather than assuming the
operation is in scope. Unscoped grants (FilesystemPermission.any, .read,
.write) are unaffected and remain allow-all.
Fixed — a failed import or export now says which file to edit (DFUB13) #
A missing import was one of the least actionable errors the interpreter could produce. Three defects conspired:
execute()'s catch-all relabelledSourceCodeD4rtExceptionas "Unexpected error: …", discarding a diagnostic the loader had deliberately composed and telling the user they had hit an interpreter bug rather than mistyped a package name.- A missing
package:URI got the generic "not a recognized Dart standard library" tail — noise, since nobody expected it to be a stdlib library, and neither real fix (supply the source, bridge the package) was mentioned. - The message named the module that could not be FOUND but never the module that ASKED for it. In a barrel chain that is the wrong half: the missing URI is the symptom, the file holding the directive is what you have to open.
wrapDirectiveError() preserves the concrete exception type rather than
returning a fixed one, because the two loaders report a missing module
differently — the filesystem loader raises SourceCodeD4rtException, the bundle
loader RuntimeD4rtException. The wrap is applied once, at the innermost frame,
so a deep chain yields one prefix instead of one per frame; and it is skipped for
a bare source: script, where owner and target coincide and a synthetic owner URI
would only restate the target.
Added — Stopwatch and UriData core bridges (SC1, SC10) #
Two dart:core classes the SDK gap audit flagged as missing are now bridged,
mirrored file-for-file into tom_d4rt_ast:
Stopwatch— the default constructor,start/stop/reset/toString, and the full getter set (elapsed,elapsedTicks,elapsedMilliseconds,elapsedMicroseconds,frequency,isRunning). A pure monotonic-clock read with no I/O, so it needs no permission gate.UriData—fromString/fromBytes/fromUri, the staticparse,contentAsBytes/contentAsString, and theuri/mimeType/charset/isBase64/parameters/contentTextgetters.contentAsStringaccepts anEncoding, whichdart:convertalready supplies asutf8/latin1/ascii.
Fixed — the Uri.data getter was missing #
Uri.dataFromString and Uri.dataFromBytes were already bridged, but Uri.data
was not — so a script could build a data: URI and then had no route back to
its payload. Adding the getter closes that loop, and is what makes the new
UriData bridge reachable from a parsed URI at all.
Documented — intentionally-unbridged SDK classes #
doc/d4rt_limitations.md gains an "Intentionally-Unbridged SDK Classes" section
separating the classes that cannot be honoured meaningfully (Zone,
Expando, WeakReference, Finalizer — each would require a guarantee about
native identity or GC timing that an interpreter cannot make) from those merely
deferred pending a consumer (Link, WebSocket, GZipCodec/ZLibCodec,
MutableRectangle). The boundary applies to both interpreter trees, which share
one mirrored stdlib set.
1.12.1 #
Fixed — toString() on a bridged enum TYPE (via runtimeType) (RCJ12) #
- Calling
.toString()on a bridged enum type — reached as a runtime value, typically throughenumValue.runtimeType— no longer throws"Undefined static method 'toString' on bridged enum '<Enum>'". TheBridgedEnummethod-invocation branch now mirrors the existing class-as-value (InterpretedClass)toStringfallback: when no matching static method exists and the call is a no-argtoString, it returns the enum type name, matching Dart'sType.toString(). This unblocks the common metadata patternvalue.runtimeType.toString()(e.g. Flutter'sDropdownMenuCloseBehaviorinspector). Calling.toString()on an enum value already worked (viaBridgedEnumValue.invoke); only the type path was misrouted.
1.12.0 #
Fixed — enum bridging for Map<String, Enum> args and native-stored round-trips (RCC7) #
- A script
Mapwhose values are bridged enums now coerces to a nativeMap<String, Enum>.D4._coerceMapValueunwrapsBridgedEnumValueto its.nativeValue, mirroring the existing_coerceMapKeyhandling. Previously aregisterAll({...})-style call with bridged-enum values threw"BridgedEnumValue is not a subtype of type <Enum>"while the scalarregister(key, enum)path worked (it unwrapped viaextractBridgedArg). wrapNativeReturnValuenow re-wraps a nativeEnumas itsBridgedEnumValuebefore thetoBridgedInstancefallback, so a native-stored enum returned to a script round-trips to the sameBridgedEnumValueand compares equal with==(and resolves custom getters) rather than needing a.name-string workaround.
1.11.0 #
Added — static method dispatch on bridged enums (GitHub issue #2) #
BridgedEnumDefinitionandBridgedEnumgained astaticMethodsmap (Map<String, BridgedStaticMethodAdapter>), wired throughbuildBridgedEnum(), plusBridgedEnum.findStaticMethodAdapter(name).visitMethodInvocationnow dispatches a static method call where the target is a bridged enum type (e.g.PageFormat.fromString('A4')). Previously only instance methods on enum values were reachable, so static factory helpers threw "Undefined property or method '…' on BridgedEnum".- Backward compatible:
staticMethodsdefaults to an empty map; existing enum bridges are unaffected. Twin oftom_d4rt_ast0.1.11.
1.10.1 #
Fixed — native→bridge resolution: precise match must beat fuzzy prefix across scopes #
-
Environment.toBridgedClassnow resolves a native runtime type in two chain walks instead of one strategy-cascade per frame:- Pass A (precise) walks the whole enclosing-scope chain trying exact
Typelookup,_FooImpl → Foocanonicalization, generic-basename/nativeNamesmatch, suffix match, name-exact, and longest-nativeNameprefix — every strategy anchored on a declared bridge identity. - Pass B (fuzzy fallback) walks the whole chain trying the loose
G-DCLI-05
name-is-a-prefix-of-the-type match (e.g.ProgressBothImpl→Progress).
Previously both passes ran interleaved within a single frame and returned on the first hit, so a fuzzy prefix match in a nearer frame short-circuited a more-correct precise match still waiting in an enclosing frame.
Concrete failure surfaced by the lazy-bridge substrate (import-optimization steps #17/#19): a
MappedListIterable<…>returned byList.map(...).toList()carries its precisenativeNamesentry on the stdlibIterablebridge, which under the warm-parent / per-module split lives in an enclosing frame. A nearer module frame held theMapbridge but notIterable, and"MappedListIterable".startsWith("Map")made the fuzzy fallback wrap it asMap— so.toList()failed withBridged class 'Map' has no instance method named 'toList'. The two-pass walk now resolves it toIterable(precise) regardless of frame distance. Mirrored intom_d4rt_ast'sEnvironment.toBridgedClass. - Pass A (precise) walks the whole enclosing-scope chain trying exact
1.10.0 #
Added — lazy bridge registration (import-optimization, additive) #
D4rt.registerBridgedClassLazy(String name, Type nativeType, BridgedClass Function() thunk, String library, {String? sourceUri})— registers a bridged class by deferred thunk: theBridgedClassbody (member maps + adapter closures) is built and memoized only when the class is first resolved by name or native type during interpretation. This is the runtime substrate the generator's lazy bridge emission targets (plan step #17): a script that touches N of a package's classes builds ≈N bridges rather than all of them.D4rt.registerBridgedClass(...)now delegates toregisterBridgedClassLazyby wrapping the already-built definition as a trivial() => definitionthunk — behaviour is unchanged for eager callers; the lazy path simply memoizes on first lookup.
This method was introduced in-tree alongside the generator's thunk emission
(commit 2d341b786) after 1.9.0 was published, so 1.9.0 carried the
providePackage / finalizeBridges pool API but not the lazy registrar that
generated *.b.dart bridges call. 1.10.0 publishes the missing public method
so downstream bridge packages (e.g. tom_d4rt_flutter) compile against a
released tom_d4rt.
1.9.0 #
Added — import-optimization API (additive, backward compatible) #
D4rt.providePackage(String)— process-global package pool gate: returnsfalsethe first time a package is seen (caller registers its bridges) andtrueonce pooled (caller skips registration and reuses the pooled definitions). The granted set is the instance's security whitelist, exposed read-only viaallowedPackages.D4rt.registerExtensions(String package, void Function() callback)/finalizeBridges()— queued bridge-package extension hooks that fire exactly once per package per process (at pool population), replacing the old once-per-instance firing.- Warm-parent reuse: each execute runs in a fresh child
Environmentchained off a shared warm parent built at most once per allowed-set signature (migrated instances) or per instance (legacy) — script declarations never leak across executes or instances. The warm parent registers only the bridge type lookup (registerBridgeType); the analyzerModuleLoaderowns per-module name registration at import time for module isolation (GEN-100/107). - Test/diagnostic introspection:
debugPooledPackages,debugPooledClassCount,debugWarmParentCacheSize,debugResetPool.
1.8.25 #
- Analyzer 10 migration (publish). Widened the
analyzerconstraint from^8.0.0to^10.0.0and applied the analyzer-10 API renames (NamedType/LibraryDirective.name2→.name;ErrorSeverity→DiagnosticSeverity;errorCode.errorSeverity→diagnosticCode.severity). The source change itself shipped earlier but was never published — the prior1.8.24on pub.dev still carriedanalyzer: ^8.0.0. This release publishes the analyzer-10 build so hosted consumers (notablytom_d4rt_generator) can resolve it. No behavioural change.
1.8.24 #
Fixes #
- Same-name bridges from different libraries now resolve to the library that
declares the requested member (B2 "MarkdownParser clash"). Previously two
packages exporting an identically named
BridgedClassregistered last-wins by simple name, so the second silently shadowed the first and static/constructor calls to the displaced library's class failed. TheEnvironmentnow stashes displaced same-name bridges and the method-invocation visitor falls back to a sibling bridge that declares the member; the module loader no longer errors on same-name/different-source class duplicates.
1.8.23 #
- Housekeeping: test artifacts now live in a gitignored
testlog/folder;doc/no longer ships machine-generated baselines or last_testrun.json. No code changes.
1.8.22 #
Fixes #
- Instance members now correctly shadow bridged top-level functions of the same name (FIX-20260613-1038-C).
- Stop using exception-as-control-flow on implicit-
thismember reads; the interpreter resolves the member directly instead of throwing and catching.
Performance #
- Cache bound-method tear-offs per instance.
- Collapse the
Environmentframe for blocks that introduce no bindings. - Stop wrapping primitive operands in binary expressions.
Documentation #
- README reframed to present the source-based interpreter as the primary, recommended option.
- Consolidated manual-intervention guidance into
doc/manual_bridge_interventions.mdand limitations intodoc/d4rt_limitations.md(now the canonical limitations reference).
1.8.21 #
Performance #
- S1–S3 static lexical resolver: depth-0 slot-eligibility analysis with an
additive, dual-write slot runtime; resolved reads served from the current
frame's
getSlotinstead of repeated name-map chain walks. - Lazily-allocated auxiliary
Environmentmaps (S2); node-keyed inline depth cache for identifier resolution; single closure-freeEnvironmentreused per classic for-loop. FrozenNameMapfor immutable class/mixin/enum member tables; per-class member-resolution cache; negative resolution cache fortoBridgedInstance; canonicalized const set/map literals.- Hot-path debug logging guarded behind
Logger.isDebug;ErrorReporteridentitySetwith default-off tracking; memoizedType.toStringinD4coercion helpers.
Fixes #
- Redirecting factory constructors resolve correctly.
- Static-field writes persist from sibling static methods.
- Clear native-side accumulator on reset.
1.8.20 #
Fixes #
- Cross-boundary native↔interpreted interop:
callInterpreterCallbackhandles plain nativeFunctionviaFunction.apply; Expando-based reverse map for native↔interpreted assignment. - Cascade setter/getter resolution unwraps
D4InterpretedProxytargets. resetScriptDeclarationsAPI +/clearREPL wiring.- Async/timer, typed_data, and bridged-setter back-ports aligned with the
flutter-material cluster fixes (kept in sync with
tom_d4rt_ast).
1.8.19 #
Fixes #
- ENV-001: Fixed generic type matching in
environment.dart— extract base type name before<for accurate BridgedClass resolution, preventing false matches likeListMapView<int>matchingViewbridge - ENV-002: Added
endsWithsuffix match fallback for generic types — types likeCastList<T>,ListIterator<T>,CastStream<T>,EfficientLengthFollowedByIterable<T>now correctly resolve to their parent bridge (List,Iterator,Stream,Iterable) - RT-001: Fixed
InterpretedClass.isSubtypeOf()— walksInterpretedClass.superclasschain checkingbridgedSuperclassandbridgedMixinsat each level instead of brokenBridgedClass.bridgedSuperclasschain - BT-001: Enum
.name/.indexfallback inBridgedInstance.get()— checksnativeObject is Enumbefore throwing on missing property - IV-001: Enum property access fix in
visitPrefixedIdentifierandvisitPropertyAccess— properly handles.name,.indexon enum values - IV-002: Enum equality intercept before
toBridgedInstancewrapping — prevents wrapping from breaking==comparisons - D4-001: Null-safe
superObjcheck inextractBridgedArg
Improvements #
- Added 16 list transformation iterable type names to
Iterablebridge (MappedListIterable,WhereIterable,CastIterable, etc.) - Added
ListMapView,_MapViewtoMapbridge nativeNames - Added
LinkedHashSet,_SetBasetoSetbridge nativeNames
1.8.18 #
Features #
- GEN-079: Added
registerFunctionTypedeftoD4rtbase class for function typedef type resolution- Function typedefs (e.g.,
VoidCallback) can now be registered so the runtime resolves them as types - Required by bridges generated with tom_d4rt_generator 1.8.18
- Function typedefs (e.g.,
1.8.11 #
Features #
- GEN-081: Added
isAssignablecallback toBridgedClassfor supertype bridge lookup on private subclasses - ENG-001: Back-ported 3 collection handling improvements to
extractBridgedArg:- List cast: try/catch fallback for non-primitive
List<T>casts - Set cast: try/catch fallback for non-primitive
Set<T>casts - Map unwrapping:
_unwrapElement()for map keys/values before casting
- List cast: try/catch fallback for non-primitive
Bug Fixes #
- Synced 4 functional gaps from tom_d4rt_ast (
extractBridgedArgcollection/enum handling) toBridgedInstanceprefers most-specificisAssignablematch- Auto-unwrap
BridgedInstance/BridgedEnumValuein callback returns - Fixed G-DOV-8, extended I-BUG-14b records to 16 fields
- Resolved all 13 open issues (161 pass, 9 skip, 0 fail)
1.8.10 #
Bug Fixes #
- RC-1: Active visitor mechanism (
D4.withActiveVisitor) for interface proxy creation inside bridge helper methods - RC-2: Generic constructor dispatch in
visitMethodInvocation+ constructor override mechanism (fires even without type args, null = fallthrough) - RC-3: StrutStyle constructor override creates
painting.StrutStyle(dart:ui version is opaque) - RC-5: Implicit bridged super for both Path A (
callable.dart) and Path B (runtime_types.dartInterpretedClass.call) - Supplementary method adapters for
@protectedmethods (e.g.,notifyListeners) GenericConstructorFactorytypedef now accepts nullabletypeArgs
1.8.9 #
Bug Fixes #
- Synced
d4.dartwith active visitor mechanism and supplementary method support - Generic constructor registry and type coercion infrastructure
1.8.8 #
Bug Fixes #
- GEN-075: Fixed required nullable argument handling in generated bridge constructors
- GEN-076: Raised combinatorial dispatch threshold for non-wrappable default parameters
1.8.7 #
Bug Fixes #
- GEN-078: Runtime bridge alias resolution via
defineBridgeAlias()in Environment - GEN-079: Generic type wrapper registration for covariant generic type resolution
- New
GenericTypeWrapperFactorytypedef andregisterGenericTypeWrapper()in D4 class extractBridgedArg<T>now looks up registered wrappers whenis Tcheck fails due to generic type argument mismatch
- New
- GEN-080: Fixed named constructor resolution for unresolved AST ambiguity
const ColorFilter.mode(...)now correctly resolves the class name instead of the named constructor part
- Fixed
BridgedInstanceunwrapping in 4 setter assignment paths invisitAssignmentExpression
1.8.6 #
Features #
- GEN-074: Added support for class aliases (type alias registration)
- New
registerClassAlias()method in D4rt for registering type aliases - New
defineClassAlias()method in Environment for alias resolution - Aliases are resolved lazily when looked up - if target class is registered, alias is resolved automatically
- New
Internal #
- Added
_classAliasesfield to D4rt for tracking registered aliases - Added
_pendingClassAliasesfield to Environment for lazy resolution
1.8.5 #
Bug Fixes #
- INTER-003: Fixed nullable double/num type promotion in
D4.extractBridgedArgextractBridgedArg<double?>now correctly promotesinttodoubleextractBridgedArg<num?>now correctly handlesintvalues- Fixes "Invalid parameter elevation: expected double?, got int" errors in Flutter bridges
- INTER-003c: Fixed
D4.coerceListto promote int elements to double inList<double>- Mixed int/double lists now correctly coerce to
List<double>
- Mixed int/double lists now correctly coerce to
Internal #
- Added
_isDoubleType<T>()and_isNumType<T>()helpers for nullable type checking - Added comprehensive D4 helper unit tests (
d4_helpers_test.dart)
1.8.3 #
Features #
- Support extensible dart: library bridges - unknown dart: URIs now check for bridged content before throwing an error
- Allows external packages to register bridges for dart:ui and other dart: libraries
1.8.1 #
Bug Fixes #
- GEN-056: Fixed extension on-type resolution for stdlib and bridge types in the interpreter
- G-DCLI-05/07/08/11/12/13/14: All DCli bridge issues resolved — proper handling of DCli-specific bridged methods and types
Tests #
- Flaky file IO tests: Fixed race condition where all file IO tests (I-FILE-144 through I-FILE-159) shared a hardcoded
/tmp/test.txtpath. Under concurrent execution, one test'sdeleteSync()would remove the file while another was still using it. Each test now uses a unique filename (/test_{ID}.txt). - 1680 tests pass (2 known I-BUG-14a/14b intentional failures excluded)
1.7.0 #
Bug Fixes #
- G-GNRC-7: Fixed
runtimeTypecomparison with type identifiers. When comparingruntimeType(which returns a nativeType) against type identifiers likeint(which resolve toBridgedClass), the interpreter now correctly compares viaBridgedClass.nativeType. This fixes F-bounded polymorphism tests involvingComparable<T>sort operations.
1.6.1 #
Documentation #
- Advanced Bridging User Guide: New comprehensive guide for the D4 helper class covering type coercion, argument extraction, target validation, and global function bridging
- Example suite: Added 5 runnable examples demonstrating D4 class usage patterns:
d4_type_coercion_example.dart- List and Map coerciond4_argument_extraction_example.dart- Positional and named argumentsd4_target_validation_example.dart- Target validation and inheritanced4_globals_example.dart- Global functions and variablesd4_complete_bridge_example.dart- Complete realistic example with enums, factories, and complex signatures
1.6.0 #
Features #
- Comprehensive Dart language coverage: All 20 areas of the Dart language now pass the dart_overview test suite
- Extension types (Dart 3.3+): Full support for inline classes / extension types
- sync generators*: Fixed infinite loop issues with sync* generators (lazy evaluation now works correctly)
- Improved extension support: Extensions on bridged types and imported extensions now work correctly
- Enhanced pattern matching: Full support for logical OR patterns, when guards, record patterns with named fields and shorthand syntax
Bug Fixes (99 total bugs tracked, 97 fixed) #
Interpreter Core
- Bug-93: Int not implicitly promoted to double return type - fixed auto-promotion in return statements
- Bug-94: Cascade index assignment on property (
..headers['key'] = value) now works correctly - Bug-96:
super.nameconstructor parameter forwarding now correctly passes values to super constructor - Bug-97:
numnow recognized as satisfyingComparable<num>type bound - Bug-98: Extension getters on bridged List resolved correctly, including accessing other extension members via implicit
this - Bug-99:
Stream.handleErrorcallback arity detection - callbacks with 1 or 2 parameters both work correctly - Bug-95:
List.forEachwith native function tear-offs (likeprint) now works - Bug-79-92: Various fixes for switch expressions, cascades, patterns, and class modifiers
Pattern Matching
- Bug-81: Pattern with
whenguard now works (case String s when s.isNotEmpty) - Bug-88: Record pattern with
:nameshorthand syntax works - Bug-66, Bug-67: Record patterns with named fields and if-case with int patterns fixed
Class System
- Bug-84, Bug-85: Mixin abstract method satisfaction and extending abstract final classes
- Bug-72: Bridged mixins properly resolved during class declaration
- Bug-51: Mixing in bridged mixins works correctly
Async/Stream
- Bug-44: Async generators completion detection
- Bug-48:
await forstream iteration - Bug-73, Bug-74: Async nested loops and return type handling
Standard Library
- Bug-89:
Enum.values.byName(via List.byName extension) bridged - Bug-82, Bug-83: Function.call and nullable function?.call() support
- Bug-65: Map.from constructor bridged
Known Limitations (Won't Fix) #
- Lim-3: Isolate execution with interpreted closures - fundamental limitation due to Dart's isolate serialization requirements
- Bug-14: Records with named fields or >9 positional fields return InterpretedRecord (Dart doesn't support dynamic record type creation)
Test Coverage #
- 1620 tests passing (3 expected failures for "Won't Fix" limitations)
- 21 dart_overview_bugs_test tests all passing
- All 20 Dart language areas demonstrated in dart_overview scripts
Documentation #
- Consolidated BRIDGING_GUIDE.md to single location in
doc/folder - Moved dart_overview and d4rt_bugs test scripts to tom_d4rt/example folder
- Updated documentation to reflect current capabilities
1.5.0 #
Features #
- Script execution module: New
ScriptExecutionResultand file-based script execution with automatic import resolution - Bridge deduplication: Complete deduplication system with
sourceUritracking to prevent duplicate registrations across packages - D4rtConfiguration enhancement: Added library info support for better multi-package configurations
- Unary operator fix: Fixed unary operators (e.g.,
-x) on bridged instances
Bug Fixes #
- Fixed typedef callback wrapping in bridge registration
- Fixed type resolution for bridges with complex generics
Internal #
- Added shared script_execution module for D4rt-based CLI tools
- Improved error aggregation for bridge registration failures
1.4.0 #
Features #
- Global getter lazy evaluation: Added
GlobalGetterwrapper class for lazy evaluation of top-level getters - registerGlobalGetter method: New D4rt method
registerGlobalGetter(name, getter)for registering getters that are evaluated at access time rather than registration time - Essential for singleton patterns and values that may not be initialized at registration time
Documentation #
- Added "Global Variables and Getters" section to BRIDGING_GUIDE.md
- Documented when to use
registerGlobalVariablevsregisterGlobalGetter
1.3.1 #
- Repository reorganization: Moved to tom_module_d4rt repository as part of modular workspace structure
- Updated repository URL to https://github.com/al-the-bear/tom_module_d4rt
1.3.0 #
- Operator bridging support: BridgedInstance now supports all Dart operators
- Arithmetic: +, -, *, /, ~/, %
- Comparison: <, >, <=, >=, ==
- Bitwise: &, |, ^, ~, <<, >>, >>>
- Index: [], []=
- Unary: - (negation)
- Added operator override documentation for UserBridge classes
- Added bridged_operators_test.dart with comprehensive operator tests
1.2.0 #
- Added D4 bridge helpers class for generated bridge code
- Type coercion helpers (coerceList, coerceMap)
- Argument extraction helpers (getRequiredArg, getOptionalArg, etc.)
- Target validation for instance methods
- Argument count validation
- D4 class moved from tom_dartscript_core to tom_d4rt
1.1.0 #
- Updated analyzer dependency to ^8.0.0 (from fixed 8.0.0)
- Bridge generator improvements and cleanup
1.0.4 #
- Changed dependency of analyzer to version 8.0.0
0.1.9 #
-
feat:positionalArgs and namedArgs - Pass arguments directly to functions via execute()
- Add
positionalArgsparameter to D4rt.execute() for passing positional arguments - Add
namedArgsparameter to D4rt.execute() for passing named arguments - Support complex data types (List, Map, nested structures) as arguments
- Support function callbacks and async functions as arguments
- Add 33 comprehensive test cases covering all argument passing patterns
- Add parameter introspection methods:
positionalParameterNamesandnamedParameterNamesgetters
- Add
-
feat: Introspection API - Analyze code structure and get metadata at runtime
- Add
analyze()method to D4rt for code analysis without execution - Create IntrospectionResult with metadata about functions, classes, enums, variables, and extensions
- Extract function signatures including parameter names, types, and default values
- Extract class information: inheritance, mixins, interfaces, constructors, methods
- Extract enum values and variants
- Extract variable declarations and initializers
- Extract extension definitions and extended types
- Use AST-based analysis for accurate metadata extraction
- Add 38 comprehensive test cases covering all declaration types and complex scenarios
- Add
-
feat: eval() method - Dynamically execute code with current execution state
- Add
eval()method to D4rt for dynamic code execution - Preserve execution environment across eval calls
- Support access to previously defined variables and functions
- Support complex expressions and statements in eval
- Support async/await in eval expressions
- Add 39 comprehensive test cases covering expression evaluation and statement execution
- Add
-
fix: Environment import handling - Tolerate duplicate imports with identical values
- Allow re-importing the same symbol if the value is identical (same reference)
- Use
identical()comparison for duplicate detection - Support imports via multiple paths without conflict errors
0.1.8 #
- fix: security sandboxing with permission checks for file, process, and network operations; add platform access control
0.1.7 #
- feat: Security sandboxing system - Comprehensive permission-based security system to restrict dangerous operations
- Implement modular permission system with
FilesystemPermission,NetworkPermission,ProcessRunPermission,IsolatePermission - Block access to dangerous modules (
dart:io,dart:isolate) by default unless explicitly granted - Add
d4rt.grant(),d4rt.revoke(),d4rt.hasPermission()methods for permission management - Integrate permission checking into module loading and import directives
- Support fine-grained permissions (specific paths, commands, network hosts)
- Add comprehensive security tests to prevent malicious code execution
- Enable safe execution environment for untrusted code
- Implement modular permission system with
0.1.6 #
- fix: Nested for-in loops in async contexts now work correctly
- fix: Async nested for-in loops with await for streams works
- feat: enhance async execution state to support nested await-for loops and improve iterator management; add comprehensive tests for complex async scenarios
- feat: Compound super operators - Support for compound assignment operators on super properties (+=, -=, *=, /=, ~/=, %=, &=, |=, ^=, <<=, >>=, >>>=)
- Implement proper lookup and evaluation of super properties in compound assignments
- Support for both interpreted and bridged superclass properties
- Add 6 comprehensive test cases covering all operator types and nested inheritance
- feat: Bridged static methods as values - Bridged static methods can now be treated as first-class function values
- Support for accessing bridged static methods as callable values (e.g.,
int.parse) - Enable passing bridged static methods to higher-order functions
- Store bridged static methods in collections and variables
- Add 5 test cases for static method value usage patterns
- Support for accessing bridged static methods as callable values (e.g.,
- feat: Complex generic type checking - Enhanced runtime type checking for generic collections with type parameters
- Support
isoperator with parameterized types (List - Runtime validation of generic type constraints
- Proper handling of nested generic types and null safety
- Add 10 comprehensive test cases for various generic type checking scenarios
- Support
- feat: Complex await assignments - Advanced await expression support in various contexts
- Support await in conditional expressions (ternary operator)
- Support await in list/map literals and collection operations
- Support await in compound assignments and complex expressions
- Support await in constructor arguments and method chains
- Add 10 test cases covering complex async assignment patterns
- feat: Stream transformers - Complete implementation of StreamTransformer and stream manipulation
- Implement
StreamTransformer.fromHandlerswith handleData, handleError, handleDone - Support stream transformation with custom logic
- Implement bidirectional stream transformers
- Support stream event handling and error propagation
- Add 10 comprehensive test cases for stream transformation patterns
- Implement
- feat: Const expressions complexes - Enhanced support for const expressions in various contexts
- Support const List and Map literals with type parameters
- Support const expressions in field initializers and default parameters
- Support nested const collections and complex const expressions
- Proper compile-time evaluation of const expressions
- Add 15 test cases covering const expression usage patterns
- feat: Feature #7 - Enhanced enums with mixins - Enums can now use mixins to add functionality
- Support
enum Name with Mixinsyntax - Mixins can add methods, getters, and properties to enum values
- Support multiple mixins on a single enum
- Full integration with enum values (index, name, toString)
- Add 15 comprehensive test cases for enum-mixin combinations
- Support
- feat: Extensions statiques - Extensions can now declare static members (methods, getters, setters, fields)
- Implement static member storage in
InterpretedExtensionclass - Add static member access via
Extension.membersyntax - Support static method calls, property access, and assignments
- Add support for prefix/postfix increment/decrement operators on static extension fields
- Add 15 comprehensive test cases covering all static extension member types
- Implement static member storage in
- feat: Enhance compound super assignments for bridged classes - Full support for compound assignments on properties inherited from bridged superclasses
- Fix
visitAssignmentExpressionto handle bridged superclass getters/setters in compoundsuperassignments - Fix
InterpretedInstance.get()to properly traverse bridged superclass hierarchy at each inheritance level - Fix
InterpretedInstance.set()to properly handle bridged superclass setters at each inheritance level - Support nested inheritance chains (Interpreted → Interpreted → Bridged)
- Add 5 comprehensive test cases for bridged super compound assignments
- Fix
- Total test count: 1269 tests passing - All 8 planned features fully implemented with comprehensive test coverage
0.1.5 #
- feat: implement handling of factory constructors in InterpreterVisitor; add comprehensive tests for factory constructor behavior
- feat: enhance async execution state and interpreter visitor to support break/continue handling; add comprehensive tests for nested async loops
- feat: enhance async execution state and interpreter visitor to support async* generators; add comprehensive tests for generator behavior and control flow
0.1.4 #
- feat: add methods to find and retrieve bridged enum values in Environment and InterpreterVisitor; enhance handling of bridged enums in property access and binary expressions
- feat: enhance documentation across multiple files; add examples and clarify class functionalities in D4rt interpreter
0.1.3 #
- Implement complete
latevariable support with lazy initialization and proper error handling - Add comprehensive late variable test coverage (33 test cases) including static fields, instance fields, final constraints, and error conditions
- Add LateVariable class with proper uninitialized access detection and assignment validation
- Enhance interpreter visitor to handle late variables in all contexts (local, static, instance)
- Fix nullable variable handling in interpreted class instances
- Add ComparableCore bridge to core standard library for better type comparison support
- Update documentation and project description for better clarity
0.1.2+1 #
- update project description in pubspec.yaml
- docs: minor updates to documentation in README.md
0.1.2 #
- Implement complete Isolate API with Capability, IsolateSpawnException, Isolate, SendPort, ReceivePort, RawReceivePort, RemoteError, and TransferableTypedData classes
- Add comprehensive isolate communication and message passing support
- Enhance async capabilities with Timer functionality and improved error handling
- Add UnawaitedAsync and TimeoutExceptionAsync classes for better async error management
- Implement additional HTTP methods and error handling in HttpClientIo
- Add toString method to DirectoryIo for better debugging
- Enhance FileSystemEntity with parentOf method and FileStat improvements
- Add FileSystemEvent static getters and methods
- Implement RawSocket and additional Socket classes for network programming
- Enhance Stream and Socket classes with additional utility methods
- Add IOSink, ProcessIo, and StringSink classes for improved I/O operations
- Implement Comparable interface for better type comparison support
- Add comprehensive test coverage for isolate, socket, and I/O functionality
- Update core typed data classes (Uint8List, Int16List, Float32List) with enhanced functionality
- Add list extension utilities for better collection manipulation
0.1.1 #
- Implement await for-in loop support for streams in interpreter
- Enhance pattern matching with support for rest elements in lists and maps
- Add support for await expressions in function and constructor arguments
- BREAKING CHANGE: BridgedClassDefinition has been removed and replaced with BridgedClass
0.1.0 #
- Added runtime checks for generic type constraints.
- Added support for compound bitwise assignment operators (&=, |=, etc.).
- Introduced Int16List and Float32List in typed_data.
0.0.9 #
- full support (generic classes/functions, type constraints, runtime validation)
- use BridgedClassDefinition for all Stdlib
- Support adjacent string literals in interpreter
- add operators support for InterpretedClass
- more features
0.0.8 #
- expose visitor getter
- add support for bridged mixins
- enhance async execution state with nested loop support
0.0.7 #
- fix: support null safety
0.0.6 #
- Update docs
0.0.5 #
- minor fix
0.0.4 #
- Add 'import/export' directive support, support for 'show' and 'hide' combinators
- Add some dart:collection & dart:typed_data
- Support for ParenthesizedExpression property access in simpleIdentifier in async state
0.0.3 #
- Fix infinite loop when using rethrow in try catch in async state
0.0.2 #
- Support web
- Fix return nativeValue for BridgedEnumValue to BridgedInstance argument
0.0.1 #
- Initial version.