Dart Bytecode File Format

Overview

This file describes the binary format of Dart bytecode files.

Dart bytecode is a binary representation of dynamic modules on the VM/AOT. It is generated by dart2bytecode tools and can be executed by the VM and AOT runtime (built with dynamic modules support).

Basic types

This document uses notation similar to Dart kernel binary format.

The follow basic data types are used to describe bytecode format.

type Byte = a byte

type UInt = packed (variable-length) unsigned integer, same format as kernel binary

type SLEB128 = packed (variable-length) signed integer

type UInt32 = little-endian 32-bit unsigned integer

type List<T> {
  UInt length
  T[length] items
}

type Pair<T0, T1> {
  T0 first
  T1 second
}

// Offset in the source file + 1.
type FileOffset = UInt

Bytecode file

Each bytecode file describes a dynamic module, a set of Dart libraries which can be loaded at runtime.

All declarations in the Dart bytecode file are sliced into several layers, which reside in different sections such as libraries, classes, members, code, etc.

type BytecodeFile {
  UInt32 magic = 0x44424333; // 'DBC3'
  UInt32 formatVersion = 1;

  // Descriptors of the sections below.
  // Each section has a fixed index in the descriptors array.
  Section[] sections;

  // Sections. The contents of the sections can occur in arbitrary
  // order and with gaps between sections, so they should be located only via
  // section descriptor.

  StringTable stringTable;
  ObjectTable objectTable;
  EntryPoint entryPoint;
  LibraryUriAndOffset[] libraryIndex;
  LibraryDeclaration[] libraries;
  ClassDeclaration[] classes;
  Members[] members;
  Code[] codes;
  SourcePositions[] sourcePositions;
  SourceFile[] sourceFiles;
  LineStarts[] lineStarts;
  LocalVariables[] localVariables;
  PackedObject[] annotations;
}

type Section {
  // Number of items in the section.
  // Zero for string table, object table and entry point sections.
  UInt32 numItems;
  // Offset of the section relative to the beginning of the bytecode file, in bytes.
  UInt32 offset;
}

String table

String table section contains strings encoded as 1-byte (Latin1) and 2-byte (UTF-16) characters. Strings are not zero-terminated.

This representation is aligned with Dart VM and allows VM to avoid extra copying and decoding of string characters when loading a string.

type StringTable = {
  UInt32 numOneByteStrings;
  UInt32 numTwoByteStrings;
  // Offsets are relative to stringContents.
  UInt32[numOneByteStrings] oneByteStringEndOffsets;
  UInt32[numTwoByteStrings] twoByteStringEndOffsets;
  // Contains string characters of one-byte strings and then two-byte strings.
  Byte[] stringContents;
}

// Reference to a string in string table.
type PackedString {
  // Bit 0: set for two-byte string
  // Bits 1+: index of string in one-byte strings (0..numOneByteStrings-1)
  // or two-byte strings (0..numTwoByteStrings-1).
  UInt indexAndKind;
}

Object table

Object table section contains various objects referenced by declarations and constant pools.

Object included into the object table can be shared by multiple constant pools, so object table provides a way to de-duplicate objects, reducing bytecode file size and bytecode loading time.

Bytecode generator can choose to include particular object into the object table or write it inline. Object is written inline (not included into object table) if there are only a few references to that object or it is small and can be created efficiently (for example, int and bool constants are always written inline).

type ObjectTable {
  UInt numEntries;

  // Total size of ‘objects’ in bytes.
  UInt objectsSize;

  ObjectContents[numEntries] objects;

  // Offsets relative to ‘objects’.
  UInt[numEntries] objectOffsets;
}


// Either reference to an object in the object table, or object contents
// written inline (determined by bit 0).
PackedObject = ObjectReference | ObjectContents;

type ObjectReference {
  // Bit 0 (reference bit): 1
  // Bits 1+: index in the object table
  UInt reference;
}

type ObjectContents {
  // Bit 0 (reference bit): 0
  // Bits 1-4: object kind
  // Bits 5+ object flags
  UInt header;
}

// Invalid/null object. Always present at index 0 of object table.
type InvalidObject extends ObjectContents {
  kind = 0;
}

type Library extends ObjectContents {
  kind = 1;
  PackedObject importUri;
}

type Script extends ObjectContents {
  kind = 2;
  flags = (hasSourceFile);
  PackedObject uri;
  if hasSourceFile
    // Offset of source file in ‘sourceFiles’ section of BytecodeFile.
    UInt sourceFileOffset;
}

