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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | frog/leg/ssa/builder.dart » ('j') | frog/leg/tree/nodes.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 interface TreeElements { 5 interface TreeElements {
6 Element operator[](Node node); 6 Element operator[](Node node);
7 Selector getSelector(Send send); 7 Selector getSelector(Send send);
8 } 8 }
9 9
10 class TreeElementMapping implements TreeElements { 10 class TreeElementMapping implements TreeElements {
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 TreeElements resolveMethodElement(FunctionElement element) { 60 TreeElements resolveMethodElement(FunctionElement element) {
61 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR && 61 if (element.kind === ElementKind.GENERATIVE_CONSTRUCTOR &&
62 constructorElements[element] !== null) { 62 constructorElements[element] !== null) {
63 return constructorElements[element]; 63 return constructorElements[element];
64 } 64 }
65 FunctionExpression tree = element.parseNode(compiler); 65 FunctionExpression tree = element.parseNode(compiler);
66 ResolverVisitor visitor = new ResolverVisitor(compiler, element); 66 ResolverVisitor visitor = new ResolverVisitor(compiler, element);
67 visitor.useElement(tree, element); 67 visitor.useElement(tree, element);
68 visitor.setupFunction(tree, element); 68 visitor.setupFunction(tree, element);
69 69
70 if (tree.initializers != null) { 70 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
71 new InitializerResolver(visitor, element).resolveInitializers(tree); 71 tree.initializers != null) {
72 if (element.kind !== ElementKind.GENERATIVE_CONSTRUCTOR) {
73 error(tree, MessageKind.FUNCTION_WITH_INITIALIZER);
74 }
75 InitializerResolver resolver = new InitializerResolver(visitor);
76 FunctionElement redirection = resolver.resolveInitializers(element, tree);
77 Set<FunctionElement> seen; // Initialized lazily, most constructors do
78 // 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.
79 while (redirection !== null) {
80 if (seen === null) {
81 seen = new Set<FunctionElement>();
82 seen.add(element);
83 }
84 if (seen.contains(redirection)) {
85 visitor.error(tree, MessageKind.REDIRECTING_CONSTRUCTOR_CYCLE);
86 break;
87 }
88 seen.add(redirection);
89 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
90 if (functionNode !== null) {
91 redirection = resolver.resolveRedirection(redirection, functionNode);
92 } else {
93 // A synthetic constructor does not have a node.
94 redirection = null;
95 }
96 }
72 } 97 }
73 visitor.visit(tree.body); 98 visitor.visit(tree.body);
74 99
75 // Resolve the type annotations encountered in the method. 100 // Resolve the type annotations encountered in the method.
76 Link<ClassElement> newResolvedClasses = const EmptyLink<ClassElement>(); 101 Link<ClassElement> newResolvedClasses = const EmptyLink<ClassElement>();
77 while (!toResolve.isEmpty()) { 102 while (!toResolve.isEmpty()) {
78 ClassElement classElement = toResolve.removeFirst(); 103 ClassElement classElement = toResolve.removeFirst();
79 if (!classElement.isResolved) { 104 if (!classElement.isResolved) {
80 classElement.resolve(compiler); 105 classElement.resolve(compiler);
81 } 106 }
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
133 // TODO(karlklose): check if type arguments match, if a classelement occurs 158 // TODO(karlklose): check if type arguments match, if a classelement occurs
134 // more than once in the supertypes. 159 // more than once in the supertypes.
135 if (classElement.allSupertypes !== null) return; 160 if (classElement.allSupertypes !== null) return;
136 final Type supertype = classElement.supertype; 161 final Type supertype = classElement.supertype;
137 if (seen.contains(classElement)) { 162 if (seen.contains(classElement)) {
138 error(classElement.parseNode(compiler), 163 error(classElement.parseNode(compiler),
139 MessageKind.CYCLIC_CLASS_HIERARCHY, 164 MessageKind.CYCLIC_CLASS_HIERARCHY,
140 [classElement.name]); 165 [classElement.name]);
141 classElement.allSupertypes = const EmptyLink<Type>(); 166 classElement.allSupertypes = const EmptyLink<Type>();
142 } else if (supertype != null) { 167 } else if (supertype != null) {
143 Type supertype = classElement.supertype;
144 seen.add(classElement); 168 seen.add(classElement);
145 Link<Type> superSupertypes = 169 Link<Type> superSupertypes =
146 getOrCalculateAllSupertypes(supertype.element, seen); 170 getOrCalculateAllSupertypes(supertype.element, seen);
147 Link<Type> supertypes = new Link<Type>(supertype, superSupertypes); 171 Link<Type> supertypes = new Link<Type>(supertype, superSupertypes);
148 for (Link<Type> interfaces = classElement.interfaces; 172 for (Link<Type> interfaces = classElement.interfaces;
149 !interfaces.isEmpty(); 173 !interfaces.isEmpty();
150 interfaces = interfaces.tail) { 174 interfaces = interfaces.tail) {
151 Element element = interfaces.head.element; 175 Element element = interfaces.head.element;
152 Link<Type> interfaceSupertypes = 176 Link<Type> interfaceSupertypes =
153 getOrCalculateAllSupertypes(element, seen); 177 getOrCalculateAllSupertypes(element, seen);
154 supertypes = supertypes.reversePrependAll(interfaceSupertypes); 178 supertypes = supertypes.reversePrependAll(interfaceSupertypes);
155 } 179 }
156 seen.remove(classElement); 180 seen.remove(classElement);
157 classElement.allSupertypes = supertypes; 181 classElement.allSupertypes = supertypes;
158 } else { 182 } else {
159 classElement.allSupertypes = const EmptyLink<Type>(); 183 classElement.allSupertypes = const EmptyLink<Type>();
160 } 184 }
161 } 185 }
162 186
163 error(Node node, MessageKind kind, [arguments = const []]) { 187 error(Node node, MessageKind kind, [arguments = const []]) {
164 ResolutionError error = new ResolutionError(kind, arguments); 188 ResolutionError message = new ResolutionError(kind, arguments);
165 compiler.reportError(node, error); 189 compiler.reportError(node, message);
166 } 190 }
167 } 191 }
168 192
169 class InitializerResolver { 193 class InitializerResolver {
170 final ResolverVisitor visitor; 194 final ResolverVisitor visitor;
171 final FunctionElement constructor;
172 final Map<SourceString, Node> initialized; 195 final Map<SourceString, Node> initialized;
173 Link<Node> initializers; 196 Link<Node> initializers;
174 bool hasSuper; 197 bool hasSuper;
175 198
176 InitializerResolver(this.visitor, this.constructor) 199 InitializerResolver(this.visitor)
177 : initialized = new Map<SourceString, Node>(), hasSuper = false; 200 : initialized = new Map<SourceString, Node>(), hasSuper = false;
178 201
179 error(Node node, MessageKind kind, [arguments = const []]) { 202 error(Node node, MessageKind kind, [arguments = const []]) {
180 visitor.error(node, kind, arguments); 203 visitor.error(node, kind, arguments);
181 } 204 }
182 205
183 warning(Node node, MessageKind kind, [arguments = const []]) { 206 warning(Node node, MessageKind kind, [arguments = const []]) {
184 visitor.warning(node, kind, arguments); 207 visitor.warning(node, kind, arguments);
185 } 208 }
186 209
187 bool isFieldInitializer(SendSet node) { 210 bool isFieldInitializer(SendSet node) {
188 if (node.selector.asIdentifier() == null) return false; 211 if (node.selector.asIdentifier() == null) return false;
189 if (node.receiver == null) return true; 212 if (node.receiver == null) return true;
190 if (node.receiver.asIdentifier() == null) return false; 213 if (node.receiver.asIdentifier() == null) return false;
191 return node.receiver.asIdentifier().isThis(); 214 return node.receiver.asIdentifier().isThis();
192 } 215 }
193 216
194 void resolveFieldInitializer(SendSet init) { 217 void resolveFieldInitializer(FunctionElement constructor, SendSet init) {
195 // init is of the form [this.]field = value. 218 // init is of the form [this.]field = value.
196 final Node selector = init.selector; 219 final Node selector = init.selector;
197 final SourceString name = selector.asIdentifier().source; 220 final SourceString name = selector.asIdentifier().source;
198 // Lookup target field. 221 // Lookup target field.
199 Element target; 222 Element target;
200 if (isFieldInitializer(init)) { 223 if (isFieldInitializer(init)) {
201 final ClassElement classElement = constructor.enclosingElement; 224 final ClassElement classElement = constructor.enclosingElement;
202 target = classElement.lookupLocalMember(name); 225 target = classElement.lookupLocalMember(name);
203 if (target === null) { 226 if (target === null) {
204 error(selector, MessageKind.CANNOT_RESOLVE, [name]); 227 error(selector, MessageKind.CANNOT_RESOLVE, [name]);
(...skipping 17 matching lines...) Expand all
222 } 245 }
223 246
224 SourceString getConstructorName(Send node) { 247 SourceString getConstructorName(Send node) {
225 if (node.receiver !== null) { 248 if (node.receiver !== null) {
226 return node.selector.asIdentifier().source; 249 return node.selector.asIdentifier().source;
227 } else { 250 } else {
228 return const SourceString(''); 251 return const SourceString('');
229 } 252 }
230 } 253 }
231 254
232 void resolveSuperOrThis(Send call) { 255 Element resolveSuperOrThis(FunctionElement constructor,
256 FunctionExpression functionNode,
257 Send call) {
233 noConstructor(e) { 258 noConstructor(e) {
234 if (e !== null) error(call, MessageKind.NO_CONSTRUCTOR, [e.name, e.kind]); 259 if (e !== null) error(call, MessageKind.NO_CONSTRUCTOR, [e.name, e.kind]);
235 } 260 }
236 261
237 ClassElement lookupTarget = constructor.enclosingElement; 262 ClassElement lookupTarget = constructor.enclosingElement;
238 bool validTarget = true; 263 bool validTarget = true;
264 FunctionElement result;
239 if (Initializers.isSuperConstructorCall(call)) { 265 if (Initializers.isSuperConstructorCall(call)) {
240 // Check for invalid initializers. 266 // Check for invalid initializers.
241 if (hasSuper) { 267 if (hasSuper) {
242 error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER); 268 error(call, MessageKind.DUPLICATE_SUPER_INITIALIZER);
243 } 269 }
244 hasSuper = true; 270 hasSuper = true;
245 // Calculate correct lookup target and constructor name. 271 // Calculate correct lookup target and constructor name.
246 if (lookupTarget.name == Types.OBJECT) { 272 if (lookupTarget.name == Types.OBJECT) {
247 error(call, MessageKind.SUPER_INITIALIZER_IN_OBJECT); 273 error(call, MessageKind.SUPER_INITIALIZER_IN_OBJECT);
248 } else { 274 } else {
249 lookupTarget = lookupTarget.supertype.element; 275 lookupTarget = lookupTarget.supertype.element;
250 } 276 }
251 } else if (Initializers.isConstructorRedirect(call)) { 277 } else if (Initializers.isConstructorRedirect(call)) {
278 // Check that there is no body (Language specification 7.5.1).
279 if (functionNode.hasBody()) {
280 error(functionNode, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_BODY);
281 }
252 // Check that there are no other initializers. 282 // Check that there are no other initializers.
253 if (!initializers.tail.isEmpty()) { 283 if (!initializers.tail.isEmpty()) {
254 error(call, MessageKind.REDIRECTING_CTOR_HAS_INITIALIZER); 284 error(call, MessageKind.REDIRECTING_CONSTRUCTOR_HAS_INITIALIZER);
255 } 285 }
256 } else { 286 } else {
257 visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED); 287 visitor.error(call, MessageKind.CONSTRUCTOR_CALL_EXPECTED);
258 validTarget = false; 288 validTarget = false;
259 } 289 }
260 290
261 if (validTarget) { 291 if (validTarget) {
262 final SourceString className = lookupTarget.name; 292 final SourceString className = lookupTarget.name;
263 final SourceString constructorName = getConstructorName(call); 293 final SourceString constructorName = getConstructorName(call);
264 FunctionElement target = 294 result = lookupTarget.lookupConstructor(className, constructorName,
265 lookupTarget.lookupConstructor(className, constructorName, 295 noConstructor);
266 noConstructor); 296 if (result === null && call.arguments.isEmpty()) {
267 if (target === null && call.arguments.isEmpty()) { 297 result = lookupTarget.getSynthesizedConstructor();
268 target = lookupTarget.getSynthesizedConstructor();
269 } 298 }
270 if (target === null) { 299 if (result === null) {
271 String name = (constructorName === const SourceString('')) 300 String name = (constructorName === const SourceString(''))
272 ? className.stringValue 301 ? className.stringValue
273 : "$className.$constructorName"; 302 : "$className.$constructorName";
274 error(call, MessageKind.CANNOT_RESOLVE_CONSTRUCTOR, [name]); 303 error(call, MessageKind.CANNOT_RESOLVE_CONSTRUCTOR, [name]);
275 } else { 304 } else {
276 final Compiler compiler = visitor.compiler; 305 final Compiler compiler = visitor.compiler;
277 // TODO(karlklose): support optional arguments. 306 // TODO(karlklose): support optional arguments.
278 if (target.parameterCount(compiler) != call.argumentCount()) { 307 if (result.parameterCount(compiler) != call.argumentCount()) {
279 error(call, MessageKind.NO_MATCHING_CONSTRUCTOR); 308 error(call, MessageKind.NO_MATCHING_CONSTRUCTOR);
280 } 309 }
281 } 310 }
282 visitor.useElement(call, target); 311 visitor.useElement(call, result);
283 } 312 }
284 // Resolve the arguments of the call. 313 // Resolve the arguments of the call.
285 for (Link<Node> arguments = call.arguments; 314 for (Link<Node> arguments = call.arguments;
286 !arguments.isEmpty(); 315 !arguments.isEmpty();
287 arguments = arguments.tail) { 316 arguments = arguments.tail) {
288 visitor.visitInStaticContext(arguments.head); 317 visitor.visitInStaticContext(arguments.head);
289 } 318 }
319 return result;
290 } 320 }
291 321
292 void resolveInitializers(FunctionExpression node) { 322 FunctionElement resolveRedirection(FunctionElement constructor,
293 if (node.initializers === null) return; 323 FunctionExpression functionNode) {
294 initializers = node.initializers.nodes; 324 if (functionNode.initializers === null) return null;
295 Compiler compiler = visitor.compiler; 325 Link<Node> link = functionNode.initializers.nodes;
326 if (!link.isEmpty() && Initializers.isConstructorRedirect(link.head)) {
327 return resolveSuperOrThis(constructor, functionNode, link.head);
328 }
329 return null;
330 }
331
332 /**
333 * Resolve all initializers of this constructor. In the case of a redirecting
334 * constructor, the resolved constructor's function element is returned.
335 */
336 FunctionElement resolveInitializers(FunctionElement constructor,
337 FunctionExpression functionNode) {
338 if (functionNode.initializers === null) return null;
339 initializers = functionNode.initializers.nodes;
340 FunctionElement result;
296 for (Link<Node> link = initializers; 341 for (Link<Node> link = initializers;
297 !link.isEmpty(); 342 !link.isEmpty();
298 link = link.tail) { 343 link = link.tail) {
299 if (link.head.asSendSet() != null) { 344 if (link.head.asSendSet() != null) {
300 final SendSet init = link.head.asSendSet(); 345 final SendSet init = link.head.asSendSet();
301 resolveFieldInitializer(init); 346 resolveFieldInitializer(constructor, init);
302 } else if (link.head.asSend() !== null) { 347 } else if (link.head.asSend() !== null) {
303 final Send call = link.head.asSend(); 348 final Send call = link.head.asSend();
304 resolveSuperOrThis(call); 349 result = resolveSuperOrThis(constructor, functionNode, call);
305 } else { 350 } else {
306 visitor.compiler.cancel('internal error: invalid initializer', 351 error(link.head, MessageKind.INVALID_INITIALIZER);
307 node: link.head);
308 } 352 }
309 } 353 }
354 return result;
310 } 355 }
311 } 356 }
312 357
313 class CommonResolverVisitor<R> extends AbstractVisitor<Element> { 358 class CommonResolverVisitor<R> extends AbstractVisitor<R> {
314 final Compiler compiler; 359 final Compiler compiler;
315 360
316 CommonResolverVisitor(Compiler this.compiler); 361 CommonResolverVisitor(Compiler this.compiler);
317 362
318 R visitNode(Node node) { 363 R visitNode(Node node) {
319 cancel(node, 'internal error'); 364 cancel(node, 'internal error');
320 } 365 }
321 366
322 /** Convenience method for visiting nodes that may be null. */ 367 /** Convenience method for visiting nodes that may be null. */
323 R visit(Node node) => (node == null) ? null : node.accept(this); 368 R visit(Node node) => (node == null) ? null : node.accept(this);
324 369
325 void error(Node node, MessageKind kind, [arguments = const []]) { 370 void error(Node node, MessageKind kind, [arguments = const []]) {
326 ResolutionError error = new ResolutionError(kind, arguments); 371 ResolutionError message = new ResolutionError(kind, arguments);
327 compiler.reportError(node, error); 372 compiler.reportError(node, message);
328 } 373 }
329 374
330 void warning(Node node, MessageKind kind, [arguments = const []]) { 375 void warning(Node node, MessageKind kind, [arguments = const []]) {
331 ResolutionWarning warning = new ResolutionWarning(kind, arguments); 376 ResolutionWarning message = new ResolutionWarning(kind, arguments);
332 compiler.reportWarning(node, warning); 377 compiler.reportWarning(node, message);
333 } 378 }
334 379
335 void cancel(Node node, String message) { 380 void cancel(Node node, String message) {
336 compiler.cancel(message, node: node); 381 compiler.cancel(message, node: node);
337 } 382 }
338 383
339 void internalError(Node node, String message) { 384 void internalError(Node node, String message) {
340 compiler.internalError(message, node: node); 385 compiler.internalError(message, node: node);
341 } 386 }
342 387
(...skipping 811 matching lines...) Expand 10 before | Expand all | Expand 10 after
1154 class TopScope extends Scope { 1199 class TopScope extends Scope {
1155 LibraryElement get library() => element; 1200 LibraryElement get library() => element;
1156 1201
1157 TopScope(LibraryElement library) : super(null, library); 1202 TopScope(LibraryElement library) : super(null, library);
1158 Element lookup(SourceString name) => library.find(name); 1203 Element lookup(SourceString name) => library.find(name);
1159 1204
1160 Element add(Element element) { 1205 Element add(Element element) {
1161 throw "Cannot add an element in the top scope"; 1206 throw "Cannot add an element in the top scope";
1162 } 1207 }
1163 } 1208 }
OLDNEW
« 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