[vm] Remove irregular type arguments parameter from factories

In the VM, factory constructors always had an extra "type arguments"
parameter, even if class is not generic. Factory constructor bodies
were using class type parameters instead of function type parameters.

This results in extra code when calling non-generic factories
which is slightly inefficient in terms of code size and performance.
Also, it creates an additional complexity throughout the system as
factories should be special cased in many places.

This change removes artificial "type arguments" parameter, treating
factory constructors basically as static methods. This matches
kernel AST representation.

TEST=ci

Change-Id: I957583cb2ce9a3c408699880a04036e06b01dd31
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501762
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Alexander Markov
2026-05-12 06:11:15 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 66b54232ee
commit 6195ea86bc
39 changed files with 310 additions and 539 deletions
+13 -65
View File
@@ -949,9 +949,7 @@ class BytecodeGenerator extends RecursiveVisitor {
void _genExternalCall(Member node) {
final function = node.function!;
if (locals.hasFactoryTypeArgsVar) {
asm.emitPush(locals.getVarIndexInFrame(locals.factoryTypeArgsVar));
} else if (locals.hasFunctionTypeArgsVar) {
if (locals.hasFunctionTypeArgsVar) {
asm.emitPush(locals.functionTypeArgsVarIndexInFrame);
}
if (locals.hasReceiver) {
@@ -1483,23 +1481,19 @@ class BytecodeGenerator extends RecursiveVisitor {
Member target,
Arguments args, {
bool hasReceiver = false,
bool isFactory = false,
bool isUnchecked = false,
TreeNode? node,
}) {
final argDesc = objectTable.getArgDescHandleByArguments(
args,
hasReceiver: hasReceiver,
isFactory: isFactory,
);
int totalArgCount = args.positional.length + args.named.length;
if (hasReceiver) {
totalArgCount++;
}
if (args.types.isNotEmpty || isFactory) {
// VM needs type arguments for every invocation of a factory constructor.
// TODO(alexmarkov): Clean this up.
if (args.types.isNotEmpty) {
totalArgCount++;
}
@@ -1573,17 +1567,9 @@ class BytecodeGenerator extends RecursiveVisitor {
void _genPushInstantiatorTypeArguments() {
if (instantiatorTypeArguments != null) {
if (locals.hasFactoryTypeArgsVar) {
assert(
enclosingMember is Procedure &&
(enclosingMember as Procedure).isFactory,
);
_genLoadVar(locals.factoryTypeArgsVar);
} else {
_genPushReceiver();
final int cpIndex = cp.addTypeArgumentsField(enclosingClass!);
asm.emitLoadTypeArgumentsField(cpIndex);
}
_genPushReceiver();
final int cpIndex = cp.addTypeArgumentsField(enclosingClass!);
asm.emitLoadTypeArgumentsField(cpIndex);
} else {
asm.emitPushNull();
}
@@ -1844,22 +1830,15 @@ class BytecodeGenerator extends RecursiveVisitor {
isClosure = false;
hasErrors = false;
staticTypeContext.enterMember(node);
final isFactory = node is Procedure && node.isFactory;
if (node.isInstanceMember || node is Constructor || isFactory) {
if (node.isInstanceMember || node is Constructor) {
if (enclosingClass!.typeParameters.isNotEmpty) {
final classTypeParameters = this.classTypeParameters =
new Set<TypeParameter>.from(enclosingClass.typeParameters);
// Treat type arguments of factory constructors as class
// type parameters.
if (isFactory) {
classTypeParameters.addAll(node.function.typeParameters);
}
this.classTypeParameters = new Set<TypeParameter>.from(
enclosingClass.typeParameters,
);
}
if (hasInstantiatorTypeArguments(enclosingClass)) {
final typeParameters = getTypeParameterTypes(
isFactory
? node.function.typeParameters
: enclosingClass.typeParameters,
enclosingClass.typeParameters,
);
instantiatorTypeArguments = flattenInstantiatorTypeArguments(
enclosingClass,
@@ -2196,8 +2175,6 @@ class BytecodeGenerator extends RecursiveVisitor {
}
if (locals.hasFunctionTypeArgsVar && function!.typeParameters.isNotEmpty) {
assert(!(node is Procedure && node.isFactory));
Label done = new Label();
if (isClosure) {
@@ -2353,9 +2330,6 @@ class BytecodeGenerator extends RecursiveVisitor {
if (locals.hasCapturedParameters) {
// Copy captured parameters to their respective locations in the context.
if (!isClosure) {
if (locals.hasFactoryTypeArgsVar) {
_copyParamIfCaptured(locals.factoryTypeArgsVar);
}
if (locals.hasCapturedReceiverVar) {
_genPushContextForVariable(locals.capturedReceiverVar);
asm.emitPush(locals.getVarIndexInFrame(locals.receiverVar));
@@ -3385,11 +3359,8 @@ class BytecodeGenerator extends RecursiveVisitor {
}
}
// _GrowableList._literal is a factory constructor.
// Type arguments passed to a factory constructor are counted as a normal
// argument and not counted in number of type arguments.
assert(growableListLiteral.isFactory);
_genDirectCall(growableListLiteral, objectTable.getArgDescHandle(2), 2);
_genDirectCall(growableListLiteral, objectTable.getArgDescHandle(1, 1), 2);
}
@override
@@ -3451,11 +3422,8 @@ class BytecodeGenerator extends RecursiveVisitor {
}
}
// Map._fromLiteral is a factory constructor.
// Type arguments passed to a factory constructor are counted as a normal
// argument and not counted in number of type arguments.
assert(mapFromLiteral.isFactory);
_genDirectCall(mapFromLiteral, objectTable.getArgDescHandle(2), 2);
_genDirectCall(mapFromLiteral, objectTable.getArgDescHandle(1, 2), 2);
}
void _genMethodInvocationUsingSpecializedBytecode(
@@ -4025,28 +3993,8 @@ class BytecodeGenerator extends RecursiveVisitor {
_generateFfiCall(args.positional.single);
return;
}
if (target.isFactory) {
final constructedClass = target.enclosingClass!;
if (hasInstantiatorTypeArguments(constructedClass)) {
_genTypeArguments(args.types, instantiatingClass: constructedClass);
} else {
assert(args.types.isEmpty);
// VM needs type arguments for every invocation of a factory
// constructor. TODO(alexmarkov): Clean this up.
asm.emitPushNull();
}
args = new Arguments(
node.arguments.positional,
named: node.arguments.named,
)..parent = node;
}
_genArguments(null, args);
_genDirectCallWithArgs(
target,
args,
isFactory: target.isFactory,
node: node,
);
_genDirectCallWithArgs(target, args, node: node);
if (target == debugger) {
// The debugger needs a pause for the current source position right after
// stepping out from the debugger function.
+2 -10
View File
@@ -692,17 +692,9 @@ class ConstantPool {
),
);
int addArgDescByArguments(
Arguments args, {
bool hasReceiver = false,
bool isFactory = false,
}) => _add(
int addArgDescByArguments(Arguments args, {bool hasReceiver = false}) => _add(
new ConstantObjectRef(
objectTable.getArgDescHandleByArguments(
args,
hasReceiver: hasReceiver,
isFactory: isFactory,
),
objectTable.getArgDescHandleByArguments(args, hasReceiver: hasReceiver),
),
);
+8 -37
View File
@@ -120,12 +120,6 @@ class LocalVariables {
bool get hasFunctionTypeArgsVar => _currentFrame.functionTypeArgsVar != null;
VariableDeclaration get factoryTypeArgsVar =>
_currentFrame.factoryTypeArgsVar ??
(throw 'FactoryTypeArgs variable is not declared in ${_currentFrame.function}');
bool get hasFactoryTypeArgsVar => _currentFrame.factoryTypeArgsVar != null;
VariableDeclaration get receiverVar =>
_currentFrame.receiverVar ??
(throw 'Receiver variable is not declared in ${_currentFrame.function}');
@@ -243,7 +237,6 @@ class Frame {
VariableDeclaration? receiverVar;
VariableDeclaration? capturedReceiverVar;
VariableDeclaration? functionTypeArgsVar;
VariableDeclaration? factoryTypeArgsVar;
VariableDeclaration? closureVar;
VariableDeclaration? contextVar;
VariableDeclaration? scratchVar;
@@ -364,29 +357,15 @@ class _ScopeBuilder extends RecursiveVisitor {
}
}
if (node is Procedure && node.isFactory) {
assert(_currentFrame.parent == null);
_currentFrame.numTypeArguments = 0;
final factoryTypeArgsVar = _currentFrame.factoryTypeArgsVar =
VariableDeclaration(':type_arguments');
_declareVariable(factoryTypeArgsVar);
} else {
_currentFrame.numTypeArguments =
(_currentFrame.parent?.numTypeArguments ?? 0) +
function.typeParameters.length;
_currentFrame.numTypeArguments =
(_currentFrame.parent?.numTypeArguments ?? 0) +
function.typeParameters.length;
if (_currentFrame.numTypeArguments > 0) {
final functionTypeArgsVar = _currentFrame.functionTypeArgsVar =
VariableDeclaration(':function_type_arguments_var')
..fileOffset = function.fileOffset;
_declareVariable(functionTypeArgsVar);
}
final parentFactoryTypeArgsVar =
_currentFrame.parent?.factoryTypeArgsVar;
if (parentFactoryTypeArgsVar != null) {
_currentFrame.factoryTypeArgsVar = parentFactoryTypeArgsVar;
}
if (_currentFrame.numTypeArguments > 0) {
final functionTypeArgsVar = _currentFrame.functionTypeArgsVar =
VariableDeclaration(':function_type_arguments_var')
..fileOffset = function.fileOffset;
_declareVariable(functionTypeArgsVar);
}
if (_hasReceiverParameter(node)) {
@@ -625,8 +604,6 @@ class _ScopeBuilder extends RecursiveVisitor {
var parent = node.parameter.declaration;
if (parent is Class) {
_useThis();
} else if (parent is Procedure && parent.isFactory) {
_useVariable(_currentFrame.factoryTypeArgsVar!);
}
node.visitChildren(this);
}
@@ -946,7 +923,6 @@ class _Allocator extends RecursiveVisitor {
}
void _allocateParameters(TreeNode node, FunctionNode function) {
final bool isFactory = node is Procedure && node.isFactory;
final bool hasReceiver = _hasReceiverParameter(node);
final bool hasClosureArg =
node is FunctionDeclaration || node is FunctionExpression;
@@ -954,7 +930,6 @@ class _Allocator extends RecursiveVisitor {
_currentFrame.numParameters =
function.positionalParameters.length +
function.namedParameters.length +
(isFactory ? 1 : 0) +
(hasReceiver ? 1 : 0) +
(hasClosureArg ? 1 : 0);
@@ -964,15 +939,11 @@ class _Allocator extends RecursiveVisitor {
function.namedParameters.isNotEmpty;
_currentFrame.hasCapturedParameters =
(isFactory && locals.isCaptured(_currentFrame.factoryTypeArgsVar!)) ||
(hasReceiver && _currentFrame.capturedReceiverVar != null) ||
function.positionalParameters.any(locals.isCaptured) ||
function.namedParameters.any(locals.isCaptured);
int count = 0;
if (isFactory) {
_allocateParameter(_currentFrame.factoryTypeArgsVar!, count++);
}
if (hasReceiver) {
assert(!locals.isCaptured(_currentFrame.receiverVar!));
_allocateParameter(_currentFrame.receiverVar!, count++);
+2 -10
View File
@@ -1920,7 +1920,6 @@ class ObjectTable implements ObjectWriter, ObjectReader {
ObjectHandle getArgDescHandleByArguments(
Arguments args, {
bool hasReceiver = false,
bool isFactory = false,
}) {
List<_PublicNameHandle> argNames = const <_PublicNameHandle>[];
final namedArguments = args.named;
@@ -1931,15 +1930,8 @@ class ObjectTable implements ObjectWriter, ObjectReader {
);
}
final int numArguments =
args.positional.length +
args.named.length +
(hasReceiver ? 1 : 0) +
// VM expects that type arguments vector passed to a factory
// constructor is counted in numArguments, and not counted in
// numTypeArgs.
// TODO(alexmarkov): Clean this up.
(isFactory ? 1 : 0);
final int numTypeArguments = isFactory ? 0 : args.types.length;
args.positional.length + args.named.length + (hasReceiver ? 1 : 0);
final int numTypeArguments = args.types.length;
return getOrAddObject(
new _ArgDescHandle(numArguments, numTypeArguments, argNames),
);
@@ -122,7 +122,7 @@ ConstantPool {
[6] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::callWithArgs::TypeParam/5
[7] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::callWithArgs::TypeParam/6
[8] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::callWithArgs::TypeParam/7
[9] = DirectCall 'dart:core::_GrowableList::_literal8 (constructor)', ArgDesc num-args 9, num-type-args 0, names []
[9] = DirectCall 'dart:core::_GrowableList::_literal8 (constructor)', ArgDesc num-args 8, num-type-args 1, names []
[10] = Reserved
[11] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
[12] = Reserved
@@ -531,7 +531,7 @@ ConstantPool {
[13] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/0::TypeParam/1
[14] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/0
[15] = Type DART_SDK/pkg/dart2bytecode/testcases/closures.dart::A::foo::Closure/1::TypeParam/1
[16] = DirectCall 'dart:core::_GrowableList::_literal8 (constructor)', ArgDesc num-args 9, num-type-args 0, names []
[16] = DirectCall 'dart:core::_GrowableList::_literal8 (constructor)', ArgDesc num-args 8, num-type-args 1, names []
[17] = Reserved
[18] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
[19] = Reserved
@@ -1035,7 +1035,7 @@ L1:
}
ConstantPool {
[0] = ObjectRef < dart:core::Function >
[1] = DirectCall 'dart:core::_GrowableList:: (constructor)', ArgDesc num-args 2, num-type-args 0, names []
[1] = DirectCall 'dart:core::_GrowableList:: (constructor)', ArgDesc num-args 1, num-type-args 1, names []
[2] = Reserved
[3] = ClosureFunction 0
[4] = EndClosureFunctionScope
+2 -2
View File
@@ -46,7 +46,7 @@ Bytecode {
ConstantPool {
[0] = ObjectRef < dart:ffi::NativeFunction < FunctionType () -> dart:ffi::Void > >
[1] = ObjectRef const 3735928559
[2] = DirectCall 'dart:ffi::Pointer::fromAddress (constructor)', ArgDesc num-args 2, num-type-args 0, names []
[2] = DirectCall 'dart:ffi::Pointer::fromAddress (constructor)', ArgDesc num-args 1, num-type-args 1, names []
[3] = Reserved
[4] = ClosureFunction 0
[5] = FfiCall
@@ -107,7 +107,7 @@ Bytecode {
ConstantPool {
[0] = ObjectRef < dart:ffi::NativeFunction < FunctionType (dart:ffi::Int64) -> dart:ffi::Int32 > >
[1] = ObjectRef const 3735928559
[2] = DirectCall 'dart:ffi::Pointer::fromAddress (constructor)', ArgDesc num-args 2, num-type-args 0, names []
[2] = DirectCall 'dart:ffi::Pointer::fromAddress (constructor)', ArgDesc num-args 1, num-type-args 1, names []
[3] = Reserved
[4] = ClosureFunction 0
[5] = Type dart:core::int
@@ -115,7 +115,7 @@ Bytecode {
}
ConstantPool {
[0] = ObjectRef < dart:core::int, dart:core::List < dart:core::String > >
[1] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::G::testFactory (constructor)', ArgDesc num-args 1, num-type-args 0, names []
[1] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::G::testFactory (constructor)', ArgDesc num-args 0, num-type-args 2, names []
[2] = Reserved
}
@@ -127,20 +127,18 @@ Function 'foo5', static, reflectable, debuggable
Bytecode {
Entry 0
CheckStack 0
PushNull
DirectCall CP#0, 1
DirectCall CP#0, 0
Drop1
PushNull
PushInt 42
DirectCall CP#2, 2
DirectCall CP#2, 1
Drop1
PushNull
ReturnTOS
}
ConstantPool {
[0] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::I::testFactory2 (constructor)', ArgDesc num-args 1, num-type-args 0, names []
[0] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::I::testFactory2 (constructor)', ArgDesc num-args 0, num-type-args 0, names []
[1] = Reserved
[2] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::I::testFactory2 (constructor)', ArgDesc num-args 2, num-type-args 0, names ['param']
[2] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::I::testFactory2 (constructor)', ArgDesc num-args 1, num-type-args 0, names ['param']
[3] = Reserved
}
@@ -158,7 +156,7 @@ Bytecode {
}
ConstantPool {
[0] = ObjectRef < dart:core::String >
[1] = DirectCall 'dart:core::_List::empty (constructor)', ArgDesc num-args 1, num-type-args 0, names []
[1] = DirectCall 'dart:core::_List::empty (constructor)', ArgDesc num-args 0, num-type-args 1, names []
[2] = Reserved
}
@@ -178,7 +176,7 @@ Bytecode {
}
ConstantPool {
[0] = ObjectRef < dart:core::int >
[1] = DirectCall 'dart:core::_List::filled (constructor)', ArgDesc num-args 3, num-type-args 0, names []
[1] = DirectCall 'dart:core::_List::filled (constructor)', ArgDesc num-args 2, num-type-args 1, names []
[2] = Reserved
}
@@ -427,7 +425,7 @@ Bytecode {
}
ConstantPool {
[0] = TypeArgumentsField DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::E
[1] = DirectCall 'dart:core::Map:: (constructor)', ArgDesc num-args 1, num-type-args 0, names []
[1] = DirectCall 'dart:core::Map:: (constructor)', ArgDesc num-args 0, num-type-args 2, names []
[2] = Reserved
}
@@ -469,7 +467,7 @@ Bytecode {
}
ConstantPool {
[0] = TypeArgumentsField DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::F
[1] = DirectCall 'dart:core::Map:: (constructor)', ArgDesc num-args 1, num-type-args 0, names []
[1] = DirectCall 'dart:core::Map:: (constructor)', ArgDesc num-args 0, num-type-args 2, names []
[2] = Reserved
}
@@ -503,15 +501,16 @@ Function 'testFactory', factory, static, reflectable, debuggable
return-type DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::G < DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::G::testFactory (constructor)::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::G::testFactory (constructor)::TypeParam/1 >
Bytecode {
Entry 1
Entry 2
CheckFunctionTypeArgs 2, r0
CheckStack 0
Push FP[-5]
PushNull
Push r0
InstantiateTypeArgumentsTOS 0, CP#1
PushConstant CP#0
AllocateT
StoreLocal r0
Push r0
StoreLocal r1
Push r1
DirectCall CP#2, 1
Drop1
ReturnTOS
@@ -574,15 +573,15 @@ Function 'testFactory2', factory, static, has-optional-named-params, reflectable
return-type DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::I
Bytecode {
EntryOptional 1, 0, 1
LoadConstant r1, CP#0
LoadConstant r1, CP#1
EntryOptional 0, 0, 1
LoadConstant r0, CP#0
LoadConstant r0, CP#1
Frame 1
CheckStack 0
Allocate CP#2
StoreLocal r2
Push r2
StoreLocal r1
Push r1
Push r0
DirectCall CP#3, 2
Drop1
ReturnTOS
@@ -649,13 +648,14 @@ Function '', factory, static, reflectable, debuggable
return-type DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::L < DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::L:: (constructor)::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/instance_creation.dart::L:: (constructor)::TypeParam/1 >
Bytecode {
Entry 1
Entry 2
CheckFunctionTypeArgs 2, r0
CheckStack 0
Push FP[-5]
Push r0
PushConstant CP#0
AllocateT
StoreLocal r0
Push r0
StoreLocal r1
Push r1
DirectCall CP#1, 1
Drop1
ReturnTOS
@@ -155,7 +155,7 @@ Bytecode {
}
ConstantPool {
[0] = ObjectRef < dart:core::int >
[1] = DirectCall 'dart:core::_GrowableList::_literal3 (constructor)', ArgDesc num-args 4, num-type-args 0, names []
[1] = DirectCall 'dart:core::_GrowableList::_literal3 (constructor)', ArgDesc num-args 3, num-type-args 1, names []
[2] = Reserved
[3] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
[4] = Reserved
@@ -256,7 +256,7 @@ Bytecode {
ConstantPool {
[0] = ObjectRef < dart:core::int, dart:core::int >
[1] = ObjectRef < dynamic >
[2] = DirectCall 'dart:core::Map::_fromLiteral (constructor)', ArgDesc num-args 2, num-type-args 0, names []
[2] = DirectCall 'dart:core::Map::_fromLiteral (constructor)', ArgDesc num-args 1, num-type-args 2, names []
[3] = Reserved
[4] = DirectCall 'dart:core::print', ArgDesc num-args 1, num-type-args 0, names []
[5] = Reserved
@@ -364,7 +364,7 @@ ConstantPool {
[2] = Type dart:core::Map < DART_SDK/pkg/dart2bytecode/testcases/type_ops.dart::D::TypeParam/0, DART_SDK/pkg/dart2bytecode/testcases/type_ops.dart::D::TypeParam/1 >
[3] = ObjectRef ''
[4] = SubtypeTestCache
[5] = DirectCall 'dart:core::_GrowableList::_literal1 (constructor)', ArgDesc num-args 2, num-type-args 0, names []
[5] = DirectCall 'dart:core::_GrowableList::_literal1 (constructor)', ArgDesc num-args 1, num-type-args 1, names []
[6] = Reserved
[7] = InterfaceCall 'dart:core::List::[]', ArgDesc num-args 2, num-type-args 0, names []
[8] = Reserved
@@ -403,23 +403,28 @@ Function '', factory, static, reflectable, debuggable
return-type DART_SDK/pkg/dart2bytecode/testcases/type_ops.dart::E < DART_SDK/pkg/dart2bytecode/testcases/type_ops.dart::E:: (constructor)::TypeParam/0 >
Bytecode {
Entry 1
CheckStack 0
Push FP[-5]
Entry 2
CheckFunctionTypeArgs 1, r0
JumpIfNotZeroTypeArgs L1
PushConstant CP#0
AllocateT
StoreLocal r0
PopLocal r0
L1:
CheckStack 0
Push r0
DirectCall CP#1, 1
PushConstant CP#1
AllocateT
StoreLocal r1
Push r1
DirectCall CP#2, 1
Drop1
ReturnTOS
}
Default function type arguments: CP#3
Default function type arguments: CP#0
ConstantPool {
[0] = Class DART_SDK/pkg/dart2bytecode/testcases/type_ops.dart::E
[1] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/type_ops.dart::E::_ (constructor)', ArgDesc num-args 1, num-type-args 0, names []
[2] = Reserved
[3] = ObjectRef < dart:core::String >
[0] = ObjectRef < dart:core::String >
[1] = Class DART_SDK/pkg/dart2bytecode/testcases/type_ops.dart::E
[2] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/type_ops.dart::E::_ (constructor)', ArgDesc num-args 1, num-type-args 0, names []
[3] = Reserved
}