type Class extends ObjectContents {
  kind = 3;
  PackedObject library;
  // Empty name is used for artificial class containing top-level
  // members of a library.
  PackedObject name;
}

type Member extends ObjectContents {
  kind = 4;
  flags = (isField, isConstructor);
  PackedObject class;
  PackedObject name;
}

type Closure extends ObjectContents {
  kind = 5;
  PackedObject enclosingMember;
  UInt closureIndex;
}

type Name extends ObjectContents {
  kind = 6;
  flags = (isPublic);

  if !isPublic
    PackedObject library;

  // Getters are prefixed with 'get:'.
  // Setters are prefixed with 'set:'.
  PackedString name;
}

abstract type ConstObject extends ObjectContents {
  kind = 7;
  flags = constantTag (4 bits);
}

type ConstInt extends ConstValue {
  kind = 7;
  constantTag = 1;
  SLEB128 value;
}

type ConstDouble extends ConstValue {
  kind = 7;
  constantTag = 2;
  // double bits are reinterpreted as 64-bit int
  SLEB128 value;
}

type ConstBool extends ConstValue {
  kind = 7;
  constantTag = 3;
  Byte isTrue;
}

type ConstString extends ConstObject {
  kind = 7;
  constantTag = 4;
  PackedString string;
}

type ConstSymbol extends ConstObject {
  kind = 7;
  constantTag = 5;
  PackedObject name;
}

type ConstInstance extends ConstObject {
  kind = 7;
  constantTag = 6;
  PackedObject type;
  List<Pair<PackedObject, PackedObject>> fieldValues;
}

type ConstList extends ConstObject {
  kind = 7;
  constantTag = 7;
  PackedObject elementType;
  List<PackedObject> elements;
}

type ConstMap extends ConstObject {
  kind = 7;
  constantTag = 8;
  // Map<K, V> type.
  PackedObject mapType;
  // [key1, value1, key2, value2, ....]
  List<PackedObject> elements;
}

type ConstSet extends ConstObject {
  kind = 7;
  constantTag = 9;
  PackedObject elementType;
  List<PackedObject> elements;
}

type ConstRecord extends ConstObject {
  kind = 7;
  constantTag = 10;
  PackedObject recordType;
  // Values of positional, then named fields.
  List<PackedObject> fieldValues;
}

type ConstTearOff extends ConstObject {
  kind = 7;
  constantTag = 11;
  PackedObject target;
}

type ConstTearOffInstantiation extends ConstObject {
  kind = 7;
  constantTag = 12;
  PackedObject tearOff;
  PackedObject typeArguments;
}

abstract type Type extends ObjectContents {
  kind = 8;
  flags = (typeTag (4 bits), isNullable);
}

type DynamicType extends Type {
  kind = 8;
  typeTag = 1;
}

type VoidType extends Type {
  kind = 8;
  typeTag = 2;
}

type NullType extends Type {
  kind = 8;
  typeTag = 3;
}

type NeverType extends Type {
  kind = 8;
  typeTag = 4;
}

// Interface type without type arguments.
type SimpleType extends Type {
  kind = 8;
  typeTag = 5;
  PackedObject class;
}

// Generic interface type.
type GenericType extends Type {
  kind = 8;
  typeTag = 6;
  PackedObject class;
  PackedObject typeArguments;
}

type TypeParameter extends Type {
  kind = 8;
  typeTag = 7;
  // Class, Member or Closure declaring this type parameter.
  // Null (Invalid) if declared by function type.
  PackedObject parent;
  // For type parameters declared by function types this index is in a concatenation
  // of type parameters of all enclosing function types.
  UInt indexInParent;
}

type FunctionType extends Type {
  kind = 8;
  typeTag = 8;

  UInt functionTypeFlags = (hasOptionalPositionalParams,
                            hasOptionalNamedParams,
                            hasTypeParams);

  if hasTypeParams
    TypeParametersDeclaration typeParameters;

  UInt numParameters;

  if hasOptionalPositionalParams || hasOptionalNamedParams
    UInt numRequiredParameters;

  Type[] positionalParameters;
  NameAndType[] namedParameters;
  PackedObject returnType;
}

type TypeParametersDeclaration {
   UInt numTypeParameters;
   PackedObject[numTypeParameters] names;
   BoundAndDefaultType[numTypeParameters] boundsAndDefaultTypes;
}

