Add typedef AST node boilerplate.

This adds the Typedef and TypedefType to the kernel AST,
with the relevant boilerplate.

Typedefs are not implemented in frontend or backend.

R=ahe@google.com, kmillikin@google.com

Committed: d1c3ee4b35
Review-Url: https://codereview.chromium.org/2825053002 .
This commit is contained in:
Asger Feldthaus 2017-04-28 13:26:44 +02:00
parent 3f1aa32183
commit ac40d0bb8e
20 changed files with 709 additions and 8 deletions

View file

@ -44,7 +44,8 @@ class FastaVerifyingVisitor extends VerifyingVisitor
}
@override
problem(TreeNode node, String details) {
problem(TreeNode node, String details, {TreeNode context}) {
context ??= this.context;
VerificationError error = new VerificationError(context, node, details);
printUnexpected(Uri.parse(fileUri), node?.fileOffset ?? -1, "$error");
errors.add(error);

View file

@ -228,6 +228,13 @@ class Reference {
}
return node as Procedure;
}
Typedef get asTypedef {
if (node == null) {
throw '$this is not bound to an AST node. A typedef was expected';
}
return node as Typedef;
}
}
// ------------------------------------------------------------------------
@ -258,6 +265,7 @@ class Library extends NamedNode implements Comparable<Library> {
String name;
final List<DeferredImport> deferredImports;
final List<Typedef> typedefs;
final List<Class> classes;
final List<Procedure> procedures;
final List<Field> fields;
@ -266,16 +274,20 @@ class Library extends NamedNode implements Comparable<Library> {
{this.name,
this.isExternal: false,
List<DeferredImport> imports,
List<Typedef> typedefs,
List<Class> classes,
List<Procedure> procedures,
List<Field> fields,
this.fileUri,
Reference reference})
: this.deferredImports = imports ?? <DeferredImport>[],
this.typedefs = typedefs ?? <Typedef>[],
this.classes = classes ?? <Class>[],
this.procedures = procedures ?? <Procedure>[],
this.fields = fields ?? <Field>[],
super(reference) {
setParents(this.deferredImports, this);
setParents(this.typedefs, this);
setParents(this.classes, this);
setParents(this.procedures, this);
setParents(this.fields, this);
@ -304,8 +316,16 @@ class Library extends NamedNode implements Comparable<Library> {
classes.add(class_);
}
void addTypedef(Typedef typedef_) {
typedef_.parent = this;
typedefs.add(typedef_);
}
void computeCanonicalNames() {
assert(canonicalName != null);
for (var typedef_ in typedefs) {
canonicalName.getChildFromTypedef(typedef_).bindTo(typedef_.reference);
}
for (var field in fields) {
canonicalName.getChildFromMember(field).bindTo(field.reference);
}
@ -321,12 +341,16 @@ class Library extends NamedNode implements Comparable<Library> {
accept(TreeVisitor v) => v.visitLibrary(this);
visitChildren(Visitor v) {
visitList(deferredImports, v);
visitList(typedefs, v);
visitList(classes, v);
visitList(procedures, v);
visitList(fields, v);
}
transformChildren(Transformer v) {
transformList(deferredImports, v, this);
transformList(typedefs, v, this);
transformList(classes, v, this);
transformList(procedures, v, this);
transformList(fields, v, this);
@ -366,6 +390,51 @@ class DeferredImport extends TreeNode {
transformChildren(Transformer v) {}
}
/// Declaration of a type alias.
class Typedef extends NamedNode {
/// The uri of the source file that contains the declaration of this typedef.
String fileUri;
List<Expression> annotations = const <Expression>[];
String name;
final List<TypeParameter> typeParameters;
DartType type;
Typedef(this.name, this.type,
{Reference reference, this.fileUri, List<TypeParameter> typeParameters})
: this.typeParameters = typeParameters ?? <TypeParameter>[],
super(reference) {
setParents(this.typeParameters, this);
}
Library get enclosingLibrary => parent;
accept(TreeVisitor v) {
return v.visitTypedef(this);
}
transformChildren(Transformer v) {
transformList(annotations, v, this);
transformList(typeParameters, v, this);
if (type != null) {
type = v.visitDartType(type);
}
}
visitChildren(Visitor v) {
visitList(annotations, v);
visitList(typeParameters, v);
type?.accept(v);
}
void addAnnotation(Expression node) {
if (annotations.isEmpty) {
annotations = <Expression>[];
}
annotations.add(node);
node.parent = this;
}
}
/// The degree to which the contents of a class have been loaded into memory.
///
/// Each level imply the requirements of the previous ones.
@ -3695,6 +3764,16 @@ abstract class DartType extends Node {
accept(DartTypeVisitor v);
bool operator ==(Object other);
/// If this is a typedef type, repeatedly unfolds its type definition until
/// the root term is not a typedef type, otherwise returns the type itself.
///
/// Will never return a typedef type.
DartType get unalias => this;
/// If this is a typedef type, unfolds its type definition once, otherwise
/// returns the type itself.
DartType get unaliasOnce => this;
}
/// The type arising from invalid type annotations.
@ -3924,6 +4003,60 @@ class FunctionType extends DartType {
}
}
/// A use of a [Typedef] as a type.
///
/// The underlying type can be extracted using [unalias].
class TypedefType extends DartType {
final Reference typedefReference;
final List<DartType> typeArguments;
TypedefType(Typedef typedefNode, [List<DartType> typeArguments])
: this.byReference(
typedefNode.reference, typeArguments ?? const <DartType>[]);
TypedefType.byReference(this.typedefReference, this.typeArguments);
Typedef get typedefNode => typedefReference.asTypedef;
accept(DartTypeVisitor v) => v.visitTypedefType(this);
visitChildren(Visitor v) {
visitList(typeArguments, v);
v.visitTypedefReference(typedefNode);
}
DartType get unaliasOnce {
return Substitution.fromTypedefType(this).substituteType(typedefNode.type);
}
DartType get unalias {
return unaliasOnce.unalias;
}
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is TypedefType) {
if (typedefReference != other.typedefReference ||
typeArguments.length != other.typeArguments.length) {
return false;
}
for (int i = 0; i < typeArguments.length; ++i) {
if (typeArguments[i] != other.typeArguments[i]) return false;
}
return true;
}
return false;
}
int get hashCode {
int hash = 0x3fffffff & typedefNode.hashCode;
for (int i = 0; i < typeArguments.length; ++i) {
hash = 0x3fffffff & (hash * 31 + (hash ^ typeArguments[i].hashCode));
}
return hash;
}
}
/// A named parameter in [FunctionType].
class NamedType extends Node implements Comparable<NamedType> {
final String name;
@ -4323,3 +4456,15 @@ CanonicalName getCanonicalNameOfLibrary(Library library) {
}
return library.canonicalName;
}
/// Returns the canonical name of [typedef_], or throws an exception if the
/// typedef has not been assigned a canonical name yet.
///
/// Returns `null` if the typedef is `null`.
CanonicalName getCanonicalNameOfTypedef(Typedef typedef_) {
if (typedef_ == null) return null;
if (typedef_.canonicalName == null) {
throw '$typedef_ has no canonical name';
}
return typedef_.canonicalName;
}

View file

@ -313,6 +313,10 @@ class BinaryBuilder {
return name?.getReference();
}
Reference readTypedefReference() {
return readCanonicalNameReference().getReference();
}
Name readName() {
String text = readStringReference();
if (text.isNotEmpty && text[0] == '_') {
@ -350,6 +354,7 @@ class BinaryBuilder {
debugPath.add(library.name ?? library.importUri?.toString() ?? 'library');
_mergeNamedNodeList(library.typedefs, readTypedef, library);
_mergeNamedNodeList(library.classes, readClass, library);
_mergeNamedNodeList(library.fields, readField, library);
_mergeNamedNodeList(library.procedures, readProcedure, library);
@ -373,6 +378,29 @@ class BinaryBuilder {
}
}
Typedef readTypedef() {
var canonicalName = readCanonicalNameReference();
var reference = canonicalName.getReference();
Typedef node = reference.node;
bool shouldWriteData = node == null || _isReadingLibraryImplementation;
if (node == null) {
node = new Typedef(null, null, reference: reference);
}
int fileOffset = readOffset();
String name = readStringReference();
String fileUri = readUriReference();
readAndPushTypeParameterList(node.typeParameters, node);
var type = readDartType();
typeParameterStack.length = 0;
if (shouldWriteData) {
node.fileOffset = fileOffset;
node.name = name;
node.fileUri = fileUri;
node.type = type;
}
return node;
}
Class readClass() {
int tag = readByte();
assert(tag == Tag.Class);
@ -1019,6 +1047,9 @@ class BinaryBuilder {
DartType readDartType() {
int tag = readByte();
switch (tag) {
case Tag.TypedefType:
return new TypedefType.byReference(
readTypedefReference(), readDartTypeList());
case Tag.VectorType:
return const VectorType();
case Tag.BottomType:

View file

@ -251,6 +251,7 @@ class BinaryPrinter extends Visitor {
// TODO(jensj): We save (almost) the same URI twice.
writeUriReference(node.fileUri ?? '');
writeDeferredImports(node);
writeNodeList(node.typedefs);
writeNodeList(node.classes);
writeNodeList(node.fields);
writeNodeList(node.procedures);
@ -273,6 +274,15 @@ class BinaryPrinter extends Visitor {
writeStringReference(node.name);
}
void visitTypedef(Typedef node) {
writeCanonicalNameReference(getCanonicalNameOfTypedef(node));
writeOffset(node.fileOffset);
writeStringReference(node.name);
writeUriReference(node.fileUri ?? '');
writeNodeList(node.typeParameters);
writeNode(node.type);
}
void writeAnnotation(Expression annotation) {
_variableIndexer ??= new VariableIndexer();
writeNode(annotation);
@ -1009,6 +1019,12 @@ class BinaryPrinter extends Visitor {
writeByte(Tag.VectorType);
}
visitTypedefType(TypedefType node) {
writeByte(Tag.TypedefType);
writeReference(node.typedefReference);
writeNodeList(node.typeArguments);
}
visitTypeParameter(TypeParameter node) {
writeStringReference(node.name ?? '');
writeNode(node.bound);

View file

@ -88,6 +88,7 @@ class Tag {
static const int FunctionDeclaration = 79;
static const int AsyncForInStatement = 80;
static const int TypedefType = 87;
static const int VectorType = 88;
static const int BottomType = 89;
static const int InvalidType = 90;

View file

@ -104,6 +104,10 @@ class CanonicalName {
.getChildFromQualifiedName(member.name);
}
CanonicalName getChildFromTypedef(Typedef typedef_) {
return getChild('@typedefs').getChild(typedef_.name);
}
void bindTo(Reference target) {
if (reference == target) return;
if (reference != null) {

View file

@ -210,6 +210,12 @@ class Printer extends Visitor<Null> {
return '$library::$name';
}
String getTypedefReference(Typedef node) {
if (node == null) return '<No Typedef>';
String library = getLibraryReference(node.enclosingLibrary);
return '$library::${node.name}';
}
static final String emptyNameString = '';
static final Name emptyName = new Name(emptyNameString);
@ -281,6 +287,7 @@ class Printer extends Visitor<Null> {
}
endLine();
var inner = new Printer._inner(this, imports);
library.typedefs.forEach(inner.writeNode);
library.classes.forEach(inner.writeNode);
library.fields.forEach(inner.writeNode);
library.procedures.forEach(inner.writeNode);
@ -313,6 +320,7 @@ class Printer extends Visitor<Null> {
writeWord(prefix);
endLine(' {');
++inner.indentation;
library.typedefs.forEach(inner.writeNode);
library.classes.forEach(inner.writeNode);
library.fields.forEach(inner.writeNode);
library.procedures.forEach(inner.writeNode);
@ -429,6 +437,15 @@ class Printer extends Visitor<Null> {
writeWord('Vector');
}
visitTypedefType(TypedefType type) {
writeTypedefReference(type.typedefNode);
if (type.typeArguments.isNotEmpty) {
writeSymbol('<');
writeList(type.typeArguments, writeType);
writeSymbol('>');
}
}
void writeModifier(bool isThere, String name) {
if (isThere) {
writeWord(name);
@ -610,6 +627,10 @@ class Printer extends Visitor<Null> {
writeWord(getClassReference(classNode));
}
void writeTypedefReference(Typedef typedefNode) {
writeWord(getTypedefReference(typedefNode));
}
void writeLibraryReference(Library library) {
writeWord(getLibraryReference(library));
}
@ -751,6 +772,16 @@ class Printer extends Visitor<Null> {
endLine('}');
}
visitTypedef(Typedef node) {
writeIndentation();
writeWord('typedef');
writeWord(node.name);
writeTypeParameterList(node.typeParameters);
writeSpaced('=');
writeNode(node.type);
endLine(';');
}
visitInvalidExpression(InvalidExpression node) {
writeWord('invalid-expression');
}

View file

@ -59,6 +59,11 @@ class Erasure extends Transformer with DartTypeVisitor<DartType> {
return type;
}
@override
TypedefType visitTypedefType(TypedefType type) {
throw 'Typedef types not implemented in erasure';
}
@override
Supertype visitSupertype(Supertype type) {
if (removeTypeParameters(type.classNode)) {

View file

@ -1056,6 +1056,10 @@ class _ExternalTypeVisitor extends DartTypeVisitor {
}
}
visitTypedefType(TypedefType node) {
throw 'TypedefType is not implemented in tree shaker';
}
visitFunctionType(FunctionType node) {
visit(node.returnType);
for (int i = 0; i < node.positionalParameters.length; ++i) {

View file

@ -173,6 +173,14 @@ abstract class Substitution {
type.classNode.typeParameters, type.typeArguments));
}
/// Substitutes the type parameters on the typedef of [type] with the
/// type arguments provided in [type].
static Substitution fromTypedefType(TypedefType type) {
if (type.typeArguments.isEmpty) return _NullSubstitution.instance;
return fromMap(new Map<TypeParameter, DartType>.fromIterables(
type.typedefNode.typeParameters, type.typeArguments));
}
/// Substitutes the Nth parameter in [parameters] with the Nth type in
/// [types].
static Substitution fromPairs(
@ -375,6 +383,14 @@ abstract class _TypeSubstitutor extends DartTypeVisitor<DartType> {
return new InterfaceType(node.classNode, typeArguments);
}
DartType visitTypedefType(TypedefType node) {
if (node.typeArguments.isEmpty) return node;
int before = useCounter;
var typeArguments = node.typeArguments.map(visit).toList();
if (useCounter == before) return node;
return new TypedefType(node.typedefNode, typeArguments);
}
List<TypeParameter> freshTypeParameters(List<TypeParameter> parameters) {
if (parameters.isEmpty) return const <TypeParameter>[];
return parameters.map(freshTypeParameter).toList();
@ -655,6 +671,10 @@ class _OccurrenceVisitor extends DartTypeVisitor<bool> {
return node.typeArguments.any(visit);
}
bool visitTypedefType(TypedefType node) {
return node.typeArguments.any(visit);
}
bool visitFunctionType(FunctionType node) {
return node.typeParameters.any(handleTypeParameter) ||
node.positionalParameters.any(visit) ||

View file

@ -145,6 +145,8 @@ abstract class SubtypeTester {
/// Returns true if [subtype] is a subtype of [supertype].
bool isSubtypeOf(DartType subtype, DartType supertype) {
subtype = subtype.unalias;
supertype = supertype.unalias;
if (identical(subtype, supertype)) return true;
if (subtype is BottomType) return true;
if (supertype is DynamicType ||

View file

@ -38,13 +38,17 @@ class VerificationError {
}
}
enum TypedefState { Done, BeingChecked }
/// Checks that a kernel program is well-formed.
///
/// This does not include any kind of type checking.
class VerifyingVisitor extends RecursiveVisitor {
final Set<Class> classes = new Set<Class>();
final Set<TypeParameter> typeParameters = new Set<TypeParameter>();
final Set<Typedef> typedefs = new Set<Typedef>();
Set<TypeParameter> typeParametersInScope = new Set<TypeParameter>();
final List<VariableDeclaration> variableStack = <VariableDeclaration>[];
final Map<Typedef, TypedefState> typedefState = <Typedef, TypedefState>{};
bool classTypeParametersAreInScope = false;
/// If true, relax certain checks for *outline* mode. For example, don't
@ -67,7 +71,8 @@ class VerifyingVisitor extends RecursiveVisitor {
visitChildren(node);
}
problem(TreeNode node, String details) {
problem(TreeNode node, String details, {TreeNode context}) {
context ??= this.context;
throw new VerificationError(context, node, details);
}
@ -75,7 +80,8 @@ class VerifyingVisitor extends RecursiveVisitor {
if (!identical(node.parent, currentParent)) {
problem(
node,
"Incorrect parent pointer: expected '${node.parent.runtimeType}',"
"Incorrect parent pointer on ${node.runtimeType}:"
" expected '${node.parent.runtimeType}',"
" but found: '${currentParent.runtimeType}'.");
}
var oldParent = currentParent;
@ -135,14 +141,14 @@ class VerifyingVisitor extends RecursiveVisitor {
void declareTypeParameters(List<TypeParameter> parameters) {
for (int i = 0; i < parameters.length; ++i) {
var parameter = parameters[i];
if (!typeParameters.add(parameter)) {
if (!typeParametersInScope.add(parameter)) {
problem(parameter, "Type parameter '$parameter' redeclared.");
}
}
}
void undeclareTypeParameters(List<TypeParameter> parameters) {
typeParameters.removeAll(parameters);
typeParametersInScope.removeAll(parameters);
}
void checkVariableInScope(VariableDeclaration variable, TreeNode where) {
@ -159,6 +165,11 @@ class VerifyingVisitor extends RecursiveVisitor {
problem(class_, "Class '$class_' declared more than once.");
}
}
for (var typedef_ in library.typedefs) {
if (!typedefs.add(typedef_)) {
problem(typedef_, "Typedef '$typedef_' declared more than once.");
}
}
library.members.forEach(declareMember);
for (var class_ in library.classes) {
class_.members.forEach(declareMember);
@ -176,6 +187,32 @@ class VerifyingVisitor extends RecursiveVisitor {
}
}
void checkTypedef(Typedef node) {
var state = typedefState[node];
if (state == TypedefState.Done) return;
if (state == TypedefState.BeingChecked) {
problem(node, "The typedef '$node' refers to itself", context: node);
}
assert(state == null);
typedefState[node] = TypedefState.BeingChecked;
var savedTypeParameters = typeParametersInScope;
typeParametersInScope = node.typeParameters.toSet();
var savedParent = currentParent;
currentParent = node;
// Visit children without checking the parent pointer on the typedef itself
// since this can be called from a context other than its true parent.
node.visitChildren(this);
currentParent = savedParent;
typeParametersInScope = savedTypeParameters;
typedefState[node] = TypedefState.Done;
}
visitTypedef(Typedef node) {
checkTypedef(node);
// Enter and exit the node to check the parent pointer on the typedef node.
exitParent(enterParent(node));
}
visitField(Field node) {
currentMember = node;
var oldParent = enterParent(node);
@ -184,6 +221,7 @@ class VerifyingVisitor extends RecursiveVisitor {
classTypeParametersAreInScope = false;
visitList(node.annotations, this);
exitParent(oldParent);
node.type.accept(this);
currentMember = null;
}
@ -491,10 +529,18 @@ class VerifyingVisitor extends RecursiveVisitor {
}
}
@override
visitTypedefReference(Typedef node) {
if (!typedefs.contains(node)) {
problem(
node, "Dangling reference to '$node', parent is: '${node.parent}'");
}
}
@override
visitTypeParameterType(TypeParameterType node) {
var parameter = node.parameter;
if (!typeParameters.contains(parameter)) {
if (!typeParametersInScope.contains(parameter)) {
problem(
currentParent,
"Type parameter '$parameter' referenced out of"
@ -519,6 +565,19 @@ class VerifyingVisitor extends RecursiveVisitor {
" ${node.classNode.typeParameters.length} parameters.");
}
}
@override
visitTypedefType(TypedefType node) {
checkTypedef(node.typedefNode);
node.visitChildren(this);
if (node.typeArguments.length != node.typedefNode.typeParameters.length) {
problem(
currentParent,
"The typedef type $node provides ${node.typeArguments.length}"
" type arguments but the typedef declares"
" ${node.typedefNode.typeParameters.length} parameters.");
}
}
}
class CheckParentPointers extends Visitor {

View file

@ -221,6 +221,7 @@ class TreeVisitor<R>
// Other tree nodes
R visitLibrary(Library node) => defaultTreeNode(node);
R visitDeferredImport(DeferredImport node) => defaultTreeNode(node);
R visitTypedef(Typedef node) => defaultTreeNode(node);
R visitTypeParameter(TypeParameter node) => defaultTreeNode(node);
R visitFunctionNode(FunctionNode node) => defaultTreeNode(node);
R visitArguments(Arguments node) => defaultTreeNode(node);
@ -242,6 +243,7 @@ class DartTypeVisitor<R> {
R visitVectorType(VectorType node) => defaultDartType(node);
R visitFunctionType(FunctionType node) => defaultDartType(node);
R visitTypeParameterType(TypeParameterType node) => defaultDartType(node);
R visitTypedefType(TypedefType node) => defaultDartType(node);
}
class MemberReferenceVisitor<R> {
@ -268,9 +270,11 @@ class Visitor<R> extends TreeVisitor<R>
R visitVectorType(VectorType node) => defaultDartType(node);
R visitFunctionType(FunctionType node) => defaultDartType(node);
R visitTypeParameterType(TypeParameterType node) => defaultDartType(node);
R visitTypedefType(TypedefType node) => defaultDartType(node);
// Class references
R visitClassReference(Class node) => null;
R visitTypedefReference(Typedef node) => null;
// Member references
R defaultMemberReference(Member node) => null;

View file

@ -141,6 +141,8 @@ class DartTypeParser {
return fail('Unresolved type $name');
} else if (target is Class) {
return new InterfaceType(target, parseOptionalTypeArgumentList());
} else if (target is Typedef) {
return new TypedefType(target, parseOptionalTypeArgumentList());
} else if (target is TypeParameter) {
if (peekToken() == Token.LeftAngle) {
return fail('Attempt to apply type arguments to a type variable');

View file

@ -0,0 +1,68 @@
// 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 file.
library kernel.typedef_unalias_test;
import 'package:kernel/ast.dart';
import 'package:test/test.dart';
import 'verify_test.dart' show TestHarness;
void harnessTest(String name, void doTest(TestHarness harness)) {
test(name, () {
doTest(new TestHarness());
});
}
main() {
harnessTest('`Foo` where typedef Foo = C', (TestHarness harness) {
var foo = new Typedef('Foo', harness.otherClass.rawType);
harness.enclosingLibrary.addTypedef(foo);
var type = new TypedefType(foo);
expect(type.unalias, equals(harness.otherClass.rawType));
});
harnessTest('`Foo<Obj>` where typedef Foo<T> = C<T>', (TestHarness harness) {
var param = harness.makeTypeParameter('T');
var foo = new Typedef('Foo',
new InterfaceType(harness.otherClass, [new TypeParameterType(param)]),
typeParameters: [param]);
harness.enclosingLibrary.addTypedef(foo);
var input = new TypedefType(foo, [harness.objectClass.rawType]);
var expected =
new InterfaceType(harness.otherClass, [harness.objectClass.rawType]);
expect(input.unalias, equals(expected));
});
harnessTest('`Bar<Obj>` where typedef Bar<T> = Foo<T>, Foo<T> = C<T>',
(TestHarness harness) {
var fooParam = harness.makeTypeParameter('T');
var foo = new Typedef(
'Foo',
new InterfaceType(
harness.otherClass, [new TypeParameterType(fooParam)]),
typeParameters: [fooParam]);
var barParam = harness.makeTypeParameter('T');
var bar = new Typedef(
'Bar', new TypedefType(foo, [new TypeParameterType(barParam)]),
typeParameters: [barParam]);
harness.enclosingLibrary.addTypedef(foo);
harness.enclosingLibrary.addTypedef(bar);
var input = new TypedefType(bar, [harness.objectClass.rawType]);
var expected =
new InterfaceType(harness.otherClass, [harness.objectClass.rawType]);
expect(input.unalias, equals(expected));
});
harnessTest('`Foo<Foo<C>>` where typedef Foo<T> = C<T>',
(TestHarness harness) {
var param = harness.makeTypeParameter('T');
var foo = new Typedef('Foo',
new InterfaceType(harness.otherClass, [new TypeParameterType(param)]),
typeParameters: [param]);
harness.enclosingLibrary.addTypedef(foo);
var input = new TypedefType(foo, [
new TypedefType(foo, [harness.objectClass.rawType])
]);
var expected = new InterfaceType(harness.otherClass, [
new TypedefType(foo, [harness.objectClass.rawType])
]);
expect(input.unalias, equals(expected));
});
}

View file

@ -199,6 +199,171 @@ main() {
test.enclosingClass.addMember(constructor);
return new ConstructorInvocation(constructor, new Arguments.empty());
});
positiveTest('Valid typedef Foo = `(C) => void`', (TestHarness test) {
var typedef_ = new Typedef(
'Foo', new FunctionType([test.otherClass.rawType], const VoidType()));
test.enclosingLibrary.addTypedef(typedef_);
});
positiveTest('Valid typedef Foo = C<dynamic>', (TestHarness test) {
var typedef_ = new Typedef(
'Foo', new InterfaceType(test.otherClass, [const DynamicType()]));
test.enclosingLibrary.addTypedef(typedef_);
});
positiveTest('Valid typedefs Foo = Bar, Bar = C', (TestHarness test) {
var foo = new Typedef('Foo', null);
var bar = new Typedef('Bar', null);
foo.type = new TypedefType(bar);
bar.type = test.otherClass.rawType;
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addTypedef(bar);
});
positiveTest('Valid typedefs Foo = C<Bar>, Bar = C', (TestHarness test) {
var foo = new Typedef('Foo', null);
var bar = new Typedef('Bar', null);
foo.type = new InterfaceType(test.otherClass, [new TypedefType(bar)]);
bar.type = test.otherClass.rawType;
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addTypedef(bar);
});
positiveTest('Valid typedef type in field', (TestHarness test) {
var typedef_ = new Typedef(
'Foo', new FunctionType([test.otherClass.rawType], const VoidType()));
var field = new Field(new Name('field'), type: new TypedefType(typedef_));
test.enclosingLibrary.addTypedef(typedef_);
test.enclosingLibrary.addMember(field);
});
negativeTest('Invalid typedef Foo = Foo', (TestHarness test) {
var typedef_ = new Typedef('Foo', null);
typedef_.type = new TypedefType(typedef_);
test.enclosingLibrary.addTypedef(typedef_);
});
negativeTest('Invalid typedef Foo = `(Foo) => void`', (TestHarness test) {
var typedef_ = new Typedef('Foo', null);
typedef_.type =
new FunctionType([new TypedefType(typedef_)], const VoidType());
test.enclosingLibrary.addTypedef(typedef_);
});
negativeTest('Invalid typedef Foo = `() => Foo`', (TestHarness test) {
var typedef_ = new Typedef('Foo', null);
typedef_.type = new FunctionType([], new TypedefType(typedef_));
test.enclosingLibrary.addTypedef(typedef_);
});
negativeTest('Invalid typedef Foo = C<Foo>', (TestHarness test) {
var typedef_ = new Typedef('Foo', null);
typedef_.type =
new InterfaceType(test.otherClass, [new TypedefType(typedef_)]);
test.enclosingLibrary.addTypedef(typedef_);
});
negativeTest('Invalid typedefs Foo = Bar, Bar = Foo', (TestHarness test) {
var foo = new Typedef('Foo', null);
var bar = new Typedef('Bar', null);
foo.type = new TypedefType(bar);
bar.type = new TypedefType(foo);
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addTypedef(bar);
});
negativeTest('Invalid typedefs Foo = Bar, Bar = C<Foo>', (TestHarness test) {
var foo = new Typedef('Foo', null);
var bar = new Typedef('Bar', null);
foo.type = new TypedefType(bar);
bar.type = new InterfaceType(test.otherClass, [new TypedefType(foo)]);
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addTypedef(bar);
});
negativeTest('Invalid typedefs Foo = C<Bar>, Bar = C<Foo>',
(TestHarness test) {
var foo = new Typedef('Foo', null);
var bar = new Typedef('Bar', null);
foo.type = new InterfaceType(test.otherClass, [new TypedefType(bar)]);
bar.type = new InterfaceType(test.otherClass, [new TypedefType(foo)]);
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addTypedef(bar);
});
positiveTest('Valid long typedefs C20 = C19 = ... = C1 = C0 = dynamic',
(TestHarness test) {
var typedef_ = new Typedef('C0', const DynamicType());
test.enclosingLibrary.addTypedef(typedef_);
for (int i = 1; i < 20; ++i) {
typedef_ = new Typedef('C$i', new TypedefType(typedef_));
test.enclosingLibrary.addTypedef(typedef_);
}
});
negativeTest('Invalid long typedefs C20 = C19 = ... = C1 = C0 = C20',
(TestHarness test) {
var typedef_ = new Typedef('C0', null);
test.enclosingLibrary.addTypedef(typedef_);
var first = typedef_;
for (int i = 1; i < 20; ++i) {
typedef_ = new Typedef('C$i', new TypedefType(typedef_));
test.enclosingLibrary.addTypedef(typedef_);
}
first.type = new TypedefType(typedef_);
});
positiveTest('Valid typedef Foo<T extends C> = C<T>', (TestHarness test) {
var param = new TypeParameter('T', test.otherClass.rawType);
var foo = new Typedef('Foo',
new InterfaceType(test.otherClass, [new TypeParameterType(param)]),
typeParameters: [param]);
test.enclosingLibrary.addTypedef(foo);
});
positiveTest('Valid typedef Foo<T extends C<T>> = C<T>', (TestHarness test) {
var param = new TypeParameter('T', test.otherClass.rawType);
param.bound =
new InterfaceType(test.otherClass, [new TypeParameterType(param)]);
var foo = new Typedef('Foo',
new InterfaceType(test.otherClass, [new TypeParameterType(param)]),
typeParameters: [param]);
test.enclosingLibrary.addTypedef(foo);
});
positiveTest('Valid typedef Foo<T> = dynamic, Bar<T extends Foo<T>> = C<T>',
(TestHarness test) {
var fooParam = test.makeTypeParameter('T');
var foo =
new Typedef('Foo', const DynamicType(), typeParameters: [fooParam]);
var barParam = new TypeParameter('T', null);
barParam.bound = new TypedefType(foo, [new TypeParameterType(barParam)]);
var bar = new Typedef('Bar',
new InterfaceType(test.otherClass, [new TypeParameterType(barParam)]),
typeParameters: [barParam]);
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addTypedef(bar);
});
negativeTest('Invalid typedefs Foo<T extends Bar<T>>, Bar<T extends Foo<T>>',
(TestHarness test) {
var fooParam = test.makeTypeParameter('T');
var foo =
new Typedef('Foo', const DynamicType(), typeParameters: [fooParam]);
var barParam = new TypeParameter('T', null);
barParam.bound = new TypedefType(foo, [new TypeParameterType(barParam)]);
var bar = new Typedef('Bar',
new InterfaceType(test.otherClass, [new TypeParameterType(barParam)]),
typeParameters: [barParam]);
fooParam.bound = new TypedefType(bar, [new TypeParameterType(fooParam)]);
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addTypedef(bar);
});
negativeTest('Invalid typedef Foo<T extends Foo<dynamic> = C<T>',
(TestHarness test) {
var param = new TypeParameter('T', null);
var foo = new Typedef('Foo',
new InterfaceType(test.otherClass, [new TypeParameterType(param)]),
typeParameters: [param]);
param.bound = new TypedefType(foo, [const DynamicType()]);
test.enclosingLibrary.addTypedef(foo);
});
negativeTest('Typedef arity error', (TestHarness test) {
var param = test.makeTypeParameter('T');
var foo =
new Typedef('Foo', test.otherClass.rawType, typeParameters: [param]);
var field = new Field(new Name('field'), type: new TypedefType(foo, []));
test.enclosingLibrary.addTypedef(foo);
test.enclosingLibrary.addMember(field);
});
negativeTest('Dangling typedef reference', (TestHarness test) {
var foo = new Typedef('Foo', test.otherClass.rawType, typeParameters: []);
var field = new Field(new Name('field'), type: new TypedefType(foo, []));
test.enclosingLibrary.addMember(field);
});
}
checkHasError(Program program) {

View file

@ -84,6 +84,20 @@ void Library::VisitChildren(Visitor* visitor) {
}
Typedef::~Typedef() {}
void Typedef::AcceptTreeVisitor(TreeVisitor* visitor) {
visitor->VisitTypedef(this);
}
void Typedef::VisitChildren(Visitor* visitor) {
VisitList(&type_parameters(), visitor);
type()->AcceptDartTypeVisitor(visitor);
}
Class::~Class() {}
@ -1192,6 +1206,19 @@ void InterfaceType::VisitChildren(Visitor* visitor) {
}
TypedefType::~TypedefType() {}
void TypedefType::AcceptDartTypeVisitor(DartTypeVisitor* visitor) {
visitor->VisitTypedefType(this);
}
void TypedefType::VisitChildren(Visitor* visitor) {
VisitList(&type_arguments(), visitor);
}
FunctionType::~FunctionType() {}

View file

@ -22,10 +22,12 @@
M(InterfaceType) \
M(FunctionType) \
M(TypeParameterType) \
M(VectorType)
M(VectorType) \
M(TypedefType)
#define KERNEL_TREE_NODES_DO(M) \
M(Library) \
M(Typedef) \
M(Class) \
M(NormalClass) \
M(MixinClass) \
@ -375,6 +377,7 @@ KERNEL_VISITORS_DO(DO)
DEFINE_IS_OPERATION(TreeNode) \
KERNEL_TREE_NODES_DO(DEFINE_IS_OPERATION)
class Typedef;
class Class;
class Constructor;
class Field;
@ -488,6 +491,7 @@ class Library : public LinkedNode {
String* import_uri() { return import_uri_; }
intptr_t source_uri_index() { return source_uri_index_; }
String* name() { return name_; }
List<Typedef>& typedefs() { return typedefs_; }
List<Class>& classes() { return classes_; }
List<Field>& fields() { return fields_; }
List<Procedure>& procedures() { return procedures_; }
@ -504,6 +508,7 @@ class Library : public LinkedNode {
Ref<String> name_;
Ref<String> import_uri_;
intptr_t source_uri_index_;
List<Typedef> typedefs_;
List<Class> classes_;
List<Field> fields_;
List<Procedure> procedures_;
@ -514,6 +519,40 @@ class Library : public LinkedNode {
};
class Typedef : public LinkedNode {
public:
Typedef* ReadFrom(Reader* reader);
virtual ~Typedef();
DEFINE_CASTING_OPERATIONS(Typedef);
virtual void AcceptTreeVisitor(TreeVisitor* visitor);
virtual void VisitChildren(Visitor* visitor);
Library* parent() { return parent_; }
String* name() { return name_; }
intptr_t source_uri_index() { return source_uri_index_; }
TokenPosition position() { return position_; }
TypeParameterList& type_parameters() { return type_parameters_; }
DartType* type() { return type_; }
protected:
Typedef() : position_(TokenPosition::kNoSource) {}
private:
template <typename T>
friend class List;
Ref<Library> parent_;
Ref<String> name_;
intptr_t source_uri_index_;
TokenPosition position_;
TypeParameterList type_parameters_;
Child<DartType> type_;
};
class Class : public LinkedNode {
public:
Class* ReadFrom(Reader* reader);
@ -2755,6 +2794,32 @@ class InterfaceType : public DartType {
};
class TypedefType : public DartType {
public:
static TypedefType* ReadFrom(Reader* reader);
explicit TypedefType(CanonicalName* class_reference)
: typedef_reference_(class_reference) {}
virtual ~TypedefType();
DEFINE_CASTING_OPERATIONS(TypedefType);
virtual void AcceptDartTypeVisitor(DartTypeVisitor* visitor);
virtual void VisitChildren(Visitor* visitor);
CanonicalName* typedef_reference() { return typedef_reference_; }
List<DartType>& type_arguments() { return type_arguments_; }
private:
TypedefType() {}
Ref<CanonicalName> typedef_reference_; // Typedef.
List<DartType> type_arguments_;
DISALLOW_COPY_AND_ASSIGN(TypedefType);
};
class FunctionType : public DartType {
public:
static FunctionType* ReadFrom(Reader* reader);
@ -2899,6 +2964,8 @@ class Reference : public AllStatic {
static CanonicalName* ReadClassFrom(Reader* reader, bool allow_null = false);
static CanonicalName* ReadTypedefFrom(Reader* reader);
static String* ReadStringFrom(Reader* reader);
};
@ -3148,6 +3215,9 @@ class DartTypeVisitor {
VisitDefaultDartType(node);
}
virtual void VisitVectorType(VectorType* node) { VisitDefaultDartType(node); }
virtual void VisitTypedefType(TypedefType* node) {
VisitDefaultDartType(node);
}
};
@ -3187,6 +3257,7 @@ class TreeVisitor : public ExpressionVisitor,
virtual void VisitCatch(Catch* node) { VisitDefaultTreeNode(node); }
virtual void VisitMapEntry(MapEntry* node) { VisitDefaultTreeNode(node); }
virtual void VisitProgram(Program* node) { VisitDefaultTreeNode(node); }
virtual void VisitTypedef(Typedef* node) { VisitDefaultTreeNode(node); }
};

View file

@ -215,6 +215,11 @@ Library* Library::ReadFrom(Reader* reader) {
if (num_imports != 0) {
FATAL("Deferred imports not implemented in VM");
}
int num_typedefs = reader->ReadUInt();
typedefs().EnsureInitialized(num_typedefs);
for (intptr_t i = 0; i < num_typedefs; i++) {
typedefs().GetOrCreate<Typedef>(i, this)->ReadFrom(reader);
}
int num_classes = reader->ReadUInt();
classes().EnsureInitialized(num_classes);
for (intptr_t i = 0; i < num_classes; i++) {
@ -230,6 +235,20 @@ Library* Library::ReadFrom(Reader* reader) {
}
Typedef* Typedef::ReadFrom(Reader* reader) {
TRACE_READ_OFFSET();
canonical_name_ = reader->ReadCanonicalNameReference();
position_ = reader->ReadPosition(false);
name_ = Reference::ReadStringFrom(reader);
source_uri_index_ = reader->ReadUInt();
type_parameters_.ReadFrom(reader);
type_ = DartType::ReadFrom(reader);
return this;
}
Class* Class::ReadFrom(Reader* reader) {
TRACE_READ_OFFSET();
@ -313,6 +332,20 @@ CanonicalName* Reference::ReadClassFrom(Reader* reader, bool allow_null) {
}
CanonicalName* Reference::ReadTypedefFrom(Reader* reader) {
TRACE_READ_OFFSET();
CanonicalName* canonical_name = reader->ReadCanonicalNameReference();
if (canonical_name == NULL) {
FATAL("Expected a valid typedef reference, but got `null`");
}
canonical_name->set_referenced(true);
return canonical_name;
}
String* Reference::ReadStringFrom(Reader* reader) {
int index = reader->ReadUInt();
return reader->helper()->program()->string_table().strings()[index];
@ -1411,6 +1444,8 @@ DartType* DartType::ReadFrom(Reader* reader) {
return TypeParameterType::ReadFrom(reader);
case kVectorType:
return VectorType::ReadFrom(reader);
case kTypedefType:
return TypedefType::ReadFrom(reader);
default:
UNREACHABLE();
}
@ -1456,6 +1491,15 @@ InterfaceType* InterfaceType::ReadFrom(Reader* reader,
}
TypedefType* TypedefType::ReadFrom(Reader* reader) {
TRACE_READ_OFFSET();
CanonicalName* typedef_name = Reference::ReadTypedefFrom(reader);
TypedefType* type = new TypedefType(typedef_name);
type->type_arguments().ReadFromStatic<DartType>(reader);
return type;
}
FunctionType* FunctionType::ReadFrom(Reader* reader) {
TRACE_READ_OFFSET();
FunctionType* type = new FunctionType();

View file

@ -105,6 +105,7 @@ enum Tag {
kFunctionDeclaration = 79,
kAsyncForInStatement = 80,
kTypedefType = 87,
kVectorType = 88,
kInvalidType = 90,
kDynamicType = 91,