99a370942b
Add a flag to SourceFile which is true if the associated script contains a list of covered const constructors, and serialize said list if true. Delay reading and resolving the list of covered const constructors in the bytecode reader until code is read. Update usage counters for interpreted functions in Entry instructions. Fix up cases where SourceReport assumed compiled code as appropriate. Remaining: * Record call and assert coverage information if the current isolate group has coverage enabled. * Record branch coverage information if the current isolate group has branch coverage enabled. TEST=pkg/vm_service pkg/dart2bytecode Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try,vm-dyn-mac-debug-arm64-try Change-Id: I14b6ffba1e175993e992c0fe939473557303bfeb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/449900 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Tess Strickland <sstrickl@google.com>
1585 lines
52 KiB
Dart
1585 lines
52 KiB
Dart
// Copyright (c) 2024, 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.
|
|
|
|
import 'dart:typed_data' show Uint8List;
|
|
|
|
import 'package:kernel/ast.dart' show TreeNode, listHashCode, listEquals;
|
|
import 'package:kernel/core_types.dart' show CoreTypes;
|
|
import 'bytecode_serialization.dart'
|
|
show
|
|
BufferedWriter,
|
|
BufferedReader,
|
|
BytecodeDeclaration,
|
|
BytecodeSizeStatistics,
|
|
StringTable;
|
|
import 'constant_pool.dart' show ConstantPool;
|
|
import 'dbc.dart' show bytecodeFormatVersion;
|
|
import 'disassembler.dart' show BytecodeDisassembler;
|
|
import 'exceptions.dart' show ExceptionsTable;
|
|
import 'local_variable_table.dart' show LocalVariableTable;
|
|
import 'object_table.dart'
|
|
show
|
|
ObjectTable,
|
|
ObjectHandle,
|
|
NameAndType,
|
|
ParameterFlags,
|
|
TypeParameterDeclaration;
|
|
import 'source_positions.dart' show LineStarts, SourcePositions;
|
|
|
|
class LibraryDeclaration extends BytecodeDeclaration {
|
|
static const usesDartMirrorsFlag = 1 << 0;
|
|
static const usesDartFfiFlag = 1 << 1;
|
|
|
|
ObjectHandle? importUri;
|
|
final int flags;
|
|
final ObjectHandle name;
|
|
final ObjectHandle script;
|
|
final List<ClassDeclaration> classes;
|
|
|
|
LibraryDeclaration(
|
|
this.importUri, this.flags, this.name, this.script, this.classes);
|
|
|
|
void write(BufferedWriter writer) {
|
|
final start = writer.offset;
|
|
writer.writePackedUInt30(flags);
|
|
writer.writePackedObject(name);
|
|
writer.writePackedObject(script);
|
|
writer.writePackedUInt30(classes.length);
|
|
for (var cls in classes) {
|
|
writer.writePackedObject(cls.name!);
|
|
writer.writeLinkOffset(cls);
|
|
}
|
|
BytecodeSizeStatistics.librariesSize += (writer.offset - start);
|
|
}
|
|
|
|
factory LibraryDeclaration.read(BufferedReader reader) {
|
|
final flags = reader.readPackedUInt30();
|
|
final ObjectHandle name = reader.readPackedObject();
|
|
final ObjectHandle script = reader.readPackedObject();
|
|
final classes =
|
|
List<ClassDeclaration>.generate(reader.readPackedUInt30(), (_) {
|
|
final ObjectHandle className = reader.readPackedObject();
|
|
return reader.readLinkOffset<ClassDeclaration>()..name = className;
|
|
});
|
|
return new LibraryDeclaration(null, flags, name, script, classes);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
final StringBuffer sb = new StringBuffer();
|
|
sb.writeln('Library $importUri');
|
|
sb.writeln(' name $name');
|
|
sb.writeln(' script $script');
|
|
if ((flags & usesDartMirrorsFlag) != 0) {
|
|
sb.writeln(' uses dart:mirrors');
|
|
}
|
|
if ((flags & usesDartFfiFlag) != 0) {
|
|
sb.writeln(' uses dart:ffi');
|
|
}
|
|
sb.writeln();
|
|
for (var cls in classes) {
|
|
sb.write(cls);
|
|
}
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
class ClassDeclaration extends BytecodeDeclaration {
|
|
static const isAbstractFlag = 1 << 0;
|
|
static const isEnumFlag = 1 << 1;
|
|
static const hasTypeParamsFlag = 1 << 2;
|
|
static const hasTypeArgumentsFlag = 1 << 3;
|
|
static const isTransformedMixinApplicationFlag = 1 << 4;
|
|
static const hasSourcePositionsFlag = 1 << 5;
|
|
static const hasAnnotationsFlag = 1 << 6;
|
|
static const hasPragmaFlag = 1 << 7;
|
|
static const hasConstConstructorFlag = 1 << 8;
|
|
static const isSealedFlag = 1 << 9;
|
|
static const isMixinClassFlag = 1 << 10;
|
|
static const isBaseClassFlag = 1 << 11;
|
|
static const isInterfaceFlag = 1 << 12;
|
|
static const isFinalFlag = 1 << 13;
|
|
|
|
ObjectHandle? name;
|
|
final int flags;
|
|
final ObjectHandle script;
|
|
final int position;
|
|
final int endPosition;
|
|
final TypeParametersDeclaration? typeParameters;
|
|
final int numTypeArguments;
|
|
final ObjectHandle? superType;
|
|
final List<ObjectHandle> interfaces;
|
|
final Members members;
|
|
final AnnotationsDeclaration? annotations;
|
|
|
|
ClassDeclaration(
|
|
this.name,
|
|
this.flags,
|
|
this.script,
|
|
this.position,
|
|
this.endPosition,
|
|
this.typeParameters,
|
|
this.numTypeArguments,
|
|
this.superType,
|
|
this.interfaces,
|
|
this.members,
|
|
this.annotations);
|
|
|
|
void write(BufferedWriter writer) {
|
|
final start = writer.offset;
|
|
writer.writePackedUInt30(flags);
|
|
writer.writePackedObject(script);
|
|
|
|
if ((flags & hasSourcePositionsFlag) != 0) {
|
|
writer.writePackedUInt30(position + 1);
|
|
writer.writePackedUInt30(endPosition + 1);
|
|
}
|
|
if ((flags & hasTypeArgumentsFlag) != 0) {
|
|
writer.writePackedUInt30(numTypeArguments);
|
|
}
|
|
if ((flags & hasTypeParamsFlag) != 0) {
|
|
typeParameters!.write(writer);
|
|
}
|
|
writer.writePackedObject(superType);
|
|
writer.writePackedList(interfaces);
|
|
if ((flags & hasAnnotationsFlag) != 0) {
|
|
writer.writeLinkOffset(annotations!);
|
|
}
|
|
writer.writeLinkOffset(members);
|
|
BytecodeSizeStatistics.classesSize += (writer.offset - start);
|
|
}
|
|
|
|
factory ClassDeclaration.read(BufferedReader reader) {
|
|
final flags = reader.readPackedUInt30();
|
|
final ObjectHandle script = reader.readPackedObject();
|
|
final position = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: TreeNode.noOffset;
|
|
final endPosition = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: TreeNode.noOffset;
|
|
final numTypeArguments =
|
|
((flags & hasTypeArgumentsFlag) != 0) ? reader.readPackedUInt30() : 0;
|
|
final typeParameters = ((flags & hasTypeParamsFlag) != 0)
|
|
? new TypeParametersDeclaration.read(reader)
|
|
: null;
|
|
final ObjectHandle? superType = reader.readPackedObject();
|
|
final interfaces = reader.readPackedList<ObjectHandle>();
|
|
final annotations = ((flags & hasAnnotationsFlag) != 0)
|
|
? reader.readLinkOffset<AnnotationsDeclaration>()
|
|
: null;
|
|
final members = reader.readLinkOffset<Members>();
|
|
return new ClassDeclaration(
|
|
null,
|
|
flags,
|
|
script,
|
|
position,
|
|
endPosition,
|
|
typeParameters,
|
|
numTypeArguments,
|
|
superType,
|
|
interfaces,
|
|
members,
|
|
annotations);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
final StringBuffer sb = new StringBuffer();
|
|
sb.write('Class $name, script = $script');
|
|
if ((flags & isAbstractFlag) != 0) {
|
|
sb.write(', abstract');
|
|
}
|
|
if ((flags & isEnumFlag) != 0) {
|
|
sb.write(', enum');
|
|
}
|
|
if ((flags & isTransformedMixinApplicationFlag) != 0) {
|
|
sb.write(', mixin-application');
|
|
}
|
|
if ((flags & hasPragmaFlag) != 0) {
|
|
sb.write(', has-pragma');
|
|
}
|
|
if ((flags & hasSourcePositionsFlag) != 0) {
|
|
sb.write(', pos = $position, end-pos = $endPosition');
|
|
}
|
|
sb.writeln();
|
|
if ((flags & hasTypeParamsFlag) != 0) {
|
|
sb.write(' type-params $typeParameters (args: $numTypeArguments)\n');
|
|
}
|
|
if (superType != null) {
|
|
sb.write(' extends $superType\n');
|
|
}
|
|
if (interfaces.isNotEmpty) {
|
|
sb.write(' implements $interfaces\n');
|
|
}
|
|
if ((flags & hasAnnotationsFlag) != 0) {
|
|
sb.write(' annotations $annotations\n');
|
|
}
|
|
sb.writeln();
|
|
sb.write(members.toString());
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
class SourceFile extends BytecodeDeclaration {
|
|
static const hasLineStartsFlag = 1 << 0;
|
|
static const hasSourceFlag = 1 << 1;
|
|
static const hasConstConstructorCoverageFlag = 1 << 2;
|
|
|
|
final ObjectHandle importUri;
|
|
LineStarts? lineStarts;
|
|
String? source;
|
|
List<ObjectHandle>? coveredConstConstructors;
|
|
|
|
SourceFile(this.importUri,
|
|
[this.coveredConstConstructors, this.lineStarts, this.source]);
|
|
|
|
void write(BufferedWriter writer) {
|
|
int flags = 0;
|
|
if (lineStarts != null) {
|
|
flags |= hasLineStartsFlag;
|
|
}
|
|
if (coveredConstConstructors?.isNotEmpty ?? false) {
|
|
flags |= hasConstConstructorCoverageFlag;
|
|
}
|
|
if (source != null && source != '') {
|
|
flags |= hasSourceFlag;
|
|
}
|
|
writer.writePackedUInt30(flags);
|
|
writer.writePackedObject(importUri);
|
|
if ((flags & hasLineStartsFlag) != 0) {
|
|
writer.writeLinkOffset(lineStarts!);
|
|
}
|
|
if ((flags & hasSourceFlag) != 0) {
|
|
writer.writePackedStringReference(source!);
|
|
}
|
|
if ((flags & hasConstConstructorCoverageFlag) != 0) {
|
|
writer.writePackedList(coveredConstConstructors!);
|
|
}
|
|
}
|
|
|
|
factory SourceFile.read(BufferedReader reader) {
|
|
final flags = reader.readPackedUInt30();
|
|
final ObjectHandle importUri = reader.readPackedObject();
|
|
final lineStarts = ((flags & hasLineStartsFlag) != 0)
|
|
? reader.readLinkOffset<LineStarts>()
|
|
: null;
|
|
final source = ((flags & hasSourceFlag) != 0)
|
|
? reader.readPackedStringReference()
|
|
: null;
|
|
final coveredConstConstructors =
|
|
((flags & hasConstConstructorCoverageFlag) != 0)
|
|
? reader.readPackedList<ObjectHandle>()
|
|
: null;
|
|
return new SourceFile(
|
|
importUri, coveredConstConstructors, lineStarts, source);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
final StringBuffer sb = new StringBuffer();
|
|
sb.write('source: import-uri $importUri');
|
|
if (source != null && source != '') {
|
|
sb.write(', ${source!.length} text chars');
|
|
}
|
|
if (lineStarts != null) {
|
|
sb.write(', ${lineStarts!.lineStarts.length} line starts');
|
|
}
|
|
if (coveredConstConstructors?.isNotEmpty ?? false) {
|
|
sb.write(', covered const constructors: [');
|
|
sb.writeAll(coveredConstConstructors!, ', ');
|
|
sb.write(']');
|
|
}
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
class Members extends BytecodeDeclaration {
|
|
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) {
|
|
final start = writer.offset;
|
|
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);
|
|
}
|
|
BytecodeSizeStatistics.membersSize += (writer.offset - start);
|
|
}
|
|
|
|
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() => "${fields.join('\n')}\n"
|
|
"${functions.join('\n')}";
|
|
}
|
|
|
|
class FieldDeclaration {
|
|
static const isStaticFlag = 1 << 0;
|
|
static const isConstFlag = 1 << 1;
|
|
static const isFinalFlag = 1 << 2;
|
|
static const isLateFlag = 1 << 3;
|
|
static const isCovariantFlag = 1 << 4;
|
|
static const isCovariantByClassFlag = 1 << 5;
|
|
static const isExtensionMemberFlag = 1 << 6;
|
|
static const isReflectableFlag = 1 << 7;
|
|
static const hasGetterFlag = 1 << 8;
|
|
static const hasSetterFlag = 1 << 9;
|
|
static const hasInitializerFlag = 1 << 10;
|
|
static const hasNontrivialInitializerFlag = 1 << 11;
|
|
static const hasInitializerCodeFlag = 1 << 12;
|
|
static const hasSourcePositionsFlag = 1 << 13;
|
|
static const hasAnnotationsFlag = 1 << 14;
|
|
static const hasPragmaFlag = 1 << 15;
|
|
static const hasCustomScriptFlag = 1 << 16;
|
|
|
|
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 AnnotationsDeclaration? 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 & hasInitializerCodeFlag) != 0) {
|
|
writer.writeLinkOffset(initializerCode!);
|
|
}
|
|
if ((flags & hasNontrivialInitializerFlag) == 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 ObjectHandle name = reader.readPackedObject();
|
|
final ObjectHandle type = reader.readPackedObject();
|
|
final script = ((flags & hasCustomScriptFlag) != 0)
|
|
? reader.readPackedObject<ObjectHandle>()
|
|
: null;
|
|
final position = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: TreeNode.noOffset;
|
|
final endPosition = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: TreeNode.noOffset;
|
|
final initializerCode = ((flags & hasInitializerCodeFlag) != 0)
|
|
? reader.readLinkOffset<Code>()
|
|
: null;
|
|
final value = ((flags & hasNontrivialInitializerFlag) == 0)
|
|
? reader.readPackedObject<ObjectHandle?>()
|
|
: null;
|
|
final getterName = ((flags & hasGetterFlag) != 0)
|
|
? reader.readPackedObject<ObjectHandle>()
|
|
: null;
|
|
final setterName = ((flags & hasSetterFlag) != 0)
|
|
? reader.readPackedObject<ObjectHandle>()
|
|
: null;
|
|
final annotations = ((flags & hasAnnotationsFlag) != 0)
|
|
? reader.readLinkOffset<AnnotationsDeclaration>()
|
|
: null;
|
|
return new FieldDeclaration(flags, name, type, value, script, position,
|
|
endPosition, getterName, setterName, initializerCode, annotations);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
final 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 & isLateFlag) != 0) {
|
|
sb.write(', is-late');
|
|
}
|
|
if ((flags & isExtensionMemberFlag) != 0) {
|
|
sb.write(', extension-member');
|
|
}
|
|
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');
|
|
}
|
|
if ((flags & hasInitializerFlag) != 0) {
|
|
sb.write(', has-initializer');
|
|
}
|
|
sb.writeln();
|
|
if ((flags & hasInitializerCodeFlag) != 0) {
|
|
sb.write(' initializer\n$initializerCode\n');
|
|
}
|
|
if ((flags & hasNontrivialInitializerFlag) == 0) {
|
|
sb.write(' value = $value\n');
|
|
}
|
|
if ((flags & hasAnnotationsFlag) != 0) {
|
|
sb.write(' annotations $annotations\n');
|
|
}
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
class FunctionDeclaration {
|
|
static const isStaticFlag = 1 << 0;
|
|
static const isAbstractFlag = 1 << 1;
|
|
static const isGetterFlag = 1 << 2;
|
|
static const isSetterFlag = 1 << 3;
|
|
static const isConstructorFlag = 1 << 4;
|
|
static const isFactoryFlag = 1 << 5;
|
|
static const isConstFlag = 1 << 6;
|
|
static const hasOptionalPositionalParamsFlag = 1 << 7;
|
|
static const hasOptionalNamedParamsFlag = 1 << 8;
|
|
static const hasTypeParamsFlag = 1 << 9;
|
|
static const hasParameterFlagsFlag = 1 << 10;
|
|
static const isExtensionMemberFlag = 1 << 11;
|
|
static const isReflectableFlag = 1 << 12;
|
|
static const isDebuggableFlag = 1 << 13;
|
|
static const isAsyncFlag = 1 << 14;
|
|
static const isAsyncStarFlag = 1 << 15;
|
|
static const isSyncStarFlag = 1 << 16;
|
|
static const isNoSuchMethodForwarderFlag = 1 << 17;
|
|
static const isExternalFlag = 1 << 18;
|
|
static const isNativeFlag = 1 << 19;
|
|
static const hasSourcePositionsFlag = 1 << 20;
|
|
static const hasAnnotationsFlag = 1 << 21;
|
|
static const hasPragmaFlag = 1 << 22;
|
|
static const hasCustomScriptFlag = 1 << 23;
|
|
|
|
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;
|
|
// Only contains the required flag for named parameters when present.
|
|
final List<int>? parameterFlags;
|
|
final ObjectHandle returnType;
|
|
final ObjectHandle? nativeName;
|
|
final Code? code;
|
|
final AnnotationsDeclaration? annotations;
|
|
|
|
FunctionDeclaration(
|
|
this.flags,
|
|
this.name,
|
|
this.script,
|
|
this.position,
|
|
this.endPosition,
|
|
this.typeParameters,
|
|
this.numRequiredParameters,
|
|
this.parameters,
|
|
this.parameterFlags,
|
|
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);
|
|
}
|
|
if ((flags & hasParameterFlagsFlag) != 0) {
|
|
final parameterFlags = this.parameterFlags!;
|
|
writer.writePackedUInt30(parameterFlags.length);
|
|
parameterFlags.forEach((flags) => writer.writePackedUInt30(flags));
|
|
}
|
|
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 ObjectHandle name = reader.readPackedObject();
|
|
|
|
final script = ((flags & hasCustomScriptFlag) != 0)
|
|
? reader.readPackedObject<ObjectHandle>()
|
|
: null;
|
|
final position = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: TreeNode.noOffset;
|
|
final endPosition = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: TreeNode.noOffset;
|
|
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 parameterFlags = ((flags & hasParameterFlagsFlag) != 0)
|
|
? List<int>.generate(
|
|
reader.readPackedUInt30(), (_) => reader.readPackedUInt30())
|
|
: null;
|
|
final ObjectHandle returnType = reader.readPackedObject();
|
|
final nativeName = ((flags & isNativeFlag) != 0)
|
|
? reader.readPackedObject<ObjectHandle>()
|
|
: null;
|
|
final code =
|
|
((flags & isAbstractFlag) == 0) ? reader.readLinkOffset<Code>() : null;
|
|
final annotations = ((flags & hasAnnotationsFlag) != 0)
|
|
? reader.readLinkOffset<AnnotationsDeclaration>()
|
|
: null;
|
|
return new FunctionDeclaration(
|
|
flags,
|
|
name,
|
|
script,
|
|
position,
|
|
endPosition,
|
|
typeParameters,
|
|
numRequiredParameters,
|
|
parameters,
|
|
parameterFlags,
|
|
returnType,
|
|
nativeName,
|
|
code,
|
|
annotations);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
final 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 & isExtensionMemberFlag) != 0) {
|
|
sb.write(', extension-member');
|
|
}
|
|
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 & 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');
|
|
if ((flags & hasParameterFlagsFlag) != 0) {
|
|
sb.write(' parameter-flags $parameterFlags\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<TypeParameterDeclaration> 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.bound);
|
|
writer.writePackedObject(tp.defaultType);
|
|
}
|
|
}
|
|
|
|
factory TypeParametersDeclaration.read(BufferedReader reader) {
|
|
final int numTypeParams = reader.readPackedUInt30();
|
|
final names = List<ObjectHandle>.generate(
|
|
numTypeParams, (_) => reader.readPackedObject());
|
|
return TypeParametersDeclaration(List<TypeParameterDeclaration>.generate(
|
|
numTypeParams,
|
|
(int i) => TypeParameterDeclaration(
|
|
names[i], reader.readPackedObject(), reader.readPackedObject())));
|
|
}
|
|
|
|
@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 {
|
|
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 ObjectHandle name = reader.readPackedObject();
|
|
final ObjectHandle type = reader.readPackedObject();
|
|
return new ParameterDeclaration(name, type);
|
|
}
|
|
|
|
@override
|
|
String toString() => '$type $name';
|
|
}
|
|
|
|
class Code extends BytecodeDeclaration {
|
|
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;
|
|
static const hasLocalVariablesFlag = 1 << 7;
|
|
|
|
final ConstantPool constantPool;
|
|
final Uint8List bytecodes;
|
|
final ExceptionsTable exceptionsTable;
|
|
final SourcePositions? sourcePositions;
|
|
final LocalVariableTable? localVariables;
|
|
final List<ObjectHandle> nullableFields;
|
|
final List<ClosureDeclaration> closures;
|
|
// Covariant and CovariantByClass flags for all parameters.
|
|
final List<int>? parameterFlags;
|
|
final int? forwardingStubTargetCpIndex;
|
|
final int? defaultFunctionTypeArgsCpIndex;
|
|
|
|
bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty;
|
|
bool get hasSourcePositions => sourcePositions?.isNotEmpty ?? false;
|
|
bool get hasLocalVariables => localVariables?.isNotEmpty ?? false;
|
|
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) |
|
|
(hasLocalVariables ? hasLocalVariablesFlag : 0);
|
|
|
|
Code(
|
|
this.constantPool,
|
|
this.bytecodes,
|
|
this.exceptionsTable,
|
|
this.sourcePositions,
|
|
this.localVariables,
|
|
this.nullableFields,
|
|
this.closures,
|
|
this.parameterFlags,
|
|
this.forwardingStubTargetCpIndex,
|
|
this.defaultFunctionTypeArgsCpIndex);
|
|
|
|
void write(BufferedWriter writer) {
|
|
final start = writer.offset;
|
|
writer.writePackedUInt30(flags);
|
|
final parameterFlags = this.parameterFlags;
|
|
if (parameterFlags != null) {
|
|
writer.writePackedUInt30(parameterFlags.length);
|
|
parameterFlags.forEach((flags) => writer.writePackedUInt30(flags));
|
|
}
|
|
final forwardingStubTargetCpIndex = this.forwardingStubTargetCpIndex;
|
|
if (forwardingStubTargetCpIndex != null) {
|
|
writer.writePackedUInt30(forwardingStubTargetCpIndex);
|
|
}
|
|
final defaultFunctionTypeArgsCpIndex = this.defaultFunctionTypeArgsCpIndex;
|
|
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 (hasLocalVariables) {
|
|
writer.writeLinkOffset(localVariables!);
|
|
}
|
|
if (hasNullableFields) {
|
|
writer.writePackedList(nullableFields);
|
|
}
|
|
if (hasClosures) {
|
|
closures.forEach((c) => c.code!.write(writer));
|
|
}
|
|
BytecodeSizeStatistics.codeSize += (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 Uint8List bytecodes = _readBytecodeInstructions(reader);
|
|
final exceptionsTable = ((flags & hasExceptionsTableFlag) != 0)
|
|
? new ExceptionsTable.read(reader)
|
|
: new ExceptionsTable();
|
|
final sourcePositions = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readLinkOffset<SourcePositions>()
|
|
: null;
|
|
final localVariables = ((flags & hasLocalVariablesFlag) != 0)
|
|
? reader.readLinkOffset<LocalVariableTable>()
|
|
: 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,
|
|
localVariables,
|
|
nullableFields,
|
|
closures,
|
|
parameterFlags,
|
|
forwardingStubTargetCpIndex,
|
|
defaultFunctionTypeArgsCpIndex);
|
|
}
|
|
|
|
// TODO(alexmarkov): Consider printing constant pool before bytecode.
|
|
@override
|
|
String toString() => "Bytecode {\n"
|
|
"${new BytecodeDisassembler().disassemble(bytecodes, exceptionsTable, annotations: [
|
|
hasSourcePositions
|
|
? sourcePositions!.getBytecodeAnnotations()
|
|
: const <int, String>{},
|
|
hasLocalVariables
|
|
? localVariables!.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 hasOptionalPositionalParamsFlag = 1 << 0;
|
|
static const hasOptionalNamedParamsFlag = 1 << 1;
|
|
static const hasTypeParamsFlag = 1 << 2;
|
|
static const hasSourcePositionsFlag = 1 << 3;
|
|
static const isAsyncFlag = 1 << 4;
|
|
static const isAsyncStarFlag = 1 << 5;
|
|
static const isSyncStarFlag = 1 << 6;
|
|
static const isDebuggableFlag = 1 << 7;
|
|
static const hasParameterFlagsFlag = 1 << 8;
|
|
static const hasAnnotationsFlag = 1 << 9;
|
|
static const hasPragmaFlag = 1 << 10;
|
|
|
|
int flags;
|
|
final ObjectHandle parent;
|
|
final ObjectHandle name;
|
|
final int position;
|
|
final int endPosition;
|
|
final TypeParametersDeclaration? typeParameters;
|
|
final int numRequiredParams;
|
|
final int numNamedParams;
|
|
final List<NameAndType> parameters;
|
|
// Only contains the required flag for named parameters when present.
|
|
final List<int>? parameterFlags;
|
|
final ObjectHandle returnType;
|
|
final AnnotationsDeclaration? annotations;
|
|
ClosureCode? code;
|
|
|
|
ClosureDeclaration(
|
|
this.flags,
|
|
this.parent,
|
|
this.name,
|
|
this.position,
|
|
this.endPosition,
|
|
this.typeParameters,
|
|
this.numRequiredParams,
|
|
this.numNamedParams,
|
|
this.parameters,
|
|
this.parameterFlags,
|
|
this.returnType,
|
|
this.annotations);
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedUInt30(flags);
|
|
writer.writePackedObject(parent);
|
|
writer.writePackedObject(name);
|
|
|
|
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 | hasOptionalNamedParamsFlag) !=
|
|
0) {
|
|
writer.writePackedUInt30(numRequiredParams);
|
|
}
|
|
for (var param in parameters) {
|
|
writer.writePackedObject(param.name);
|
|
writer.writePackedObject(param.type);
|
|
}
|
|
if ((flags & hasParameterFlagsFlag) != 0) {
|
|
final parameterFlags = this.parameterFlags!;
|
|
writer.writePackedUInt30(parameterFlags.length);
|
|
for (var pf in parameterFlags) {
|
|
writer.writePackedUInt30(pf);
|
|
}
|
|
}
|
|
writer.writePackedObject(returnType);
|
|
if ((flags & hasAnnotationsFlag) != 0) {
|
|
writer.writeLinkOffset(annotations!);
|
|
}
|
|
}
|
|
|
|
factory ClosureDeclaration.read(BufferedReader reader) {
|
|
final int flags = reader.readPackedUInt30();
|
|
final ObjectHandle parent = reader.readPackedObject();
|
|
final ObjectHandle name = reader.readPackedObject();
|
|
final position = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: TreeNode.noOffset;
|
|
final endPosition = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readPackedUInt30() - 1
|
|
: TreeNode.noOffset;
|
|
final TypeParametersDeclaration? typeParameters =
|
|
((flags & hasTypeParamsFlag) != 0)
|
|
? TypeParametersDeclaration.read(reader)
|
|
: null;
|
|
final numParams = reader.readPackedUInt30();
|
|
final numRequiredParams = (flags &
|
|
(hasOptionalPositionalParamsFlag |
|
|
hasOptionalNamedParamsFlag) !=
|
|
0)
|
|
? reader.readPackedUInt30()
|
|
: numParams;
|
|
final numNamedParams = (flags & hasOptionalNamedParamsFlag != 0)
|
|
? (numParams - numRequiredParams)
|
|
: 0;
|
|
final List<NameAndType> parameters = new List<NameAndType>.generate(
|
|
numParams,
|
|
(_) => new NameAndType(
|
|
reader.readPackedObject(), reader.readPackedObject()));
|
|
List<int>? parameterFlags;
|
|
if ((flags & hasParameterFlagsFlag) != 0) {
|
|
final int numParameterFlags = reader.readPackedUInt30();
|
|
parameterFlags = List<int>.generate(
|
|
numParameterFlags, (_) => reader.readPackedUInt30());
|
|
}
|
|
final ObjectHandle returnType = reader.readPackedObject();
|
|
final annotations = ((flags & hasAnnotationsFlag) != 0)
|
|
? reader.readLinkOffset<AnnotationsDeclaration>()
|
|
: null;
|
|
return new ClosureDeclaration(
|
|
flags,
|
|
parent,
|
|
name,
|
|
position,
|
|
endPosition,
|
|
typeParameters,
|
|
numRequiredParams,
|
|
numNamedParams,
|
|
parameters,
|
|
parameterFlags,
|
|
returnType,
|
|
annotations);
|
|
}
|
|
|
|
void _writeParamsToBuffer(
|
|
StringBuffer sb, List<NameAndType> params, List<int>? flags) {
|
|
assert(flags == null || (params.length == flags.length));
|
|
for (int i = 0; i < params.length; i++) {
|
|
if (i != 0) {
|
|
sb.write(', ');
|
|
}
|
|
// We only store the required flag for ClosureDeclarations.
|
|
if (flags != null && (flags[i] & ParameterFlags.isRequiredFlag) != 0) {
|
|
sb.write('required ');
|
|
}
|
|
sb.write(params[i]);
|
|
}
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
final StringBuffer sb = new StringBuffer();
|
|
sb.write('Closure $parent::$name');
|
|
if ((flags & isAsyncFlag) != 0) {
|
|
sb.write(' async');
|
|
}
|
|
if ((flags & isAsyncStarFlag) != 0) {
|
|
sb.write(' async*');
|
|
}
|
|
if ((flags & isSyncStarFlag) != 0) {
|
|
sb.write(' sync*');
|
|
}
|
|
if (position != TreeNode.noOffset) {
|
|
sb.write(' pos = $position, end-pos = $endPosition');
|
|
}
|
|
if ((flags & hasAnnotationsFlag) != 0) {
|
|
sb.write(' annotations $annotations\n');
|
|
}
|
|
if ((flags & hasTypeParamsFlag) != 0) {
|
|
sb.write(' type-params $typeParameters');
|
|
}
|
|
sb.write(' (');
|
|
_writeParamsToBuffer(sb, parameters.sublist(0, numRequiredParams), null);
|
|
if (numRequiredParams != parameters.length) {
|
|
if (numRequiredParams > 0) {
|
|
sb.write(', ');
|
|
}
|
|
sb.write(numNamedParams > 0 ? '{ ' : '[ ');
|
|
_writeParamsToBuffer(
|
|
sb, parameters.sublist(numRequiredParams), parameterFlags);
|
|
sb.write(numNamedParams > 0 ? ' }' : ' ]');
|
|
}
|
|
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 {
|
|
static const hasExceptionsTableFlag = 1 << 0;
|
|
static const hasSourcePositionsFlag = 1 << 1;
|
|
static const hasLocalVariablesFlag = 1 << 2;
|
|
|
|
final Uint8List bytecodes;
|
|
final ExceptionsTable exceptionsTable;
|
|
final SourcePositions? sourcePositions;
|
|
final LocalVariableTable? localVariables;
|
|
|
|
bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty;
|
|
bool get hasSourcePositions => sourcePositions?.isNotEmpty ?? false;
|
|
bool get hasLocalVariables => localVariables?.isNotEmpty ?? false;
|
|
|
|
int get flags =>
|
|
(hasExceptionsTable ? hasExceptionsTableFlag : 0) |
|
|
(hasSourcePositions ? hasSourcePositionsFlag : 0) |
|
|
(hasLocalVariables ? hasLocalVariablesFlag : 0);
|
|
|
|
ClosureCode(this.bytecodes, this.exceptionsTable, this.sourcePositions,
|
|
this.localVariables);
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedUInt30(flags);
|
|
_writeBytecodeInstructions(writer, bytecodes);
|
|
if (hasExceptionsTable) {
|
|
exceptionsTable.write(writer);
|
|
}
|
|
if (hasSourcePositions) {
|
|
writer.writeLinkOffset(sourcePositions!);
|
|
}
|
|
if (hasLocalVariables) {
|
|
writer.writeLinkOffset(localVariables!);
|
|
}
|
|
}
|
|
|
|
factory ClosureCode.read(BufferedReader reader) {
|
|
final int flags = reader.readPackedUInt30();
|
|
final Uint8List bytecodes = _readBytecodeInstructions(reader);
|
|
final exceptionsTable = ((flags & hasExceptionsTableFlag) != 0)
|
|
? new ExceptionsTable.read(reader)
|
|
: new ExceptionsTable();
|
|
final sourcePositions = ((flags & hasSourcePositionsFlag) != 0)
|
|
? reader.readLinkOffset<SourcePositions>()
|
|
: null;
|
|
final localVariables = ((flags & hasLocalVariablesFlag) != 0)
|
|
? reader.readLinkOffset<LocalVariableTable>()
|
|
: null;
|
|
return new ClosureCode(
|
|
bytecodes, exceptionsTable, sourcePositions, localVariables);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.writeln('ClosureCode {');
|
|
sb.write(new BytecodeDisassembler()
|
|
.disassemble(bytecodes, exceptionsTable, annotations: [
|
|
hasSourcePositions
|
|
? sourcePositions!.getBytecodeAnnotations()
|
|
: const <int, String>{},
|
|
hasLocalVariables
|
|
? localVariables!.getBytecodeAnnotations()
|
|
: const <int, String>{}
|
|
]));
|
|
sb.writeln('}');
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
class AnnotationsDeclaration extends BytecodeDeclaration {
|
|
final ObjectHandle? object;
|
|
|
|
AnnotationsDeclaration(this.object);
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedObject(object);
|
|
}
|
|
|
|
factory AnnotationsDeclaration.read(BufferedReader reader) {
|
|
return new AnnotationsDeclaration(reader.readPackedObject());
|
|
}
|
|
|
|
@override
|
|
String toString() => object.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 = 0x44424333; // 'DBC3'
|
|
static const int numSections = 13;
|
|
static const int sectionAlignment = 4;
|
|
|
|
// UInt32 magic, version, numSections x (numItems, offset)
|
|
static const int headerSize = (2 + numSections * 2) * 4;
|
|
|
|
late StringTable stringTable;
|
|
late ObjectTable objectTable;
|
|
final List<LibraryDeclaration> libraries = <LibraryDeclaration>[];
|
|
final List<ClassDeclaration> classes = <ClassDeclaration>[];
|
|
final List<Members> members = <Members>[];
|
|
final List<Code> codes = <Code>[];
|
|
final List<SourcePositions> sourcePositions = <SourcePositions>[];
|
|
final List<LineStarts> lineStarts = <LineStarts>[];
|
|
final List<SourceFile> sourceFiles = <SourceFile>[];
|
|
final Map<Uri, SourceFile> uriToSource = <Uri, SourceFile>{};
|
|
final List<LocalVariableTable> localVariables = <LocalVariableTable>[];
|
|
final List<AnnotationsDeclaration> annotations = <AnnotationsDeclaration>[];
|
|
ObjectHandle? dynModuleEntryPoint;
|
|
|
|
Component(CoreTypes coreTypes)
|
|
: stringTable = new StringTable(),
|
|
objectTable = new ObjectTable(coreTypes);
|
|
|
|
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 annotationsWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var annot in annotations) {
|
|
writer.linkWriter.put(annot, annotationsWriter.offset);
|
|
annot.write(annotationsWriter);
|
|
}
|
|
BytecodeSizeStatistics.annotationsSize += annotationsWriter.offset;
|
|
|
|
final localVariablesWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var lv in localVariables) {
|
|
writer.linkWriter.put(lv, localVariablesWriter.offset);
|
|
lv.write(localVariablesWriter);
|
|
}
|
|
BytecodeSizeStatistics.localVariablesSize += localVariablesWriter.offset;
|
|
|
|
final lineStartsWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var ls in lineStarts) {
|
|
writer.linkWriter.put(ls, lineStartsWriter.offset);
|
|
ls.write(lineStartsWriter);
|
|
}
|
|
BytecodeSizeStatistics.lineStartsSize += lineStartsWriter.offset;
|
|
|
|
final sourceFilesWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var sf in sourceFiles) {
|
|
writer.linkWriter.put(sf, sourceFilesWriter.offset);
|
|
sf.write(sourceFilesWriter);
|
|
}
|
|
BytecodeSizeStatistics.sourceFilesSize += sourceFilesWriter.offset;
|
|
|
|
final sourcePositionsWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var sp in sourcePositions) {
|
|
writer.linkWriter.put(sp, sourcePositionsWriter.offset);
|
|
sp.write(sourcePositionsWriter);
|
|
}
|
|
BytecodeSizeStatistics.sourcePositionsSize += sourcePositionsWriter.offset;
|
|
|
|
final codesWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var code in codes) {
|
|
writer.linkWriter.put(code, codesWriter.offset);
|
|
code.write(codesWriter);
|
|
}
|
|
|
|
final membersWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var m in members) {
|
|
writer.linkWriter.put(m, membersWriter.offset);
|
|
m.write(membersWriter);
|
|
}
|
|
|
|
final classesWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var cls in classes) {
|
|
writer.linkWriter.put(cls, classesWriter.offset);
|
|
cls.write(classesWriter);
|
|
}
|
|
|
|
final librariesWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var library in libraries) {
|
|
writer.linkWriter.put(library, librariesWriter.offset);
|
|
library.write(librariesWriter);
|
|
}
|
|
|
|
final libraryIndexWriter = new BufferedWriter.fromWriter(writer);
|
|
for (var library in libraries) {
|
|
libraryIndexWriter.writePackedObject(library.importUri);
|
|
libraryIndexWriter.writeLinkOffset(library);
|
|
}
|
|
BytecodeSizeStatistics.librariesSize += libraryIndexWriter.offset;
|
|
|
|
BufferedWriter? mainWriter;
|
|
if (dynModuleEntryPoint != null) {
|
|
mainWriter = new BufferedWriter.fromWriter(writer);
|
|
mainWriter.writePackedObject(dynModuleEntryPoint);
|
|
}
|
|
|
|
final objectsWriter = new BufferedWriter.fromWriter(writer);
|
|
objectTable.write(objectsWriter);
|
|
|
|
final 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(libraries.length, libraryIndexWriter),
|
|
new _Section(libraries.length, librariesWriter),
|
|
new _Section(classes.length, classesWriter),
|
|
new _Section(members.length, membersWriter),
|
|
new _Section(codes.length, codesWriter),
|
|
new _Section(sourcePositions.length, sourcePositionsWriter),
|
|
new _Section(sourceFiles.length, sourceFilesWriter),
|
|
new _Section(lineStarts.length, lineStartsWriter),
|
|
new _Section(localVariables.length, localVariablesWriter),
|
|
new _Section(annotations.length, annotationsWriter),
|
|
];
|
|
assert(sections.length == numSections);
|
|
|
|
int offset = headerSize;
|
|
for (var section in sections) {
|
|
if (section.writer != null) {
|
|
section.offset = offset;
|
|
offset += section.size;
|
|
} else {
|
|
section.offset = 0;
|
|
}
|
|
}
|
|
|
|
final start = writer.offset;
|
|
|
|
writer.writeUInt32(magicValue);
|
|
writer.writeUInt32(bytecodeFormatVersion);
|
|
for (var section in sections) {
|
|
writer.writeUInt32(section.numItems);
|
|
writer.writeUInt32(section.offset!);
|
|
}
|
|
assert(writer.offset - start == headerSize);
|
|
for (var section in sections) {
|
|
final sectionWriter = section.writer;
|
|
if (sectionWriter != null) {
|
|
assert(writer.offset - start == section.offset);
|
|
writer.appendWriter(sectionWriter);
|
|
}
|
|
}
|
|
|
|
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';
|
|
}
|
|
|
|
final version = reader.readUInt32();
|
|
if (version != bytecodeFormatVersion) {
|
|
throw 'Error: unexpected bytecode format version $version (expected $bytecodeFormatVersion)';
|
|
}
|
|
|
|
reader.readUInt32();
|
|
final stringTableOffset = reader.readUInt32();
|
|
|
|
reader.readUInt32();
|
|
final objectTableOffset = reader.readUInt32();
|
|
|
|
reader.readUInt32();
|
|
final mainOffset = reader.readUInt32();
|
|
|
|
final librariesNum = reader.readUInt32();
|
|
final libraryIndexOffset = reader.readUInt32();
|
|
|
|
reader.readUInt32();
|
|
final librariesOffset = reader.readUInt32();
|
|
|
|
final classesNum = reader.readUInt32();
|
|
final classesOffset = 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 sourceFilesNum = reader.readUInt32();
|
|
final sourceFilesOffset = reader.readUInt32();
|
|
|
|
final lineStartsNum = reader.readUInt32();
|
|
final lineStartsOffset = reader.readUInt32();
|
|
|
|
final localVariablesNum = reader.readUInt32();
|
|
final localVariablesOffset = 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;
|
|
AnnotationsDeclaration annot = new AnnotationsDeclaration.read(reader);
|
|
reader.linkReader.setOffset(annot, offset);
|
|
annotations.add(annot);
|
|
}
|
|
|
|
final lineStartsStart = start + lineStartsOffset;
|
|
reader.offset = lineStartsStart;
|
|
for (int i = 0; i < lineStartsNum; ++i) {
|
|
int offset = reader.offset - lineStartsStart;
|
|
LineStarts ls = new LineStarts.read(reader);
|
|
reader.linkReader.setOffset(ls, offset);
|
|
lineStarts.add(ls);
|
|
}
|
|
|
|
final sourceFilesStart = start + sourceFilesOffset;
|
|
reader.offset = sourceFilesStart;
|
|
for (int i = 0; i < sourceFilesNum; ++i) {
|
|
int offset = reader.offset - sourceFilesStart;
|
|
SourceFile sf = new SourceFile.read(reader);
|
|
reader.linkReader.setOffset(sf, offset);
|
|
sourceFiles.add(sf);
|
|
}
|
|
|
|
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 localVariablesStart = start + localVariablesOffset;
|
|
reader.offset = localVariablesStart;
|
|
for (int i = 0; i < localVariablesNum; ++i) {
|
|
int offset = reader.offset - localVariablesStart;
|
|
LocalVariableTable lv = new LocalVariableTable.read(reader);
|
|
reader.linkReader.setOffset(lv, offset);
|
|
localVariables.add(lv);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
final classesStart = start + classesOffset;
|
|
reader.offset = classesStart;
|
|
for (int i = 0; i < classesNum; ++i) {
|
|
int offset = reader.offset - classesStart;
|
|
ClassDeclaration cls = new ClassDeclaration.read(reader);
|
|
reader.linkReader.setOffset(cls, offset);
|
|
classes.add(cls);
|
|
}
|
|
|
|
final librariesStart = start + librariesOffset;
|
|
reader.offset = librariesStart;
|
|
for (int i = 0; i < librariesNum; ++i) {
|
|
int offset = reader.offset - librariesStart;
|
|
LibraryDeclaration library = new LibraryDeclaration.read(reader);
|
|
reader.linkReader.setOffset(library, offset);
|
|
libraries.add(library);
|
|
}
|
|
|
|
final libraryIndexStart = start + libraryIndexOffset;
|
|
reader.offset = libraryIndexStart;
|
|
for (int i = 0; i < librariesNum; ++i) {
|
|
final ObjectHandle importUri = reader.readPackedObject();
|
|
final library = reader.readLinkOffset<LibraryDeclaration>();
|
|
library.importUri = importUri;
|
|
}
|
|
|
|
if (mainOffset != 0) {
|
|
reader.offset = start + mainOffset;
|
|
dynModuleEntryPoint = reader.readPackedObject();
|
|
}
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
final StringBuffer sb = new StringBuffer();
|
|
sb.writeln("Bytecode");
|
|
if (dynModuleEntryPoint != null) {
|
|
sb.writeln("Dynamic Module Entry Point: $dynModuleEntryPoint");
|
|
}
|
|
for (var library in libraries) {
|
|
sb.write(library);
|
|
}
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
void _writeBytecodeInstructions(BufferedWriter writer, Uint8List bytecodes) {
|
|
writer.writePackedUInt30(bytecodes.length);
|
|
writer.appendUint8List(bytecodes);
|
|
BytecodeSizeStatistics.instructionsSize += bytecodes.length;
|
|
}
|
|
|
|
Uint8List _readBytecodeInstructions(BufferedReader reader) {
|
|
int len = reader.readPackedUInt30();
|
|
return reader.readBytesAsUint8List(len);
|
|
}
|