type BoundAndDefaultType {
  PackedObject bound;
  PackedObject defaultType;
}

type NameAndType {
  PackedObject name;
  PackedObject type;
}

type RecordType extends Type {
  kind = 8;
  typeTag = 9;
  UInt numPositionalFields;
  UInt numNamedFields;
  Type[numPositionalFields] positionalFields;
  NameAndType[numNamedFields] namedFields;
}

// Type arguments vector.
type TypeArguments extends ObjectContents {
  kind = 6;
  List<PackedObject> args;
}

// Describes shape of arguments passed to a call.
type ArgDesc extends ObjectContents {
  kind = 8;
  flags = (hasNamedArgs, hasTypeArgs);

  UInt numArguments;

 if hasTypeArgs
   UInt numTypeArguments;

 if hasNamedArgs
   List<PackedObject> argNames;
}

Entry point

Entry point section contains a reference to a dynamic module entry point function.

type EntryPoint {
  PackedObject member;
}

Library index

Library index section contains the list of library “forward declarations” in the form of library URI and offset of its full declaration.

type LibraryUriAndOffset {
  PackedObject uri;

  // Offset of library declaration in the ‘libraries’ section of BytecodeFile.
  UInt libraryOffset
}

Libraries

Libraries section contains declarations of libraries included into a dynamic module. Number of items in the library index section and libraries section should be the same.

type LibraryDeclaration {
  UInt flags = (usesDartMirrors, usesDartFfi);
  PackedObject name;
  PackedObject script;

  // Classes declared in this library.
  // The first class is an artificial ‘top-level’ class containing top-level
  // fields and functions, as well as library annotations.
  List<ClassNameAndOffset> classes;
}

type ClassNameAndOffset {
  PackedObject className;

  // Offset of class declaration in the ‘classes’ section of BytecodeFile.
  UInt classOffset;
}

Classes

Classes section contains declarations of classes included into a dynamic module. Each class in the classes section should be mentioned in its library declaration.

type ClassDeclaration {
  UInt flags = (isAbstract, isEnum,
                hasTypeParams, hasTypeArguments,
                isTransformedMixinApplication,
                hasSourcePositions, hasAnnotations, hasPragma);
  PackedObject script;

 if hasSourcePositions
   FileOffset position;
   FileOffset endPosition;

 if hasTypeArguments
    UInt numTypeArguments;
 if hasTypeParams
    TypeParametersDeclaration typeParameters;

  PackedObject superType;
  List<PackedObject> interfaces;

 if hasAnnotations
   UInt annotationsOffset;

  // Offset of class members in the ‘members’ section of BytecodeFile.
  UInt membersOffset;
}

Members

Members section contains declaration of members (fields, methods and constructors). Member declarations are organized per class.

type Members {
  // Total number of functions, including field getters and setters.
  UInt numFunctions;

  List<FieldDeclaration> fields;
  List<FunctionDeclaration> functions;
}

type FieldDeclaration {
  UInt flags = (isStatic, isConst, isFinal, isLate,
                isCovariant, isCovariantByClass,
                isExtensionMember, isReflectable,
                hasGetter, hasSetter,
                hasInitializer, hasNontrivialInitializer, hasInitializerCode,
                hasSourcePositions, hasAnnotations, hasPragma,
                hasCustomScript);
  PackedObject name;
  PackedObject type;

  if hasCustomScript
    PackedObject script;
  if hasSourcePositions
    FileOffset position;
    FileOffset endPosition;

  if hasInitializerCode
    // Offset of initializer code in the ‘code’ section of BytecodeFile.
    UInt initializerCodeOffset;
  if !hasNontrivialInitializer
    PackedObject value;

  if hasGetter
    PackedObject getterName;
  if hasSetter
    PackedObject setterName;
  if hasAnnotations
   UInt annotationsOffset;
}

