Compiler, platform and library linking
- Compiler, platform and library linking
- Platform declarations and value representations
- Default arguments and loops
- Value representations
- Generic-array storage specialization
- Reference-array initialization
- Compilation, specialization and linking
- Compiler preparation and caching
- Specialization and library ownership
- Built-in module graph
- Collection storage and universal views
- Reference arrays and register allocation
- Enum identity storage
Platform declarations and value representations
Built-in Guest Kotlin declarations are authored in guest-platform as separately versioned modules. Its build
produces only the canonical base platform bundle consumed by both compilation and IDE analysis. Optional integrations
own and build their Guest declarations as addon bundles against that base; addons/create, addons/sable and addons/propulsion own independent integration bundles.
The public ru.lazyhat.compukters.addon Gradle plugin resolves an isolated builder, base bundle, Minecraft-independent
host API, and thin target adapter from one independently versioned SDK release by Maven coordinate. The SDK version
changes with its public compatibility boundary rather than every Compukters release. The plugin derives the wire
schema and bindings from Guest Kotlin declarations, validates a checked-in selector lock, generates the typed JVM host
contract, and packages the resulting .cagb in the independent addon JAR.
platform-bundle owns the base bundle model, codec, module graph, and default imports; platform-k2 exposes that
metadata to K2 without making the K2 implementation part of the platform format.
Default arguments and loops
Constant Int and qualified enum-entry defaults cross this bundle explicitly; compiler lowering materializes an
omitted platform argument without a JVM-style mask dispatcher. The bundle also represents explicit null defaults and array-receiver-size defaults. Project function defaults use admitted Guest expressions lowered at the call site. Receivers and explicit
arguments are evaluated and saved before temporarily binding callee and default-owner parameters. Missing
arguments then evaluate in parameter order, with bindings and generic substitutions restored after nested
calls. Inherited defaults use their declaring symbols while the final call retains virtual dispatch.
Default-only generic helper calls participate in specialization discovery. Recursively expanding the same
source default produces a bounded target diagnostic instead of unbounded compiler recursion. None of these
source expressions are encoded into the platform bundle or require a new VM instruction.
Default-only function-reference adaptations compile the checked K2 adapter body with the ordinary function compiler. A bound leading adapter parameter maps to a closure capture and is excluded from invocation arguments; its source expression evaluates when the closure is constructed. Adapter calls therefore share scoped default evaluation and dispatch with direct calls, without mask dispatchers or a new artifact representation. Unsupported signature conversions retain target diagnostics.
Before capture discovery, GuestLocalSpecialization materializes concrete generic local declarations
and generic top-level lexical owners, including their generic callers. Anonymous callbacks and
callable-reference adapters trigger the same owner materialization as named local declarations;
otherwise their signatures and captures could retain unsubstituted owner type parameters. It shares typed
IR deep-copying
with inline specialization: each owner copy remaps its lexical symbols and substitutes only that owner’s
type parameters, leaving nested generic parameters for their own specialization. Recursive calls reuse
cached concrete variants; calls that change type arguments discover distinct variants. This gives closures
and mutable cells concrete per-owner layouts before managed types are assigned. The lexical pass shares
the 256 function-variant budget with ordinary emitter specialization and bounds expansion depth to 64.
testKotlinGenericLocalFunctionsVmConformance exercises these paths without a runtime or ABI change.
prepareGuestFunctionBodies returns the concrete function/class instances and executable lexical roots
before capture discovery. Function/class dependencies and copied local declarations iterate to a fixed
point. A generic member instance retains its source declaration for dispatch/signature identity and a
separate symbol-remapped concrete declaration for executable bodies. The shared IR copier substitutes
owner parameters while preserving nested local parameters until lexical specialization. Copies retain a
generic-equality origin so Float/Double specializations keep boxed NaN and signed-zero semantics; the
collection value conformance also exercises generic class comparisons and floating map keys. Member bodies
are cached per declaration and concrete class instance; recursive calls and member references retain
the source dispatch symbol while returns target their concrete executable copy. Generic class discovery defers unresolved lexical types rather than treating
them as concrete instances. testKotlinGenericClassCapturesVmConformance exercises owner retention,
all primitive signatures, shared cells, dispatch and small-heap collection without a VM/ABI change.
Direct generic ordinary methods extend function-instance identity with both concrete method arguments and
concrete owner arguments. One substitutor and the typed IR copier cover both parameter sets before lexical
normalization and dependency discovery; canonical member symbols still own call lookup. Concrete method
defaults share the same copied parameter and closure symbols as capture layout. Function discovery bounds
method variants with the existing function budget. Artifact ordering groups all variants with their owner’s
other methods, preserving contiguous nominal method ranges; concrete method arguments distinguish their
names. Generic virtual/interface dispatch and generic callable references retain source diagnostics.
testKotlinGenericMethodsVmConformance executes this path through the unchanged artifact and VM contract.
Generic ordinary class initialization uses one typed symbol copier per concrete owner for the primary
constructor, class receiver, backing-field initializer expressions and init bodies. Original class,
field and constructor identities continue to own layout and lookup; copied lexical symbols own execution
and captures. Concrete initialization roots participate in dependency convergence, closure/cell discovery
and literal/import collection. Constructor emission walks the original declaration order while reading
its corresponding concrete expressions and blocks. Call-site defaults use the copied constructor
parameters, so their captures match the concrete caller argument bindings. Local declarations inside
initializers remain static local functions even when K2 gives them a class parent; they do not enter
member dispatch tables. testKotlinGenericInitializerCapturesVmConformance verifies these paths without
a runtime or ABI change.
Named local functions are discovered before closure layout. A fixed-point free-value analysis includes
captures required by local callees and referenced helpers. Direct calls prepend hidden typed capture
arguments; reassigned variables use the same capture cells as lambdas. Local declarations are static
artifact functions, including declarations whose K2 parent is a class initializer. References use the
existing managed closure adapters and retain captures across the enclosing return. No new instruction or
artifact ABI is introduced. testKotlinLocalFunctionsVmConformance covers recursion, default arguments,
all primitive capture families, nested helpers, constructor initialization and independent escaping cells.
Callable signature discovery also visits concrete constructor parameter and backing-field types before
class layout. Constructor signatures and fields use the same generated callable interfaces as locals and
ordinary function parameters. Field writes preserve the existing exact-signature policy, including default
accessors, custom backing-field writes and body initializers. Generic owners substitute their concrete types;
managed field references preserve closure/capture reachability without new runtime instructions. Callable
invocation lowers arguments using the expected parameter types, including typed null arguments.
testKotlinFunctionFieldsVmConformance exercises these paths.
Callable shape identity ignores outer reference nullability. Nullable-only local declarations participate
in signature discovery, including recursively nested callable parameter/result types; nullable backing
fields and parameters use the same generated interface. Exact signature checks admit typed null and
reference nullability changes while still rejecting variance between different signatures. Constructor
default evaluation scopes its concrete class type substitution before checking and compiling expressions.
Safe invocation reuses normal K2 null branches and existing reference instructions. Safe Unit results use
the canonical managed Unit singleton or null, while direct Unit calls retain their zero-slot result.
testKotlinNullableCallbacksVmConformance executes callback replacement/reset under a 64 KiB heap, keeping
an older alias live across 200 allocations of 12 KiB captured arrays with mutable capture cells. No artifact
encoding, instruction or GC protocol change is introduced.
CovariantInterfaceBridges derives canonical concrete read views for supported declaration-site
out interfaces during class discovery. The set depends on the specialization and its bounds,
rather than consumer demand, preserving identical layouts when a trusted library owner is reused.
Interface parent edges express reference and nested result covariance. Concrete implementations
receive synthetic result-adapting methods for scalar, Unit and value-class results; managed value-class
method tables share these generated methods. Interface results widened to Any receive
reference forwarding methods because artifact interfaces cannot have a class parent. These methods
reuse the normal function compiler and managed boxing/cast instructions. The original object, fields
and direct methods retain their layouts. Parameters and callable result signatures must remain unchanged.
Existing trusted collection bridges remain canonical. Depth/combination/method limits
bound expansion, and library specialization validation retains one nominal owner. Sequence and
generic-library conformance exercise this path through the unchanged artifact/VM contract.
Canonical IntRange and IntProgression declarations let K2 resolve ascending, descending, and stepped Int loops.
The compiler recognizes only their canonical for shape and evaluates start, end, and step once into scalar registers;
no range or iterator object enters the VM. It validates a positive step before iteration, calculates the next index in
Long to avoid Int overflow, and retains a loop-header quota safepoint. Stored progressions and non-Int range loops use ordinary Guest library classes and iterators; the allocation-free optimization is specific to the recognized direct Int shape.
Text iteration uses the ordinary managed CharSequence.iterator / CharIterator library path, including
String, StringBuilder and Guest implementations. It traverses UTF-16 units and allocates an iterator;
there is no direct text-loop allocation optimization. The generated canonical runtime owns CharSequence
and String’s interface methods; stdlib exports the canonical CharIterator base class once.
Direct primitive and reference-array for loops follow the same canonical-shape rule: the compiler snapshots the array reference, loads
its length, and reads each element with existing array instructions at the current index. The loop header retains its
quota safepoint; no iterator object is created. A source variable may be reassigned without changing the traversed
array, while writes to that array’s elements remain visible to later iterations.
Reference-array loops share the indexed-read path for concrete generic element types and nullable/value-class
storage; non-null value-class elements unbox before the body. Array.iterator is a structural external
declaration consumed by canonical-loop recognition; stored iterator calls remain unsupported.
Loop jumps select an active loop by K2 IR identity, so labels also work across different loop forms. The target
records its enclosing finally depth and condition or advancement point. Only cleanup scopes crossed by the
jump execute; cleanup can replace it with another exit. Optimized for loops emit an advancement block only
when the body can fall through or has a continue targeting that loop.
Library compilation exports every enum cache together with its concrete collection read views. Its class specialization budget is 1024, separate from the existing 256-class project budget; the generic function budget remains 256. This keeps whole-platform exports bounded without reducing the immutable list contract or increasing user-program expansion limits.
Value representations
Scalar value classes retain their scalar representation for direct typed calls. Nullable/Any/interface boundaries and reference
collection storage use ordinary nominal managed wrapper classes with payload fields matching the value layout. Canonical platform and addon
libraries export the wrapper type and payload field through existing type/field links; consumers import these instead
of introducing a second nominal identity. Source value classes own their application-local wrappers. Equality checks
nominal type and payload, hashing uses the payload, and virtual toString dispatch preserves default or custom rendering.
Wrappers carry the value class’s interface parents. The established direct scalar fast path applies to single Int/Boolean/Char layouts; other payload layouts use nominal inline values. Concrete interface implementations get virtual bridges that
read the scalar receiver and call its static implementation; an underlying-property getter returns that payload
directly. Inherited interface defaults use existing interface dispatch, including concrete generic specializations.
Scalar boxing uses ordinary managed classes and the established ABI. Multi-field and other non-scalar
value layouts use ABI 1.15’s nominal inline types and compact frame components for direct calls and locals.
Reference contexts box flattened payloads through ordinary managed fields; nested source properties retain
source grouping for hashing/text. Libraries export the direct inline type separately from its managed class
and export every payload field. Consumers import both identities, including concrete generic layouts; linker
signature matching resolves nominal inline identities across modules. Bound method references store the
boxed receiver in their closure environment and reconstruct its direct layout before static invocation.
Constructor references reuse constructor lowering, including argument layout and initializer execution.
References to stateless trusted platform object methods use the same receiver-free signature as direct calls,
so factories such as Matrix4f::perspective do not capture an absent runtime companion instance. Managed
object and companion methods retain their ordinary receiver capture.
Compiler and IDE sessions share CompuktersLanguageVersionSettings so native multi-field declarations
receive consistent frontend analysis.
Nullable String and supported Guest class source references use the artifact’s existing nullable reference types and
Null instruction. K2’s safe-call and Elvis branches lower through ordinary verified control flow, with reference
casts when a non-null branch joins a nullable result. All twelve nullable primitives use their distinct nominal managed boxes or null. Safe casts and non-null assertions on value classes preserve their direct layout; see the language contract for the tested boundary.
Generic-array storage specialization
Array discovery runs after concrete generic function/class substitution. Each admitted Array<T> has a
nominal application array type; non-null primitive elements select the same GuestPrimitive scalar and
storage width as dedicated primitive arrays. This preserves Array<Int> versus IntArray type identity
while reusing allocation, indexed access, loops and bulk-copy instructions. Nullable and heterogeneous
arrays retain nominal reference element storage. Array-to-Any transitions preserve the array reference;
scalar-to-reference transitions use the existing primitive boxes.
Copies retaining compatible storage use ArrayCopy. Resized generic copies return Array<T?>, so dense
primitive sources use a charged scalar-load/box/store loop. The same conversion handles copyInto to
nullable and Any destinations. Entire source/destination ranges are checked before writes with
subtractions only after non-negative endpoint checks, avoiding overflow and preserving empty-range errors.
Scalar/reference layouts cannot alias; compatible layouts keep VM overlap handling. Loop headers and
allocations use ordinary safepoints and GC roots. Existing artifact encoding and runtime ABI are unchanged.
Reference-array initialization
The canonical Array<T>(size, init) constructor is lowered with the same existing allocation and charged
index loop as primitive-array initializers. Size and callback expressions evaluate before allocation;
NewArray enforces negative-size errors before the first callback invocation. Function-interface calls
produce each value in ascending index order, and value-class results are boxed at the reference-array storage
boundary. Concrete generic container specializations retain their callback and element shapes; bare type-parameter array
construction retains K2’s reification requirement, and reified inline functions remain outside the Guest subset. Allocation,
exceptions, suspension and GC use the ordinary VM mechanisms; Kotlin does not run the initializer on the host.
Compilation, specialization and linking
The trusted server JVM owns compiler scheduling, diagnostics, artifact production, and the server-global content-addressed cache. The Rust machine validates guest paths, captures an immutable source snapshot plus filesystem preconditions, and suspends only the requesting foreground process. The server tick submits and polls bounded compiler work without waiting on the worker or cache. On completion Rust re-verifies the complete artifact and atomically installs it only if the source and output preconditions still match. Native buffers remain caller-owned and no Rust pointer enters Kotlin.
Compiler preparation and caching
Compiler service initialization runs in ServerStartingEvent, after the world filesystem store is opened.
Ordinary computer creation and target-profile requests require an already prepared service and cannot lazily unpack
worker resources on the server tick. Worker compilation remains asynchronous; this changes the preparation phase,
not the compilation scheduler or wire formats.
Each pinned compiler adapter retains prepared library artifacts, symbol links and resolved source-library FIR for its most recent validated module selection, keyed by module names and content hashes. A different selection disposes that one entry; failed preparation is not retained. Module closure and addon payload identities are still checked on every request before reuse. Only admitted platform and addon modules enter this cache; project files and prospective user source libraries require their own source invalidation policy.
The retained entry owns a library-only compiler environment. Each request creates a fresh project/PSI environment and
source session over the same admitted metadata symbols, preserving built-in type identity across module boundaries.
FIR-to-IR uses K2’s reusable-FIR configuration when source libraries participate: ordinary KLIB conversion clears
resolved declaration bodies and cannot safely consume the same FIR twice. IR, lowering/specialization state, source
snapshots and request limits remain specific to each compilation. Canonical intrinsic registries index admitted
handlers by full callable name and signature once; lookup still filters by source-module ownership or selected
modules and rejects ambiguous matches. Each compilation session snapshots platform function links and indexes
all overloads by symbol. These indexes contain immutable declaration metadata, not project IR or resolved calls. The worker closes the retained environment on
shutdown. K2CompilerAdapterTest compares fresh and reused artifacts while alternating project declarations and
generic specializations, including an unresolved reference to a previous project’s function and subsequent recovery.
The prepared selection also owns PreparedLibraryModules: semantic hashes and export indexes keyed by module
instance identity for its immutable library artifacts. Specialization lookup preserves duplicate exports and
canonical defining-library precedence. Each link supplements this metadata with request-local caches for project,
pruned and relocated modules; those caches never grow the retained library entry. Mutable project files and future
user libraries cannot reuse library metadata without an explicit invalidation policy. Replacing or closing the
prepared selection releases its frontend and linker metadata together.
Hash-only encoding checks debug sizes and source coordinates without materializing debug sections. Full artifact
encoding still includes those sections. The compiler adapter uses LibraryModuleLinker.linkAndWrite to assemble,
validate and serialize the executable once. The write applies the stricter of the ordinary linker ceiling and the
request ceiling for every resource, retaining the request’s diagnostic budget. Ordinary link callers still receive
a validated model and an exception for invalid linked artifacts; the serialized API returns writer diagnostics.
The packaged tooling payload is validated and published beneath <world>/compukters/compiler-worker; temporary
worker state is kept separately beneath <world>/compukters/compiler-temp. Successful server artifacts are stored
beneath <world>/compukters/compiler-cache/v1, shared by every computer and dimension in that server world, and reused
after restart. Cache keys cover the ordered source snapshot, compiler and payload identity, target, selected trusted
platform modules, and compilation limits. Cache publication and cache hits both pass the stateless Rust artifact
verifier over FFM; runtime admission quotas are deliberately not part of cache validity.
Specialization and library ownership
KotlinProjectLowering coordinates body preparation, layouts, emission and artifact assembly.
GuestPlatformLinking owns library symbol discovery and deterministic import binding. Discovery returns
source-symbol links before layout; binding runs after value-class component discovery and allocates type,
boxed/inline value, field and function imports in their existing canonical order. Its bound tables serve
both artifact assembly and function emission, including imported payload fields. FunctionCompiler
receives the library dependencies as GuestFunctionImports; register, block, capture and default-expansion
state remains local to each function compiler. This phase split preserves the artifact format and source
versus concrete function identity used by specialization and dispatch.
Guest generic functions and supported generic classes are specialized before artifact writing. Final classes,
concrete value classes and the admitted abstract/open generic provider hierarchies share the bounded specialization model.
Ordinary generic subclasses retain substituted superclass layouts. Final generic method discovery
resolves K2 fake overrides to the executable declaring method; discovery, field-accessor lookup and
call emission share traversal of the substituted superclass graph. Specialization identity belongs
to the original declaration and its instantiated owner, independent of the subclass used at the call site.
Function and class specializations are discovered together until no further dependencies appear, including helpers called from generic
members whose bodies introduce other generic classes. The VM receives ordinary
concrete function and class records, with typed scalar or reference fields and calls. Platform ABI 3 records the
symbol and signature of each declaration requiring source compilation: generic implementations, classes with generic
supertypes, members of those classes, and inline bodies. One module can retain an ordinary precompiled library fragment
alongside those source bodies. The compiler resolves each source library as a separate Kotlin module over one shared
metadata session, preserving internal and private boundaries. It includes canonical sources for resolution and
specialization but binds ordinary implementations, including private helpers, to their compiled fragment instead of
emitting them again in the consumer. Kotlin source visibility still governs access to those helpers. Ordinary function
exports use the qualified declaration symbol and canonical signature, so same-name functions in different packages or
owners remain distinct even when their lowered signatures coincide. Fragment assembly retains exported
implementations, complete exported class/interface method ranges and their dependencies. Final application linking
retains methods reached by calls, imports and class initializers, then closes over matching name/parameter-count
candidates on reachable types for every retained virtual or interface-owned declaration. This conservative closure
preserves implementations, inherited methods, interface defaults and bridges required by native admission, even for
virtual declarations reached through direct calls. Newly reached types and method bodies extend the closure until
stable. Relocation rebuilds both method start and count while preserving contiguous order. Fields remain conservative;
unused methods and their otherwise unreachable dependencies disappear without changing the executable format or ABI.
Minimum semantic Runtime ABI is recomputed from the remaining instructions and types, including ABI 1.10 when an
array or exception root retains superclass methods; deleted method bodies cannot implicitly supply that requirement.
Tooling normalizes dependency module indexes into symbolic identities before linking so a shared library retains one owner across
fragment containers. The final executable restores concrete indexes. Generic binary templates are not part of the
platform bundle or VM artifact contract. Bundle format 9 and standalone module format 5 reject older representations.
Selected addon modules can provide source templates through the same verified source-library path. Their metadata
carrier retains template declaration identities while the separate source carrier supplies the matching files;
sources are reattached before template validation. Standalone module decoding still rejects detached templates.
Managed object/companion instances use existing class initializers and static fields, including exported singleton
fields for independent libraries. Typed callback interfaces use names derived from their complete signatures and
participate in trusted specialization ownership, so producer/consumer discovery order cannot change their identity.
These changes reuse the existing bundle and artifact representations and do not change the native ABI.
Default-argument metadata includes an explicit array-receiver size expression, evaluated from the already computed
receiver rather than re-evaluating its source expression, and an explicit null value distinct from an absent default.
Private Kotlin metadata format 6 carries the same defaults, including primary-constructor defaults without a receiver slot.
Ordinary public library constructors are linked as static functions receiving the already allocated object;
direct construction and superclass calls share argument/default evaluation. Their Kotlin bodies are compiled in
the owning library, not regenerated or bypassed by the consumer.
The standard library has one owner, stdlib:core: core helpers, inline scope functions, repeat, ranges, collections and portable kotlin.math.
Its generic and inline source bodies coexist with ordinary precompiled implementations.
Ordinary library implementations may materialize concrete generic classes and structural collection interfaces,
including ArrayList<String> and List<String>. These variants have canonical internal type, constructor, method
and field exports in the owning library artifact. Linking redirects matching trusted template materializations to
that owner and removes the superseded local records; variants absent from dependencies continue to specialize from
source. Source provenance, rather than a user class’s spelling, controls eligibility. Ambiguous owners, incompatible
layouts and missing members are rejected. Internal linking exports do not change Kotlin source visibility. The
representation uses the existing artifact import/export format, without new VM instructions or binary generic
templates. Only concrete variants are shared; inline and generic top-level source bodies retain their normal
consumer specialization behavior. K2 still materializes a consumer variant transiently for type and layout checking;
this linking contract does not yet skip that lowering work. Trusted collection variants include their supported
read-view interfaces regardless of consumer usage, so covariance cannot change a specialization’s nominal layout.
Collection value semantics add internal read-only nominal protocols after frontend analysis. Generated bridges
use the concrete receiver’s typed size, iterator and entry reads, boxing only returned values; trusted hash
containers use internal Any-key lookups, while custom reference implementations scan typed elements/entries.
Protocol methods have reserved artifact names separate from user declarations. Covariant source views
exclude these compiler-only protocols as widening targets. The protocols participate in
trusted specialization ownership, so a precompiled library and consumer agree on their type and method identity.
Repeated lowering of reused library IR retains the existing synthetic wrappers as executable roots; source
declaration collectors omit their undefined offsets. They expose no source API or mutable widening and
reuse existing dispatch, boxes and artifact representations.
User-defined concrete type arguments retain their qualified identity, with a separate root-package namespace, rather than
Kotlin metadata’s short display names. User classes named String therefore cannot reuse a built-in String variant.
Inline source normalization admits top-level functions and methods of named objects and companions. Generic object methods specialize under their original owner, preserving the dispatch receiver; class traversal snapshots tolerate new variants. Unused inline templates are removed recursively from objects, while runtime-referenced methods remain available. Ordinary class/local, suspend and reified inline methods remain outside this subset. The expansion and specialization budgets are unchanged.
Built-in module graph
Math public functions and extension getters are ordinary canonical library bodies; their internal floating primitives resolve through exact trusted signatures and emit ABI 1.16 math instructions. This keeps callable references on the existing precompiled library path. Integer helpers, arbitrary-base logarithms and integer rounding compose ordinary Guest instructions. The owning numerical contract and evidence are in portable mathematics.
The complete built-in graph is kotlin:builtins → stdlib:core → compukter:core. Compukter core owns environment
and VM-runtime APIs: compiler, child processes, redstone, sound, text displays, terminal, filesystem, and cooperative
tasks. This includes the environment-dependent kotlin.io facade without changing its package or default
imports. Module ownership changes the canonical platform content identity, not capability operation schemas or the
VM executable ABI. Tooling and addons using removed split-module IDs must rebuild against the consolidated owners.
Collection storage and universal views
The standard library publishes read-only lists and public ArrayList<T> with source bodies for specialization.
When consecutive platform libraries reuse dependency IR, collection value bridges retain their original
internal read-protocol declarations and methods. Lowering recovers those declarations from the existing
bridges and traverses their result types before using plugin-context lookup. This keeps protocol types
and interface dispatch available even when the next module cannot reference the internal class by name.
Bridge probing skips local types carrying enclosing arguments that do not instantiate their own declaration.
listOf and emptyList create fresh ArrayList<T> instances exposed as List<T>, with capacity equal to the
number of elements. Factories fill them through the specialized add method in source order. A read-only view can be
cast to MutableList<T> or ArrayList<T> and changed; it is not an immutable object. All twelve non-null primitive families use their corresponding primitive arrays, including compact Byte/Short and unsigned storage; reference and nullable storage uses typed reference arrays. Collection interfaces
and iterator methods are specialized in the consuming artifact. Covariant widening to List<Any> retains the same
list and backing array. The compiler publishes get, iterator, and next bridges with reference result signatures
alongside the typed methods. An Int read through Any allocates an ordinary managed kotlin.Int box; a reference
read preserves the element reference. The box’s runtime type and i32 payload support checked is Int and as Int,
while typed paths remain unboxed. Direct List<Any> factories use a local nominal Array<Any> whose reference slots
hold boxed Int and supported objects; each value is converted once before storage. Direct source Array<Any>
factories and indexed writes use the same boxing boundary; array reads return the stored reference. Collection<T>
owns size, isEmpty, and contains; specialized lists publish an Any argument bridge for covariant contains
calls. Iterable<T> search extensions contains, indexOf, and lastIndexOf scan with the iterator, while the
List<T> index methods use direct indexed access and publish Any and Any? argument bridges for covariant calls. The
Iterable<T> predicate extensions any, all, and none specialize both the element and function value signature
before lowering calls to the predicate.
List storage and lazy sequence caches use one internal compiler registry to select concrete
primitive/reference constructors by GuestPrimitive and nullability. Lists retain their typed
array-backed implementations. Sequence filtering uses a separate one-value storage interface:
primitive leaves have scalar fields; reference leaves have nullable storage, with occupancy
owned by the filtering iterator and consumed references cleared. The internal factory lowers
to ordinary object allocation and constructor calls; it adds no VM opcode or host operation.
MutableList<T> is invariant. ArrayList<T> uses a typed storage interface, with the compiler selecting the matching primitive or
reference storage at its trusted factory call. Growth, shifting, searches, slot clearing, and mutable iteration are
ordinary Guest bodies; all allocations use the VM’s existing metered array instructions. Read-only aliases share
the same list. Iterator bridges expose both read-only and mutable result signatures, and a modification counter
rejects stale iterator next/remove calls after structural mutations.
ArrayList is the sole library list implementation. Its indexed-search read-view helpers live alongside it in
ArrayList.kt and operate over List<T> to preserve specialization for typed, nullable and universal reads;
read-only and mutable views share the same iterator implementation with mutation checks and removal state. The compiler
uses one trusted collection read-view registry for bridge method names and additional interface types.
Library lists also publish nullable element read bridges for read-only views such as List<Int?> over List<Int>.
These views share storage: scalar reads allocate boxes, and reference reads retain identity. filterNotNull narrows
elements into a new typed list, unboxing nullable Int values and retaining non-null references. Generic smart-cast
arguments use checked reference conversions against their instantiated non-null parameter types.
Generic call arguments are converted against the specialized parameter types, so searching a widened List<Any>
boxes an Int argument before comparison. Universal equality over references uses the canonical virtual
Any.equals(Any?) method. Nullable == evaluates both operands once, compares null left receivers by identity,
and dispatches non-null left receivers. A literal-null comparison remains a reference test; explicit .equals(null)
still invokes the receiver. Root Any and arrays use identity, String compares UTF-16 content, and typed boxes compare
values with canonical NaN and distinct signed zero for Float. Primitive Float == retains IEEE semantics.
Generated data-class equals is an ordinary class method: it checks identity/type and compares primary-constructor
properties through the same scalar/reference contract, including nullable and object fields. Array properties compare
identity, while their generated hash uses content. Body properties do not participate. Custom/inherited methods and
compiled libraries share dispatch; there is no compiler-side enumeration of class layouts at each equality call.
Each nullable primitive uses a nullable reference to its nominal managed box. Conversion to the direct scalar checks the box and reads its
payload; nullable and Any boundaries preserve existing box references. Virtual hashCode and reference-default string
conversion are described in the ABI reference (Runtime ABI 1.11 and 1.10). This equality implementation reuses ABI 1.11
instructions without adding an opcode, C ABI change or persisted-state format. Compiled libraries must rebuild
against the updated canonical runtime module.
Nullable reference arrays have distinct nominal types with nullable element descriptors. Array<Int?> and
List<Int?> store the same managed Int boxes or null, while List<Int> retains scalar storage. Nullable and non-null
lists expose Any? bridges for reads, iteration, and searches.
Reference arrays and register allocation
Concrete Array<GuestClass> uses become distinct local nominal array types whose elements are exact Guest class
references. Existing array instructions allocate, load, and store them; the verifier checks element types and GC
follows live references in their slots. Execution tracing consults each frame’s safepoint map before resolving a
reference register, so a dead register left after GC cannot invalidate a running program.
After reachability pruning and relocation, the linker reuses temporary registers whose lifetimes do not overlap. Allocation uses the same control-flow analysis as GC roots, including loop back edges, exceptional edges and resume blocks. The analysis indexes instruction nodes and read/write sets once per function, propagates liveness with request-local bitsets, and materializes register sets only for the final boundaries. An exception destination is killed only on its exceptional edge; normal continuations and other handlers retain their own live inputs. Only identical semantic types and physical shapes share a register; parameters, caught-exception destinations and operands in unreachable code retain dedicated slots. Inputs and destinations remain distinct through an instruction, including allocation and suspended calls. Instruction order, debug boundaries and metered costs are unchanged. Root maps, linked module hashes and frame storage requirements are rebuilt after allocation. The artifact encoding and runtime layout/verifier contract are unchanged; already compiled artifacts retain their original frame storage.
ServerCompilerService performs bounded asynchronous preparation, persistent-cache lookup, and single-flight
deduplication by compilation identity. Minecraft-facing code submits requests and drains completions; worker and cache
I/O run outside the server tick thread.
Enum identity storage
The generated Kotlin runtime module owns a non-generic abstract kotlin.Enum class with name/ordinal
fields. Guest source Enum<E> and Enum<*> map to this base, while each concrete enum retains its own
nominal type and singleton fields. Initializers create entry objects, set exact declaration names and
indices, then publish static references. Trusted getters emit ordinary base field reads, including for
separately compiled enums. This introduces no host lookup, per-enum dispatch table or VM instruction.