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

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 Florian's 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') | frog/leg/tree/nodes.dart » ('J')
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 d6836a2825540ea05f57ac8ba14868d964e65c10..743a76fef41b8df14e58054c733b720b162b7cf2 100644
--- a/frog/leg/resolver.dart
+++ b/frog/leg/resolver.dart
@@ -67,8 +67,33 @@ 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 &&
ngeoffray 2012/02/16 11:23:50 I don't think we ever see a GENERATIVE_CONSTRUCTOR
karlklose 2012/02/17 13:35:00 We do, the builder resolves its elements to put in
+ 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);
+ Set<FunctionElement> seen; // Initialized lazily, most constructors do
+ // have redirections.
ngeoffray 2012/02/16 11:23:50 Instead of doing this, I suggest creating a method
karlklose 2012/02/17 13:35:00 Done.
+ while (redirection !== null) {
+ if (seen === null) {
+ seen = new Set<FunctionElement>();
+ seen.add(element);
+ }
+ if (seen.contains(redirection)) {
+ visitor.error(tree, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE);
+ break;
+ }
+ seen.add(redirection);
+ FunctionExpression functionNode = redirection.parseNode(compiler);
ngeoffray 2012/02/16 11:23:50 Would a compiler.getRedirectingConstructor make se
karlklose 2012/02/17 13:35:00 I changed the code to do simple lookups using the
+ if (functionNode !== null) {
+ redirection = resolver.resolveRedirection(redirection, functionNode);
+ } else {
+ // A synthetic constructor does not have a node.
+ redirection = null;
+ }
+ }
}
visitor.visit(tree.body);
@@ -140,7 +165,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 +185,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 +214,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;
@@ -229,13 +252,16 @@ class InitializerResolver {
}
}
- 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 +275,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);
@@ -261,13 +291,12 @@ 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();
+ result = lookupTarget.lookupConstructor(className, constructorName,
+ noConstructor);
+ if (result === null && call.arguments.isEmpty()) {
+ result = lookupTarget.getSynthesizedConstructor();
}
- if (target === null) {
+ if (result === null) {
String name = (constructorName === const SourceString(''))
? className.stringValue
: "$className.$constructorName";
@@ -275,11 +304,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 +316,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 +368,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') | frog/leg/tree/nodes.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698