type FunctionDeclaration {
  UInt flags = (isStatic, isAbstract, isGetter, isSetter,
                isConstructor, isFactory, isConst,
                hasOptionalPositionalParams, hasOptionalNamedParams,
                hasTypeParams, hasParameterFlags,
                isExtensionMember, isReflectable, isDebuggable,
                isAsync, isAsyncStar, isSyncStar,
                isNoSuchMethodForwarder, isExternal, isNative,
                hasSourcePositions, hasAnnotations, hasPragma,
                hasCustomScript);

  PackedObject name;

  if hasCustomScript
    PackedObject script;
  if hasSourcePositions
    FileOffset position;
    FileOffset endPosition;

  if hasTypeParams
    TypeParametersDeclaration typeParameters;

  UInt numParameters;

  if hasOptionalPositionalParams || hasOptionalNamedParams
    UInt numRequiredParameters;

  NameAndType[numParameters] parameters;
  if hasParameterFlags
    // (isCovariant, isCovariantByClass, isFinal, isRequired)
    List<UInt> parameterFlags;

  PackedObject returnType;

 if isNative
   PackedObject nativeName;

 if !isAbstract
   // Offset of code in the ‘code’ section of BytecodeFile.
   UInt codeOffset;

 if hasAnnotations
   // Offset of function annotations in ‘annotations’ section of
   // component, followed by parameter annotations.
   UInt annotationsOffset;
}

Code

Code section contains bodies of members (including field initializers).

type Code {
  UInt flags = (hasExceptionsTable, hasSourcePositions, hasNullableFields,
                hasClosures, hasParameterFlags, hasForwardingStubTarget,
                hasDefaultFunctionTypeArgs, hasLocalVariables)

  if hasParameterFlags
    // (isCovariant, isCovariantByClass, isFinal, isRequired)
    List<UInt> flags;

  if hasForwardingStubTarget
    ConstantIndex forwardingStubTarget;

  if hasDefaultFunctionTypeArgs
    ConstantIndex defaultFunctionTypeArgs;

  if hasClosures
    List<ClosureDeclaration> closureDeclarations;

  ConstantPool constantPool;

  UInt bytecodeSizeInBytes
  Byte[bytecodeSizeInBytes] bytecodes;

  if hasExceptionsTable
    ExceptionsTable exceptionsTable;

  if hasSourcePositions
    // Offset of SourcePositions in ‘sourcePositions’ section of BytecodeFile.
    UInt sourcePositionsOffset;

  if hasLocalVariables
    // Offset of LocalVariables in ‘localVariables’ section of BytecodeFile.
    UInt localVariablesOffset;

  if hasNullableFields
    List<PackedObject> nullableFields;

  if hasClosures
    // Parallel to closureDeclarations.
    ClosureCode[] closures;
}

type ClosureDeclaration {
  UInt flags = (hasOptionalPositionalParams, hasOptionalNamedParams,
                hasTypeParams, hasSourcePositions,
                isAsync, isAsyncStar, isSyncStar, isDebuggable,
                hasParameterFlags)

  // Member or Closure.
  PackedObject parent;
  PackedObject name;

  if hasSourcePositions
    FileOffset position;
    FileOffset endPosition;

  if hasTypeParams
    TypeParametersDeclaration typeParameters;

  UInt numParameters;

  if hasOptionalPositionalParams || hasOptionalNamedParams
    UInt numRequiredParameters;

  NameAndType[numParameters] parameters;

  if hasParameterFlags
    // (isCovariant, isCovariantByClass, isFinal, isRequired)
    List<UInt> flags;

  PackedObject returnType;
}

type ClosureCode {
  UInt flags = (hasExceptionsTable, hasSourcePositions, hasLocalVariables)

  UInt bytecodeSizeInBytes;
  Byte[bytecodeSizeInBytes] bytecodes;

  if hasExceptionsTable
    ExceptionsTable exceptionsTable;

  if hasSourcePositions
    // Offset of SourcePositions in ‘sourcePositions’ section of BytecodeFile.
    UInt sourcePositionsOffset;

  if hasLocalVariables
    // Offset of LocalVariables in ‘localVariables’ section of BytecodeFile.
    UInt localVariablesOffset;
}

Constant Pool

Each member code has its own constant pool which describes an array of objects used by the interpreter at runtime along with bytecode instructions. Member constant pool is shared by all closure functions declared in the member. Bytecode instructions reference constant pool entries by index.

type ConstantPool {
  List<ConstantPoolEntry> entries;
}

// Index of entry in the constant pool.
type ConstantIndex = UInt;

abstract type ConstantPoolEntry {
  Byte tag;
}

type ConstantObjectRef extends ConstantPoolEntry {
  Byte tag = 1;
  PackedObject object;
}

type ConstantClass extends ConstantPoolEntry {
  Byte tag = 2;
  PackedObject class;
}

type ConstantType extends ConstantPoolEntry {
  Byte tag = 3;
  PackedObject type;
}

type ConstantStaticField extends ConstantPoolEntry {
  Byte tag = 4;
  PackedObject field;
}

