Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 /** | 5 /** |
| 6 * A simple code analyzer for Dart. | 6 * A simple code analyzer for Dart. |
| 7 * | 7 * |
| 8 * Currently used to ensure all concrete generic types are visited. | 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 | 9 * Also performs all static type checks - so these don't need to be |
| 10 * done in later phases. | 10 * done in later phases. |
| 11 * | 11 * |
| 12 * Ultimately, this should include abstract interpreter work. This will | 12 * Ultimately, this should include abstract interpreter work. This will |
| 13 * result in an interesting split beteen this class and MethodGenerator | 13 * result in an interesting split beteen this class and MethodGenerator |
| 14 * which should be turned into nothing more than a code generator. | 14 * which should be turned into nothing more than a code generator. |
| 15 */ | 15 */ |
| 16 // TODO(jimhug): This class shares too much code with MethodGenerator. | 16 // TODO(jimhug): This class shares too much code with MethodGenerator. |
| 17 class MethodAnalyzer implements TreeVisitor { | 17 class MethodAnalyzer implements TreeVisitor { |
| 18 MethodMember method; | 18 MethodMember method; |
| 19 Statement body; | 19 Statement body; |
| 20 | 20 |
| 21 CallFrame _frame; | 21 CallFrame _frame; |
| 22 | 22 |
| 23 /** | 23 /** |
| 24 * Track whether or not [body] refers to any type parameters from the | 24 * Track whether or not [body] refers to any type parameters from the |
| 25 * enclosing type to advise future code generation and analysis. | 25 * enclosing type to advise future code generation and analysis. |
| 26 */ | 26 */ |
| 27 bool hasTypeParams = false; | 27 bool hasTypeParams = false; |
| 28 bool visitingInitializers = false; | |
| 28 | 29 |
| 29 MethodAnalyzer(this.method, this.body); | 30 MethodAnalyzer(this.method, this.body); |
| 30 | 31 |
| 31 // TODO(jimhug): Type issue with requiring CallFrame here... | 32 // TODO(jimhug): Type issue with requiring CallFrame here... |
| 32 void analyze(CallFrame context) { | 33 void analyze(CallFrame context) { |
| 33 var thisValue; | 34 var thisValue; |
| 34 // TODO(jimhug): Move Constructor analysis to here and below. | 35 // TODO(jimhug): Move Constructor analysis to here and below. |
| 35 | 36 |
| 36 if (context != null) { | 37 if (context != null) { |
| 37 thisValue = context.thisValue; | 38 thisValue = context.thisValue; |
| 38 } else { | 39 } else { |
| 39 thisValue = new PureStaticValue(method.declaringType, null); | 40 thisValue = new PureStaticValue(method.declaringType, null); |
| 40 } | 41 } |
| 41 var values = []; | 42 var values = []; |
| 42 for (var p in method.parameters) { | 43 for (var p in method.parameters) { |
| 43 values.add(new PureStaticValue(p.type, null)); | 44 values.add(new PureStaticValue(p.type, null)); |
| 44 } | 45 } |
| 45 var args = new Arguments(null, values); | 46 var args = new Arguments(null, values); |
| 46 | 47 |
| 47 _frame = new CallFrame(this, method, thisValue, args, context); | 48 _frame = new CallFrame(this, method, thisValue, args, context); |
| 48 _bindArguments(_frame.args); | 49 _bindArguments(_frame.args); |
| 49 body.visit(this); | 50 |
| 51 final declaredInitializers = method.definition.dynamic.initializers; | |
|
kasperl
2012/02/03 15:39:20
Add a comment here that explains how this is dealt
ngeoffray
2012/02/06 11:50:31
Done.
| |
| 52 var initializerCall = null; | |
| 53 if (declaredInitializers != null) { | |
| 54 visitingInitializers = true; | |
| 55 for (var init in declaredInitializers) { | |
| 56 if (init is CallExpression) { | |
|
kasperl
2012/02/03 15:39:20
How bad would it be to add an optional parameter t
ngeoffray
2012/02/06 11:50:31
Done.
| |
| 57 init.visit(this);; | |
|
kasperl
2012/02/03 15:39:20
;; -> ;
ngeoffray
2012/02/06 11:50:31
Done.
| |
| 58 } | |
| 59 } | |
| 60 visitingInitializers = false; | |
| 61 } | |
| 62 | |
| 63 if (body != null) body.visit(this); | |
| 50 } | 64 } |
| 51 | 65 |
| 52 /* Checks whether or not a particular TypeReference Node includes references | 66 /* Checks whether or not a particular TypeReference Node includes references |
| 53 * to type parameters. */ | 67 * to type parameters. */ |
| 54 bool _hasTypeParams(node) { | 68 bool _hasTypeParams(node) { |
| 55 if (node is NameTypeReference) { | 69 if (node is NameTypeReference) { |
| 56 var name = node.name.name; | 70 var name = node.name.name; |
| 57 return (method.declaringType.lookupTypeParam(name) != null); | 71 return (method.declaringType.lookupTypeParam(name) != null); |
| 58 } else if (node is GenericTypeReference) { | 72 } else if (node is GenericTypeReference) { |
| 59 for (var typeArg in node.typeArguments) { | 73 for (var typeArg in node.typeArguments) { |
| (...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 387 visitLambdaExpression(LambdaExpression node) { | 401 visitLambdaExpression(LambdaExpression node) { |
| 388 var name = (node.func.name != null) ? node.func.name.name : ''; | 402 var name = (node.func.name != null) ? node.func.name.name : ''; |
| 389 | 403 |
| 390 MethodMember meth = _makeLambdaMethod(name, node.func); | 404 MethodMember meth = _makeLambdaMethod(name, node.func); |
| 391 // TODO(jimhug): Worry about proper scope for recursive lambda. | 405 // TODO(jimhug): Worry about proper scope for recursive lambda. |
| 392 meth.methodData.analyze(); | 406 meth.methodData.analyze(); |
| 393 | 407 |
| 394 return _frame._makeValue(world.functionType, node); | 408 return _frame._makeValue(world.functionType, node); |
| 395 } | 409 } |
| 396 | 410 |
| 411 analyzeThisOrSuperConstructorCall(CallExpression node, | |
|
kasperl
2012/02/03 15:39:20
analyzeInitializerConstructorCall instead? In this
ngeoffray
2012/02/06 11:50:31
Done.
| |
| 412 Expression receiver, | |
| 413 String name) { | |
| 414 var type = _frame.method.declaringType; | |
| 415 if (receiver is SuperExpression) { | |
| 416 type = type.parent; | |
| 417 } | |
| 418 var member = type.getConstructor(name); | |
| 419 if (member !== null) { | |
| 420 return member.invoke(_frame, node, _frame.makeThisValue(node), | |
| 421 _visitArgs(node.arguments)); | |
| 422 } else { | |
| 423 world.warning('cannot find "$name"', node.span); | |
|
kasperl
2012/02/03 15:39:20
How does this look if name is the empty string? Ma
ngeoffray
2012/02/06 11:50:31
Done.
| |
| 424 return _frame._makeValue(world.varType, node); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 bool isThisOrSuper(Expression node) { | |
| 429 return node is ThisExpression || node is SuperExpression; | |
| 430 } | |
| 431 | |
| 397 Value visitCallExpression(CallExpression node) { | 432 Value visitCallExpression(CallExpression node) { |
| 398 var target; | 433 var target; |
| 399 var position = node.target; | 434 var position = node.target; |
| 400 var name = ':call'; | 435 var name = ':call'; |
| 401 if (node.target is DotExpression) { | 436 if (node.target is DotExpression) { |
| 402 DotExpression dot = node.target; | 437 DotExpression dot = node.target; |
| 403 target = dot.self.visit(this); | 438 target = dot.self.visit(this); |
| 404 name = dot.name.name; | 439 name = dot.name.name; |
| 405 position = dot.name; | 440 if (isThisOrSuper(dot.self) && visitingInitializers) { |
| 441 return analyzeThisOrSuperConstructorCall(node, dot.self, name); | |
| 442 } else { | |
| 443 position = dot.name; | |
| 444 } | |
| 445 } else if (isThisOrSuper(node.target) && visitingInitializers) { | |
| 446 return analyzeThisOrSuperConstructorCall(node, node.target, ''); | |
| 406 } else if (node.target is VarExpression) { | 447 } else if (node.target is VarExpression) { |
| 407 VarExpression varExpr = node.target; | 448 VarExpression varExpr = node.target; |
| 408 name = varExpr.name.name; | 449 name = varExpr.name.name; |
| 409 // First check in block scopes. | 450 // First check in block scopes. |
| 410 target = _frame.lookup(name); | 451 target = _frame.lookup(name); |
| 411 if (target != null) { | 452 if (target != null) { |
| 412 return target.get(position).invoke(_frame, ':call', node, | 453 return target.get(position).invoke(_frame, ':call', node, |
| 413 _visitArgs(node.arguments)); | 454 _visitArgs(node.arguments)); |
| 414 } | 455 } |
| 415 | 456 |
| (...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 741 for (var item in node.pieces) { | 782 for (var item in node.pieces) { |
| 742 var val = visitValue(item); | 783 var val = visitValue(item); |
| 743 var sval = val.invoke(_frame, 'toString', item, Arguments.EMPTY); | 784 var sval = val.invoke(_frame, 'toString', item, Arguments.EMPTY); |
| 744 ret = ret.binop(TokenKind.ADD, sval, _frame, item); | 785 ret = ret.binop(TokenKind.ADD, sval, _frame, item); |
| 745 } | 786 } |
| 746 return _frame._makeValue(world.stringType, node); //???ret; | 787 return _frame._makeValue(world.stringType, node); //???ret; |
| 747 */ | 788 */ |
| 748 } | 789 } |
| 749 } | 790 } |
| 750 | 791 |
| OLD | NEW |