Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(143)

Unified Diff: frog/leg/resolver.dart

Issue 9391004: Implement cycle-checking and code generation for redirecting constructors. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments. Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | frog/leg/ssa/builder.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: frog/leg/resolver.dart
diff --git a/frog/leg/resolver.dart b/frog/leg/resolver.dart
index 1ed0cb449ee59a5fb557d0eb3f7855d1cbd1b3c2..5ff1962591ddef50f12a78dbd671f22beaee80a0 100644
--- a/frog/leg/resolver.dart
+++ b/frog/leg/resolver.dart
@@ -57,6 +57,56 @@ class ResolverTask extends CompilerTask {
});
}
+ SourceString getConstructorName(Send node) {
+ if (node.receiver !== null) {
+ return node.selector.asIdentifier().source;
+ } else {
+ return const SourceString('');
+ }
+ }
+
+ FunctionElement lookupConstructor(ClassElement classElement, Send send,
+ [noConstructor(Element)]) {
+ final SourceString constructorName = getConstructorName(send);
+ final SourceString className = classElement.name;
+ FunctionElement result = classElement.lookupConstructor(className,
+ constructorName,
+ noConstructor);
+ if (result === null && send.arguments.isEmpty()) {
+ result = classElement.getSynthesizedConstructor();
+ }
+ return result;
+ }
+
+ FunctionElement resolveConstructorRedirection(FunctionElement constructor) {
+ FunctionExpression node = constructor.parseNode(compiler);
+ // A synthetic constructor does not have a node.
+ if (node === null) return null;
+ if (node.initializers === null) return null;
+ Link<Node> initializers = node.initializers.nodes;
+ if (!initializers.isEmpty() &&
+ Initializers.isConstructorRedirect(initializers.head)) {
+ return lookupConstructor(constructor.enclosingElement, initializers.head);
+ }
+ return null;
+ }
+
+ void resolveRedirectingConstructor(InitializerResolver resolver,
+ Node node,
+ FunctionElement constructor,
+ FunctionElement redirection) {
+ Set<FunctionElement> seen = new Set<FunctionElement>();
+ seen.add(constructor);
+ while (redirection !== null) {
+ if (seen.contains(redirection)) {
+ resolver.visitor.error(node, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE);
+ return;
+ }
+ seen.add(redirection);
+ redirection = resolveConstructorRedirection(redirection);
+ }
+ }
+
TreeElements resolveMethodElement(FunctionElement element) {
if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR &&
constructorElements[element] !== null) {
@@ -67,8 +117,16 @@ class ResolverTask extends CompilerTask {
visitor.useElement(tree, element);
visitor.setupFunction(tree, element);
- if (tree.initializers != null) {
- new InitializerResolver(visitor, element).resolveInitializers(tree);
+ if (element.kind !== ElementKind.GENERATIVE_CONSTRUCTOR_BODY &&
+ tree.initializers != null) {
+ if (element.kind !== ElementKind.GENERATIVE_CONSTRUCTOR) {
+ error(tree, MessageKind.FUNCTION_WITH_INITIALIZER);
+ }
+ InitializerResolver resolver = new InitializerResolver(visitor);
+ FunctionElement redirection = resolver.resolveInitializers(element, tree);
+ if (redirection !== null) {
+ resolveRedirectingConstructor(resolver, tree, element, redirection);
+ }
}
visitor.visit(tree.body);
@@ -140,7 +198,6 @@ class ResolverTask extends CompilerTask {
[classElement.name]);
classElement.allSupertypes = const EmptyLink<Type>();
} else if (supertype != null) {
- Type supertype = classElement.supertype;
seen.add(classElement);
Link<Type> superSupertypes =
getOrCalculateAllSupertypes(supertype.element, seen);
@@ -161,19 +218,18 @@ class ResolverTask extends CompilerTask {
}
error(Node node, MessageKind kind, [arguments = const []]) {
- ResolutionError error = new ResolutionError(kind, arguments);
- compiler.reportError(node, error);
+ ResolutionError message = new ResolutionError(kind, arguments);
+ compiler.reportError(node, message);
}
}
class InitializerResolver {
final ResolverVisitor visitor;
- final FunctionElement constructor;
final Map<SourceString, Node> initialized;
Link<Node> initializers;
bool hasSuper;
- InitializerResolver(this.visitor, this.constructor)
+ InitializerResolver(this.visitor)
: initialized = new Map<SourceString, Node>(), hasSuper = false;
error(Node node, MessageKind kind, [arguments = const []]) {
@@ -191,7 +247,7 @@ class InitializerResolver {
return node.receiver.asIdentifier().isThis();
}
- void resolveFieldInitializer(SendSet init) {
+ void resolveFieldInitializer(FunctionElement constructor, SendSet init) {
// init is of the form [this.]field = value.
final Node selector = init.selector;
final SourceString name = selector.asIdentifier().source;
@@ -221,21 +277,16 @@ class InitializerResolver {
visitor.visitInStaticContext(init.arguments.head);
}
- SourceString getConstructorName(Send node) {
- if (node.receiver !== null) {
- return node.selector.asIdentifier().source;
- } else {
- return const SourceString('');
- }
- }
-
- void resolveSuperOrThis(Send call) {
+ Element resolveSuperOrThis(FunctionElement constructor,
+ FunctionExpression functionNode,
+ Send call) {
noConstructor(e) {
if (e !== null) error(call, MessageKind.NO_CONSTRUCTOR, [e.name, e.kind]);
}
ClassElement lookupTarget = constructor.enclosingElement;
bool validTarget = true;
+ FunctionElement result;
if (Initializers.isSuperConstructorCall(call)) {
// Check for invalid initializers.
if (hasSuper) {
@@ -249,9 +300,13 @@ class InitializerResolver {
lookupTarget = lookupTarget.supertype.element;
}
} else if (Initializers.isConstructorRedirect(call)) {
+ // Check that there is no body (Language specification 7.5.1).
+ if (functionNode.hasBody()) {
+ error(functionNode, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_BODY);
+ }
// Check that there are no other initializers.
if (!initializers.tail.isEmpty()) {
- error(call, MessageKind.REDIRECTING_CTOR_HAS_INITIALIZER);
+ error(call, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER);
}
} else {
visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED);
@@ -259,15 +314,11 @@ class InitializerResolver {
}
if (validTarget) {
- final SourceString className = lookupTarget.name;
- final SourceString constructorName = getConstructorName(call);
- FunctionElement target =
- lookupTarget.lookupConstructor(className, constructorName,
- noConstructor);
- if (target === null && call.arguments.isEmpty()) {
- target = lookupTarget.getSynthesizedConstructor();
- }
- if (target === null) {
+ ResolverTask resolver = visitor.compiler.resolver;
+ result = resolver.lookupConstructor(lookupTarget, call);
+ if (result === null) {
+ SourceString constructorName = resolver.getConstructorName(call);
+ SourceString className = lookupTarget.name;
String name = (constructorName === const SourceString(''))
? className.stringValue
: "$className.$constructorName";
@@ -275,11 +326,11 @@ class InitializerResolver {
} else {
final Compiler compiler = visitor.compiler;
// TODO(karlklose): support optional arguments.
- if (target.parameterCount(compiler) != call.argumentCount()) {
+ if (result.parameterCount(compiler) != call.argumentCount()) {
error(call, MessageKind.NO_MATCHING_CONSTRUCTOR);
}
}
- visitor.useElement(call, target);
+ visitor.useElement(call, result);
}
// Resolve the arguments of the call.
for (Link<Node> arguments = call.arguments;
@@ -287,30 +338,46 @@ class InitializerResolver {
arguments = arguments.tail) {
visitor.visitInStaticContext(arguments.head);
}
+ return result;
+ }
+
+ FunctionElement resolveRedirection(FunctionElement constructor,
+ FunctionExpression functionNode) {
+ if (functionNode.initializers === null) return null;
+ Link<Node> link = functionNode.initializers.nodes;
+ if (!link.isEmpty() && Initializers.isConstructorRedirect(link.head)) {
+ return resolveSuperOrThis(constructor, functionNode, link.head);
+ }
+ return null;
}
- void resolveInitializers(FunctionExpression node) {
- if (node.initializers === null) return;
- initializers = node.initializers.nodes;
- Compiler compiler = visitor.compiler;
+ /**
+ * Resolve all initializers of this constructor. In the case of a redirecting
+ * constructor, the resolved constructor's function element is returned.
+ */
+ FunctionElement resolveInitializers(FunctionElement constructor,
+ FunctionExpression functionNode) {
+ if (functionNode.initializers === null) return null;
+ initializers = functionNode.initializers.nodes;
+ FunctionElement result;
for (Link<Node> link = initializers;
!link.isEmpty();
link = link.tail) {
if (link.head.asSendSet() != null) {
final SendSet init = link.head.asSendSet();
- resolveFieldInitializer(init);
+ resolveFieldInitializer(constructor, init);
} else if (link.head.asSend() !== null) {
final Send call = link.head.asSend();
- resolveSuperOrThis(call);
+ result = resolveSuperOrThis(constructor, functionNode, call);
} else {
- visitor.compiler.cancel('internal error: invalid initializer',
- node: link.head);
+ error(link.head, MessageKind.INVALID_INITIALIZER);
}
}
+ return result;
}
}
-class CommonResolverVisitor<R> extends AbstractVisitor<Element> {
+class CommonResolverVisitor<R> extends AbstractVisitor<R> {
final Compiler compiler;
CommonResolverVisitor(Compiler this.compiler);
@@ -323,13 +390,13 @@ class CommonResolverVisitor<R> extends AbstractVisitor<Element> {
R visit(Node node) => (node == null) ? null : node.accept(this);
void error(Node node, MessageKind kind, [arguments = const []]) {
- ResolutionError error = new ResolutionError(kind, arguments);
- compiler.reportError(node, error);
+ ResolutionError message = new ResolutionError(kind, arguments);
+ compiler.reportError(node, message);
}
void warning(Node node, MessageKind kind, [arguments = const []]) {
- ResolutionWarning warning = new ResolutionWarning(kind, arguments);
- compiler.reportWarning(node, warning);
+ ResolutionWarning message = new ResolutionWarning(kind, arguments);
+ compiler.reportWarning(node, message);
}
void cancel(Node node, String message) {
« no previous file with comments | « no previous file | frog/leg/ssa/builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698