// Occupies 2 entries in the constant pool.
// (to hold offset and field object).
type ConstantInstanceField extends ConstantPoolEntry {
  Byte tag = 5;
  PackedObject field;
}

type ConstantTypeArgumentsField extends ConstantPoolEntry {
  Byte tag = 6;
  PackedObject class;
}

type ConstantClosureFunction extends ConstantPoolEntry {
  Byte tag = 7;
  UInt closureIndex;
}

type ConstantEndClosureFunctionScope extends ConstantPoolEntry {
  Byte tag = 8;
}

type ConstantSubtypeTestCache extends ConstantPoolEntry {
  Byte tag = 9;
}

type ConstantEmptyTypeArguments extends ConstantPoolEntry {
  Byte tag = 10;
}

// Occupies 2 entries in the constant pool
// (to hold target and arguments descriptor).
type ConstantDirectCall extends ConstantPoolEntry {
  Byte tag = 11;
  PackedObject target;
  PackedObject argDesc;
}

// Occupies 2 entries in the constant pool
// (to hold interface target and arguments descriptor).
type ConstantInterfaceCall extends ConstantPoolEntry {
  Byte tag = 12;
  PackedObject target;
  PackedObject argDesc;
}

// Occupies 3 entries in the constant pool.
type ConstantInstantiatedInterfaceCall extends ConstantPoolEntry {
  Byte tag = 13;
  PackedObject target;
  PackedObject argDesc;
  PackedObject staticReceiverType;
}

// Occupies 2 entries in the constant pool
type ConstantDynamicCall extends ConstantPoolEntry {
  Byte tag = 14;
  PackedObject selectorName;
  PackedObject argDesc;
}

Exceptions table

Each Code and ClosureCode may have an optional table of try blocks which maps ranges of bytecode instructions to corresponding exception handlers.

// Offset of a bytecode instruction in 'bytecodes'.
type BytecodeOffset = UInt;

type TryBlock {
  UInt outerTryIndexPlus1;
  BytecodeOffset startPC; // Inclusive.
  BytecodeOffset endPC; // Exclusive.
  BytecodeOffset handlerPC;
  Byte flags = (needsStackTrace, isSynthetic);
  List<ConstantIndex> types;
}

type ExceptionsTable {
  // Ordered by startPC, then by nesting (outer precedes inner).
  // Try blocks are properly nested. It means there are no partially
  // overlapping try blocks - each pair of try block regions either
  // has no intersection or one try block region encloses another.
  List<TryBlock> tryBlocks;
}

Source positions

Source positions information maps ranges of bytecode instructions to corresponding source positions. This information is optional and resides in the ‘sourcePositions’ section of BytecodeFile.

type SourcePositions {
  UInt numEntries;
  // Encoded list of pairs (PC, FileOffset), ordered by PC (offset of bytecode instruction).
  // For two consecutive entries (PC1, FileOffset1), (PC2, FileOffset2) all
  // bytecode instructions in range [PC1,PC2) correspond to a source
  // position FileOffset1.
  SourcePositionEntry[numEntries] entries
}

type SourcePositionEntry = {
  // Delta-encoded PC (delta from the PC of previous entry).
  UInt PCDelta;
  // Delta-encoded FileOffset (delta from the FileOffset of previous entry).
  SLEB128 FileOffsetDelta;
}

Source files and line starts

type SourceFile {
  UInt flags = (hasLineStarts, hasSource)
  PackedObject importUri;

  if hasLineStarts
    // Offset of line starts in ‘lineStarts’ section of BytecodeFile.
    UInt lineStartsOffset;

  if hasSource
    PackedString source;
}

type LineStarts {
  // Can be also treated as line lengths.
  List<UInt> deltaEncodedLineStarts;
}

Local variables

type LocalVariables {
  UInt numEntries;
  // Encoded list of entries ordered by PC (offset of bytecode instruction).
  LocalVariableEntry[numEntries] entries;
}

abstract type LocalVariableEntry = {
  // Bits 0-3: kind
  // Bits 4-7: flags
  Byte kindAndFlags;
  // Delta-encoded start PC.
  SLEB128 startPCDelta;
}

type Scope extends LocalVariableEntry {
  kind = 1
  // Offset to the end PC relative to startPC.
  UInt endPCDelta;
  // Level of context containing captured variables in this scope.
  // -1 if there is no context.
  SLEB128 contextLevel;
  FileOffset position;
  FileOffset endPosition;
}

