18a44b3095
TEST=language/nnbd/subtyping/function_type_required_params_test Change-Id: I050f8fdd331985512f204b265d81c7deec0d67fe Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/415640 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
1375 lines
35 KiB
Markdown
1375 lines
35 KiB
Markdown
# 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](https://github.com/dart-lang/sdk/blob/main/pkg/kernel/binary.md).
|
||
|
||
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,
|
||
hasEnclosingTypeParameters,
|
||
hasParameterFlags);
|
||
|
||
if hasEnclosingTypeParameters
|
||
UInt numEnclosingTypeParameters;
|
||
|
||
if hasTypeParams
|
||
TypeParametersDeclaration typeParameters;
|
||
|
||
UInt numParameters;
|
||
|
||
if hasOptionalPositionalParams || hasOptionalNamedParams
|
||
UInt numRequiredParameters;
|
||
|
||
Type[] positionalParameters;
|
||
NameAndType[] namedParameters;
|
||
|
||
if hasParameterFlags
|
||
// For named parameters: (isRequired)
|
||
List<UInt> parameterFlags;
|
||
|
||
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
|
||
// For named parameters: (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
|
||
// For all parameters: (isCovariant, isCovariantByClass)
|
||
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
|
||
// For named parameters: (isRequired)
|
||
List<UInt> parameterFlags;
|
||
|
||
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] <op> 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] <op> 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] <op> 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] <op> 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.
|
||
|