61f0f5bc43
This change replaces kernel AST declarations of fields and functions with bytecode declarations. Size of dilp files is reduced by 11-12%. Startup latency: Time to the first full frame: 1.945s -> 1.687s FinalizeClass: 554ms -> 277ms FinishClassLoading: 296ms -> 156ms There are following regressions in bytecode mode, which will be fixed in future: * dart:mirrors are not supported yet (implementation of mirrors relies on reading kernel AST in certain cases). As the result, lib_2/mirrors/* tests fail. * native extensions are not supported yet (annotations on libraries and classes in AST are cleaned up as they could reference members which are now removed from AST). As the result, standalone_2/entrypoints_verification_test test fails. * language_2/spread_collections/const_error_test/* tests fail due to https://github.com/dart-lang/sdk/issues/36286. Change-Id: I5130f401fd7b84038b136136e7ccc1a6e51b6cea Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97561 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
1042 lines
33 KiB
Dart
1042 lines
33 KiB
Dart
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
library vm.bytecode.declarations;
|
|
|
|
import 'package:kernel/ast.dart';
|
|
import 'bytecode_serialization.dart'
|
|
show BufferedWriter, BufferedReader, BytecodeSizeStatistics, StringTable;
|
|
import 'constant_pool.dart' show ConstantPool;
|
|
import 'dbc.dart'
|
|
show
|
|
currentBytecodeFormatVersion,
|
|
futureBytecodeFormatVersion,
|
|
bytecodeInstructionsAlignment;
|
|
import 'disassembler.dart' show BytecodeDisassembler;
|
|
import 'exceptions.dart' show ExceptionsTable;
|
|
import 'object_table.dart' show ObjectTable, ObjectHandle, NameAndType;
|
|
import 'source_positions.dart' show SourcePositions;
|
|
|
|
class Members {
|
|
final List<FieldDeclaration> fields;
|
|
final List<FunctionDeclaration> functions;
|
|
|
|
Members(this.fields, this.functions);
|
|
|
|
int countFunctions() {
|
|
int count = functions.length;
|
|
for (var field in fields) {
|
|
if ((field.flags & FieldDeclaration.hasGetterFlag) != 0) {
|
|
++count;
|
|
}
|
|
if ((field.flags & FieldDeclaration.hasSetterFlag) != 0) {
|
|
++count;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedUInt30(countFunctions());
|
|
writer.writePackedUInt30(fields.length);
|
|
for (var field in fields) {
|
|
field.write(writer);
|
|
}
|
|
writer.writePackedUInt30(functions.length);
|
|
for (var func in functions) {
|
|
func.write(writer);
|
|
}
|
|
}
|
|
|
|
factory Members.read(BufferedReader reader) {
|
|
reader.readPackedUInt30(); // numFunctions
|
|
final fields = new List<FieldDeclaration>.generate(
|
|
reader.readPackedUInt30(), (_) => new FieldDeclaration.read(reader));
|
|
final functions = new List<FunctionDeclaration>.generate(
|
|
reader.readPackedUInt30(), (_) => new FunctionDeclaration.read(reader));
|
|
return new Members(fields, functions);
|
|
}
|
|
|
|
@override
|
|
String toString() => "\n"
|
|
"Members {\n"
|
|
"${fields.join('\n')}\n"
|
|
"${functions.join('\n')}"
|
|
"}\n";
|
|
}
|
|
|
|
class FieldDeclaration {
|
|
static const hasInitializerFlag = 1 << 0;
|
|
static const hasGetterFlag = 1 << 1;
|
|
static const hasSetterFlag = 1 << 2;
|
|
static const isReflectableFlag = 1 << 3;
|
|
static const isStaticFlag = 1 << 4;
|
|
static const isConstFlag = 1 << 5;
|
|
static const isFinalFlag = 1 << 6;
|
|
static const isCovariantFlag = 1 << 7;
|
|
static const isGenericCovariantImplFlag = 1 << 8;
|
|
static const hasSourcePositionsFlag = 1 << 9;
|
|
static const hasAnnotationsFlag = 1 << 10;
|
|
static const hasPragmaFlag = 1 << 11;
|
|
static const hasCustomScriptFlag = 1 << 12;
|
|
|
|
final int flags;
|
|
final ObjectHandle name;
|
|
final ObjectHandle type;
|
|
final ObjectHandle value;
|
|
final ObjectHandle script;
|
|
final int position;
|
|
final int endPosition;
|
|
final ObjectHandle getterName;
|
|
final ObjectHandle setterName;
|
|
final Code initializerCode;
|
|
final ObjectHandle annotations;
|
|
|
|
FieldDeclaration(
|
|
this.flags,
|
|
this.name,
|
|
this.type,
|
|
this.value,
|
|
this.script,
|
|
this.position,
|
|
this.endPosition,
|
|
this.getterName,
|
|
this.setterName,
|
|
this.initializerCode,
|
|
this.annotations);
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedUInt30(flags);
|
|
writer.writePackedObject(name);
|
|
writer.writePackedObject(type);
|
|
|
|
if ((flags & hasCustomScriptFlag) != 0) {
|
|
writer.writePackedObject(script);
|
|
}
|
|
if ((flags & hasSourcePositionsFlag) != 0) {
|
|
writer.writePackedUInt30(position + 1);
|
|
writer.writePackedUInt30(endPosition + 1);
|
|
}
|
|
if ((flags & hasInitializerFlag) != 0 && (flags & isStaticFlag) != 0) {
|
|
writer.writeLinkOffset(initializerCode);
|
|
}
|
|
if ((flags & hasInitializerFlag) == 0) {
|
|
writer.writePackedObject(value);
|
|
}
|
|
if ((flags & hasGetterFlag) != 0) {
|
|
writer.writePackedObject(getterName);
|
|
}
|
|
if ((flags & hasSetterFlag) != 0) {
|
|
writer.writePackedObject(setterName);
|
|
}
|
|
if ((flags & hasAnnotationsFlag) != 0) {
|
|
writer.writeLinkOffset(annotations);
|
|
}
|
|
}
|
|
|
|
factory FieldDeclaration.read(BufferedReader reader) {
|
|
final flags = reader.readPackedUInt30();
|
|
final name = reader.readPackedObject();
|
|
final type = reader.readPackedObject();
|
|
final script =
|
|
((flags & hasCustomScriptFlag) != 0) ? reader.readPackedObject() : null;
|
|
final position = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: 0;
|
|
final endPosition = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: 0;
|
|
final initializerCode =
|
|
((flags & hasInitializerFlag) != 0 && (flags & isStaticFlag) != 0)
|
|
? reader.readLinkOffset<Code>()
|
|
: null;
|
|
final value =
|
|
((flags & hasInitializerFlag) == 0) ? reader.readPackedObject() : null;
|
|
final getterName =
|
|
((flags & hasGetterFlag) != 0) ? reader.readPackedObject() : null;
|
|
final setterName =
|
|
((flags & hasSetterFlag) != 0) ? reader.readPackedObject() : null;
|
|
final annotations = ((flags & hasAnnotationsFlag) != 0)
|
|
? reader.readLinkOffset<ObjectHandle>()
|
|
: null;
|
|
return new FieldDeclaration(flags, name, type, value, script, position,
|
|
endPosition, getterName, setterName, initializerCode, annotations);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.write('Field $name, type = $type');
|
|
if ((flags & hasGetterFlag) != 0) {
|
|
sb.write(', getter = $getterName');
|
|
}
|
|
if ((flags & hasSetterFlag) != 0) {
|
|
sb.write(', setter = $setterName');
|
|
}
|
|
if ((flags & isReflectableFlag) != 0) {
|
|
sb.write(', reflectable');
|
|
}
|
|
if ((flags & isStaticFlag) != 0) {
|
|
sb.write(', static');
|
|
}
|
|
if ((flags & isConstFlag) != 0) {
|
|
sb.write(', const');
|
|
}
|
|
if ((flags & isFinalFlag) != 0) {
|
|
sb.write(', final');
|
|
}
|
|
if ((flags & hasPragmaFlag) != 0) {
|
|
sb.write(', has-pragma');
|
|
}
|
|
if ((flags & hasCustomScriptFlag) != 0) {
|
|
sb.write(', custom-script = $script');
|
|
}
|
|
if ((flags & hasSourcePositionsFlag) != 0) {
|
|
sb.write(', pos = $position, end-pos = $endPosition');
|
|
}
|
|
sb.writeln();
|
|
if ((flags & hasInitializerFlag) != 0) {
|
|
sb.write(' initializer $initializerCode\n');
|
|
} else {
|
|
sb.write(' value = $value\n');
|
|
}
|
|
if ((flags & hasAnnotationsFlag) != 0) {
|
|
sb.write(' annotations $annotations\n');
|
|
}
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
class FunctionDeclaration {
|
|
static const isConstructorFlag = 1 << 0;
|
|
static const isGetterFlag = 1 << 1;
|
|
static const isSetterFlag = 1 << 2;
|
|
static const isFactoryFlag = 1 << 3;
|
|
static const isStaticFlag = 1 << 4;
|
|
static const isAbstractFlag = 1 << 5;
|
|
static const isConstFlag = 1 << 6;
|
|
static const hasOptionalPositionalParamsFlag = 1 << 7;
|
|
static const hasOptionalNamedParamsFlag = 1 << 8;
|
|
static const hasTypeParamsFlag = 1 << 9;
|
|
static const isReflectableFlag = 1 << 10;
|
|
static const isDebuggableFlag = 1 << 11;
|
|
static const isAsyncFlag = 1 << 12;
|
|
static const isAsyncStarFlag = 1 << 13;
|
|
static const isSyncStarFlag = 1 << 14;
|
|
static const isForwardingStubFlag = 1 << 15;
|
|
static const isNoSuchMethodForwarderFlag = 1 << 16;
|
|
static const isNativeFlag = 1 << 17;
|
|
static const isExternalFlag = 1 << 18;
|
|
static const hasSourcePositionsFlag = 1 << 19;
|
|
static const hasAnnotationsFlag = 1 << 20;
|
|
static const hasPragmaFlag = 1 << 21;
|
|
static const hasCustomScriptFlag = 1 << 22;
|
|
|
|
final int flags;
|
|
final ObjectHandle name;
|
|
final ObjectHandle script;
|
|
final int position;
|
|
final int endPosition;
|
|
final TypeParametersDeclaration typeParameters;
|
|
final int numRequiredParameters;
|
|
final List<ParameterDeclaration> parameters;
|
|
final ObjectHandle returnType;
|
|
final ObjectHandle nativeName;
|
|
final Code code;
|
|
final ObjectHandle annotations;
|
|
|
|
FunctionDeclaration(
|
|
this.flags,
|
|
this.name,
|
|
this.script,
|
|
this.position,
|
|
this.endPosition,
|
|
this.typeParameters,
|
|
this.numRequiredParameters,
|
|
this.parameters,
|
|
this.returnType,
|
|
this.nativeName,
|
|
this.code,
|
|
this.annotations);
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedUInt30(flags);
|
|
writer.writePackedObject(name);
|
|
if ((flags & hasCustomScriptFlag) != 0) {
|
|
writer.writePackedObject(script);
|
|
}
|
|
if ((flags & hasSourcePositionsFlag) != 0) {
|
|
writer.writePackedUInt30(position + 1);
|
|
writer.writePackedUInt30(endPosition + 1);
|
|
}
|
|
if ((flags & hasTypeParamsFlag) != 0) {
|
|
typeParameters.write(writer);
|
|
}
|
|
writer.writePackedUInt30(parameters.length);
|
|
if ((flags & hasOptionalPositionalParamsFlag) != 0 ||
|
|
(flags & hasOptionalNamedParamsFlag) != 0) {
|
|
writer.writePackedUInt30(numRequiredParameters);
|
|
}
|
|
for (var param in parameters) {
|
|
param.write(writer);
|
|
}
|
|
writer.writePackedObject(returnType);
|
|
if ((flags & isNativeFlag) != 0) {
|
|
writer.writePackedObject(nativeName);
|
|
}
|
|
if ((flags & isAbstractFlag) == 0) {
|
|
writer.writeLinkOffset(code);
|
|
}
|
|
if ((flags & hasAnnotationsFlag) != 0) {
|
|
writer.writeLinkOffset(annotations);
|
|
}
|
|
}
|
|
|
|
factory FunctionDeclaration.read(BufferedReader reader) {
|
|
final flags = reader.readPackedUInt30();
|
|
final name = reader.readPackedObject();
|
|
|
|
final script =
|
|
((flags & hasCustomScriptFlag) != 0) ? reader.readPackedObject() : null;
|
|
final position = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: 0;
|
|
final endPosition = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: 0;
|
|
final typeParameters = ((flags & hasTypeParamsFlag) != 0)
|
|
? new TypeParametersDeclaration.read(reader)
|
|
: null;
|
|
|
|
final numParameters = reader.readPackedUInt30();
|
|
final numRequiredParameters =
|
|
((flags & hasOptionalPositionalParamsFlag) != 0 ||
|
|
(flags & hasOptionalNamedParamsFlag) != 0)
|
|
? reader.readPackedUInt30()
|
|
: numParameters;
|
|
|
|
final parameters = new List<ParameterDeclaration>.generate(
|
|
numParameters, (_) => new ParameterDeclaration.read(reader));
|
|
final returnType = reader.readPackedObject();
|
|
final nativeName =
|
|
((flags & isNativeFlag) != 0) ? reader.readPackedObject() : null;
|
|
final code =
|
|
((flags & isAbstractFlag) == 0) ? reader.readLinkOffset<Code>() : null;
|
|
final annotations = ((flags & hasAnnotationsFlag) != 0)
|
|
? reader.readLinkOffset<ObjectHandle>()
|
|
: null;
|
|
return new FunctionDeclaration(
|
|
flags,
|
|
name,
|
|
script,
|
|
position,
|
|
endPosition,
|
|
typeParameters,
|
|
numRequiredParameters,
|
|
parameters,
|
|
returnType,
|
|
nativeName,
|
|
code,
|
|
annotations);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.write('Function $name');
|
|
if ((flags & isConstructorFlag) != 0) {
|
|
sb.write(', constructor');
|
|
}
|
|
if ((flags & isGetterFlag) != 0) {
|
|
sb.write(', getter');
|
|
}
|
|
if ((flags & isSetterFlag) != 0) {
|
|
sb.write(', setter');
|
|
}
|
|
if ((flags & isFactoryFlag) != 0) {
|
|
sb.write(', factory');
|
|
}
|
|
if ((flags & isStaticFlag) != 0) {
|
|
sb.write(', static');
|
|
}
|
|
if ((flags & isAbstractFlag) != 0) {
|
|
sb.write(', abstract');
|
|
}
|
|
if ((flags & isConstFlag) != 0) {
|
|
sb.write(', const');
|
|
}
|
|
if ((flags & hasOptionalPositionalParamsFlag) != 0) {
|
|
sb.write(', has-optional-positional-params');
|
|
}
|
|
if ((flags & hasOptionalNamedParamsFlag) != 0) {
|
|
sb.write(', has-optional-named-params');
|
|
}
|
|
if ((flags & isReflectableFlag) != 0) {
|
|
sb.write(', reflectable');
|
|
}
|
|
if ((flags & isDebuggableFlag) != 0) {
|
|
sb.write(', debuggable');
|
|
}
|
|
if ((flags & isAsyncFlag) != 0) {
|
|
sb.write(', async');
|
|
}
|
|
if ((flags & isAsyncStarFlag) != 0) {
|
|
sb.write(', async*');
|
|
}
|
|
if ((flags & isSyncStarFlag) != 0) {
|
|
sb.write(', sync*');
|
|
}
|
|
if ((flags & isForwardingStubFlag) != 0) {
|
|
sb.write(', forwarding-stub');
|
|
}
|
|
if ((flags & isNoSuchMethodForwarderFlag) != 0) {
|
|
sb.write(', no-such-method-forwarder');
|
|
}
|
|
if ((flags & isNativeFlag) != 0) {
|
|
sb.write(', native $nativeName');
|
|
}
|
|
if ((flags & isExternalFlag) != 0) {
|
|
sb.write(', external');
|
|
}
|
|
if ((flags & hasPragmaFlag) != 0) {
|
|
sb.write(', has-pragma');
|
|
}
|
|
if ((flags & hasCustomScriptFlag) != 0) {
|
|
sb.write(', custom-script = $script');
|
|
}
|
|
if ((flags & hasSourcePositionsFlag) != 0) {
|
|
sb.write(', pos = $position, end-pos = $endPosition');
|
|
}
|
|
sb.writeln();
|
|
if ((flags & hasTypeParamsFlag) != 0) {
|
|
sb.write(' type-params $typeParameters\n');
|
|
}
|
|
sb.write(' parameters $parameters (required: $numRequiredParameters)\n');
|
|
sb.write(' return-type $returnType\n');
|
|
if ((flags & hasAnnotationsFlag) != 0) {
|
|
sb.write(' annotations $annotations\n');
|
|
}
|
|
if ((flags & isAbstractFlag) == 0 && (flags & isExternalFlag) == 0) {
|
|
sb.write('\n$code\n');
|
|
}
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
class TypeParametersDeclaration {
|
|
final List<NameAndType> typeParams;
|
|
|
|
TypeParametersDeclaration(this.typeParams);
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedUInt30(typeParams.length);
|
|
for (var tp in typeParams) {
|
|
writer.writePackedObject(tp.name);
|
|
}
|
|
for (var tp in typeParams) {
|
|
writer.writePackedObject(tp.type);
|
|
}
|
|
}
|
|
|
|
factory TypeParametersDeclaration.read(BufferedReader reader) {
|
|
final int numTypeParams = reader.readPackedUInt30();
|
|
List<ObjectHandle> names = new List<ObjectHandle>.generate(
|
|
numTypeParams, (_) => reader.readPackedObject());
|
|
List<ObjectHandle> bounds = new List<ObjectHandle>.generate(
|
|
numTypeParams, (_) => reader.readPackedObject());
|
|
return new TypeParametersDeclaration(new List<NameAndType>.generate(
|
|
numTypeParams, (int i) => new NameAndType(names[i], bounds[i])));
|
|
}
|
|
|
|
@override
|
|
int get hashCode => listHashCode(typeParams);
|
|
|
|
@override
|
|
bool operator ==(other) =>
|
|
other is TypeParametersDeclaration &&
|
|
listEquals(this.typeParams, other.typeParams);
|
|
|
|
@override
|
|
String toString() => '<${typeParams.join(', ')}>';
|
|
}
|
|
|
|
class ParameterDeclaration {
|
|
// Parameter flags are written separately (in Code).
|
|
static const isCovariantFlag = 1 << 0;
|
|
static const isGenericCovariantImplFlag = 1 << 1;
|
|
|
|
final ObjectHandle name;
|
|
final ObjectHandle type;
|
|
|
|
ParameterDeclaration(this.name, this.type);
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedObject(name);
|
|
writer.writePackedObject(type);
|
|
}
|
|
|
|
factory ParameterDeclaration.read(BufferedReader reader) {
|
|
final name = reader.readPackedObject();
|
|
final type = reader.readPackedObject();
|
|
return new ParameterDeclaration(name, type);
|
|
}
|
|
|
|
@override
|
|
String toString() => '$type $name';
|
|
}
|
|
|
|
class Code {
|
|
static const hasExceptionsTableFlag = 1 << 0;
|
|
static const hasSourcePositionsFlag = 1 << 1;
|
|
static const hasNullableFieldsFlag = 1 << 2;
|
|
static const hasClosuresFlag = 1 << 3;
|
|
static const hasParameterFlagsFlag = 1 << 4;
|
|
static const hasForwardingStubTargetFlag = 1 << 5;
|
|
static const hasDefaultFunctionTypeArgsFlag = 1 << 6;
|
|
|
|
final ConstantPool constantPool;
|
|
final List<int> bytecodes;
|
|
final ExceptionsTable exceptionsTable;
|
|
final SourcePositions sourcePositions;
|
|
final List<ObjectHandle> nullableFields;
|
|
final List<ClosureDeclaration> closures;
|
|
final List<int> parameterFlags;
|
|
final int forwardingStubTargetCpIndex;
|
|
final int defaultFunctionTypeArgsCpIndex;
|
|
|
|
bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty;
|
|
bool get hasSourcePositions =>
|
|
sourcePositions != null && sourcePositions.mapping.isNotEmpty;
|
|
bool get hasNullableFields => nullableFields.isNotEmpty;
|
|
bool get hasClosures => closures.isNotEmpty;
|
|
|
|
int get flags =>
|
|
(hasExceptionsTable ? hasExceptionsTableFlag : 0) |
|
|
(hasSourcePositions ? hasSourcePositionsFlag : 0) |
|
|
(hasNullableFields ? hasNullableFieldsFlag : 0) |
|
|
(hasClosures ? hasClosuresFlag : 0) |
|
|
(parameterFlags != null ? hasParameterFlagsFlag : 0) |
|
|
(forwardingStubTargetCpIndex != null ? hasForwardingStubTargetFlag : 0) |
|
|
(defaultFunctionTypeArgsCpIndex != null
|
|
? hasDefaultFunctionTypeArgsFlag
|
|
: 0);
|
|
|
|
Code(
|
|
this.constantPool,
|
|
this.bytecodes,
|
|
this.exceptionsTable,
|
|
this.sourcePositions,
|
|
this.nullableFields,
|
|
this.closures,
|
|
this.parameterFlags,
|
|
this.forwardingStubTargetCpIndex,
|
|
this.defaultFunctionTypeArgsCpIndex);
|
|
|
|
void write(BufferedWriter writer) {
|
|
final start = writer.offset;
|
|
writer.writePackedUInt30(flags);
|
|
if (parameterFlags != null) {
|
|
writer.writePackedUInt30(parameterFlags.length);
|
|
parameterFlags.forEach((flags) => writer.writePackedUInt30(flags));
|
|
}
|
|
if (forwardingStubTargetCpIndex != null) {
|
|
writer.writePackedUInt30(forwardingStubTargetCpIndex);
|
|
}
|
|
if (defaultFunctionTypeArgsCpIndex != null) {
|
|
writer.writePackedUInt30(defaultFunctionTypeArgsCpIndex);
|
|
}
|
|
if (hasClosures) {
|
|
writer.writePackedUInt30(closures.length);
|
|
closures.forEach((c) => c.write(writer));
|
|
}
|
|
constantPool.write(writer);
|
|
_writeBytecodeInstructions(writer, bytecodes);
|
|
if (hasExceptionsTable) {
|
|
exceptionsTable.write(writer);
|
|
}
|
|
if (hasSourcePositions) {
|
|
writer.writeLinkOffset(sourcePositions);
|
|
}
|
|
if (hasNullableFields) {
|
|
writer.writePackedList(nullableFields);
|
|
}
|
|
if (hasClosures) {
|
|
closures.forEach((c) => c.code.write(writer));
|
|
}
|
|
BytecodeSizeStatistics.membersSize += (writer.offset - start);
|
|
}
|
|
|
|
factory Code.read(BufferedReader reader) {
|
|
int flags = reader.readPackedUInt30();
|
|
final parameterFlags = ((flags & hasParameterFlagsFlag) != 0)
|
|
? new List<int>.generate(
|
|
reader.readPackedUInt30(), (_) => reader.readPackedUInt30())
|
|
: null;
|
|
final forwardingStubTargetCpIndex =
|
|
((flags & hasForwardingStubTargetFlag) != 0)
|
|
? reader.readPackedUInt30()
|
|
: null;
|
|
final defaultFunctionTypeArgsCpIndex =
|
|
((flags & hasDefaultFunctionTypeArgsFlag) != 0)
|
|
? reader.readPackedUInt30()
|
|
: null;
|
|
final List<ClosureDeclaration> closures = ((flags & hasClosuresFlag) != 0)
|
|
? new List<ClosureDeclaration>.generate(reader.readPackedUInt30(),
|
|
(_) => new ClosureDeclaration.read(reader))
|
|
: const <ClosureDeclaration>[];
|
|
final ConstantPool constantPool = new ConstantPool.read(reader);
|
|
final List<int> bytecodes = _readBytecodeInstructions(reader);
|
|
final exceptionsTable = ((flags & hasExceptionsTableFlag) != 0)
|
|
? new ExceptionsTable.read(reader)
|
|
: new ExceptionsTable();
|
|
final sourcePositions = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readLinkOffset<SourcePositions>()
|
|
: null;
|
|
final List<ObjectHandle> nullableFields =
|
|
((flags & hasNullableFieldsFlag) != 0)
|
|
? reader.readPackedList<ObjectHandle>()
|
|
: const <ObjectHandle>[];
|
|
for (var c in closures) {
|
|
c.code = new ClosureCode.read(reader);
|
|
}
|
|
return new Code(
|
|
constantPool,
|
|
bytecodes,
|
|
exceptionsTable,
|
|
sourcePositions,
|
|
nullableFields,
|
|
closures,
|
|
parameterFlags,
|
|
forwardingStubTargetCpIndex,
|
|
defaultFunctionTypeArgsCpIndex);
|
|
}
|
|
|
|
// TODO(alexmarkov): Consider printing constant pool before bytecode.
|
|
@override
|
|
String toString() => "\n"
|
|
"Bytecode {\n"
|
|
"${new BytecodeDisassembler().disassemble(bytecodes, exceptionsTable, annotations: [
|
|
hasSourcePositions
|
|
? sourcePositions.getBytecodeAnnotations()
|
|
: const <int, String>{}
|
|
])}}\n"
|
|
"$exceptionsTable"
|
|
"${nullableFields.isEmpty ? '' : 'Nullable fields: $nullableFields\n'}"
|
|
"${parameterFlags == null ? '' : 'Parameter flags: $parameterFlags\n'}"
|
|
"${forwardingStubTargetCpIndex == null ? '' : 'Forwarding stub target: CP#$forwardingStubTargetCpIndex\n'}"
|
|
"${defaultFunctionTypeArgsCpIndex == null ? '' : 'Default function type arguments: CP#$defaultFunctionTypeArgsCpIndex\n'}"
|
|
"$constantPool"
|
|
"${closures.join('\n')}";
|
|
}
|
|
|
|
class ClosureDeclaration {
|
|
static const int flagHasOptionalPositionalParams = 1 << 0;
|
|
static const int flagHasOptionalNamedParams = 1 << 1;
|
|
static const int flagHasTypeParams = 1 << 2;
|
|
|
|
final ObjectHandle parent;
|
|
final ObjectHandle name;
|
|
final List<NameAndType> typeParams;
|
|
final int numRequiredParams;
|
|
final int numNamedParams;
|
|
final List<NameAndType> parameters;
|
|
final ObjectHandle returnType;
|
|
ClosureCode code;
|
|
|
|
ClosureDeclaration(
|
|
this.parent,
|
|
this.name,
|
|
this.typeParams,
|
|
this.numRequiredParams,
|
|
this.numNamedParams,
|
|
this.parameters,
|
|
this.returnType);
|
|
|
|
void write(BufferedWriter writer) {
|
|
int flags = 0;
|
|
if (numRequiredParams != parameters.length) {
|
|
if (numNamedParams > 0) {
|
|
flags |= flagHasOptionalNamedParams;
|
|
} else {
|
|
flags |= flagHasOptionalPositionalParams;
|
|
}
|
|
}
|
|
if (typeParams.isNotEmpty) {
|
|
flags |= flagHasTypeParams;
|
|
}
|
|
writer.writePackedUInt30(flags);
|
|
writer.writePackedObject(parent);
|
|
writer.writePackedObject(name);
|
|
|
|
if (flags & flagHasTypeParams != 0) {
|
|
writer.writePackedUInt30(typeParams.length);
|
|
for (var tp in typeParams) {
|
|
writer.writePackedObject(tp.name);
|
|
}
|
|
for (var tp in typeParams) {
|
|
writer.writePackedObject(tp.type);
|
|
}
|
|
}
|
|
writer.writePackedUInt30(parameters.length);
|
|
if (flags &
|
|
(flagHasOptionalPositionalParams | flagHasOptionalNamedParams) !=
|
|
0) {
|
|
writer.writePackedUInt30(numRequiredParams);
|
|
}
|
|
for (var param in parameters) {
|
|
writer.writePackedObject(param.name);
|
|
writer.writePackedObject(param.type);
|
|
}
|
|
writer.writePackedObject(returnType);
|
|
}
|
|
|
|
factory ClosureDeclaration.read(BufferedReader reader) {
|
|
final int flags = reader.readPackedUInt30();
|
|
final parent = reader.readPackedObject();
|
|
final name = reader.readPackedObject();
|
|
List<NameAndType> typeParams;
|
|
if ((flags & flagHasTypeParams) != 0) {
|
|
final int numTypeParams = reader.readPackedUInt30();
|
|
List<ObjectHandle> names = new List<ObjectHandle>.generate(
|
|
numTypeParams, (_) => reader.readPackedObject());
|
|
List<ObjectHandle> bounds = new List<ObjectHandle>.generate(
|
|
numTypeParams, (_) => reader.readPackedObject());
|
|
typeParams = new List<NameAndType>.generate(
|
|
numTypeParams, (int i) => new NameAndType(names[i], bounds[i]));
|
|
} else {
|
|
typeParams = const <NameAndType>[];
|
|
}
|
|
final numParams = reader.readPackedUInt30();
|
|
final numRequiredParams = (flags &
|
|
(flagHasOptionalPositionalParams |
|
|
flagHasOptionalNamedParams) !=
|
|
0)
|
|
? reader.readPackedUInt30()
|
|
: numParams;
|
|
final numNamedParams = (flags & flagHasOptionalNamedParams != 0)
|
|
? (numParams - numRequiredParams)
|
|
: 0;
|
|
final List<NameAndType> parameters = new List<NameAndType>.generate(
|
|
numParams,
|
|
(_) => new NameAndType(
|
|
reader.readPackedObject(), reader.readPackedObject()));
|
|
final returnType = reader.readPackedObject();
|
|
return new ClosureDeclaration(parent, name, typeParams, numRequiredParams,
|
|
numNamedParams, parameters, returnType);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.write('Closure $parent::$name');
|
|
if (typeParams.isNotEmpty) {
|
|
sb.write(' <${typeParams.join(', ')}>');
|
|
}
|
|
sb.write(' (');
|
|
sb.write(parameters.sublist(0, numRequiredParams).join(', '));
|
|
if (numRequiredParams != parameters.length) {
|
|
if (numRequiredParams > 0) {
|
|
sb.write(', ');
|
|
}
|
|
if (numNamedParams > 0) {
|
|
sb.write('{ ${parameters.sublist(numRequiredParams).join(', ')} }');
|
|
} else {
|
|
sb.write('[ ${parameters.sublist(numRequiredParams).join(', ')} ]');
|
|
}
|
|
}
|
|
sb.write(') -> ');
|
|
sb.writeln(returnType);
|
|
if (code != null) {
|
|
sb.write(code.toString());
|
|
}
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
/// Bytecode of a nested function (closure).
|
|
/// Closures share the constant pool of a top-level member.
|
|
class ClosureCode {
|
|
final List<int> bytecodes;
|
|
final ExceptionsTable exceptionsTable;
|
|
final SourcePositions sourcePositions;
|
|
|
|
bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty;
|
|
bool get hasSourcePositions =>
|
|
sourcePositions != null && sourcePositions.mapping.isNotEmpty;
|
|
|
|
int get flags =>
|
|
(hasExceptionsTable ? Code.hasExceptionsTableFlag : 0) |
|
|
(hasSourcePositions ? Code.hasSourcePositionsFlag : 0);
|
|
|
|
ClosureCode(this.bytecodes, this.exceptionsTable, this.sourcePositions);
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedUInt30(flags);
|
|
_writeBytecodeInstructions(writer, bytecodes);
|
|
if (hasExceptionsTable) {
|
|
exceptionsTable.write(writer);
|
|
}
|
|
if (hasSourcePositions) {
|
|
writer.writeLinkOffset(sourcePositions);
|
|
}
|
|
}
|
|
|
|
factory ClosureCode.read(BufferedReader reader) {
|
|
final int flags = reader.readPackedUInt30();
|
|
final List<int> bytecodes = _readBytecodeInstructions(reader);
|
|
final exceptionsTable = ((flags & Code.hasExceptionsTableFlag) != 0)
|
|
? new ExceptionsTable.read(reader)
|
|
: new ExceptionsTable();
|
|
final sourcePositions = ((flags & Code.hasSourcePositionsFlag) != 0)
|
|
? reader.readLinkOffset<SourcePositions>()
|
|
: null;
|
|
return new ClosureCode(bytecodes, exceptionsTable, sourcePositions);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.writeln('ClosureCode {');
|
|
sb.writeln(new BytecodeDisassembler()
|
|
.disassemble(bytecodes, exceptionsTable, annotations: [
|
|
hasSourcePositions
|
|
? sourcePositions.getBytecodeAnnotations()
|
|
: const <int, String>{}
|
|
]));
|
|
sb.writeln('}');
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
class _Section {
|
|
int numItems;
|
|
int offset;
|
|
BufferedWriter writer;
|
|
|
|
_Section(this.numItems, this.writer);
|
|
|
|
int get size => writer.offset;
|
|
}
|
|
|
|
class Component {
|
|
static const int magicValue = 0x44424332; // 'DBC2'
|
|
static const int numSections = 7;
|
|
static const int sectionAlignment = 4;
|
|
|
|
// UInt32 magic, version, numSections x (numItems, offset)
|
|
static const int headerSize = (2 + numSections * 2) * 4;
|
|
|
|
int version;
|
|
StringTable stringTable;
|
|
ObjectTable objectTable;
|
|
List<Members> members = <Members>[];
|
|
List<Code> codes = <Code>[];
|
|
List<SourcePositions> sourcePositions = <SourcePositions>[];
|
|
List<ObjectHandle> annotations = <ObjectHandle>[];
|
|
ObjectHandle mainLibrary;
|
|
|
|
Component(this.version)
|
|
: stringTable = new StringTable(),
|
|
objectTable = new ObjectTable();
|
|
|
|
void write(BufferedWriter writer) {
|
|
objectTable.allocateIndexTable();
|
|
|
|
// Write sections to their own buffers in reverse order as section may
|
|
// reference data structures from successor sections by offsets.
|
|
|
|
final BufferedWriter annotationsWriter =
|
|
new BufferedWriter.fromWriter(writer);
|
|
for (var annot in annotations) {
|
|
writer.linkWriter.put(annot, annotationsWriter.offset);
|
|
annotationsWriter.writePackedObject(annot);
|
|
}
|
|
|
|
final BufferedWriter sourcePositionsWriter =
|
|
new BufferedWriter.fromWriter(writer);
|
|
for (var sp in sourcePositions) {
|
|
writer.linkWriter.put(sp, sourcePositionsWriter.offset);
|
|
sp.write(sourcePositionsWriter);
|
|
}
|
|
|
|
final BufferedWriter codesWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var code in codes) {
|
|
writer.linkWriter.put(code, codesWriter.offset);
|
|
code.write(codesWriter);
|
|
}
|
|
|
|
final BufferedWriter membersWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var m in members) {
|
|
writer.linkWriter.put(m, membersWriter.offset);
|
|
m.write(membersWriter);
|
|
}
|
|
|
|
BufferedWriter mainWriter;
|
|
if (mainLibrary != null) {
|
|
mainWriter = new BufferedWriter.fromWriter(writer);
|
|
mainWriter.writePackedObject(mainLibrary);
|
|
}
|
|
|
|
final BufferedWriter objectsWriter = new BufferedWriter.fromWriter(writer);
|
|
objectTable.write(objectsWriter);
|
|
|
|
final BufferedWriter stringsWriter = new BufferedWriter.fromWriter(writer);
|
|
stringTable.write(stringsWriter);
|
|
|
|
List<_Section> sections = [
|
|
new _Section(0, stringsWriter),
|
|
new _Section(0, objectsWriter),
|
|
new _Section(0, mainWriter),
|
|
new _Section(members.length, membersWriter),
|
|
new _Section(codes.length, codesWriter),
|
|
new _Section(sourcePositions.length, sourcePositionsWriter),
|
|
new _Section(annotations.length, annotationsWriter),
|
|
];
|
|
assert(sections.length == numSections);
|
|
|
|
int offset = headerSize;
|
|
for (var section in sections) {
|
|
if (section.writer != null) {
|
|
offset = (offset + sectionAlignment - 1) & ~(sectionAlignment - 1);
|
|
section.offset = offset;
|
|
offset += section.size;
|
|
} else {
|
|
section.offset = 0;
|
|
}
|
|
}
|
|
|
|
final start = writer.offset;
|
|
|
|
writer.writeUInt32(magicValue);
|
|
writer.writeUInt32(version);
|
|
for (var section in sections) {
|
|
writer.writeUInt32(section.numItems);
|
|
writer.writeUInt32(section.offset);
|
|
}
|
|
assert(writer.offset - start == headerSize);
|
|
for (var section in sections) {
|
|
if (section.writer != null) {
|
|
writer.align(sectionAlignment);
|
|
assert(writer.offset - start == section.offset);
|
|
writer.writeBytes(section.writer.takeBytes());
|
|
}
|
|
}
|
|
|
|
BytecodeSizeStatistics.componentSize += (writer.offset - start);
|
|
}
|
|
|
|
Component.read(BufferedReader reader) {
|
|
final int start = reader.offset;
|
|
|
|
final int magic = reader.readUInt32();
|
|
if (magic != magicValue) {
|
|
throw 'Error: unexpected bytecode magic $magic';
|
|
}
|
|
|
|
version = reader.readUInt32();
|
|
if (version != currentBytecodeFormatVersion) {
|
|
throw 'Error: unexpected bytecode format version $version';
|
|
}
|
|
|
|
reader.formatVersion = version;
|
|
|
|
reader.readUInt32();
|
|
final stringTableOffset = reader.readUInt32();
|
|
|
|
reader.readUInt32();
|
|
final objectTableOffset = reader.readUInt32();
|
|
|
|
reader.readUInt32();
|
|
final mainOffset = reader.readUInt32();
|
|
|
|
final membersNum = reader.readUInt32();
|
|
final membersOffset = reader.readUInt32();
|
|
|
|
final codesNum = reader.readUInt32();
|
|
final codesOffset = reader.readUInt32();
|
|
|
|
final sourcePositionsNum = reader.readUInt32();
|
|
final sourcePositionsOffset = reader.readUInt32();
|
|
|
|
final annotationsNum = reader.readUInt32();
|
|
final annotationsOffset = reader.readUInt32();
|
|
|
|
reader.offset = start + stringTableOffset;
|
|
stringTable = new StringTable.read(reader);
|
|
reader.stringReader = stringTable;
|
|
|
|
reader.offset = start + objectTableOffset;
|
|
objectTable = new ObjectTable.read(reader);
|
|
reader.objectReader = objectTable;
|
|
|
|
// Read sections in the reverse order as section may reference
|
|
// successor sections by offsets.
|
|
|
|
final annotationsStart = start + annotationsOffset;
|
|
reader.offset = annotationsStart;
|
|
for (int i = 0; i < annotationsNum; ++i) {
|
|
int offset = reader.offset - annotationsStart;
|
|
ObjectHandle annot = reader.readPackedObject();
|
|
reader.linkReader.setOffset(annot, offset);
|
|
annotations.add(annot);
|
|
}
|
|
|
|
final sourcePositionsStart = start + sourcePositionsOffset;
|
|
reader.offset = sourcePositionsStart;
|
|
for (int i = 0; i < sourcePositionsNum; ++i) {
|
|
int offset = reader.offset - sourcePositionsStart;
|
|
SourcePositions sp = new SourcePositions.read(reader);
|
|
reader.linkReader.setOffset(sp, offset);
|
|
sourcePositions.add(sp);
|
|
}
|
|
|
|
final codesStart = start + codesOffset;
|
|
reader.offset = codesStart;
|
|
for (int i = 0; i < codesNum; ++i) {
|
|
int offset = reader.offset - codesStart;
|
|
Code code = new Code.read(reader);
|
|
reader.linkReader.setOffset(code, offset);
|
|
codes.add(code);
|
|
}
|
|
|
|
final membersStart = start + membersOffset;
|
|
reader.offset = membersStart;
|
|
for (int i = 0; i < membersNum; ++i) {
|
|
int offset = reader.offset - membersStart;
|
|
Members m = new Members.read(reader);
|
|
reader.linkReader.setOffset(m, offset);
|
|
members.add(m);
|
|
}
|
|
|
|
if (mainOffset != 0) {
|
|
reader.offset = start + mainOffset;
|
|
mainLibrary = reader.readPackedObject();
|
|
}
|
|
}
|
|
|
|
String toString() => "\n"
|
|
"Bytecode"
|
|
" (version: "
|
|
"${version == currentBytecodeFormatVersion ? 'stable' : version == futureBytecodeFormatVersion ? 'future' : "v$version"}"
|
|
")\n"
|
|
// "$objectTable\n"
|
|
// "$stringTable\n"
|
|
"${mainLibrary != null ? 'Main library: $mainLibrary\n' : ''}"
|
|
// "${members.join('\n')}\n"
|
|
;
|
|
}
|
|
|
|
void _writeBytecodeInstructions(BufferedWriter writer, List<int> bytecodes) {
|
|
writer.writePackedUInt30(bytecodes.length);
|
|
writer.align(bytecodeInstructionsAlignment);
|
|
writer.writeBytes(bytecodes);
|
|
BytecodeSizeStatistics.instructionsSize += bytecodes.length;
|
|
}
|
|
|
|
List<int> _readBytecodeInstructions(BufferedReader reader) {
|
|
int len = reader.readPackedUInt30();
|
|
reader.align(bytecodeInstructionsAlignment);
|
|
return reader.readBytesAsUint8List(len);
|
|
}
|