// Variable declarations immediately follow their corresponding Scope
type VariableDeclaration extends LocalVariableEntry {
  kind = 2
  flags = (isCaptured)
  // Index of local variable in the stack frame or in the context.
  SLEB128 index;
  ConstantIndex name;
  ConstantIndex type;
  // Source position where variable is declared.
  FileOffset position;
  // Source position after variable is initialized.
  FileOffset initializedPosition;
}

type ContextVariable extends LocalVariableEntry {
  kind = 3
  // Index of a local variable holding context (in the stack frame).
  SLEB128 index;
}

Bytecode instructions

Execution state

Dart bytecode interpreter is a stack machine with local variables.

Bytecode instructions should begin by creating a frame using Entry, EntryOptional or EntrySuspendable instruction.

After creating a frame, local variables and parameters can be referenced as Locals[i] up to the size of the frame. For frames created with Entry instruction, parameter n is accessible via Locals[-numParams-kParamEndSlotFromFp+n] (where kParamEndSlotFromFp == 4). For frames created with EntryOptional or EntrySuspendable instructions, parameter n is copied to Locals[n].

Most bytecode instructions take values from the expression stack or push values onto the stack. Stack and local variables are disjoint, e.g. instructions operating with locals cannot be used to access stack and vice versa. Stack pointer is referred as SP, stack grows up. SP[0] is the value on top of the stack, SP[-1], SP[-2] etc are values pushed previously. Stack can be used after frame setup and initially empty.

Data flow merges are not allowed on the stack: at each PC and stack location, there should be exactly one bytecode instructions which could have pushed a value into that stack location, regardless of how control reached that PC.

Bytecode instructions can reference constant pool via ConstantPool[i].

Instruction encoding

Each instruction starts with opcode byte. Certain instructions have wide encoding variant. In such case, the least significant bit of opcode is 0 for compact variant and 1 for wide variant.

The following operand encodings are used:

0........8.......16.......24.......32.......40.......48
+--------+
| opcode |                              0: no operands
+--------+

+--------+--------+
| opcode |    A   |                     A: unsigned 8-bit operand
+--------+--------+

+--------+--------+
| opcode |   D    |                     D: unsigned 8/32-bit operand
+--------+--------+

+--------+----------------------------------+
| opcode |                D                 |            D (wide)
+--------+----------------------------------+

+--------+--------+
| opcode |   X    |                     X: signed 8/32-bit operand
+--------+--------+

+--------+----------------------------------+
| opcode |                X                 |            X (wide)
+--------+----------------------------------+

+--------+--------+
| opcode |    T   |                     T: signed 8/24-bit operand
+--------+--------+

+--------+--------------------------+
| opcode |            T             |   T (wide)
+--------+--------------------------+

+--------+--------+--------+
| opcode |    A   |   E    |            A_E: unsigned 8-bit operand and
+--------+--------+--------+                 unsigned 8/32-bit operand

+--------+--------+----------------------------------+
| opcode |    A   |                 E                |   A_E (wide)
+--------+--------+----------------------------------+

+--------+--------+--------+
| opcode |    A   |   Y    |            A_Y: unsigned 8-bit operand and
+--------+--------+--------+                 signed 8/32-bit operand

+--------+--------+----------------------------------+
| opcode |    A   |                 Y                |   A_Y (wide)
+--------+--------+----------------------------------+

+--------+--------+--------+
| opcode |    D   |   F    |            D_F: unsigned 8/32-bit operand and
+--------+--------+--------+                 unsigned 8-bit operand

+--------+----------------------------------+--------+
| opcode |                 D                |    F   |   D_F (wide)
+--------+----------------------------------+--------+

+--------+--------+--------+--------+
| opcode |    A   |    B   |    C   |   A_B_C: 3 unsigned 8-bit operands
+--------+--------+--------+--------+

Instruction Set

Trap

Unreachable instruction.

Entry D

Function prologue for the function. D - number of local slots to reserve.

EntryOptional A, B, C

Function prologue for the function with optional or named arguments. A - expected number of positional arguments. B - number of optional positional arguments. C - number of named arguments.

Only one of B and C can be not 0.

If B is not 0 then EntryOptional bytecode is followed by B LoadConstant bytecodes specifying default values for optional positional arguments.

If C is not 0 then EntryOptional is followed by 2 * C LoadConstant bytecodes. Bytecode at 2 * i specifies name of the i-th named argument and at 2 * i + 1 default value. ‘A’ operand of the LoadConstant bytecode specifies the location of the parameter on the stack. Named arguments are sorted alphabetically.

