| // 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 'local_vars.dart' show AwaiterLink; | |
| import 'object_table.dart' | |
| show | |
| ObjectTable, | |
| ObjectHandle, | |
| NameAndType, | |
| ParameterFlags, | |
| TypeParameterDeclaration; | |
| import 'source_positions.dart' | |
| show LineStarts, RecordedCoverageArray, 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; | |
| static const isDeeplyImmutableFlag = 1 << 14; | |
| 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 & isDeeplyImmutableFlag) != 0) { | |
| sb.write(', deeply-immutable'); | |
| } | |
| if ((flags & hasSourcePositionsFlag) != 0) { | |
| sb.write(', pos = $position, end-pos = $endPosition'); | |
| } | |
| sb.writeln(); | |
| if ((flags & hasTypeParamsFlag) != 0) { | |
| sb.write(' type-params $typeParameters (args: $numTypeArguments)\n'); | |
| } | |
| if (superType != null) { | |
| sb.write(' extends $superType\n'); | |
| } | |
| if (interfaces.isNotEmpty) { | |
| sb.write(' implements $interfaces\n'); | |
| } | |
| if ((flags & hasAnnotationsFlag) != 0) { | |
| sb.write(' annotations $annotations\n'); | |
| } | |
| sb.writeln(); | |
| sb.write(members.toString()); | |
| return sb.toString(); | |
| } | |
| } | |
| class SourceFile extends BytecodeDeclaration { | |
| static const hasLineStartsFlag = 1 << 0; | |
| static const hasSourceFlag = 1 << 1; | |
| static const hasConstConstructorCoverageFlag = 1 << 2; | |
| final ObjectHandle importUri; | |
| LineStarts? lineStarts; | |
| String? source; | |
| List<ObjectHandle>? coveredConstConstructors; | |
| SourceFile( | |
| this.importUri, [ | |
| this.coveredConstConstructors, | |
| this.lineStarts, | |
| this.source, | |
| ]); | |
| void write(BufferedWriter writer) { | |
| int flags = 0; | |
| if (lineStarts != null) { | |
| flags |= hasLineStartsFlag; | |
| } | |
| if (coveredConstConstructors?.isNotEmpty ?? false) { | |
| flags |= hasConstConstructorCoverageFlag; | |
| } | |
| if (source != null && source != '') { | |
| flags |= hasSourceFlag; | |
| } | |
| writer.writePackedUInt30(flags); | |
| writer.writePackedObject(importUri); | |
| if ((flags & hasLineStartsFlag) != 0) { | |
| writer.writeLinkOffset(lineStarts!); | |
| } | |
| if ((flags & hasSourceFlag) != 0) { | |
| writer.writePackedStringReference(source!); | |
| } | |
| if ((flags & hasConstConstructorCoverageFlag) != 0) { | |
| writer.writePackedList(coveredConstConstructors!); | |
| } | |
| } | |
| factory SourceFile.read(BufferedReader reader) { | |
| final flags = reader.readPackedUInt30(); | |
| final ObjectHandle importUri = reader.readPackedObject(); | |
| final lineStarts = ((flags & hasLineStartsFlag) != 0) | |
| ? reader.readLinkOffset<LineStarts>() | |
| : null; | |
| final source = ((flags & hasSourceFlag) != 0) | |
| ? reader.readPackedStringReference() | |
| : null; | |
| final coveredConstConstructors = | |
| ((flags & hasConstConstructorCoverageFlag) != 0) | |
| ? reader.readPackedList<ObjectHandle>() | |
| : null; | |
| return new SourceFile( | |
| importUri, | |
| coveredConstConstructors, | |
| lineStarts, | |
| source, | |
| ); | |
| } | |
| @override | |
| String toString() { | |
| final StringBuffer sb = new StringBuffer(); | |
| sb.write('source: import-uri $importUri'); | |
| if (source != null && source != '') { | |
| sb.write(', ${source!.length} text chars'); | |
| } | |
| if (lineStarts != null) { | |
| sb.write(', ${lineStarts!.lineStarts.length} line starts'); | |
| } | |
| if (coveredConstConstructors?.isNotEmpty ?? false) { | |
| sb.write(', covered const constructors: ['); | |
| sb.writeAll(coveredConstConstructors!, ', '); | |
| sb.write(']'); | |
| } | |
| return sb.toString(); | |
| } | |
| } | |
| class Members extends BytecodeDeclaration { | |
| final List<FieldDeclaration> fields; | |
| final List<FunctionDeclaration> functions; | |
| Members(this.fields, this.functions); | |
| int countFunctions() { | |
| int count = functions.length; | |
| for (var field in fields) { | |
| if ((field.flags & FieldDeclaration.hasGetterFlag) != 0) { | |
| ++count; | |
| } | |
| if ((field.flags & FieldDeclaration.hasSetterFlag) != 0) { | |
| ++count; | |
| } | |
| } | |
| return count; | |
| } | |
| void write(BufferedWriter writer) { | |
| final start = writer.offset; | |
| writer.writePackedUInt30(countFunctions()); | |
| writer.writePackedUInt30(fields.length); | |
| for (var field in fields) { | |
| field.write(writer); | |
| } | |
| writer.writePackedUInt30(functions.length); | |
| for (var func in functions) { | |
| func.write(writer); | |
| } | |
| BytecodeSizeStatistics.membersSize += (writer.offset - start); | |
| } | |
| factory Members.read(BufferedReader reader) { | |
| reader.readPackedUInt30(); // numFunctions | |
| final fields = new List<FieldDeclaration>.generate( | |
| reader.readPackedUInt30(), | |
| (_) => new FieldDeclaration.read(reader), | |
| ); | |
| final functions = new List<FunctionDeclaration>.generate( | |
| reader.readPackedUInt30(), | |
| (_) => new FunctionDeclaration.read(reader), | |
| ); | |
| return new Members(fields, functions); | |
| } | |
| @override | |
| String toString() => | |
| "${fields.join('\n')}\n" | |
| "${functions.join('\n')}"; | |
| } | |
| class FieldDeclaration { | |
| static const isStaticFlag = 1 << 0; | |
| static const isConstFlag = 1 << 1; | |
| static const isFinalFlag = 1 << 2; | |
| static const isLateFlag = 1 << 3; | |
| static const isCovariantFlag = 1 << 4; | |
| static const isCovariantByClassFlag = 1 << 5; | |
| static const isExtensionMemberFlag = 1 << 6; | |
| static const isReflectableFlag = 1 << 7; | |
| static const hasGetterFlag = 1 << 8; | |
| static const hasSetterFlag = 1 << 9; | |
| static const hasInitializerFlag = 1 << 10; | |
| static const hasNontrivialInitializerFlag = 1 << 11; | |
| static const hasInitializerCodeFlag = 1 << 12; | |
| static const hasSourcePositionsFlag = 1 << 13; | |
| static const hasAnnotationsFlag = 1 << 14; | |
| static const hasPragmaFlag = 1 << 15; | |
| static const hasCustomScriptFlag = 1 << 16; | |
| static const isExtensionTypeMemberFlag = 1 << 17; | |
| static const isShared = 1 << 18; | |
| 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 & isExtensionTypeMemberFlag) != 0) { | |
| sb.write(', extension-type-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'); | |
| } | |
| if ((flags & isShared) != 0) { | |
| sb.write(', shared'); | |
| } | |
| 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; | |
| static const isExtensionTypeMemberFlag = 1 << 24; | |
| static const isInvisibleFlag = 1 << 25; | |
| 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 & isExtensionTypeMemberFlag) != 0) { | |
| sb.write(', extension-type-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 & isInvisibleFlag) != 0) { | |
| sb.write(', invisible'); | |
| } | |
| 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; | |
| static const hasRecordedCoverageFlag = 1 << 8; | |
| final ConstantPool constantPool; | |
| final Uint8List bytecodes; | |
| final ExceptionsTable exceptionsTable; | |
| final SourcePositions? sourcePositions; | |
| final LocalVariableTable? localVariables; | |
| final RecordedCoverageArray? recordedCoverage; | |
| 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 hasRecordedCoverage => recordedCoverage?.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) | | |
| (hasRecordedCoverage ? hasRecordedCoverageFlag : 0); | |
| Code( | |
| this.constantPool, | |
| this.bytecodes, | |
| this.exceptionsTable, | |
| this.sourcePositions, | |
| this.localVariables, | |
| this.recordedCoverage, | |
| 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 (hasRecordedCoverage) { | |
| writer.writeLinkOffset(recordedCoverage!); | |
| } | |
| 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 recordedCoverage = ((flags & hasRecordedCoverageFlag) != 0) | |
| ? reader.readLinkOffset<RecordedCoverageArray>() | |
| : 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, | |
| recordedCoverage, | |
| 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" | |
| "${recordedCoverage == null ? '' : 'Coverage array: $recordedCoverage\n'}" | |
| "${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; | |
| static const isInvisibleFlag = 1 << 11; | |
| 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 & isInvisibleFlag) != 0) { | |
| sb.write(' invisible'); | |
| } | |
| 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; | |
| static const capturesOnlyFinalNotLateVarsFlag = 1 << 3; | |
| static const hasLocalFunctionIdFlag = 1 << 4; | |
| static const hasRecordedCoverageFlag = 1 << 5; | |
| static const hasAwaiterLinkFlag = 1 << 6; | |
| final Uint8List bytecodes; | |
| final ExceptionsTable exceptionsTable; | |
| final SourcePositions? sourcePositions; | |
| final LocalVariableTable? localVariables; | |
| final RecordedCoverageArray? recordedCoverage; | |
| final AwaiterLink? awaiterLink; | |
| final bool capturesOnlyFinalNotLateVars; | |
| final int localFunctionId; | |
| bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty; | |
| bool get hasSourcePositions => sourcePositions?.isNotEmpty ?? false; | |
| bool get hasLocalVariables => localVariables?.isNotEmpty ?? false; | |
| bool get hasRecordedCoverage => recordedCoverage?.isNotEmpty ?? false; | |
| bool get hasLocalFunctionId => localFunctionId > 0; | |
| bool get hasAwaiterLink => awaiterLink != null; | |
| int get flags => | |
| (hasExceptionsTable ? hasExceptionsTableFlag : 0) | | |
| (hasSourcePositions ? hasSourcePositionsFlag : 0) | | |
| (hasLocalVariables ? hasLocalVariablesFlag : 0) | | |
| (capturesOnlyFinalNotLateVars ? capturesOnlyFinalNotLateVarsFlag : 0) | | |
| (hasLocalFunctionId ? hasLocalFunctionIdFlag : 0) | | |
| (hasAwaiterLink ? hasAwaiterLinkFlag : 0) | | |
| (hasRecordedCoverage ? hasRecordedCoverageFlag : 0); | |
| ClosureCode( | |
| this.bytecodes, | |
| this.exceptionsTable, | |
| this.sourcePositions, | |
| this.localVariables, | |
| this.recordedCoverage, | |
| this.awaiterLink, | |
| this.capturesOnlyFinalNotLateVars, | |
| this.localFunctionId, | |
| ); | |
| void write(BufferedWriter writer) { | |
| writer.writePackedUInt30(flags); | |
| if (hasLocalFunctionId) { | |
| writer.writePackedUInt30(localFunctionId); | |
| } | |
| if (hasAwaiterLink) { | |
| final (:depth, :index) = awaiterLink!; | |
| writer.writePackedUInt30(depth); | |
| writer.writePackedUInt30(index); | |
| } | |
| _writeBytecodeInstructions(writer, bytecodes); | |
| if (hasExceptionsTable) { | |
| exceptionsTable.write(writer); | |
| } | |
| if (hasSourcePositions) { | |
| writer.writeLinkOffset(sourcePositions!); | |
| } | |
| if (hasLocalVariables) { | |
| writer.writeLinkOffset(localVariables!); | |
| } | |
| if (hasRecordedCoverage) { | |
| writer.writeLinkOffset(recordedCoverage!); | |
| } | |
| } | |
| factory ClosureCode.read(BufferedReader reader) { | |
| final int flags = reader.readPackedUInt30(); | |
| final localFunctionId = ((flags & hasLocalFunctionIdFlag) != 0) | |
| ? reader.readPackedUInt30() | |
| : -1; | |
| final awaiterLink = (flags & hasAwaiterLinkFlag != 0) | |
| ? (depth: reader.readPackedUInt30(), index: reader.readPackedUInt30()) | |
| : null; | |
| 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 recordedCoverage = ((flags & hasRecordedCoverageFlag) != 0) | |
| ? reader.readLinkOffset<RecordedCoverageArray>() | |
| : null; | |
| final capturesOnlyFinalNotLateVars = | |
| (flags & capturesOnlyFinalNotLateVarsFlag) != 0; | |
| return new ClosureCode( | |
| bytecodes, | |
| exceptionsTable, | |
| sourcePositions, | |
| localVariables, | |
| recordedCoverage, | |
| awaiterLink, | |
| capturesOnlyFinalNotLateVars, | |
| localFunctionId, | |
| ); | |
| } | |
| @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>{}, | |
| ], | |
| ), | |
| ); | |
| if (hasRecordedCoverage) { | |
| sb.writeln('Coverage array: $recordedCoverage'); | |
| } | |
| if (hasAwaiterLink) { | |
| sb.writeln('Awaiter link: $awaiterLink'); | |
| } | |
| 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 = 14; | |
| 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<RecordedCoverageArray> recordedCoverage = | |
| <RecordedCoverageArray>[]; | |
| 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 recordedCoverageWriter = new BufferedWriter.fromWriter(writer); | |
| for (var rc in recordedCoverage) { | |
| writer.linkWriter.put(rc, recordedCoverageWriter.offset); | |
| rc.write(recordedCoverageWriter); | |
| } | |
| BytecodeSizeStatistics.recordedCoverageSize += | |
| recordedCoverageWriter.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(recordedCoverage.length, recordedCoverageWriter), | |
| new _Section(annotations.length, annotationsWriter), | |
| ]; | |
| if (sections.length != numSections) { | |
| throw StateError( | |
| 'Expected $numSections sections, got ${sections.length}', | |
| ); | |
| } | |
| 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 recordedCoverageNum = reader.readUInt32(); | |
| final recordedCoverageOffset = 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 recordedCoverageStart = start + recordedCoverageOffset; | |
| reader.offset = recordedCoverageStart; | |
| for (int i = 0; i < recordedCoverageNum; ++i) { | |
| int offset = reader.offset - recordedCoverageStart; | |
| RecordedCoverageArray rc = new RecordedCoverageArray.read(reader); | |
| reader.linkReader.setOffset(rc, offset); | |
| recordedCoverage.add(rc); | |
| } | |
| 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 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 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); | |
| } |