| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| library vm.bytecode.declarations; | |
| import 'package:kernel/ast.dart'; | |
| import 'bytecode_serialization.dart' | |
| show BufferedWriter, BufferedReader, BytecodeSizeStatistics, StringTable; | |
| import 'constant_pool.dart' show ConstantPool; | |
| import 'dbc.dart' | |
| show currentBytecodeFormatVersion, futureBytecodeFormatVersion; | |
| import 'disassembler.dart' show BytecodeDisassembler; | |
| import 'exceptions.dart' show ExceptionsTable; | |
| import 'object_table.dart' show ObjectTable, ObjectHandle, NameAndType; | |
| import 'source_positions.dart' show SourcePositions; | |
| class Members { | |
| final List<FieldDeclaration> fields; | |
| final List<FunctionDeclaration> functions; | |
| Members(this.fields, this.functions); | |
| int countFunctions() { | |
| int count = functions.length; | |
| for (var field in fields) { | |
| if ((field.flags & FieldDeclaration.hasGetterFlag) != 0) { | |
| ++count; | |
| } | |
| if ((field.flags & FieldDeclaration.hasSetterFlag) != 0) { | |
| ++count; | |
| } | |
| } | |
| return count; | |
| } | |
| void write(BufferedWriter writer) { | |
| writer.writePackedUInt30(countFunctions()); | |
| writer.writePackedUInt30(fields.length); | |
| for (var field in fields) { | |
| field.write(writer); | |
| } | |
| writer.writePackedUInt30(functions.length); | |
| for (var func in functions) { | |
| func.write(writer); | |
| } | |
| } | |
| factory Members.read(BufferedReader reader) { | |
| reader.readPackedUInt30(); // numFunctions | |
| final fields = new List<FieldDeclaration>.generate( | |
| reader.readPackedUInt30(), (_) => new FieldDeclaration.read(reader)); | |
| final functions = new List<FunctionDeclaration>.generate( | |
| reader.readPackedUInt30(), (_) => new FunctionDeclaration.read(reader)); | |
| return new Members(fields, functions); | |
| } | |
| @override | |
| String toString() => "\n" | |
| "Members {\n" | |
| "${fields.join('\n')}\n" | |
| "${functions.join('\n')}" | |
| "}\n"; | |
| } | |
| class FieldDeclaration { | |
| static const hasInitializerFlag = 1 << 0; | |
| static const hasGetterFlag = 1 << 1; | |
| static const hasSetterFlag = 1 << 2; | |
| static const isReflectableFlag = 1 << 3; | |
| static const isStaticFlag = 1 << 4; | |
| static const isConstFlag = 1 << 5; | |
| static const isFinalFlag = 1 << 6; | |
| static const isCovariantFlag = 1 << 7; | |
| static const isGenericCovariantImplFlag = 1 << 8; | |
| static const hasSourcePositionsFlag = 1 << 9; | |
| static const hasAnnotationsFlag = 1 << 10; | |
| static const hasPragmaFlag = 1 << 11; | |
| static const hasCustomScriptFlag = 1 << 12; | |
| final int flags; | |
| final ObjectHandle name; | |
| final ObjectHandle type; | |
| final ObjectHandle value; | |
| final ObjectHandle script; | |
| final int position; | |
| final int endPosition; | |
| final ObjectHandle getterName; | |
| final ObjectHandle setterName; | |
| final Code initializerCode; | |
| final ObjectHandle annotations; | |
| FieldDeclaration( | |
| this.flags, | |
| this.name, | |
| this.type, | |
| this.value, | |
| this.script, | |
| this.position, | |
| this.endPosition, | |
| this.getterName, | |
| this.setterName, | |
| this.initializerCode, | |
| this.annotations); | |
| void write(BufferedWriter writer) { | |
| writer.writePackedUInt30(flags); | |
| writer.writePackedObject(name); | |
| writer.writePackedObject(type); | |
| if ((flags & hasCustomScriptFlag) != 0) { | |
| writer.writePackedObject(script); | |
| } | |
| if ((flags & hasSourcePositionsFlag) != 0) { | |
| writer.writePackedUInt30(position + 1); | |
| writer.writePackedUInt30(endPosition + 1); | |
| } | |
| if ((flags & hasInitializerFlag) != 0 && (flags & isStaticFlag) != 0) { | |
| writer.writeLinkOffset(initializerCode); | |
| } | |
| if ((flags & hasInitializerFlag) == 0) { | |
| writer.writePackedObject(value); | |
| } | |
| if ((flags & hasGetterFlag) != 0) { | |
| writer.writePackedObject(getterName); | |
| } | |
| if ((flags & hasSetterFlag) != 0) { | |
| writer.writePackedObject(setterName); | |
| } | |
| if ((flags & hasAnnotationsFlag) != 0) { | |
| writer.writeLinkOffset(annotations); | |
| } | |
| } | |
| factory FieldDeclaration.read(BufferedReader reader) { | |
| final flags = reader.readPackedUInt30(); | |
| final name = reader.readPackedObject(); | |
| final type = reader.readPackedObject(); | |
| final script = | |
| ((flags & hasCustomScriptFlag) != 0) ? reader.readPackedObject() : null; | |
| final position = ((flags & hasSourcePositionsFlag) != 0) | |
| ? reader.readPackedUInt30() - 1 | |
| : TreeNode.noOffset; | |
| final endPosition = ((flags & hasSourcePositionsFlag) != 0) | |
| ? reader.readPackedUInt30() - 1 | |
| : TreeNode.noOffset; | |
| final initializerCode = | |
| ((flags & hasInitializerFlag) != 0 && (flags & isStaticFlag) != 0) | |
| ? reader.readLinkOffset<Code>() | |
| : null; | |
| final value = | |
| ((flags & hasInitializerFlag) == 0) ? reader.readPackedObject() : null; | |
| final getterName = | |
| ((flags & hasGetterFlag) != 0) ? reader.readPackedObject() : null; | |
| final setterName = | |
| ((flags & hasSetterFlag) != 0) ? reader.readPackedObject() : null; | |
| final annotations = ((flags & hasAnnotationsFlag) != 0) | |
| ? reader.readLinkOffset<ObjectHandle>() | |
| : null; | |
| return new FieldDeclaration(flags, name, type, value, script, position, | |
| endPosition, getterName, setterName, initializerCode, annotations); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.write('Field $name, type = $type'); | |
| if ((flags & hasGetterFlag) != 0) { | |
| sb.write(', getter = $getterName'); | |
| } | |
| if ((flags & hasSetterFlag) != 0) { | |
| sb.write(', setter = $setterName'); | |
| } | |
| if ((flags & isReflectableFlag) != 0) { | |
| sb.write(', reflectable'); | |
| } | |
| if ((flags & isStaticFlag) != 0) { | |
| sb.write(', static'); | |
| } | |
| if ((flags & isConstFlag) != 0) { | |
| sb.write(', const'); | |
| } | |
| if ((flags & isFinalFlag) != 0) { | |
| sb.write(', final'); | |
| } | |
| if ((flags & hasPragmaFlag) != 0) { | |
| sb.write(', has-pragma'); | |
| } | |
| if ((flags & hasCustomScriptFlag) != 0) { | |
| sb.write(', custom-script = $script'); | |
| } | |
| if ((flags & hasSourcePositionsFlag) != 0) { | |
| sb.write(', pos = $position, end-pos = $endPosition'); | |
| } | |
| sb.writeln(); | |
| if ((flags & hasInitializerFlag) != 0) { | |
| sb.write(' initializer $initializerCode\n'); | |
| } else { | |
| sb.write(' value = $value\n'); | |
| } | |
| if ((flags & hasAnnotationsFlag) != 0) { | |
| sb.write(' annotations $annotations\n'); | |
| } | |
| return sb.toString(); | |
| } | |
| } | |
| class FunctionDeclaration { | |
| static const isConstructorFlag = 1 << 0; | |
| static const isGetterFlag = 1 << 1; | |
| static const isSetterFlag = 1 << 2; | |
| static const isFactoryFlag = 1 << 3; | |
| static const isStaticFlag = 1 << 4; | |
| static const isAbstractFlag = 1 << 5; | |
| static const isConstFlag = 1 << 6; | |
| static const hasOptionalPositionalParamsFlag = 1 << 7; | |
| static const hasOptionalNamedParamsFlag = 1 << 8; | |
| static const hasTypeParamsFlag = 1 << 9; | |
| static const isReflectableFlag = 1 << 10; | |
| static const isDebuggableFlag = 1 << 11; | |
| static const isAsyncFlag = 1 << 12; | |
| static const isAsyncStarFlag = 1 << 13; | |
| static const isSyncStarFlag = 1 << 14; | |
| static const isForwardingStubFlag = 1 << 15; | |
| static const isNoSuchMethodForwarderFlag = 1 << 16; | |
| static const isNativeFlag = 1 << 17; | |
| static const isExternalFlag = 1 << 18; | |
| static const hasSourcePositionsFlag = 1 << 19; | |
| static const hasAnnotationsFlag = 1 << 20; | |
| static const hasPragmaFlag = 1 << 21; | |
| static const hasCustomScriptFlag = 1 << 22; | |
| final int flags; | |
| final ObjectHandle name; | |
| final ObjectHandle script; | |
| final int position; | |
| final int endPosition; | |
| final TypeParametersDeclaration typeParameters; | |
| final int numRequiredParameters; | |
| final List<ParameterDeclaration> parameters; | |
| final ObjectHandle returnType; | |
| final ObjectHandle nativeName; | |
| final Code code; | |
| final ObjectHandle annotations; | |
| FunctionDeclaration( | |
| this.flags, | |
| this.name, | |
| this.script, | |
| this.position, | |
| this.endPosition, | |
| this.typeParameters, | |
| this.numRequiredParameters, | |
| this.parameters, | |
| this.returnType, | |
| this.nativeName, | |
| this.code, | |
| this.annotations); | |
| void write(BufferedWriter writer) { | |
| writer.writePackedUInt30(flags); | |
| writer.writePackedObject(name); | |
| if ((flags & hasCustomScriptFlag) != 0) { | |
| writer.writePackedObject(script); | |
| } | |
| if ((flags & hasSourcePositionsFlag) != 0) { | |
| writer.writePackedUInt30(position + 1); | |
| writer.writePackedUInt30(endPosition + 1); | |
| } | |
| if ((flags & hasTypeParamsFlag) != 0) { | |
| typeParameters.write(writer); | |
| } | |
| writer.writePackedUInt30(parameters.length); | |
| if ((flags & hasOptionalPositionalParamsFlag) != 0 || | |
| (flags & hasOptionalNamedParamsFlag) != 0) { | |
| writer.writePackedUInt30(numRequiredParameters); | |
| } | |
| for (var param in parameters) { | |
| param.write(writer); | |
| } | |
| writer.writePackedObject(returnType); | |
| if ((flags & isNativeFlag) != 0) { | |
| writer.writePackedObject(nativeName); | |
| } | |
| if ((flags & isAbstractFlag) == 0) { | |
| writer.writeLinkOffset(code); | |
| } | |
| if ((flags & hasAnnotationsFlag) != 0) { | |
| writer.writeLinkOffset(annotations); | |
| } | |
| } | |
| factory FunctionDeclaration.read(BufferedReader reader) { | |
| final flags = reader.readPackedUInt30(); | |
| final name = reader.readPackedObject(); | |
| final script = | |
| ((flags & hasCustomScriptFlag) != 0) ? reader.readPackedObject() : null; | |
| final position = ((flags & hasSourcePositionsFlag) != 0) | |
| ? reader.readPackedUInt30() - 1 | |
| : 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 returnType = reader.readPackedObject(); | |
| final nativeName = | |
| ((flags & isNativeFlag) != 0) ? reader.readPackedObject() : null; | |
| final code = | |
| ((flags & isAbstractFlag) == 0) ? reader.readLinkOffset<Code>() : null; | |
| final annotations = ((flags & hasAnnotationsFlag) != 0) | |
| ? reader.readLinkOffset<ObjectHandle>() | |
| : null; | |
| return new FunctionDeclaration( | |
| flags, | |
| name, | |
| script, | |
| position, | |
| endPosition, | |
| typeParameters, | |
| numRequiredParameters, | |
| parameters, | |
| returnType, | |
| nativeName, | |
| code, | |
| annotations); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.write('Function $name'); | |
| if ((flags & isConstructorFlag) != 0) { | |
| sb.write(', constructor'); | |
| } | |
| if ((flags & isGetterFlag) != 0) { | |
| sb.write(', getter'); | |
| } | |
| if ((flags & isSetterFlag) != 0) { | |
| sb.write(', setter'); | |
| } | |
| if ((flags & isFactoryFlag) != 0) { | |
| sb.write(', factory'); | |
| } | |
| if ((flags & isStaticFlag) != 0) { | |
| sb.write(', static'); | |
| } | |
| if ((flags & isAbstractFlag) != 0) { | |
| sb.write(', abstract'); | |
| } | |
| if ((flags & isConstFlag) != 0) { | |
| sb.write(', const'); | |
| } | |
| if ((flags & hasOptionalPositionalParamsFlag) != 0) { | |
| sb.write(', has-optional-positional-params'); | |
| } | |
| if ((flags & hasOptionalNamedParamsFlag) != 0) { | |
| sb.write(', has-optional-named-params'); | |
| } | |
| if ((flags & isReflectableFlag) != 0) { | |
| sb.write(', reflectable'); | |
| } | |
| if ((flags & isDebuggableFlag) != 0) { | |
| sb.write(', debuggable'); | |
| } | |
| if ((flags & isAsyncFlag) != 0) { | |
| sb.write(', async'); | |
| } | |
| if ((flags & isAsyncStarFlag) != 0) { | |
| sb.write(', async*'); | |
| } | |
| if ((flags & isSyncStarFlag) != 0) { | |
| sb.write(', sync*'); | |
| } | |
| if ((flags & isForwardingStubFlag) != 0) { | |
| sb.write(', forwarding-stub'); | |
| } | |
| if ((flags & isNoSuchMethodForwarderFlag) != 0) { | |
| sb.write(', no-such-method-forwarder'); | |
| } | |
| if ((flags & isNativeFlag) != 0) { | |
| sb.write(', native $nativeName'); | |
| } | |
| if ((flags & isExternalFlag) != 0) { | |
| sb.write(', external'); | |
| } | |
| if ((flags & hasPragmaFlag) != 0) { | |
| sb.write(', has-pragma'); | |
| } | |
| if ((flags & hasCustomScriptFlag) != 0) { | |
| sb.write(', custom-script = $script'); | |
| } | |
| if ((flags & hasSourcePositionsFlag) != 0) { | |
| sb.write(', pos = $position, end-pos = $endPosition'); | |
| } | |
| sb.writeln(); | |
| if ((flags & hasTypeParamsFlag) != 0) { | |
| sb.write(' type-params $typeParameters\n'); | |
| } | |
| sb.write(' parameters $parameters (required: $numRequiredParameters)\n'); | |
| sb.write(' return-type $returnType\n'); | |
| if ((flags & hasAnnotationsFlag) != 0) { | |
| sb.write(' annotations $annotations\n'); | |
| } | |
| if ((flags & isAbstractFlag) == 0 && (flags & isExternalFlag) == 0) { | |
| sb.write('\n$code\n'); | |
| } | |
| return sb.toString(); | |
| } | |
| } | |
| class TypeParametersDeclaration { | |
| final List<NameAndType> typeParams; | |
| TypeParametersDeclaration(this.typeParams); | |
| void write(BufferedWriter writer) { | |
| writer.writePackedUInt30(typeParams.length); | |
| for (var tp in typeParams) { | |
| writer.writePackedObject(tp.name); | |
| } | |
| for (var tp in typeParams) { | |
| writer.writePackedObject(tp.type); | |
| } | |
| } | |
| factory TypeParametersDeclaration.read(BufferedReader reader) { | |
| final int numTypeParams = reader.readPackedUInt30(); | |
| List<ObjectHandle> names = new List<ObjectHandle>.generate( | |
| numTypeParams, (_) => reader.readPackedObject()); | |
| List<ObjectHandle> bounds = new List<ObjectHandle>.generate( | |
| numTypeParams, (_) => reader.readPackedObject()); | |
| return new TypeParametersDeclaration(new List<NameAndType>.generate( | |
| numTypeParams, (int i) => new NameAndType(names[i], bounds[i]))); | |
| } | |
| @override | |
| int get hashCode => listHashCode(typeParams); | |
| @override | |
| bool operator ==(other) => | |
| other is TypeParametersDeclaration && | |
| listEquals(this.typeParams, other.typeParams); | |
| @override | |
| String toString() => '<${typeParams.join(', ')}>'; | |
| } | |
| class ParameterDeclaration { | |
| // Parameter flags are written separately (in Code). | |
| static const isCovariantFlag = 1 << 0; | |
| static const isGenericCovariantImplFlag = 1 << 1; | |
| final ObjectHandle name; | |
| final ObjectHandle type; | |
| ParameterDeclaration(this.name, this.type); | |
| void write(BufferedWriter writer) { | |
| writer.writePackedObject(name); | |
| writer.writePackedObject(type); | |
| } | |
| factory ParameterDeclaration.read(BufferedReader reader) { | |
| final name = reader.readPackedObject(); | |
| final type = reader.readPackedObject(); | |
| return new ParameterDeclaration(name, type); | |
| } | |
| @override | |
| String toString() => '$type $name'; | |
| } | |
| class Code { | |
| static const hasExceptionsTableFlag = 1 << 0; | |
| static const hasSourcePositionsFlag = 1 << 1; | |
| static const hasNullableFieldsFlag = 1 << 2; | |
| static const hasClosuresFlag = 1 << 3; | |
| static const hasParameterFlagsFlag = 1 << 4; | |
| static const hasForwardingStubTargetFlag = 1 << 5; | |
| static const hasDefaultFunctionTypeArgsFlag = 1 << 6; | |
| final ConstantPool constantPool; | |
| final List<int> bytecodes; | |
| final ExceptionsTable exceptionsTable; | |
| final SourcePositions sourcePositions; | |
| final List<ObjectHandle> nullableFields; | |
| final List<ClosureDeclaration> closures; | |
| final List<int> parameterFlags; | |
| final int forwardingStubTargetCpIndex; | |
| final int defaultFunctionTypeArgsCpIndex; | |
| bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty; | |
| bool get hasSourcePositions => | |
| sourcePositions != null && sourcePositions.mapping.isNotEmpty; | |
| bool get hasNullableFields => nullableFields.isNotEmpty; | |
| bool get hasClosures => closures.isNotEmpty; | |
| int get flags => | |
| (hasExceptionsTable ? hasExceptionsTableFlag : 0) | | |
| (hasSourcePositions ? hasSourcePositionsFlag : 0) | | |
| (hasNullableFields ? hasNullableFieldsFlag : 0) | | |
| (hasClosures ? hasClosuresFlag : 0) | | |
| (parameterFlags != null ? hasParameterFlagsFlag : 0) | | |
| (forwardingStubTargetCpIndex != null ? hasForwardingStubTargetFlag : 0) | | |
| (defaultFunctionTypeArgsCpIndex != null | |
| ? hasDefaultFunctionTypeArgsFlag | |
| : 0); | |
| Code( | |
| this.constantPool, | |
| this.bytecodes, | |
| this.exceptionsTable, | |
| this.sourcePositions, | |
| this.nullableFields, | |
| this.closures, | |
| this.parameterFlags, | |
| this.forwardingStubTargetCpIndex, | |
| this.defaultFunctionTypeArgsCpIndex); | |
| void write(BufferedWriter writer) { | |
| final start = writer.offset; | |
| writer.writePackedUInt30(flags); | |
| if (parameterFlags != null) { | |
| writer.writePackedUInt30(parameterFlags.length); | |
| parameterFlags.forEach((flags) => writer.writePackedUInt30(flags)); | |
| } | |
| if (forwardingStubTargetCpIndex != null) { | |
| writer.writePackedUInt30(forwardingStubTargetCpIndex); | |
| } | |
| if (defaultFunctionTypeArgsCpIndex != null) { | |
| writer.writePackedUInt30(defaultFunctionTypeArgsCpIndex); | |
| } | |
| if (hasClosures) { | |
| writer.writePackedUInt30(closures.length); | |
| closures.forEach((c) => c.write(writer)); | |
| } | |
| constantPool.write(writer); | |
| _writeBytecodeInstructions(writer, bytecodes); | |
| if (hasExceptionsTable) { | |
| exceptionsTable.write(writer); | |
| } | |
| if (hasSourcePositions) { | |
| writer.writeLinkOffset(sourcePositions); | |
| } | |
| if (hasNullableFields) { | |
| writer.writePackedList(nullableFields); | |
| } | |
| if (hasClosures) { | |
| closures.forEach((c) => c.code.write(writer)); | |
| } | |
| BytecodeSizeStatistics.membersSize += (writer.offset - start); | |
| } | |
| factory Code.read(BufferedReader reader) { | |
| int flags = reader.readPackedUInt30(); | |
| final parameterFlags = ((flags & hasParameterFlagsFlag) != 0) | |
| ? new List<int>.generate( | |
| reader.readPackedUInt30(), (_) => reader.readPackedUInt30()) | |
| : null; | |
| final forwardingStubTargetCpIndex = | |
| ((flags & hasForwardingStubTargetFlag) != 0) | |
| ? reader.readPackedUInt30() | |
| : null; | |
| final defaultFunctionTypeArgsCpIndex = | |
| ((flags & hasDefaultFunctionTypeArgsFlag) != 0) | |
| ? reader.readPackedUInt30() | |
| : null; | |
| final List<ClosureDeclaration> closures = ((flags & hasClosuresFlag) != 0) | |
| ? new List<ClosureDeclaration>.generate(reader.readPackedUInt30(), | |
| (_) => new ClosureDeclaration.read(reader)) | |
| : const <ClosureDeclaration>[]; | |
| final ConstantPool constantPool = new ConstantPool.read(reader); | |
| final List<int> bytecodes = _readBytecodeInstructions(reader); | |
| final exceptionsTable = ((flags & hasExceptionsTableFlag) != 0) | |
| ? new ExceptionsTable.read(reader) | |
| : new ExceptionsTable(); | |
| final sourcePositions = ((flags & hasSourcePositionsFlag) != 0) | |
| ? reader.readLinkOffset<SourcePositions>() | |
| : null; | |
| final List<ObjectHandle> nullableFields = | |
| ((flags & hasNullableFieldsFlag) != 0) | |
| ? reader.readPackedList<ObjectHandle>() | |
| : const <ObjectHandle>[]; | |
| for (var c in closures) { | |
| c.code = new ClosureCode.read(reader); | |
| } | |
| return new Code( | |
| constantPool, | |
| bytecodes, | |
| exceptionsTable, | |
| sourcePositions, | |
| nullableFields, | |
| closures, | |
| parameterFlags, | |
| forwardingStubTargetCpIndex, | |
| defaultFunctionTypeArgsCpIndex); | |
| } | |
| // TODO(alexmarkov): Consider printing constant pool before bytecode. | |
| @override | |
| String toString() => "\n" | |
| "Bytecode {\n" | |
| "${new BytecodeDisassembler().disassemble(bytecodes, exceptionsTable, annotations: [ | |
| hasSourcePositions | |
| ? sourcePositions.getBytecodeAnnotations() | |
| : const <int, String>{} | |
| ])}}\n" | |
| "$exceptionsTable" | |
| "${nullableFields.isEmpty ? '' : 'Nullable fields: $nullableFields\n'}" | |
| "${parameterFlags == null ? '' : 'Parameter flags: $parameterFlags\n'}" | |
| "${forwardingStubTargetCpIndex == null ? '' : 'Forwarding stub target: CP#$forwardingStubTargetCpIndex\n'}" | |
| "${defaultFunctionTypeArgsCpIndex == null ? '' : 'Default function type arguments: CP#$defaultFunctionTypeArgsCpIndex\n'}" | |
| "$constantPool" | |
| "${closures.join('\n')}"; | |
| } | |
| class ClosureDeclaration { | |
| static const int hasOptionalPositionalParamsFlag = 1 << 0; | |
| static const int hasOptionalNamedParamsFlag = 1 << 1; | |
| static const int hasTypeParamsFlag = 1 << 2; | |
| static const int hasSourcePositionsFlag = 1 << 3; | |
| final ObjectHandle parent; | |
| final ObjectHandle name; | |
| final int position; | |
| final int endPosition; | |
| final List<NameAndType> typeParams; | |
| final int numRequiredParams; | |
| final int numNamedParams; | |
| final List<NameAndType> parameters; | |
| final ObjectHandle returnType; | |
| ClosureCode code; | |
| ClosureDeclaration( | |
| this.parent, | |
| this.name, | |
| this.position, | |
| this.endPosition, | |
| this.typeParams, | |
| this.numRequiredParams, | |
| this.numNamedParams, | |
| this.parameters, | |
| this.returnType); | |
| void write(BufferedWriter writer) { | |
| int flags = 0; | |
| if (numRequiredParams != parameters.length) { | |
| if (numNamedParams > 0) { | |
| flags |= hasOptionalNamedParamsFlag; | |
| } else { | |
| flags |= hasOptionalPositionalParamsFlag; | |
| } | |
| } | |
| if (typeParams.isNotEmpty) { | |
| flags |= hasTypeParamsFlag; | |
| } | |
| if (position != TreeNode.noOffset) { | |
| flags |= hasSourcePositionsFlag; | |
| } | |
| 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); | |
| } | |
| 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<NameAndType> typeParams; | |
| if ((flags & hasTypeParamsFlag) != 0) { | |
| final int numTypeParams = reader.readPackedUInt30(); | |
| List<ObjectHandle> names = new List<ObjectHandle>.generate( | |
| numTypeParams, (_) => reader.readPackedObject()); | |
| List<ObjectHandle> bounds = new List<ObjectHandle>.generate( | |
| numTypeParams, (_) => reader.readPackedObject()); | |
| typeParams = new List<NameAndType>.generate( | |
| numTypeParams, (int i) => new NameAndType(names[i], bounds[i])); | |
| } else { | |
| typeParams = const <NameAndType>[]; | |
| } | |
| final numParams = reader.readPackedUInt30(); | |
| final numRequiredParams = (flags & | |
| (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())); | |
| final returnType = reader.readPackedObject(); | |
| return new ClosureDeclaration(parent, name, position, endPosition, | |
| typeParams, numRequiredParams, numNamedParams, parameters, returnType); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.write('Closure $parent::$name'); | |
| 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 (code != null) { | |
| sb.write(code.toString()); | |
| } | |
| return sb.toString(); | |
| } | |
| } | |
| /// Bytecode of a nested function (closure). | |
| /// Closures share the constant pool of a top-level member. | |
| class ClosureCode { | |
| final List<int> bytecodes; | |
| final ExceptionsTable exceptionsTable; | |
| final SourcePositions sourcePositions; | |
| bool get hasExceptionsTable => exceptionsTable.blocks.isNotEmpty; | |
| bool get hasSourcePositions => | |
| sourcePositions != null && sourcePositions.mapping.isNotEmpty; | |
| int get flags => | |
| (hasExceptionsTable ? Code.hasExceptionsTableFlag : 0) | | |
| (hasSourcePositions ? Code.hasSourcePositionsFlag : 0); | |
| ClosureCode(this.bytecodes, this.exceptionsTable, this.sourcePositions); | |
| void write(BufferedWriter writer) { | |
| writer.writePackedUInt30(flags); | |
| _writeBytecodeInstructions(writer, bytecodes); | |
| if (hasExceptionsTable) { | |
| exceptionsTable.write(writer); | |
| } | |
| if (hasSourcePositions) { | |
| writer.writeLinkOffset(sourcePositions); | |
| } | |
| } | |
| factory ClosureCode.read(BufferedReader reader) { | |
| final int flags = reader.readPackedUInt30(); | |
| final List<int> bytecodes = _readBytecodeInstructions(reader); | |
| final exceptionsTable = ((flags & Code.hasExceptionsTableFlag) != 0) | |
| ? new ExceptionsTable.read(reader) | |
| : new ExceptionsTable(); | |
| final sourcePositions = ((flags & Code.hasSourcePositionsFlag) != 0) | |
| ? reader.readLinkOffset<SourcePositions>() | |
| : null; | |
| return new ClosureCode(bytecodes, exceptionsTable, sourcePositions); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer sb = new StringBuffer(); | |
| sb.writeln('ClosureCode {'); | |
| sb.writeln(new BytecodeDisassembler() | |
| .disassemble(bytecodes, exceptionsTable, annotations: [ | |
| hasSourcePositions | |
| ? sourcePositions.getBytecodeAnnotations() | |
| : const <int, String>{} | |
| ])); | |
| sb.writeln('}'); | |
| return sb.toString(); | |
| } | |
| } | |
| class _Section { | |
| int numItems; | |
| int offset; | |
| BufferedWriter writer; | |
| _Section(this.numItems, this.writer); | |
| int get size => writer.offset; | |
| } | |
| class Component { | |
| static const int magicValue = 0x44424332; // 'DBC2' | |
| static const int numSections = 7; | |
| static const int sectionAlignment = 4; | |
| // UInt32 magic, version, numSections x (numItems, offset) | |
| static const int headerSize = (2 + numSections * 2) * 4; | |
| int version; | |
| StringTable stringTable; | |
| ObjectTable objectTable; | |
| List<Members> members = <Members>[]; | |
| List<Code> codes = <Code>[]; | |
| List<SourcePositions> sourcePositions = <SourcePositions>[]; | |
| List<ObjectHandle> annotations = <ObjectHandle>[]; | |
| ObjectHandle mainLibrary; | |
| Component(this.version) | |
| : stringTable = new StringTable(), | |
| objectTable = new ObjectTable(); | |
| void write(BufferedWriter writer) { | |
| objectTable.allocateIndexTable(); | |
| // Write sections to their own buffers in reverse order as section may | |
| // reference data structures from successor sections by offsets. | |
| final BufferedWriter annotationsWriter = | |
| new BufferedWriter.fromWriter(writer); | |
| for (var annot in annotations) { | |
| writer.linkWriter.put(annot, annotationsWriter.offset); | |
| annotationsWriter.writePackedObject(annot); | |
| } | |
| final BufferedWriter sourcePositionsWriter = | |
| new BufferedWriter.fromWriter(writer); | |
| for (var sp in sourcePositions) { | |
| writer.linkWriter.put(sp, sourcePositionsWriter.offset); | |
| sp.write(sourcePositionsWriter); | |
| } | |
| final BufferedWriter codesWriter = new BufferedWriter.fromWriter(writer); | |
| for (var code in codes) { | |
| writer.linkWriter.put(code, codesWriter.offset); | |
| code.write(codesWriter); | |
| } | |
| final BufferedWriter membersWriter = new BufferedWriter.fromWriter(writer); | |
| for (var m in members) { | |
| writer.linkWriter.put(m, membersWriter.offset); | |
| m.write(membersWriter); | |
| } | |
| BufferedWriter mainWriter; | |
| if (mainLibrary != null) { | |
| mainWriter = new BufferedWriter.fromWriter(writer); | |
| mainWriter.writePackedObject(mainLibrary); | |
| } | |
| final BufferedWriter objectsWriter = new BufferedWriter.fromWriter(writer); | |
| objectTable.write(objectsWriter); | |
| final BufferedWriter stringsWriter = new BufferedWriter.fromWriter(writer); | |
| stringTable.write(stringsWriter); | |
| List<_Section> sections = [ | |
| new _Section(0, stringsWriter), | |
| new _Section(0, objectsWriter), | |
| new _Section(0, mainWriter), | |
| new _Section(members.length, membersWriter), | |
| new _Section(codes.length, codesWriter), | |
| new _Section(sourcePositions.length, sourcePositionsWriter), | |
| new _Section(annotations.length, annotationsWriter), | |
| ]; | |
| assert(sections.length == numSections); | |
| int offset = headerSize; | |
| for (var section in sections) { | |
| if (section.writer != null) { | |
| offset = (offset + sectionAlignment - 1) & ~(sectionAlignment - 1); | |
| section.offset = offset; | |
| offset += section.size; | |
| } else { | |
| section.offset = 0; | |
| } | |
| } | |
| final start = writer.offset; | |
| writer.writeUInt32(magicValue); | |
| writer.writeUInt32(version); | |
| for (var section in sections) { | |
| writer.writeUInt32(section.numItems); | |
| writer.writeUInt32(section.offset); | |
| } | |
| assert(writer.offset - start == headerSize); | |
| for (var section in sections) { | |
| if (section.writer != null) { | |
| writer.align(sectionAlignment); | |
| assert(writer.offset - start == section.offset); | |
| writer.writeBytes(section.writer.takeBytes()); | |
| } | |
| } | |
| BytecodeSizeStatistics.componentSize += (writer.offset - start); | |
| } | |
| Component.read(BufferedReader reader) { | |
| final int start = reader.offset; | |
| final int magic = reader.readUInt32(); | |
| if (magic != magicValue) { | |
| throw 'Error: unexpected bytecode magic $magic'; | |
| } | |
| version = reader.readUInt32(); | |
| if (version != currentBytecodeFormatVersion) { | |
| throw 'Error: unexpected bytecode format version $version'; | |
| } | |
| reader.formatVersion = version; | |
| reader.readUInt32(); | |
| final stringTableOffset = reader.readUInt32(); | |
| reader.readUInt32(); | |
| final objectTableOffset = reader.readUInt32(); | |
| reader.readUInt32(); | |
| final mainOffset = reader.readUInt32(); | |
| final membersNum = reader.readUInt32(); | |
| final membersOffset = reader.readUInt32(); | |
| final codesNum = reader.readUInt32(); | |
| final codesOffset = reader.readUInt32(); | |
| final sourcePositionsNum = reader.readUInt32(); | |
| final sourcePositionsOffset = reader.readUInt32(); | |
| final annotationsNum = reader.readUInt32(); | |
| final annotationsOffset = reader.readUInt32(); | |
| reader.offset = start + stringTableOffset; | |
| stringTable = new StringTable.read(reader); | |
| reader.stringReader = stringTable; | |
| reader.offset = start + objectTableOffset; | |
| objectTable = new ObjectTable.read(reader); | |
| reader.objectReader = objectTable; | |
| // Read sections in the reverse order as section may reference | |
| // successor sections by offsets. | |
| final annotationsStart = start + annotationsOffset; | |
| reader.offset = annotationsStart; | |
| for (int i = 0; i < annotationsNum; ++i) { | |
| int offset = reader.offset - annotationsStart; | |
| ObjectHandle annot = reader.readPackedObject(); | |
| reader.linkReader.setOffset(annot, offset); | |
| annotations.add(annot); | |
| } | |
| final sourcePositionsStart = start + sourcePositionsOffset; | |
| reader.offset = sourcePositionsStart; | |
| for (int i = 0; i < sourcePositionsNum; ++i) { | |
| int offset = reader.offset - sourcePositionsStart; | |
| SourcePositions sp = new SourcePositions.read(reader); | |
| reader.linkReader.setOffset(sp, offset); | |
| sourcePositions.add(sp); | |
| } | |
| final codesStart = start + codesOffset; | |
| reader.offset = codesStart; | |
| for (int i = 0; i < codesNum; ++i) { | |
| int offset = reader.offset - codesStart; | |
| Code code = new Code.read(reader); | |
| reader.linkReader.setOffset(code, offset); | |
| codes.add(code); | |
| } | |
| final membersStart = start + membersOffset; | |
| reader.offset = membersStart; | |
| for (int i = 0; i < membersNum; ++i) { | |
| int offset = reader.offset - membersStart; | |
| Members m = new Members.read(reader); | |
| reader.linkReader.setOffset(m, offset); | |
| members.add(m); | |
| } | |
| if (mainOffset != 0) { | |
| reader.offset = start + mainOffset; | |
| mainLibrary = reader.readPackedObject(); | |
| } | |
| } | |
| String toString() => "\n" | |
| "Bytecode" | |
| " (version: " | |
| "${version == currentBytecodeFormatVersion ? 'stable' : version == futureBytecodeFormatVersion ? 'future' : "v$version"}" | |
| ")\n" | |
| // "$objectTable\n" | |
| // "$stringTable\n" | |
| "${mainLibrary != null ? 'Main library: $mainLibrary\n' : ''}" | |
| // "${members.join('\n')}\n" | |
| ; | |
| } | |
| void _writeBytecodeInstructions(BufferedWriter writer, List<int> bytecodes) { | |
| writer.writePackedUInt30(bytecodes.length); | |
| writer.writeBytes(bytecodes); | |
| BytecodeSizeStatistics.instructionsSize += bytecodes.length; | |
| } | |
| List<int> _readBytecodeInstructions(BufferedReader reader) { | |
| int len = reader.readPackedUInt30(); | |
| return reader.readBytesAsUint8List(len); | |
| } |