Note: Unlike Entry bytecode EntryOptional does not setup the frame for local variables. This is done by a separate bytecode Frame, which should follow EntryOptional and its LoadConstant instructions.

EntrySuspendable A, B, C

Similar to EntryOptional, but also reserves a local variable slot for suspend state variable.

LoadConstant A, E

Used in conjunction with EntryOptional and EntrySuspendable instructions to describe names and default values of optional parameters.

Frame D

Reserve space for D local variables. Local variables are initially initialized with null.

CheckFunctionTypeArgs A, E

Check for a passed-in type argument vector of length A and store it at Locals[E].

CheckStack A

Compare SP against stack limit and call stack overflow or interrupt handler if necessary. Should be used in prologue (A = 0), or at the beginning of a loop with depth A.

Allocate D

Allocate object of class ConstantPool[D] with no type arguments.

AllocateT

Allocate object of class SP[0] with type arguments SP[-1].

CreateArrayTOS

Allocate array of length SP[0] with type arguments SP[-1].

AllocateContext A, E

Allocate Context object holding E context variables. A is a static ID of the context. Static ID of a context may be used to disambiguate accesses to different context objects. Context objects with the same ID should have the same number of context variables.

CloneContext A, E

Clone Context object SP[0] holding E context variables. A is a static ID of the context. Cloned context has the same ID.

LoadContextParent

Load parent from context SP[0].

StoreContextParent

Store context SP[0] into parent field of context SP[-1].

LoadContextVar A, E

Load value from context SP[0] at index E. A is a static ID of the context.

StoreContextVar A, E

Store value SP[0] into context SP[-1] at index E. A is a static ID of the context.

PushConstant D

Push value ConstantPool[D] onto the stack.

PushNull

Push null onto the stack.

PushTrue

Push true onto the stack.

PushFalse

Push false onto the stack.

PushInt X

Push int X onto the stack.

Drop1

Drop 1 value from the stack

Push X

Push Locals[X] to the stack.

StoreLocal X; PopLocal X

Store top of the stack into Locals[X] and pop it if needed.

LoadFieldTOS D

Push value of field ConstantPool[D] from object SP[0].

StoreFieldTOS D

Store value SP[0] into field ConstantPool[D] of object SP[-1].

StoreIndexedTOS

Store SP[0] into array SP[-2] at index SP[-1]. No type or index checking is done. SP[-2] is assumed to be an array (created with CreateArrayTOS), SP[-1] is an int.

PushStatic D

Pushes value of the static field ConstantPool[D] on to the stack.

StoreStaticTOS D

Stores SP[0] into the static field ConstantPool[D].

Jump target

Jump to the given target. Target is specified as offset from the PC of the jump instruction.

JumpIfNoAsserts target

Jump to the given target if assertions are not enabled. Target is specified as offset from the PC of the jump instruction.

JumpIfNotZeroTypeArgs target

Jump to the given target if number of passed function type arguments is not zero. Target is specified as offset from the PC of the jump instruction.

JumpIfUnchecked target

May jump to the given target if current function was called through unchecked call instruction (UncheckedInterfaceCall, UncheckedClosureCall). This instruction can be used to skip checking of type argument bounds and types of generic-covariant parameters. This is an optimization, so it is valid to treat this instruction as a no-op. Target is specified as offset from the PC of the jump instruction.

JumpIfEqStrict target; JumpIfNeStrict target

Jump to the given target if SP[-1] is the same (JumpIfEqStrict) / not the same (JumpIfNeStrict) object as SP[0].

JumpIfTrue target; JumpIfFalse target; JumpIfNull target; JumpIfNotNull target

Jump to the given target if SP[0] is true/false/null/not null.

Suspend target

Create a snapshot of the current frame and store it in the suspend state object. Execution can be resumed from the suspend state at the given target PC. Target is specified as offset from the PC of the suspend instruction. The filled suspend state object is stored into the reserved suspend state local variable. Current function frame should be created with EntrySuspendable instruction.

DirectCall D, F

Invoke the target function with arguments SP[-(F-1)], ..., SP[0]. ContantPool[D] is a ConstantDirectCall specifying target and arguments descriptor.

InterfaceCall D, F

