// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. library vm.bytecode.declarations; import 'package:kernel/ast.dart' 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 currentBytecodeFormatVersion; 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; import 'source_positions.dart' show LineStarts, SourcePositions; import 'dart:typed_data' show Uint8List; class LibraryDeclaration extends BytecodeDeclaration { static const usesDartMirrorsFlag = 1 << 0; static const usesDartFfiFlag = 1 << 1; static const hasExtensionsFlag = 1 << 2; static const isNonNullableByDefaultFlag = 1 << 3; ObjectHandle importUri; final int flags; final ObjectHandle name; final ObjectHandle script; final List extensionUris; final List classes; LibraryDeclaration(this.importUri, this.flags, this.name, this.script, this.extensionUris, this.classes); void write(BufferedWriter writer) { final start = writer.offset; writer.writePackedUInt30(flags); writer.writePackedObject(name); writer.writePackedObject(script); if ((flags & hasExtensionsFlag) != 0) { writer.writePackedList(extensionUris); } 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 name = reader.readPackedObject(); final script = reader.readPackedObject(); final classes = List.generate(reader.readPackedUInt30(), (_) { final className = reader.readPackedObject(); return reader.readLinkOffset()..name = className; }); final extensionUris = ((flags & hasExtensionsFlag) != 0) ? reader.readPackedList() : const []; return new LibraryDeclaration( null, flags, name, script, extensionUris, 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'); } if ((flags & hasExtensionsFlag) != 0) { sb.writeln(' extensions: $extensionUris'); } if ((flags & isNonNullableByDefaultFlag) != 0) { sb.writeln(' is nnbd'); } 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; 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 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 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 superType = reader.readPackedObject(); final interfaces = reader.readPackedList(); final annotations = ((flags & hasAnnotationsFlag) != 0) ? reader.readLinkOffset() : null; final members = reader.readLinkOffset(); 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 importUri = reader.readPackedObject(); final lineStarts = ((flags & hasLineStartsFlag) != 0) ? reader.readLinkOffset() : 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 fields; final List 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.generate( reader.readPackedUInt30(), (_) => new FieldDeclaration.read(reader)); final functions = new List.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 hasNontrivialInitializerFlag = 1 << 0; static const hasGetterFlag = 1 << 1; static const hasSetterFlag = 1 << 2; static const isReflectableFlag = 1 << 3; static const isStaticFlag = 1 << 4; static const isConstFlag = 1 << 5; static const isFinalFlag = 1 << 6; static const isCovariantFlag = 1 << 7; static const isGenericCovariantImplFlag = 1 << 8; static const hasSourcePositionsFlag = 1 << 9; static const hasAnnotationsFlag = 1 << 10; static const hasPragmaFlag = 1 << 11; static const hasCustomScriptFlag = 1 << 12; static const hasInitializerCodeFlag = 1 << 13; static const hasAttributesFlag = 1 << 14; static const isLateFlag = 1 << 15; static const isExtensionMemberFlag = 1 << 16; static const hasInitializerFlag = 1 << 17; 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; final ObjectHandle attributes; FieldDeclaration( this.flags, this.name, this.type, this.value, this.script, this.position, this.endPosition, this.getterName, this.setterName, this.initializerCode, this.annotations, this.attributes); 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); } if ((flags & hasAttributesFlag) != 0) { writer.writePackedObject(attributes); } } factory FieldDeclaration.read(BufferedReader reader) { final flags = reader.readPackedUInt30(); final name = reader.readPackedObject(); final type = reader.readPackedObject(); final script = ((flags & hasCustomScriptFlag) != 0) ? reader.readPackedObject() : null; final position = ((flags & hasSourcePositionsFlag) != 0) ? reader.readPackedUInt30() - 1 : TreeNode.noOffset; final endPosition = ((flags & hasSourcePositionsFlag) != 0) ? reader.readPackedUInt30() - 1 : TreeNode.noOffset; final initializerCode = ((flags & hasInitializerCodeFlag) != 0) ? reader.readLinkOffset() : null; final value = ((flags & hasNontrivialInitializerFlag) == 0) ? reader.readPackedObject() : null; final getterName = ((flags & hasGetterFlag) != 0) ? reader.readPackedObject() : null; final setterName = ((flags & hasSetterFlag) != 0) ? reader.readPackedObject() : null; final annotations = ((flags & hasAnnotationsFlag) != 0) ? reader.readLinkOffset() : null; final attributes = ((flags & hasAttributesFlag) != 0) ? reader.readPackedObject() : null; return new FieldDeclaration( flags, name, type, value, script, position, endPosition, getterName, setterName, initializerCode, annotations, attributes); } @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'); } if ((flags & hasAttributesFlag) != 0) { sb.write(' attributes $attributes\n'); } return sb.toString(); } } class FunctionDeclaration { static const isConstructorFlag = 1 << 0; static const isGetterFlag = 1 << 1; static const isSetterFlag = 1 << 2; static const isFactoryFlag = 1 << 3; static const isStaticFlag = 1 << 4; static const isAbstractFlag = 1 << 5; static const isConstFlag = 1 << 6; static const hasOptionalPositionalParamsFlag = 1 << 7; static const hasOptionalNamedParamsFlag = 1 << 8; static const hasTypeParamsFlag = 1 << 9; static const isReflectableFlag = 1 << 10; static const isDebuggableFlag = 1 << 11; static const isAsyncFlag = 1 << 12; static const isAsyncStarFlag = 1 << 13; static const isSyncStarFlag = 1 << 14; static const isForwardingStubFlag = 1 << 15; static const isNoSuchMethodForwarderFlag = 1 << 16; static const isNativeFlag = 1 << 17; static const isExternalFlag = 1 << 18; static const hasSourcePositionsFlag = 1 << 19; static const hasAnnotationsFlag = 1 << 20; static const hasPragmaFlag = 1 << 21; static const hasCustomScriptFlag = 1 << 22; static const hasAttributesFlag = 1 << 23; static const isExtensionMemberFlag = 1 << 24; final int flags; final ObjectHandle name; final ObjectHandle script; final int position; final int endPosition; final TypeParametersDeclaration typeParameters; final int numRequiredParameters; final List parameters; final ObjectHandle returnType; final ObjectHandle nativeName; final Code code; final AnnotationsDeclaration annotations; final ObjectHandle attributes; FunctionDeclaration( this.flags, this.name, this.script, this.position, this.endPosition, this.typeParameters, this.numRequiredParameters, this.parameters, this.returnType, this.nativeName, this.code, this.annotations, this.attributes); void write(BufferedWriter writer) { writer.writePackedUInt30(flags); writer.writePackedObject(name); if ((flags & hasCustomScriptFlag) != 0) { writer.writePackedObject(script); } if ((flags & hasSourcePositionsFlag) != 0) { writer.writePackedUInt30(position + 1); writer.writePackedUInt30(endPosition + 1); } if ((flags & hasTypeParamsFlag) != 0) { typeParameters.write(writer); } writer.writePackedUInt30(parameters.length); if ((flags & hasOptionalPositionalParamsFlag) != 0 || (flags & hasOptionalNamedParamsFlag) != 0) { writer.writePackedUInt30(numRequiredParameters); } for (var param in parameters) { param.write(writer); } writer.writePackedObject(returnType); if ((flags & isNativeFlag) != 0) { writer.writePackedObject(nativeName); } if ((flags & isAbstractFlag) == 0) { writer.writeLinkOffset(code); } if ((flags & hasAnnotationsFlag) != 0) { writer.writeLinkOffset(annotations); } if ((flags & hasAttributesFlag) != 0) { writer.writePackedObject(attributes); } } factory FunctionDeclaration.read(BufferedReader reader) { final flags = reader.readPackedUInt30(); final name = reader.readPackedObject(); final script = ((flags & hasCustomScriptFlag) != 0) ? reader.readPackedObject() : null; final position = ((flags & hasSourcePositionsFlag) != 0) ? reader.readPackedUInt30() - 1 : 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.generate( numParameters, (_) => new ParameterDeclaration.read(reader)); final returnType = reader.readPackedObject(); final nativeName = ((flags & isNativeFlag) != 0) ? reader.readPackedObject() : null; final code = ((flags & isAbstractFlag) == 0) ? reader.readLinkOffset() : null; final annotations = ((flags & hasAnnotationsFlag) != 0) ? reader.readLinkOffset() : null; final attributes = ((flags & hasAttributesFlag) != 0) ? reader.readPackedObject() : null; return new FunctionDeclaration( flags, name, script, position, endPosition, typeParameters, numRequiredParameters, parameters, returnType, nativeName, code, annotations, attributes); } @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 & isForwardingStubFlag) != 0) { sb.write(', forwarding-stub'); } if ((flags & isNoSuchMethodForwarderFlag) != 0) { sb.write(', no-such-method-forwarder'); } if ((flags & isNativeFlag) != 0) { sb.write(', native $nativeName'); } if ((flags & isExternalFlag) != 0) { sb.write(', external'); } if ((flags & hasPragmaFlag) != 0) { sb.write(', has-pragma'); } if ((flags & hasCustomScriptFlag) != 0) { sb.write(', custom-script = $script'); } if ((flags & hasSourcePositionsFlag) != 0) { sb.write(', pos = $position, end-pos = $endPosition'); } sb.writeln(); if ((flags & hasTypeParamsFlag) != 0) { sb.write(' type-params $typeParameters\n'); } sb.write(' parameters $parameters (required: $numRequiredParameters)\n'); sb.write(' return-type $returnType\n'); if ((flags & hasAnnotationsFlag) != 0) { sb.write(' annotations $annotations\n'); } if ((flags & hasAttributesFlag) != 0) { sb.write(' attributes $attributes\n'); } if ((flags & isAbstractFlag) == 0 && (flags & isExternalFlag) == 0) { sb.write('\n$code\n'); } return sb.toString(); } } class TypeParametersDeclaration { final List typeParams; TypeParametersDeclaration(this.typeParams); void write(BufferedWriter writer) { writer.writePackedUInt30(typeParams.length); for (var tp in typeParams) { writer.writePackedObject(tp.name); } for (var tp in typeParams) { writer.writePackedObject(tp.type); } } factory TypeParametersDeclaration.read(BufferedReader reader) { final int numTypeParams = reader.readPackedUInt30(); List names = new List.generate( numTypeParams, (_) => reader.readPackedObject()); List bounds = new List.generate( numTypeParams, (_) => reader.readPackedObject()); return new TypeParametersDeclaration(new List.generate( numTypeParams, (int i) => new NameAndType(names[i], bounds[i]))); } @override int get hashCode => listHashCode(typeParams); @override bool operator ==(other) => other is TypeParametersDeclaration && listEquals(this.typeParams, other.typeParams); @override String toString() => '<${typeParams.join(', ')}>'; } class ParameterDeclaration { // Parameter flags are written separately (in Code). static const isCovariantFlag = 1 << 0; static const isGenericCovariantImplFlag = 1 << 1; static const isFinalFlag = 1 << 2; final ObjectHandle name; final ObjectHandle type; ParameterDeclaration(this.name, this.type); void write(BufferedWriter writer) { writer.writePackedObject(name); writer.writePackedObject(type); } factory ParameterDeclaration.read(BufferedReader reader) { final name = reader.readPackedObject(); final type = reader.readPackedObject(); return new ParameterDeclaration(name, type); } @override String toString() => '$type $name'; } class Code 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 nullableFields; final List closures; final List parameterFlags; final int forwardingStubTargetCpIndex; final int defaultFunctionTypeArgsCpIndex; bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty; bool get hasSourcePositions => sourcePositions != null && sourcePositions.isNotEmpty; bool get hasLocalVariables => localVariables != null && localVariables.isNotEmpty; bool get hasNullableFields => nullableFields.isNotEmpty; bool get hasClosures => closures.isNotEmpty; int get flags => (hasExceptionsTable ? hasExceptionsTableFlag : 0) | (hasSourcePositions ? hasSourcePositionsFlag : 0) | (hasNullableFields ? hasNullableFieldsFlag : 0) | (hasClosures ? hasClosuresFlag : 0) | (parameterFlags != null ? hasParameterFlagsFlag : 0) | (forwardingStubTargetCpIndex != null ? hasForwardingStubTargetFlag : 0) | (defaultFunctionTypeArgsCpIndex != null ? hasDefaultFunctionTypeArgsFlag : 0) | (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); if (parameterFlags != null) { writer.writePackedUInt30(parameterFlags.length); parameterFlags.forEach((flags) => writer.writePackedUInt30(flags)); } if (forwardingStubTargetCpIndex != null) { writer.writePackedUInt30(forwardingStubTargetCpIndex); } if (defaultFunctionTypeArgsCpIndex != null) { writer.writePackedUInt30(defaultFunctionTypeArgsCpIndex); } if (hasClosures) { writer.writePackedUInt30(closures.length); closures.forEach((c) => c.write(writer)); } constantPool.write(writer); _writeBytecodeInstructions(writer, bytecodes); if (hasExceptionsTable) { exceptionsTable.write(writer); } if (hasSourcePositions) { writer.writeLinkOffset(sourcePositions); } if (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.generate( reader.readPackedUInt30(), (_) => reader.readPackedUInt30()) : null; final forwardingStubTargetCpIndex = ((flags & hasForwardingStubTargetFlag) != 0) ? reader.readPackedUInt30() : null; final defaultFunctionTypeArgsCpIndex = ((flags & hasDefaultFunctionTypeArgsFlag) != 0) ? reader.readPackedUInt30() : null; final List closures = ((flags & hasClosuresFlag) != 0) ? new List.generate(reader.readPackedUInt30(), (_) => new ClosureDeclaration.read(reader)) : const []; 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() : null; final localVariables = ((flags & hasLocalVariablesFlag) != 0) ? reader.readLinkOffset() : null; final List nullableFields = ((flags & hasNullableFieldsFlag) != 0) ? reader.readPackedList() : const []; 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 {}, hasLocalVariables ? localVariables.getBytecodeAnnotations() : const {} ])}}\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 hasAttributesFlag = 1 << 8; int flags; final ObjectHandle parent; final ObjectHandle name; final int position; final int endPosition; final List typeParams; final int numRequiredParams; final int numNamedParams; final List parameters; final ObjectHandle returnType; ObjectHandle attributes; ClosureCode code; ClosureDeclaration( this.flags, this.parent, this.name, this.position, this.endPosition, this.typeParams, this.numRequiredParams, this.numNamedParams, this.parameters, this.returnType, [this.attributes]); 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) { writer.writePackedUInt30(typeParams.length); for (var tp in typeParams) { writer.writePackedObject(tp.name); } for (var tp in typeParams) { writer.writePackedObject(tp.type); } } writer.writePackedUInt30(parameters.length); if (flags & (hasOptionalPositionalParamsFlag | hasOptionalNamedParamsFlag) != 0) { writer.writePackedUInt30(numRequiredParams); } for (var param in parameters) { writer.writePackedObject(param.name); writer.writePackedObject(param.type); } writer.writePackedObject(returnType); if ((flags & hasAttributesFlag) != 0) { writer.writePackedObject(attributes); } } factory ClosureDeclaration.read(BufferedReader reader) { final int flags = reader.readPackedUInt30(); final parent = reader.readPackedObject(); final name = reader.readPackedObject(); final position = ((flags & hasSourcePositionsFlag) != 0) ? reader.readPackedUInt30() - 1 : TreeNode.noOffset; final endPosition = ((flags & hasSourcePositionsFlag) != 0) ? reader.readPackedUInt30() - 1 : TreeNode.noOffset; List typeParams; if ((flags & hasTypeParamsFlag) != 0) { final int numTypeParams = reader.readPackedUInt30(); List names = new List.generate( numTypeParams, (_) => reader.readPackedObject()); List bounds = new List.generate( numTypeParams, (_) => reader.readPackedObject()); typeParams = new List.generate( numTypeParams, (int i) => new NameAndType(names[i], bounds[i])); } else { typeParams = const []; } final numParams = reader.readPackedUInt30(); final numRequiredParams = (flags & (hasOptionalPositionalParamsFlag | hasOptionalNamedParamsFlag) != 0) ? reader.readPackedUInt30() : numParams; final numNamedParams = (flags & hasOptionalNamedParamsFlag != 0) ? (numParams - numRequiredParams) : 0; final List parameters = new List.generate( numParams, (_) => new NameAndType( reader.readPackedObject(), reader.readPackedObject())); final returnType = reader.readPackedObject(); final attributes = ((flags & hasAttributesFlag) != 0) ? reader.readPackedObject() : null; return new ClosureDeclaration( flags, parent, name, position, endPosition, typeParams, numRequiredParams, numNamedParams, parameters, returnType, attributes); } @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 (typeParams.isNotEmpty) { sb.write(' <${typeParams.join(', ')}>'); } sb.write(' ('); sb.write(parameters.sublist(0, numRequiredParams).join(', ')); if (numRequiredParams != parameters.length) { if (numRequiredParams > 0) { sb.write(', '); } if (numNamedParams > 0) { sb.write('{ ${parameters.sublist(numRequiredParams).join(', ')} }'); } else { sb.write('[ ${parameters.sublist(numRequiredParams).join(', ')} ]'); } } sb.write(') -> '); sb.writeln(returnType); if ((flags & hasAttributesFlag) != 0) { sb.write(' attributes $attributes\n'); } 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 != null && sourcePositions.isNotEmpty; bool get hasLocalVariables => localVariables != null && localVariables.isNotEmpty; 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() : null; final localVariables = ((flags & hasLocalVariablesFlag) != 0) ? reader.readLinkOffset() : 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 {}, hasLocalVariables ? localVariables.getBytecodeAnnotations() : const {} ])); 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 = 0x44424332; // 'DBC2' static const int numSections = 14; static const int sectionAlignment = 4; // UInt32 magic, version, numSections x (numItems, offset) static const int headerSize = (2 + numSections * 2) * 4; int version; StringTable stringTable; ObjectTable objectTable; final List libraries = []; final List classes = []; final List members = []; final List codes = []; final List sourcePositions = []; final List lineStarts = []; final List sourceFiles = []; final Map uriToSource = {}; final List localVariables = []; final List annotations = []; Set protectedNames; ObjectHandle mainLibrary; Component(this.version, 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 protectedNamesWriter = new BufferedWriter.fromWriter(writer); if (protectedNames != null && protectedNames.isNotEmpty) { for (var name in protectedNames) { protectedNamesWriter.writePackedStringReference(name); } } 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 (mainLibrary != null) { mainWriter = new BufferedWriter.fromWriter(writer); mainWriter.writePackedObject(mainLibrary); } 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), new _Section(protectedNames != null ? protectedNames.length : 0, protectedNamesWriter), ]; 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(version); for (var section in sections) { writer.writeUInt32(section.numItems); writer.writeUInt32(section.offset); } assert(writer.offset - start == headerSize); for (var section in sections) { if (section.writer != null) { assert(writer.offset - start == section.offset); writer.appendWriter(section.writer); } } BytecodeSizeStatistics.componentSize += (writer.offset - start); } Component.read(BufferedReader reader) { final int start = reader.offset; final int magic = reader.readUInt32(); if (magic != magicValue) { throw 'Error: unexpected bytecode magic $magic'; } version = reader.readUInt32(); if (version != currentBytecodeFormatVersion) { throw 'Error: unexpected bytecode format version $version'; } reader.formatVersion = version; reader.readUInt32(); final stringTableOffset = reader.readUInt32(); reader.readUInt32(); final objectTableOffset = reader.readUInt32(); reader.readUInt32(); final mainOffset = reader.readUInt32(); final 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(); final protectedNamesNum = reader.readUInt32(); final protectedNamesOffset = 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. if (protectedNamesNum != 0) { protectedNames = new Set(); reader.offset = start + protectedNamesOffset; for (int i = 0; i < protectedNamesNum; ++i) { protectedNames.add(reader.readPackedStringReference()); } } 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 importUri = reader.readPackedObject(); final library = reader.readLinkOffset(); library.importUri = importUri; } if (mainOffset != 0) { reader.offset = start + mainOffset; mainLibrary = reader.readPackedObject(); } } @override String toString() { final StringBuffer sb = new StringBuffer(); sb.write("Bytecode (version: "); if (version == currentBytecodeFormatVersion) { sb.write("stable"); } else { sb.write("v$version"); } sb.writeln(")"); if (mainLibrary != null) { sb.writeln("Main library: $mainLibrary"); } 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); }