a3038b85f8
Pragmas on local functions are used internally by FFI. TEST=ci Change-Id: I3ffb9984d8fcd943b22a98746faa915b2ce7c9fa Cq-Include-Trybots: luci.dart.try:vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try,vm-dyn-linux-debug-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/446121 Reviewed-by: Tess Strickland <sstrickl@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
1566 lines
51 KiB
Dart
1566 lines
51 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;
|
|
|
|
final ObjectHandle importUri;
|
|
LineStarts? lineStarts;
|
|
String? source;
|
|
|
|
SourceFile(this.importUri, [this.lineStarts, this.source]);
|
|
|
|
void write(BufferedWriter writer) {
|
|
int flags = 0;
|
|
if (lineStarts != null) {
|
|
flags |= hasLineStartsFlag;
|
|
}
|
|
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!);
|
|
}
|
|
}
|
|
|
|
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;
|
|
return new SourceFile(importUri, 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');
|
|
}
|
|
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);
|
|
}
|