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

Side by Side Diff: frog/analyze.dart

Issue 9270048: Lots of frog cleanup (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 11 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
6 * A simple code analyzer for Dart.
7 *
8 * Currently used to ensure all concrete generic types are visited.
9 * Also performs all static type checks - so these don't need to be
10 * done in later phases.
11 *
12 * Ultimately, this should include abstract interpreter work. This will
13 * result in an interesting split beteen this class and MethodGenerator
14 * which should be turned into nothing more than a code generator.
15 */
16 // TODO(jimhug): This class shares too much code with MethodGenerator.
17 class MethodAnalyzer implements TreeVisitor {
18 MethodMember method;
19 Statement body;
20
21 CallFrame _frame;
22
23 /**
24 * Track whether or not [body] refers to any type parameters from the
25 * enclosing type to advise future code generation and analysis.
26 */
27 bool hasTypeParams = false;
28
29 MethodAnalyzer(this.method, this.body);
30
31 // TODO(jimhug): Type issue with requiring CallFrame here...
32 void analyze(CallFrame context) {
33 var thisValue;
34 // TODO(jimhug): Move Constructor analysis to here and below.
35
36 if (context != null) {
37 thisValue = context.thisValue;
38 } else {
39 thisValue = new PureStaticValue(method.declaringType, null);
40 }
41 var values = [];
42 for (var p in method.parameters) {
43 values.add(new PureStaticValue(p.type, null));
44 }
45 var args = new Arguments(null, values);
46
47 _frame = new CallFrame(this, method, thisValue, args, context);
48 _bindArguments(_frame.args);
49 body.visit(this);
50 }
51
52 /* Checks whether or not a particular TypeReference Node includes references
53 * to type parameters. */
54 bool _hasTypeParams(node) {
55 if (node is NameTypeReference) {
56 var name = node.name.name;
57 return (method.declaringType.lookupTypeParam(name) != null);
58 } else if (node is GenericTypeReference) {
59 for (var typeArg in node.typeArguments) {
60 if (_hasTypeParams(typeArg)) return true;
61 }
62 return false;
63 } else {
64 // TODO(jimhug): Do we need to include FunctionTypeReference here?
65 return false;
66 }
67 }
68
69 Type resolveType(TypeReference node, bool typeErrors,
70 bool allowTypeParams) {
71 if (!hasTypeParams && _hasTypeParams(node)) {
72 hasTypeParams = true;
73 }
74 return method.resolveType(node, typeErrors, allowTypeParams);
75 }
76
77
78 Value makeNeutral(Value inValue, SourceSpan span) {
79 return new PureStaticValue(inValue.type, span);
80 }
81
82 void _bindArguments(Arguments args) {
83 for (int i = 0; i < method.parameters.length; i++) {
84 var p = method.parameters[i];
85 Value currentArg = null;
86 if (i < args.bareCount) {
87 currentArg = args.values[i];
88 } else {
89 // Handle named or missing arguments
90 currentArg = args.getValue(p.name);
91 if (currentArg === null) {
92 // Ensure default value for param has been generated
93 p.genValue(method, _frame); // TODO(jimhug): Needed here?
94 if (p.value === null) {
95 world.warning('missing argument at call - does not match');
96 }
97 currentArg = p.value;
98 }
99 }
100
101 currentArg = makeNeutral(currentArg, p.definition.span);
102
103 // TODO(jimhug): Add checks for constructor initializers.
104 _frame.declareParameter(p, currentArg);
105 // TODO(jimhug): Add type check?
106 }
107 }
108
109 visitBool(Expression node) {
110 return visitTypedValue(node, world.boolType);
111 }
112
113 visitValue(Expression node) {
114 if (node == null) return null;
115
116 var value = node.visit(this);
117 value.checkFirstClass(node.span);
118 return value;
119 }
120
121 /**
122 * Visit [node] and ensure statically or with an runtime check that it has
123 * the expected type (if specified).
124 */
125 visitTypedValue(Expression node, Type expectedType) {
126 final val = visitValue(node);
127 return val;
128 // TODO(jimhug): val.convertTo(this, expectedType);
129 }
130
131 visitVoid(Expression node) {
132 // TODO(jimhug): Add some helpful diagnostics for silly void uses.
133 return visitValue(node);
134 }
135
136
137 Arguments _visitArgs(List<ArgumentNode> arguments) {
138 var args = [];
139 bool seenLabel = false;
140 for (var arg in arguments) {
141 if (arg.label != null) {
142 seenLabel = true;
143 } else if (seenLabel) {
144 // TODO(jimhug): Move this into parser?
145 world.error('bare argument can not follow named arguments', arg.span);
146 }
147 args.add(visitValue(arg.value));
148 }
149
150 return new Arguments(arguments, args);
151 }
152
153 MethodMember _makeLambdaMethod(String name, FunctionDefinition func) {
154 var meth = new MethodMember(name, method.declaringType, func);
155 meth.isLambda = true;
156 meth.enclosingElement = method;
157 meth._methodData = new MethodData(meth, _frame);
158 meth.resolve();
159 return meth;
160 }
161
162 void _pushBlock(Node node) {
163 _frame.pushBlock(node);
164 }
165 void _popBlock(Node node) {
166 _frame.popBlock(node);
167 }
168
169 Member _resolveBare(String name, Node node) {
170 var type = _frame.method.declaringType;
171 var member = type.getMember(name);
172 if (member == null || member.declaringType != type) {
173 var libMember = _frame.library.lookup(name, node.span);
174 if (libMember !== null) return libMember;
175 }
176
177 if (member !== null && !member.isStatic && _frame.isStatic) {
178 world.error('can not refer to instance member from static method',
179 node.span);
180 }
181 return member;
182 }
183
184 // ******************* Statements *******************
185
186 void visitDietStatement(DietStatement node) {
187 var parser = new Parser(node.span.file, startOffset: node.span.start);
188 parser.block().visit(this);
189 }
190
191 void visitVariableDefinition(VariableDefinition node) {
192 var isFinal = false;
193 // TODO(jimhug): Clean this up and share modifier parsing somewhere.
194 if (node.modifiers != null && node.modifiers[0].kind == TokenKind.FINAL) {
195 isFinal = true;
196 }
197 var type = resolveType(node.type, false, true);
198 for (int i=0; i < node.names.length; i++) {
199 final name = node.names[i].name;
200 var value = visitValue(node.values[i]);
201 _frame.create(name, type, node.names[i], isFinal, value);
202 }
203 }
204
205 void visitFunctionDefinition(FunctionDefinition node) {
206 var meth = _makeLambdaMethod(node.name.name, node);
207 // TODO(jimhug): Better FunctionValue that tracks actual function
208 var funcValue = _frame.create(meth.name, meth.functionType,
209 method.definition, true, null);
210
211 meth.methodData.analyze();
212 }
213
214
215 void visitReturnStatement(ReturnStatement node) {
216 if (node.value == null) {
217 _frame.returns(Value.fromNull(node.span));
218 } else {
219 _frame.returns(visitValue(node.value));
220 }
221 }
222
223 void visitThrowStatement(ThrowStatement node) {
224 // Dart allows throwing anything, just like JS
225 if (node.value != null) {
226 var value = visitValue(node.value);
227 } else {
228 // skip
229 }
230 }
231
232 void visitAssertStatement(AssertStatement node) {
233 // be sure to walk test for static checking even is asserts disabled
234 var test = visitValue(node.test); // TODO(jimhug): check bool or callable.
235 }
236
237 void visitBreakStatement(BreakStatement node) {
238 }
239
240 void visitContinueStatement(ContinueStatement node) {
241 }
242
243 void visitIfStatement(IfStatement node) {
244 var test = visitBool(node.test);
245 node.trueBranch.visit(this);
246 if (node.falseBranch != null) {
247 node.falseBranch.visit(this);
248 }
249 }
250
251 void visitWhileStatement(WhileStatement node) {
252 var test = visitBool(node.test);
253 node.body.visit(this);
254 }
255
256 void visitDoStatement(DoStatement node) {
257 node.body.visit(this);
258 var test = visitBool(node.test);
259 }
260
261 void visitForStatement(ForStatement node) {
262 _pushBlock(node);
263 if (node.init != null) node.init.visit(this);
264
265 if (node.test != null) {
266 var test = visitBool(node.test);
267 }
268 for (var s in node.step) {
269 var sv = visitVoid(s);
270 }
271
272 _pushBlock(node.body);
273 node.body.visit(this);
274 _popBlock(node.body);
275
276
277 _popBlock(node);
278 }
279
280 void visitForInStatement(ForInStatement node) {
281 // TODO(jimhug): visitValue and other cleanups here.
282 var itemType = resolveType(node.item.type, false, true);
283 var list = node.list.visit(this);
284 _visitForInBody(node, itemType, list);
285 }
286
287
288 bool _isFinal(typeRef) {
289 if (typeRef is GenericTypeReference) {
290 typeRef = typeRef.baseType;
291 } else if (typeRef is SimpleTypeReference) {
292 return false;
293 }
294 return typeRef != null && typeRef.isFinal;
295 }
296
297 void _visitForInBody(ForInStatement node, Type itemType, Value list) {
298 // TODO(jimhug): Check that itemType matches list members...
299 _pushBlock(node);
300
301 bool isFinal = _isFinal(node.item.type);
302 var itemName = node.item.name.name;
303 var item =
304 _frame.create(itemName, itemType, node.item.name, isFinal, null);
305
306 var iterator =
307 list.invoke(_frame, 'iterator', node.list, Arguments.EMPTY);
308
309 node.body.visit(this);
310 _popBlock(node);
311 }
312
313 _createDI(DeclaredIdentifier di) {
314 _frame.create(di.name.name, resolveType(di.type, false, true), di.name,
315 true, null);
316 }
317
318 void visitTryStatement(TryStatement node) {
319 _pushBlock(node.body);
320 node.body.visit(this);
321 _popBlock(node.body);
322 if (node.catches.length > 0) {
323 for (int i = 0; i < node.catches.length; i++) {
324 var catch_ = node.catches[i];
325 _pushBlock(catch_);
326 _createDI(catch_.exception);
327 if (catch_.trace !== null) {
328 _createDI(catch_.trace);
329 }
330 catch_.body.visit(this);
331 _popBlock(catch_);
332 }
333 }
334
335 if (node.finallyBlock != null) {
336 node.finallyBlock.visit(this);
337 }
338 }
339
340 void visitSwitchStatement(SwitchStatement node) {
341 var test = visitValue(node.test);
342 for (var case_ in node.cases) {
343 _pushBlock(case_);
344
345 for (int i=0; i < case_.cases.length; i++) {
346 var expr = case_.cases[i];
347 if (expr == null) {
348 //skip
349 } else {
350 var value = visitValue(expr);
351 }
352 }
353 _visitAllStatements(case_.statements);
354 _popBlock(case_);
355 }
356 }
357
358 _visitAllStatements(statementList) {
359 for (int i = 0; i < statementList.length; i++) {
360 var stmt = statementList[i];
361 stmt.visit(this);
362 }
363 }
364
365 void visitBlockStatement(BlockStatement node) {
366 _pushBlock(node);
367 _visitAllStatements(node.body);
368 _popBlock(node);
369 }
370
371 void visitLabeledStatement(LabeledStatement node) {
372 node.body.visit(this);
373 }
374
375 void visitExpressionStatement(ExpressionStatement node) {
376 var value = visitVoid(node.body);
377 }
378
379 void visitEmptyStatement(EmptyStatement node) {
380 }
381
382
383
384 // ******************* Expressions *******************
385 visitLambdaExpression(LambdaExpression node) {
386 var name = (node.func.name != null) ? node.func.name.name : '';
387
388 MethodMember meth = _makeLambdaMethod(name, node.func);
389 // TODO(jimhug): Worry about proper scope for recursive lambda.
390 meth.methodData.analyze();
391
392 return _frame._makeValue(world.functionType, node);
393 }
394
395 Value visitCallExpression(CallExpression node) {
396 var target;
397 var position = node.target;
398 var name = ':call';
399 if (node.target is DotExpression) {
400 DotExpression dot = node.target;
401 target = dot.self.visit(this);
402 name = dot.name.name;
403 position = dot.name;
404 } else if (node.target is VarExpression) {
405 VarExpression varExpr = node.target;
406 name = varExpr.name.name;
407 // First check in block scopes.
408 target = _frame.lookup(name);
409 if (target != null) {
410 return target.get().invoke(_frame, ':call', node,
411 _visitArgs(node.arguments));
412 }
413
414 var member = _resolveBare(name, varExpr.name);
415 if (member !== null) {
416 return member.invoke(_frame, node, _frame.makeThisValue(node),
417 _visitArgs(node.arguments));
418 } else {
419 world.warning('can not find "$name"', node.span);
420 return _frame._makeValue(world.varType, node);
421 }
422 } else {
423 target = node.target.visit(this);
424 }
425
426 return target.invoke(_frame, name, position, _visitArgs(node.arguments));
427 }
428
429 Value visitIndexExpression(IndexExpression node) {
430 var target = visitValue(node.target);
431 var index = visitValue(node.index);
432
433 return target.invoke(_frame, ':index', node,
434 new Arguments(null, [index]));
435 }
436
437
438 Value visitBinaryExpression(BinaryExpression node, [bool isVoid = false]) {
439 final kind = node.op.kind;
440
441 if (kind == TokenKind.AND || kind == TokenKind.OR) {
442 var xb = visitBool(node.x);
443 var yb = visitBool(node.y);
444 return xb.binop(kind, yb, _frame, node);
445 }
446
447 final assignKind = TokenKind.kindFromAssign(node.op.kind);
448 if (assignKind == -1) {
449 final x = visitValue(node.x);
450 final y = visitValue(node.y);
451 return x.binop(kind, y, _frame, node);
452 } else {
453 return _visitAssign(assignKind, node.x, node.y, node);
454 }
455 }
456
457 /**
458 * Visits an assignment expression.
459 */
460 Value _visitAssign(int kind, Expression xn, Expression yn, Node position) {
461 if (xn is VarExpression) {
462 return _visitVarAssign(kind, xn, yn, position);
463 } else if (xn is IndexExpression) {
464 return _visitIndexAssign(kind, xn, yn, position);
465 } else if (xn is DotExpression) {
466 return _visitDotAssign(kind, xn, yn, position);
467 } else {
468 world.error('illegal lhs', xn.span);
469 }
470 }
471
472 _visitVarAssign(int kind, VarExpression xn, Expression yn, Node node) {
473 final value = visitValue(yn);
474 final name = xn.name.name;
475
476 // First check in block scopes.
477 var slot = _frame.lookup(name);
478 if (slot != null) {
479 slot.set(value);
480 } else {
481 var member = _resolveBare(name, xn.name);
482 if (member !== null) {
483 member._set(_frame, node, _frame.makeThisValue(node), value);
484 } else {
485 world.warning('can not find "$name"', node.span);
486 }
487 }
488 return _frame._makeValue(value.type, node);
489 }
490
491 _visitIndexAssign(int kind, IndexExpression xn, Expression yn,
492 Node position) {
493 var target = visitValue(xn.target);
494 var index = visitValue(xn.index);
495 var y = visitValue(yn);
496
497 return target.setIndex(_frame, index, position, y, kind: kind);
498 }
499
500 _visitDotAssign(int kind, DotExpression xn, Expression yn, Node position) {
501 // This is not visitValue because types members are assignable.
502 var target = xn.self.visit(this);
503 var y = visitValue(yn);
504
505 return target.set_(_frame, xn.name.name, xn.name, y, kind: kind);
506 }
507
508 visitUnaryExpression(UnaryExpression node) {
509 var value = visitValue(node.self);
510 switch (node.op.kind) {
511 case TokenKind.INCR:
512 case TokenKind.DECR:
513 return value.binop(TokenKind.ADD,
514 _frame._makeValue(world.intType, node), _frame, node);
515 }
516 return value.unop(node.op.kind, _frame, node);
517 }
518
519 visitDeclaredIdentifier(DeclaredIdentifier node) {
520 world.error('Expected expression', node.span);
521 }
522
523 visitAwaitExpression(AwaitExpression node) {
524 world.internalError(
525 'Await expressions should have been eliminated before code generation',
526 node.span);
527 }
528
529 Value visitPostfixExpression(PostfixExpression node,
530 [bool isVoid = false]) {
531 var value = visitValue(node.body);
532
533 return _frame._makeValue(value.type, node);
534 }
535
536 Value visitNewExpression(NewExpression node) {
537 var typeRef = node.type;
538
539 var constructorName = '';
540 if (node.name != null) {
541 constructorName = node.name.name;
542 }
543
544 // Named constructors and library prefixes, oh my!
545 // At last, we can collapse the ambiguous wave function...
546 if (constructorName == '' && typeRef is NameTypeReference &&
547 typeRef.names != null) {
548
549 // Pull off the last name from the type, guess it's the constructor name.
550 var names = new List.from(typeRef.names);
551 constructorName = names.removeLast().name;
552 if (names.length == 0) names = null;
553
554 typeRef = new NameTypeReference(
555 typeRef.isFinal, typeRef.name, names, typeRef.span);
556 }
557
558 var type = resolveType(typeRef, true, true);
559 if (type.isTop) {
560 type = type.library.findTypeByName(constructorName);
561 constructorName = '';
562 }
563
564 if (type is ParameterType) {
565 world.error('cannot instantiate a type parameter', node.span);
566 return _frame._makeValue(world.varType, node);
567 }
568
569 var m = type.getConstructor(constructorName);
570 if (m == null) {
571 var name = type.jsname;
572 if (type.isVar) {
573 name = typeRef.name.name;
574 }
575 world.warning('no matching constructor for $name', node.span);
576 return _frame._makeValue(type, node);
577 }
578
579 if (node.isConst) {
580 if (!m.isConst) {
581 world.error('can\'t use const on a non-const constructor', node.span);
582 }
583 for (var arg in node.arguments) {
584 // TODO(jimhug): Remove this double walk of arguments.
585 if (!visitValue(arg.value).isConst) {
586 world.error('const constructor expects const arguments', arg.span);
587 }
588 }
589 }
590
591
592 var args = _visitArgs(node.arguments);
593 var target = new TypeValue(type, typeRef.span);
594 return new PureStaticValue(type, node.span, node.isConst);
595 }
596
597 Value visitListExpression(ListExpression node) {
598 var argValues = [];
599 var listType = world.listType;
600 var type = world.varType;
601 if (node.itemType != null) {
602 type = resolveType(node.itemType, true, !node.isConst);
603 if (node.isConst && (type is ParameterType || type.hasTypeParams)) {
604 world.error('type parameter cannot be used in const list literals');
605 }
606 listType = listType.getOrMakeConcreteType([type]);
607 }
608 for (var item in node.values) {
609 var arg = visitTypedValue(item, type);
610 argValues.add(arg);
611 // TODO(jimhug): Reenable these checks here - and remove from MethodGen
612 //if (node.isConst && !arg.isConst) {
613 // world.error('const list can only contain const values', arg.span);
614 //}
615 }
616
617 world.listFactoryType.markUsed();
618
619 return new PureStaticValue(listType, node.span, node.isConst);
620 }
621
622
623 Value visitMapExpression(MapExpression node) {
624 var values = <Value>[];
625 var valueType = world.varType, keyType = world.stringType;
626 var mapType = world.mapType; // TODO(jimhug): immutable type?
627 if (node.valueType !== null) {
628 if (node.keyType !== null) {
629 keyType = method.resolveType(node.keyType, true, !node.isConst);
630 // TODO(jimhug): Would be nice to allow arbitrary keys here (this is
631 // currently not allowed by the spec).
632 if (!keyType.isString) {
633 world.error('the key type of a map literal must be "String"',
634 keyType.span);
635 }
636 if (node.isConst &&
637 (keyType is ParameterType || keyType.hasTypeParams)) {
638 world.error('type parameter cannot be used in const map literals');
639 }
640 }
641
642 valueType = resolveType(node.valueType, true, !node.isConst);
643 if (node.isConst &&
644 (valueType is ParameterType || valueType.hasTypeParams)) {
645 world.error('type parameter cannot be used in const map literals');
646 }
647
648 mapType = mapType.getOrMakeConcreteType([keyType, valueType]);
649 }
650
651 for (int i = 0; i < node.items.length; i += 2) {
652 var key = visitTypedValue(node.items[i], keyType);
653 // TODO(jimhug): Reenable these checks here - and remove from MethodGen
654 //if (node.isConst && !key.isConst) {
655 // world.error('const map can only contain const keys', key.span);
656 //}
657 values.add(key);
658
659 var value = visitTypedValue(node.items[i + 1], valueType);
660 if (node.isConst && !value.isConst) {
661 world.error('const map can only contain const values', value.span);
662 }
663 values.add(value);
664 }
665
666 return new PureStaticValue(mapType, node.span, node.isConst);
667 }
668
669
670 Value visitConditionalExpression(ConditionalExpression node) {
671 var test = visitBool(node.test);
672 var trueBranch = visitValue(node.trueBranch);
673 var falseBranch = visitValue(node.falseBranch);
674
675 // TODO(jimhug): Should be unioning values, not just types.
676 return _frame._makeValue(Type.union(trueBranch.type, falseBranch.type),
677 node);
678 }
679
680 Value visitIsExpression(IsExpression node) {
681 var value = visitValue(node.x);
682 var type = resolveType(node.type, false, true);
683 return _frame._makeValue(world.boolType, node);
684 }
685
686 Value visitParenExpression(ParenExpression node) {
687 return visitValue(node.body);
688 }
689
690 Value visitDotExpression(DotExpression node) {
691 // Types are legal targets of .
692 var target = node.self.visit(this);
693 return target.get_(_frame, node.name.name, node);
694 }
695
696
697 Value visitVarExpression(VarExpression node) {
698 final name = node.name.name;
699
700 // First check in block scopes.
701 var slot = _frame.lookup(name);
702 if (slot != null) {
703 return slot.get();
704 }
705
706 var member = _resolveBare(name, node.name);
707 if (member !== null) {
708 if (member is TypeMember) {
709 return new PureStaticValue(member.dynamic.type, node.span, true,
710 true);
711 } else {
712 return member._get(_frame, node, _frame.makeThisValue(node));
713 }
714 } else {
715 world.warning('can not find "$name"', node.span);
716 return _frame._makeValue(world.varType, node);
717 }
718 }
719
720 Value visitThisExpression(ThisExpression node) {
721 return _frame.makeThisValue(node);
722 }
723
724 Value visitSuperExpression(SuperExpression node) {
725 return _frame.makeSuperValue(node);
726 }
727
728 Value visitLiteralExpression(LiteralExpression node) {
729 return new PureStaticValue(node.value.type, node.span, true);
730 }
731
732 Value visitStringInterpExpression(StringInterpExpression node) {
733 return _frame._makeValue(world.stringType, node);
734
735 // TODO(jimhug): Do we need this more elaborate analysis?
736 /*
737 var ret = Value.fromString('', node.span);
738
739 for (var item in node.pieces) {
740 var val = visitValue(item);
741 var sval = val.invoke(_frame, 'toString', item, Arguments.EMPTY);
742 ret = ret.binop(TokenKind.ADD, sval, _frame, item);
743 }
744 return _frame._makeValue(world.stringType, node); //???ret;
745 */
746 }
747 }
748
OLDNEW
« no previous file with comments | « client/tests/client/client.status ('k') | frog/analyze_frame.dart » ('j') | frog/gen.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698