blob: 964854ac39669680d872175ca1f981460bd51236 [file] [log] [blame]
// Copyright (c) 2016, 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.md file.
import 'dart:async';
import 'dart:collection' show Queue;
import 'package:kernel/ast.dart' as ir;
import 'package:kernel/verifier.dart' show CheckParentPointers;
import '../common.dart';
import '../common/names.dart';
import '../compiler.dart' show Compiler;
import '../constants/expressions.dart'
show ConstantExpression, TypeConstantExpression;
import '../dart_types.dart'
show DartType, FunctionType, InterfaceType, TypeKind, TypeVariableType;
import '../diagnostics/messages.dart' show MessageKind;
import '../diagnostics/spannable.dart' show Spannable;
import '../elements/elements.dart'
show
ClassElement,
ConstructorElement,
Element,
ExportElement,
FieldElement,
FunctionElement,
ImportElement,
LibraryElement,
LocalFunctionElement,
MetadataAnnotation,
MixinApplicationElement,
TypeVariableElement;
import '../elements/modelx.dart' show ErroneousFieldElementX;
import '../tree/tree.dart' show FunctionExpression, Node;
import 'constant_visitor.dart';
import 'kernel_visitor.dart' show IrFunction, KernelVisitor;
typedef void WorkAction();
class WorkItem {
final Element element;
final WorkAction action;
WorkItem(this.element, this.action);
}
class Kernel {
final Compiler compiler;
final Map<LibraryElement, ir.Library> libraries =
<LibraryElement, ir.Library>{};
final Map<ClassElement, ir.Class> classes = <ClassElement, ir.Class>{};
final Map<FunctionElement, ir.Member> functions =
<FunctionElement, ir.Member>{};
final Map<LocalFunctionElement, ir.Node> localFunctions =
<LocalFunctionElement, ir.Node>{};
final Map<FieldElement, ir.Field> fields = <FieldElement, ir.Field>{};
final Map<TypeVariableElement, ir.TypeParameter> typeParameters =
<TypeVariableElement, ir.TypeParameter>{};
final Map<TypeVariableElement, ir.TypeParameter> factoryTypeParameters =
<TypeVariableElement, ir.TypeParameter>{};
final Set<ir.TreeNode> checkedNodes = new Set<ir.TreeNode>();
final Map<LibraryElement, Map<String, int>> mixinApplicationNamesByLibrary =
<LibraryElement, Map<String, int>>{};
final Map<ir.Node, Element> nodeToElement = <ir.Node, Element>{};
final Map<ir.Node, Node> nodeToAst = <ir.Node, Node>{};
final Map<ir.Node, Node> nodeToAstOperator = <ir.Node, Node>{};
// Synthetic nodes are nodes we generated that do not correspond to
// [ast.Node]s. A node should be in one of nodeToAst or syntheticNodes but not
// both.
final Set<ir.Node> syntheticNodes = new Set<ir.Node>();
final Map<ir.Node, ConstantExpression> parameterInitializerNodeToConstant =
<ir.Node, ConstantExpression>{};
/// FIFO queue of work that needs to be completed before the returned AST
/// nodes are correct.
final Queue<WorkItem> workQueue = new Queue<WorkItem>();
Kernel(this.compiler);
void addWork(Element element, WorkAction action) {
workQueue.addLast(new WorkItem(element, action));
}
void checkMember(Element key, ir.TreeNode value) {
if (!checkedNodes.add(value)) return;
if (value.parent == null) {
internalError(key, "Missing parent on IR node.");
}
try {
CheckParentPointers.check(value);
} catch (e, s) {
internalError(key, "$e\n$s");
}
}
void checkLibrary(Element key, ir.Library library) {
if (!checkedNodes.add(library)) return;
CheckParentPointers.check(library);
}
void processWorkQueue() {
while (workQueue.isNotEmpty) {
WorkItem work = workQueue.removeFirst();
work.action();
}
assert(() {
libraries.forEach(checkLibrary);
classes.forEach(checkMember);
functions.forEach(checkMember);
fields.forEach(checkMember);
return true;
});
}
ir.Name irName(String name, Element element) {
ir.Library irLibrary = null;
if (name.startsWith("_")) {
ClassElement cls = element.enclosingClass;
if (cls != null && cls.isMixinApplication) {
MixinApplicationElement mixinApplication = cls;
element = mixinApplication.mixin;
}
irLibrary = libraryToIr(element.library);
}
return new ir.Name(name, irLibrary);
}
Future<ir.Library> loadLibrary(Uri uri) async {
return libraryToIr(await compiler.libraryLoader.loadLibrary(uri));
}
ir.Library libraryToIr(LibraryElement library) {
library = library.declaration;
return libraries.putIfAbsent(library, () {
String name = library.hasLibraryName ? library.libraryName : null;
ir.Library libraryNode = new ir.Library(library.canonicalUri,
name: name, classes: null, procedures: null, fields: null);
addWork(library, () {
Queue<ir.Class> classes = new Queue<ir.Class>();
Queue<ir.Member> members = new Queue<ir.Member>();
library.implementation.forEachLocalMember((Element e) {
if (e.isClass) {
classes.addFirst(classToIr(e));
} else if (e.isFunction || e.isAccessor) {
members.addFirst(functionToIr(e));
} else if (e.isField) {
members.addFirst(fieldToIr(e));
} else if (e.isTypedef) {
// Ignored, typedefs are unaliased on use.
} else {
internalError(e, "Unhandled library member: $e");
}
});
// The elements were inserted in reverse order as forEachLocalMember
// above gives them in reversed order.
classes.forEach(libraryNode.addClass);
members.forEach(libraryNode.addMember);
});
return libraryNode;
});
}
/// Compute a name for [cls]. We want to have unique names in a library, but
/// mixin applications can lead to multiple classes with the same name. So
/// for those we append `#` and a number.
String computeName(ClassElement cls) {
String name = cls.name;
if (!cls.isUnnamedMixinApplication) return name;
Map<String, int> mixinApplicationNames = mixinApplicationNamesByLibrary
.putIfAbsent(cls.library.implementation, () => <String, int>{});
int count = mixinApplicationNames.putIfAbsent(name, () => 0);
mixinApplicationNames[name] = count + 1;
return "$name#$count";
}
ir.Class classToIr(ClassElement cls) {
cls = cls.declaration;
return classes.putIfAbsent(cls, () {
cls.ensureResolved(compiler.resolution);
compiler.enqueuer.resolution.emptyDeferredQueueForTesting();
String name = computeName(cls);
ir.Class classNode = new ir.Class(
name: name,
isAbstract: cls.isAbstract,
typeParameters: null,
implementedTypes: null,
constructors: null,
procedures: null,
fields: null);
addWork(cls, () {
if (cls.supertype != null) {
classNode.supertype = supertypeToIr(cls.supertype);
}
if (cls.isMixinApplication) {
MixinApplicationElement mixinApplication = cls;
classNode.mixedInType = supertypeToIr(mixinApplication.mixinType);
}
classNode.parent = libraryToIr(cls.library);
if (cls.isUnnamedMixinApplication) {
classNode.enclosingLibrary.addClass(classNode);
}
cls.implementation
.forEachMember((ClassElement enclosingClass, Element member) {
if (member.enclosingClass.declaration != cls) {
// TODO(het): figure out why impact_test triggers this
//internalError(cls, "`$member` isn't mine.");
} else if (member.isConstructor) {
ConstructorElement constructor = member;
ir.Member memberNode = functionToIr(member);
if (!constructor.isRedirectingFactory) {
classNode.addMember(memberNode);
}
} else if (member.isFunction || member.isAccessor) {
classNode.addMember(functionToIr(member));
} else if (member.isField) {
classNode.addMember(fieldToIr(member));
} else {
internalError(member, "Unhandled class member: $member");
}
});
classNode.typeParameters.addAll(typeVariablesToIr(cls.typeVariables));
for (ir.Supertype supertype
in supertypesToIr(cls.interfaces.reverse().toList())) {
if (supertype != classNode.mixedInType) {
classNode.implementedTypes.add(supertype);
}
}
addWork(cls, () {
addDefaultInstanceFieldInitializers(classNode);
});
});
addWork(cls.declaration, () {
for (MetadataAnnotation metadata in cls.declaration.metadata) {
classNode.addAnnotation(
const ConstantVisitor().visit(metadata.constant, this));
}
});
return classNode;
});
}
/// Adds initializers to instance fields that are have no initializer and are
/// not initialized by all constructors in the class.
///
/// This is more or less copied directly from `ast_from_analyzer.dart` in
/// dartk.
void addDefaultInstanceFieldInitializers(ir.Class node) {
List<ir.Field> uninitializedFields = new List<ir.Field>();
for (ir.Field field in node.fields) {
if (field.initializer != null || field.isStatic) continue;
uninitializedFields.add(field);
}
if (uninitializedFields.isEmpty) return;
constructorLoop:
for (ir.Constructor constructor in node.constructors) {
Set<ir.Field> remainingFields = uninitializedFields.toSet();
for (ir.Initializer initializer in constructor.initializers) {
if (initializer is ir.FieldInitializer) {
remainingFields.remove(initializer.field);
} else if (initializer is ir.RedirectingInitializer) {
// The target constructor will be checked in another iteration.
continue constructorLoop;
}
}
for (ir.Field field in remainingFields) {
if (field.initializer == null) {
field.initializer = new ir.NullLiteral()..parent = field;
}
}
}
}
bool hasHierarchyProblem(ClassElement cls) => cls.hasIncompleteHierarchy;
ir.InterfaceType interfaceTypeToIr(InterfaceType type) {
ir.Class cls = classToIr(type.element);
if (type.typeArguments.isEmpty) {
return cls.rawType;
} else {
return new ir.InterfaceType(cls, typesToIr(type.typeArguments));
}
}
ir.Supertype supertypeToIr(InterfaceType type) {
ir.Class cls = classToIr(type.element);
if (type.typeArguments.isEmpty) {
return cls.asRawSupertype;
} else {
return new ir.Supertype(cls, typesToIr(type.typeArguments));
}
}
ir.FunctionType functionTypeToIr(FunctionType type) {
List<ir.TypeParameter> typeParameters = <ir.TypeParameter>[];
int requiredParameterCount = type.parameterTypes.length;
List<ir.DartType> positionalParameters =
new List<ir.DartType>.from(typesToIr(type.parameterTypes))
..addAll(typesToIr(type.optionalParameterTypes));
List<ir.NamedType> namedParameters = new List<ir.NamedType>.generate(
type.namedParameters.length,
(i) => new ir.NamedType(
type.namedParameters[i], typeToIr(type.namedParameterTypes[i])));
ir.DartType returnType = typeToIr(type.returnType);
return new ir.FunctionType(positionalParameters, returnType,
namedParameters: namedParameters,
typeParameters: typeParameters,
requiredParameterCount: requiredParameterCount);
}
ir.TypeParameterType typeVariableTypeToIr(TypeVariableType type) {
return new ir.TypeParameterType(typeVariableToIr(type.element));
}
List<ir.DartType> typesToIr(List<DartType> types) {
List<ir.DartType> result = new List<ir.DartType>(types.length);
for (int i = 0; i < types.length; i++) {
result[i] = typeToIr(types[i]);
}
return result;
}
List<ir.Supertype> supertypesToIr(List<DartType> types) {
List<ir.Supertype> result = new List<ir.Supertype>(types.length);
for (int i = 0; i < types.length; i++) {
result[i] = supertypeToIr(types[i]);
}
return result;
}
ir.DartType typeToIr(DartType type) {
switch (type.kind) {
case TypeKind.FUNCTION:
return functionTypeToIr(type);
case TypeKind.INTERFACE:
return interfaceTypeToIr(type);
case TypeKind.TYPEDEF:
type.computeUnaliased(compiler.resolution);
return typeToIr(type.unaliased);
case TypeKind.TYPE_VARIABLE:
return typeVariableTypeToIr(type);
case TypeKind.MALFORMED_TYPE:
return const ir.InvalidType();
case TypeKind.DYNAMIC:
return const ir.DynamicType();
case TypeKind.VOID:
return const ir.VoidType();
}
}
ir.DartType typeLiteralToIr(TypeConstantExpression constant) {
return typeToIr(constant.type);
}
void setParent(ir.Member member, Element element) {
if (element.isLocal) {
member.parent = elementToIr(element.enclosingElement);
} else if (element.isTopLevel) {
member.parent = elementToIr(element.library);
} else if (element.isClassMember) {
member.parent = elementToIr(element.enclosingClass);
} else {
member.parent = elementToIr(element.enclosingElement);
}
}
bool isNativeMethod(FunctionElement element) {
// This method is a (modified) copy of the same method in
// `pkg/compiler/lib/src/native/enqueue.dart`.
if (!compiler.backend.canLibraryUseNative(element.library)) return false;
return compiler.reporter.withCurrentElement(element, () {
FunctionExpression functionExpression =
element.node?.asFunctionExpression();
if (functionExpression == null) return false;
Node body = functionExpression.body;
if (body == null) return false;
if (identical(body.getBeginToken().stringValue, 'native')) return true;
return false;
});
}
ir.Member functionToIr(FunctionElement function) {
if (function.isDeferredLoaderGetter) {
internalError(function, "Deferred loader.");
}
if (function.isLocal) {
internalError(function, "Local function.");
}
if (isSyntheticError(function)) {
internalError(function, "Synthetic error function: $function.");
}
function = function.declaration;
return functions.putIfAbsent(function, () {
compiler.resolution.ensureResolved(function);
compiler.enqueuer.resolution.emptyDeferredQueueForTesting();
function = function.implementation;
ir.Member member;
ir.Constructor constructor;
ir.Procedure procedure;
ir.Name name = irName(function.name, function);
bool isNative = isNativeMethod(function);
if (function.isGenerativeConstructor) {
member = constructor = new ir.Constructor(null,
name: name,
isConst: function.isConst,
isExternal: isNative || function.isExternal,
initializers: null);
} else {
member = procedure = new ir.Procedure(name, null, null,
isAbstract: function.isAbstract,
isStatic: function.isStatic ||
function.isTopLevel ||
function.isFactoryConstructor,
isExternal: isNative || function.isExternal,
isConst: false); // TODO(ahe): When is this true?
}
addWork(function, () {
setParent(member, function);
KernelVisitor visitor =
new KernelVisitor(function, function.treeElements, this);
beginFactoryScope(function);
IrFunction irFunction = visitor.buildFunction();
// TODO(ahe): Add addFunction/set function to [ir.Procedure].
irFunction.node.parent = member;
if (irFunction.isConstructor) {
assert(irFunction.kind == null);
constructor.function = irFunction.node;
constructor.initializers = irFunction.initializers;
// TODO(ahe): Add setInitializers to [ir.Constructor].
for (ir.Initializer initializer in irFunction.initializers) {
initializer.parent = constructor;
}
} else {
assert(irFunction.kind != null);
procedure.function = irFunction.node;
procedure.kind = irFunction.kind;
}
endFactoryScope(function);
irFunction.node.typeParameters
.addAll(typeVariablesToIr(function.typeVariables));
member.transformerFlags = visitor.transformerFlags;
assert(() {
visitor.locals.forEach(checkMember);
return true;
});
});
addWork(function.declaration, () {
for (MetadataAnnotation metadata in function.declaration.metadata) {
member.addAnnotation(
const ConstantVisitor().visit(metadata.constant, this));
}
});
return member;
});
}
/// Adds the type parameters of the enclosing class of [function] to
/// [factoryTypeParameters]. This serves as a local scope for type variables
/// resolved inside the factory.
///
/// This method solves the problem that a factory method really is a generic
/// method that has its own type parameters, one for each type parameter in
/// the enclosing class.
void beginFactoryScope(FunctionElement function) {
assert(factoryTypeParameters.isEmpty);
if (!function.isFactoryConstructor) return;
ClassElement cls = function.enclosingClass;
for (DartType type in cls.typeVariables) {
if (type.isTypeVariable) {
TypeVariableElement variable = type.element;
factoryTypeParameters[variable] =
new ir.TypeParameter(variable.name, null);
}
}
for (DartType type in cls.typeVariables) {
if (type.isTypeVariable) {
TypeVariableElement variable = type.element;
factoryTypeParameters[variable].bound = typeToIr(variable.bound);
}
}
}
/// Ends the local scope started by [beginFactoryScope].
void endFactoryScope(FunctionElement function) {
factoryTypeParameters.clear();
}
ir.Field fieldToIr(FieldElement field) {
if (isSyntheticError(field)) {
internalError(field, "Synthetic error field: $field.");
}
field = field.declaration;
return fields.putIfAbsent(field, () {
compiler.resolution.ensureResolved(field);
compiler.enqueuer.resolution.emptyDeferredQueueForTesting();
field = field.implementation;
ir.DartType type =
field.isMalformed ? const ir.InvalidType() : typeToIr(field.type);
ir.Field fieldNode = new ir.Field(irName(field.memberName.text, field),
type: type,
initializer: null,
isFinal: field.isFinal,
isStatic: field.isStatic || field.isTopLevel,
isConst: field.isConst);
addWork(field, () {
setParent(fieldNode, field);
if (!field.isMalformed) {
if (field.initializer != null) {
KernelVisitor visitor =
new KernelVisitor(field, field.treeElements, this);
fieldNode.initializer = visitor.buildInitializer()
..parent = fieldNode;
} else if (!field.isInstanceMember) {
fieldNode.initializer = new ir.NullLiteral()..parent = fieldNode;
}
}
});
addWork(field.declaration, () {
for (MetadataAnnotation metadata in field.declaration.metadata) {
fieldNode.addAnnotation(
const ConstantVisitor().visit(metadata.constant, this));
}
});
return fieldNode;
});
}
ir.TypeParameter typeVariableToIr(TypeVariableElement variable) {
variable = variable.declaration;
ir.TypeParameter parameter = factoryTypeParameters[variable];
if (parameter != null) return parameter;
return typeParameters.putIfAbsent(variable, () {
ir.TypeParameter parameter = new ir.TypeParameter(variable.name, null);
addWork(variable, () {
if (variable.typeDeclaration.isClass) {
ClassElement cls = variable.typeDeclaration;
cls.ensureResolved(compiler.resolution);
parameter.parent = classToIr(cls);
} else {
FunctionElement method = variable.typeDeclaration;
parameter.parent = functionToIr(method).function;
}
parameter.bound = typeToIr(variable.bound);
});
return parameter;
});
}
List<ir.TypeParameter> typeVariablesToIr(List<DartType> variables) {
List<ir.TypeParameter> result =
new List<ir.TypeParameter>(variables.length);
for (int i = 0; i < variables.length; i++) {
TypeVariableType type = variables[i];
result[i] = typeVariableToIr(type.element);
}
return result;
}
ir.TreeNode elementToIr(Element element) {
if (element.isLibrary) return libraryToIr(element);
if (element.isClass) return classToIr(element);
if (element.isFunction || element.isAccessor) return functionToIr(element);
if (element.isField) return fieldToIr(element);
throw "unhandled element: $element";
}
void debugMessage(Spannable spannable, String message) {
compiler.reporter
.reportHintMessage(spannable, MessageKind.GENERIC, {'text': message});
}
void internalError(Spannable spannable, String message) {
compiler.reporter.internalError(spannable, message);
throw message;
}
forEachLibraryElement(f(LibraryElement library)) {
return compiler.libraryLoader.libraries.forEach(f);
}
ConstructorTarget computeEffectiveTarget(
ConstructorElement constructor, DartType type) {
constructor = constructor.implementation;
Set<ConstructorElement> seen = new Set<ConstructorElement>();
functionToIr(constructor);
while (constructor != constructor.effectiveTarget) {
type = constructor.computeEffectiveTargetType(type);
if (constructor.isGenerativeConstructor) break;
if (!seen.add(constructor)) break;
constructor = constructor.effectiveTarget.implementation;
if (isSyntheticError(constructor)) break;
functionToIr(constructor);
}
return new ConstructorTarget(constructor, type);
}
/// Compute all the dependencies on the library with [uri] (including the
/// library itself). This is useful for creating a Kernel IR `Program`.
List<ir.Library> libraryDependencies(Uri uri) {
List<ir.Library> result = <ir.Library>[];
Queue<LibraryElement> notProcessed = new Queue<LibraryElement>();
Set<LibraryElement> seen = new Set<LibraryElement>();
LibraryElement library = compiler.libraryLoader.lookupLibrary(uri);
void processLater(LibraryElement library) {
if (library != null) {
notProcessed.addLast(library);
}
}
processLater(library);
seen.add(library);
LibraryElement core =
compiler.libraryLoader.lookupLibrary(Uri.parse("dart:core"));
if (seen.add(core)) {
// `dart:core` is implicitly imported by most libraries, and for some
// reason not included in `library.imports` below.
processLater(core);
}
while (notProcessed.isNotEmpty) {
LibraryElement library = notProcessed.removeFirst();
ir.Library irLibrary = libraryToIr(library);
for (ImportElement import in library.imports) {
if (seen.add(import.importedLibrary)) {
processLater(import.importedLibrary);
}
}
for (ExportElement export in library.exports) {
if (seen.add(export.exportedLibrary)) {
processLater(export.exportedLibrary);
}
}
for (ImportElement import in library.implementation.imports) {
if (seen.add(import.importedLibrary)) {
processLater(import.importedLibrary);
}
}
for (ExportElement export in library.implementation.exports) {
if (seen.add(export.exportedLibrary)) {
processLater(export.exportedLibrary);
}
}
if (irLibrary != null) {
result.add(irLibrary);
}
}
processWorkQueue();
return result;
}
/// Returns true if [element] is synthesized to recover or represent a
/// semantic error, for example, missing, duplicated, or ambiguous elements.
/// However, returns false for elements that have an unrecoverable syntax
/// error. Both kinds of element will return true from [Element.isMalformed],
/// but they must be handled differently. For example, a static call to
/// synthetic error element should be compiled to [ir.InvalidExpression],
/// whereas a static call to a method which has a syntax error should be
/// compiled to a static call to the method. The method itself will have a
/// method body that is [ir.InvalidStatement].
bool isSyntheticError(Element element) {
if (element.isAmbiguous) return true;
if (element.isError) return true;
if (element.isField && element is ErroneousFieldElementX) {
return true;
}
return false;
}
ir.Constructor getDartCoreConstructor(
String className, String constructorName) {
LibraryElement library =
compiler.libraryLoader.lookupLibrary(Uris.dart_core);
ClassElement cls = library.implementation.localLookup(className);
assert(invariant(CURRENT_ELEMENT_SPANNABLE, cls != null,
message: 'dart:core class $className not found.'));
ConstructorElement constructor = cls.lookupConstructor(constructorName);
assert(invariant(CURRENT_ELEMENT_SPANNABLE, constructor != null,
message: "Constructor '$constructorName' not found "
"in class '$className'."));
return functionToIr(constructor);
}
ir.Procedure getDartCoreMethod(String name) {
LibraryElement library =
compiler.libraryLoader.lookupLibrary(Uris.dart_core);
Element function = library.implementation.localLookup(name);
assert(invariant(CURRENT_ELEMENT_SPANNABLE, function != null,
message: "dart:core method '$name' not found."));
return functionToIr(function);
}
ir.Procedure getMalformedTypeErrorBuilder() {
return getDartCoreMethod('_malformedTypeError');
}
ir.Procedure getUnresolvedConstructorBuilder() {
return getDartCoreMethod('_unresolvedConstructorError');
}
ir.Procedure getUnresolvedStaticGetterBuilder() {
return getDartCoreMethod('_unresolvedStaticGetterError');
}
ir.Procedure getUnresolvedStaticSetterBuilder() {
return getDartCoreMethod('_unresolvedStaticSetterError');
}
ir.Procedure getUnresolvedStaticMethodBuilder() {
return getDartCoreMethod('_unresolvedStaticMethodError');
}
ir.Procedure getUnresolvedTopLevelGetterBuilder() {
return getDartCoreMethod('_unresolvedTopLevelGetterError');
}
ir.Procedure getUnresolvedTopLevelSetterBuilder() {
return getDartCoreMethod('_unresolvedTopLevelSetterError');
}
ir.Procedure getUnresolvedTopLevelMethodBuilder() {
return getDartCoreMethod('_unresolvedTopLevelMethodError');
}
ir.Procedure getUnresolvedSuperGetterBuilder() {
return getDartCoreMethod('_unresolvedSuperGetterError');
}
ir.Procedure getUnresolvedSuperSetterBuilder() {
return getDartCoreMethod('_unresolvedSuperSetterError');
}
ir.Procedure getUnresolvedSuperMethodBuilder() {
return getDartCoreMethod('_unresolvedSuperMethodError');
}
ir.Procedure getGenericNoSuchMethodBuilder() {
return getDartCoreMethod('_genericNoSuchMethod');
}
ir.Constructor getFallThroughErrorConstructor() {
return getDartCoreConstructor('FallThroughError', '');
}
}
class ConstructorTarget {
final ConstructorElement element;
final DartType type;
ConstructorTarget(this.element, this.type);
String toString() => "ConstructorTarget($element, $type)";
}