Lookup and invoke instance method with arguments SP[-(F-1)], ..., SP[0]. ContantPool[D] is a ConstantInterfaceCall specifying interface target and arguments descriptor. Method has to be declared (explicitly or implicitly) in an interface implemented by a receiver SP[0], and passed arguments are valid for the interface method declaration.

UncheckedInterfaceCall D, F

Same as InterfaceCall, but can omit type checks of generic-covariant parameters.

InstantiatedInterfaceCall D, F

Same as InterfaceCall, but provides additional information about instantiated static type of a (generic) receiver via ConstantInstantiatedInterfaceCall. At run time, if actual type of receiver matches static type, then type checks of generic-covariant Parameters can be omitted.

UncheckedClosureCall D, F

Call closure SP[0] with arguments SP[-F], ..., SP[-1]. ContantPool[D] is a ConstantObjectRef with ArgDesc object specifying arguments descriptor. Closure should have a statically known function type; parameters should have correct types according to the closure’s function type, so parameter type checks can be omitted.

DynamicCall D, F

Lookup and invoke instance method with arguments SP[-(F-1)], ..., SP[0]. ContantPool[D] is a ConstantDynamicCall specifying selector and arguments descriptor.

ReturnTOS

Return to the caller using SP[0] as a result.

AssertAssignable A, E

Assert that instance SP[-4] is assignable to variable named SP[0] of type SP[-1] with instantiator type arguments SP[-3] and function type arguments SP[-2] using ConstantSubtypeTestCache at ConstantPool[E]. If A is 1, then the instance may be an int.

Instance remains on stack. Other arguments are consumed.

AssertSubtype

Assert that one type is a subtype of another. Throws a TypeError otherwise. The stack has the following arguments on it:

SP[-4]  instantiator type args
SP[-3]  function type args
SP[-2]  sub-type
SP[-1]  super-type
SP[-0]  destination name

All 5 arguments are consumed from the stack and no results is pushed.

LoadTypeArgumentsField D

Load instantiator type arguments from an instance SP[0]. ConstantPool[D] should be a ConstantTypeArgumentsField corresponding to an instance's generic (base) class.

InstantiateType D

Instantiate type ConstantPool[D] with instantiator type arguments SP[-1] and function type arguments SP[0].

InstantiateTypeArgumentsTOS A, E

Instantiate type arguments ConstantPool[E] with instantiator type arguments SP[-1] and function type arguments SP[0]. A != 0 indicates that resulting type arguments are all dynamic if both instantiator and function type arguments are all dynamic.

Throw A

Throw (Rethrow if A != 0) exception. Exception object and stack object are taken from the top of the stack.

MoveSpecial A, Y

Copy value from special variable to Locals[Y]. Currently only used to pass exception object (A = 0) and stack trace object (A = 1) to catch handler.

SetFrame A

Reset expression stack to empty. ‘A’ should be equal to the frame size allocated by prologue instructions. Used to drop temporaries from the stack in the exception handler.

BooleanNegateTOS

SP[0] = !SP[0]

EqualsNull

SP[0] = (SP[0] == null) ? true : false

NegateInt

Equivalent to invocation of unary int operator-. Receiver should have static type int.

SP[0] = -SP[0]

AddInt; SubInt; MulInt; TruncDivInt; ModInt; BitAndInt; BitOrInt; BitXorInt; ShlInt; ShrInt

Equivalent to invocation of binary int operator +, -, *, ~/, %, &, |, ^, << or >>. Receiver and argument should have static type int.

SP[0] = SP[-1] SP[0]

CompareIntEq; CompareIntGt; CompareIntLt; CompareIntGe; CompareIntLe

Equivalent to invocation of binary int operator ==, >, <, >= or <=. Receiver and argument should have static type int.

SP[0] = SP[-1] SP[0] ? true : false

NegateDouble

Equivalent to invocation of unary double operator-. Receiver should have static type double.

SP[0] = -SP[0]

AddDouble; SubDouble; MulDouble; DivDouble

Equivalent to invocation of binary double operator +, -, *, / Receiver and argument should have static type double.

SP[0] = SP[-1] SP[0]

CompareDoubleEq; CompareDoubleGt; CompareDoubleLt; CompareDoubleGe; CompareDoubleLe

Equivalent to invocation of binary double operator ==, >, <, >= or <=. Receiver and argument should have static type double.

SP[0] = SP[-1] SP[0] ? true : false

AllocateClosure D

Allocate closure object for closure function ConstantPool[D].

DebugCheck

No-op. Provides a point where debugger can stop executing code when single stepping or when it hits a breakpoint.