Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 * Top level generator object for writing code and keeping track of | 6 * Top level generator object for writing code and keeping track of |
| 7 * dependencies. | 7 * dependencies. |
| 8 * | 8 * |
| 9 * Should have two compilation models, but only one implemented so far. | 9 * Should have two compilation models, but only one implemented so far. |
| 10 * | 10 * |
| (...skipping 472 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 483 // (This lookup might report errors, which is a bit strange. | 483 // (This lookup might report errors, which is a bit strange. |
| 484 // But probably harmless since we have to pay for the lookup anyway.) | 484 // But probably harmless since we have to pay for the lookup anyway.) |
| 485 final type = enclosingMethod.method.declaringType; | 485 final type = enclosingMethod.method.declaringType; |
| 486 if (type.library.lookup(name, null) != null) return true; | 486 if (type.library.lookup(name, null) != null) return true; |
| 487 | 487 |
| 488 // Nobody else needs this name. It's safe to reuse. | 488 // Nobody else needs this name. It's safe to reuse. |
| 489 return false; | 489 return false; |
| 490 } | 490 } |
| 491 | 491 |
| 492 | 492 |
| 493 Value create(String name, Type type, Node location, | 493 Value create(String name, Type type, SourceSpan span, |
| 494 [bool isParameter = false]) { | 494 [bool isParameter = false]) { |
| 495 | 495 |
| 496 var jsName = world.toJsIdentifier(name); | 496 var jsName = world.toJsIdentifier(name); |
| 497 if (_vars.containsKey(name)) { | 497 if (_vars.containsKey(name)) { |
| 498 if (location != null) { | 498 world.error('duplicate name "$name"', span); |
| 499 world.error('duplicate name "$name"', location.span); | |
| 500 } else { | |
| 501 world.internalError('conflict with temporary name "$name"'); | |
| 502 } | |
| 503 } | 499 } |
| 504 | 500 |
| 505 // Make sure variables don't shadow any names we might need to access. | 501 // Make sure variables don't shadow any names we might need to access. |
| 506 if (!isParameter) { | 502 if (!isParameter) { |
| 507 int index = 0; | 503 int index = 0; |
| 508 while (_isDefinedInParent(jsName)) { | 504 while (_isDefinedInParent(jsName)) { |
| 509 jsName = '$name${index++}'; | 505 jsName = '$name${index++}'; |
| 510 } | 506 } |
| 511 } | 507 } |
| 512 | 508 |
| 513 var ret = new Value(type, jsName, location != null ? location.span : null, | 509 var ret = new Value(type, jsName, span, false); |
| 514 false, false); | |
| 515 _vars[name] = ret; | 510 _vars[name] = ret; |
| 516 return ret; | 511 return ret; |
| 517 } | 512 } |
| 518 | 513 |
| 519 Value declareParameter(Parameter p) { | 514 Value declareParameter(Parameter p) { |
| 520 return create(p.name, p.type, p.definition, isParameter:true); | 515 return create(p.name, p.type, p.definition.span, isParameter:true); |
| 521 } | 516 } |
| 522 | 517 |
| 523 /** Declares a variable in the current scope for this identifier. */ | 518 /** Declares a variable in the current scope for this identifier. */ |
| 524 Value declare(DeclaredIdentifier id) { | 519 Value declare(DeclaredIdentifier id) { |
| 525 var type = enclosingMethod.method.resolveType(id.type, false); | 520 var type = enclosingMethod.method.resolveType(id.type, false); |
| 526 return create(id.name.name, type, id); | 521 return create(id.name.name, type, id.span); |
| 527 } | 522 } |
| 528 | 523 |
| 529 /** | 524 /** |
| 530 * Finds the first lexically enclosing catch block, if any, and returns its | 525 * Finds the first lexically enclosing catch block, if any, and returns its |
| 531 * exception variable. | 526 * exception variable. |
| 532 */ | 527 */ |
| 533 Value getRethrow() { | 528 Value getRethrow() { |
| 534 var scope = this; | 529 var scope = this; |
| 535 while (scope.rethrow == null && scope.parent != null) { | 530 while (scope.rethrow == null && scope.parent != null) { |
| 536 scope = scope.parent; | 531 scope = scope.parent; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 570 if (enclosingMethod != null) { | 565 if (enclosingMethod != null) { |
| 571 _scope = new BlockScope(this, enclosingMethod._scope); | 566 _scope = new BlockScope(this, enclosingMethod._scope); |
| 572 captures = new Set(); | 567 captures = new Set(); |
| 573 } else { | 568 } else { |
| 574 _scope = new BlockScope(this, null); | 569 _scope = new BlockScope(this, null); |
| 575 } | 570 } |
| 576 // For named lambdas, add the name to this scope so we can call it | 571 // For named lambdas, add the name to this scope so we can call it |
| 577 // recursively. | 572 // recursively. |
| 578 if (enclosingMethod != null && method.name != '') { | 573 if (enclosingMethod != null && method.name != '') { |
| 579 MethodMember m = method; // lambdas must be MethodMembers | 574 MethodMember m = method; // lambdas must be MethodMembers |
| 580 _scope.create(m.name, m.functionType, m.definition); | 575 _scope.create(m.name, m.functionType, m.definition.span); |
| 581 } | 576 } |
| 582 _usedTemps = new Set(); | 577 _usedTemps = new Set(); |
| 583 _freeTemps = []; | 578 _freeTemps = []; |
| 584 } | 579 } |
| 585 | 580 |
| 586 Library get library() => method.library; | 581 Library get library() => method.library; |
| 587 | 582 |
| 588 // TODO(jimhug): Where does this really belong? | 583 // TODO(jimhug): Where does this really belong? |
| 589 MemberSet findMembers(String name) { | 584 MemberSet findMembers(String name) { |
| 590 return library._findMembers(name); | 585 return library._findMembers(name); |
| 591 } | 586 } |
| 592 | 587 |
| 593 bool get isClosure() => (enclosingMethod != null); | 588 bool get isClosure() => (enclosingMethod != null); |
| 594 | 589 |
| 595 bool get isStatic() => method.isStatic; | 590 bool get isStatic() => method.isStatic; |
| 596 | 591 |
| 597 Value getTemp(Value value) { | 592 Value getTemp(Value value) { |
| 598 return value.needsTemp ? forceTemp(value) : value; | 593 return value.needsTemp ? forceTemp(value) : value; |
| 599 } | 594 } |
| 600 | 595 |
| 601 Value forceTemp(Value value) { | 596 Value forceTemp(Value value) { |
| 602 String name; | 597 String name; |
| 603 if (_freeTemps.length > 0) { | 598 if (_freeTemps.length > 0) { |
| 604 name = _freeTemps.removeLast(); | 599 name = _freeTemps.removeLast(); |
| 605 } else { | 600 } else { |
| 606 name = '\$' + _usedTemps.length; | 601 name = '\$' + _usedTemps.length; |
| 607 } | 602 } |
| 608 _usedTemps.add(name); | 603 _usedTemps.add(name); |
| 609 return new Value(value.type, name, value.span, | 604 return new Value(value.type, name, value.span, /*needsTemp:*/false); |
| 610 /*isSuper:*/false, /*needsTemp:*/false); | |
| 611 } | 605 } |
| 612 | 606 |
| 613 Value assignTemp(Value tmp, Value v) { | 607 Value assignTemp(Value tmp, Value v) { |
| 614 if (tmp == v) { | 608 if (tmp == v) { |
| 615 return v; | 609 return v; |
| 616 } else { | 610 } else { |
| 617 // TODO(jmesserly): we should mark this returned value with the temp | 611 // TODO(jmesserly): we should mark this returned value with the temp |
| 618 // somehow, so getTemp will reuse it instead of allocating a new one. | 612 // somehow, so getTemp will reuse it instead of allocating a new one. |
| 619 return new Value(v.type, '(${tmp.code} = ${v.code})', v.span); | 613 return new Value(v.type, '(${tmp.code} = ${v.code})', v.span); |
| 620 } | 614 } |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 803 var field = method.declaringType.getMember(p.name); | 797 var field = method.declaringType.getMember(p.name); |
| 804 if (field == null) { | 798 if (field == null) { |
| 805 world.error('bad this parameter - no matching field', | 799 world.error('bad this parameter - no matching field', |
| 806 p.definition.span); | 800 p.definition.span); |
| 807 } | 801 } |
| 808 if (!field.isField) { | 802 if (!field.isField) { |
| 809 world.error('"this.${p.name}" does not refer to a field', | 803 world.error('"this.${p.name}" does not refer to a field', |
| 810 p.definition.span); | 804 p.definition.span); |
| 811 } | 805 } |
| 812 var paramValue = new Value(field.returnType, p.name, | 806 var paramValue = new Value(field.returnType, p.name, |
| 813 p.definition.span, false, false); | 807 p.definition.span, false); |
| 814 _paramCode.add(paramValue.code); | 808 _paramCode.add(paramValue.code); |
| 815 | 809 |
| 816 initializers.add('this.${field.jsname} = ${paramValue.code};'); | 810 initializers.add('this.${field.jsname} = ${paramValue.code};'); |
| 817 initializedFields.add(p.name); | 811 initializedFields.add(p.name); |
| 818 } else { | 812 } else { |
| 819 var paramValue = _scope.declareParameter(p); | 813 var paramValue = _scope.declareParameter(p); |
| 820 _paramCode.add(paramValue.code); | 814 _paramCode.add(paramValue.code); |
| 821 } | 815 } |
| 822 } | 816 } |
| 823 | 817 |
| (...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1065 var value = visitValue(node.values[i]); | 1059 var value = visitValue(node.values[i]); |
| 1066 if (isFinal) { | 1060 if (isFinal) { |
| 1067 if (value == null) { | 1061 if (value == null) { |
| 1068 world.error('no value specified for final variable', node.span); | 1062 world.error('no value specified for final variable', node.span); |
| 1069 } else { | 1063 } else { |
| 1070 // TODO(jimhug): Mark inferred types as special for correct errors. | 1064 // TODO(jimhug): Mark inferred types as special for correct errors. |
| 1071 if (thisType.isVar) thisType = value.type; | 1065 if (thisType.isVar) thisType = value.type; |
| 1072 } | 1066 } |
| 1073 } | 1067 } |
| 1074 | 1068 |
| 1075 var val = _scope.create(name, thisType, node.names[i]); | 1069 var val = _scope.create(name, thisType, node.names[i].span); |
| 1076 | 1070 |
| 1077 if (value == null) { | 1071 if (value == null) { |
| 1078 writer.write('${val.code}'); | 1072 writer.write('${val.code}'); |
| 1079 } else { | 1073 } else { |
| 1080 value = value.convertTo(this, type, node.values[i]); | 1074 value = value.convertTo(this, type, node.values[i]); |
| 1081 writer.write('${val.code} = ${value.code}'); | 1075 writer.write('${val.code} = ${value.code}'); |
| 1082 } | 1076 } |
| 1083 } | 1077 } |
| 1084 writer.writeln(';'); | 1078 writer.writeln(';'); |
| 1085 return false; | 1079 return false; |
| 1086 | 1080 |
| 1087 } | 1081 } |
| 1088 | 1082 |
| 1089 bool visitFunctionDefinition(FunctionDefinition node) { | 1083 bool visitFunctionDefinition(FunctionDefinition node) { |
| 1090 var name = world.toJsIdentifier(node.name.name); | 1084 var name = world.toJsIdentifier(node.name.name); |
| 1091 | 1085 |
| 1092 var meth = _makeLambdaMethod(name, node); | 1086 var meth = _makeLambdaMethod(name, node); |
| 1093 | 1087 |
| 1094 // TODO(jimhug): Pass js name into writeDefinition? | 1088 // TODO(jimhug): Pass js name into writeDefinition? |
| 1095 var funcValue = _scope.create(name, meth.functionType, method.definition); | 1089 var funcValue = |
| 1090 _scope.create(name, meth.functionType, method.definition.span); | |
| 1096 meth.generator.writeDefinition(writer, null); | 1091 meth.generator.writeDefinition(writer, null); |
| 1097 return false; | 1092 return false; |
| 1098 } | 1093 } |
| 1099 | 1094 |
| 1100 /** | 1095 /** |
| 1101 * Returns true indicating that normal control-flow is interrupted by | 1096 * Returns true indicating that normal control-flow is interrupted by |
| 1102 * this statement. (This could be a return, break, throw, or continue.) | 1097 * this statement. (This could be a return, break, throw, or continue.) |
| 1103 */ | 1098 */ |
| 1104 bool visitReturnStatement(ReturnStatement node) { | 1099 bool visitReturnStatement(ReturnStatement node) { |
| 1105 if (node.value == null) { | 1100 if (node.value == null) { |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1234 return false; | 1229 return false; |
| 1235 } | 1230 } |
| 1236 | 1231 |
| 1237 bool visitForInStatement(ForInStatement node) { | 1232 bool visitForInStatement(ForInStatement node) { |
| 1238 // TODO(jimhug): visitValue and other cleanups here. | 1233 // TODO(jimhug): visitValue and other cleanups here. |
| 1239 var itemType = method.resolveType(node.item.type, false); | 1234 var itemType = method.resolveType(node.item.type, false); |
| 1240 var itemName = node.item.name.name; | 1235 var itemName = node.item.name.name; |
| 1241 var list = node.list.visit(this); | 1236 var list = node.list.visit(this); |
| 1242 _pushBlock(/*reentrant:*/true); | 1237 _pushBlock(/*reentrant:*/true); |
| 1243 // TODO(jimhug): Check that itemType matches list members... | 1238 // TODO(jimhug): Check that itemType matches list members... |
| 1244 var item = _scope.create(itemName, itemType, node.item.name); | 1239 var item = _scope.create(itemName, itemType, node.item.name.span); |
| 1245 Value listVar = list; | 1240 Value listVar = list; |
| 1246 if (list.needsTemp) { | 1241 if (list.needsTemp) { |
| 1247 listVar = _scope.create('\$list', list.type, null); | 1242 listVar = _scope.create('\$list', list.type, null); |
| 1248 writer.writeln('var ${listVar.code} = ${list.code};'); | 1243 writer.writeln('var ${listVar.code} = ${list.code};'); |
| 1249 } | 1244 } |
| 1250 | 1245 |
| 1251 // Special path for list for readability and perf optimization. | 1246 // Special path for list for readability and perf optimization. |
| 1252 if (list.type.isList) { | 1247 if (list.type.isList) { |
| 1253 var tmpi = _scope.create('\$i', world.numType, null); | 1248 var tmpi = _scope.create('\$i', world.numType, null); |
| 1254 writer.enterBlock('for (var ${tmpi.code} = 0;' + | 1249 writer.enterBlock('for (var ${tmpi.code} = 0;' + |
| 1255 '${tmpi.code} < ${listVar.code}.length; ${tmpi.code}++) {'); | 1250 '${tmpi.code} < ${listVar.code}.length; ${tmpi.code}++) {'); |
| 1256 var value = listVar.invoke(this, '\$index', node.list, | 1251 var value = listVar.invoke(this, '\$index', node.list, |
| 1257 new Arguments(null, [tmpi])); | 1252 new Arguments(null, [tmpi])); |
| 1258 writer.writeln('var ${item.code} = ${value.code};'); | 1253 writer.writeln('var ${item.code} = ${value.code};'); |
| 1259 } else { | 1254 } else { |
| 1260 _pushBlock(); | 1255 _pushBlock(); |
| 1261 // Needed to tell the runtime that we're doing this behind its back. | 1256 var iterator = list.invoke(this, 'iterator', node.list, Arguments.EMPTY); |
| 1262 var c = world.coreimpl.types['ListIterator'].getConstructor(''); | |
| 1263 c.invoke(this, node, null, | |
| 1264 new Arguments(null, [new Value(null, 'l', node.list.span)])); | |
| 1265 | |
| 1266 var iterator = list.invoke(this, 'iterator', node.list, | |
| 1267 Arguments.EMPTY); | |
| 1268 var tmpi = _scope.create('\$i', iterator.type, null); | 1257 var tmpi = _scope.create('\$i', iterator.type, null); |
| 1269 | 1258 |
| 1270 var hasNext = tmpi.invoke(this, 'hasNext', node.list, Arguments.EMPTY); | 1259 var hasNext = tmpi.invoke(this, 'hasNext', node.list, Arguments.EMPTY); |
| 1271 var next = tmpi.invoke(this, 'next', node.list, Arguments.EMPTY); | 1260 var next = tmpi.invoke(this, 'next', node.list, Arguments.EMPTY); |
| 1272 | 1261 |
| 1273 writer.enterBlock( | 1262 writer.enterBlock( |
| 1274 'for (var ${tmpi.code} = ${iterator.code}; ${hasNext.code}; ) {'); | 1263 'for (var ${tmpi.code} = ${iterator.code}; ${hasNext.code}; ) {'); |
| 1275 writer.writeln('var ${item.code} = ${next.code};'); | 1264 writer.writeln('var ${item.code} = ${next.code};'); |
| 1276 } | 1265 } |
| 1277 | 1266 |
| 1278 visitStatementsInBlock(node.body); | 1267 visitStatementsInBlock(node.body); |
| 1279 writer.exitBlock('}'); | 1268 writer.exitBlock('}'); |
| 1280 _popBlock(); | 1269 _popBlock(); |
| 1281 return false; | 1270 return false; |
| 1282 } | 1271 } |
| 1283 | 1272 |
| 1284 void _genToDartException(String ex, Node node) { | 1273 void _genToDartException(String ex, Node node) { |
| 1285 var types = const [ | 1274 var types = const [ |
| 1286 'NullPointerException', 'ObjectNotClosureException', | 1275 'NullPointerException', 'ObjectNotClosureException', |
| 1287 'NoSuchMethodException', 'StackOverflowException']; | 1276 'NoSuchMethodException', 'StackOverflowException']; |
| 1288 var target = new Value(null, 'this', node.span); | 1277 // TODO(jimhug): This is an egregious hack to get some toStrings called. |
|
Jennifer Messerly
2011/11/12 00:39:58
Yeah, we probably just want to invoke all toString
| |
| 1278 var target = new Value(world.varType, 'this', node.span); | |
| 1289 for (var name in types) { | 1279 for (var name in types) { |
| 1290 world.corelib.types[name].markUsed(); | 1280 world.corelib.types[name].markUsed(); |
| 1291 world.corelib.types[name].members['toString'].invoke( | 1281 world.corelib.types[name].members['toString'].invoke( |
| 1292 this, node, target, Arguments.EMPTY); | 1282 this, node, target, Arguments.EMPTY); |
| 1293 } | 1283 } |
| 1294 writer.writeln('$ex = \$toDartException($ex);'); | 1284 writer.writeln('$ex = \$toDartException($ex);'); |
| 1295 world.gen.corejs.useToDartException = true; | 1285 world.gen.corejs.useToDartException = true; |
| 1296 } | 1286 } |
| 1297 | 1287 |
| 1298 bool visitTryStatement(TryStatement node) { | 1288 bool visitTryStatement(TryStatement node) { |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1496 world.warning('not allowed in static method', node.span); | 1486 world.warning('not allowed in static method', node.span); |
| 1497 } | 1487 } |
| 1498 } | 1488 } |
| 1499 | 1489 |
| 1500 _makeSuperValue(Node node) { | 1490 _makeSuperValue(Node node) { |
| 1501 var parentType = method.declaringType.parent; | 1491 var parentType = method.declaringType.parent; |
| 1502 _checkNonStatic(node); | 1492 _checkNonStatic(node); |
| 1503 if (parentType == null) { | 1493 if (parentType == null) { |
| 1504 world.error('no super class', node.span); | 1494 world.error('no super class', node.span); |
| 1505 } | 1495 } |
| 1506 return new Value(parentType, 'this', node.span, | 1496 // TODO(jimhug): Replace with SuperValue. |
|
Jennifer Messerly
2011/11/12 00:39:58
+1
| |
| 1507 /*isSuper:*/true, /*needsTemp:*/false); | 1497 var ret = new Value(parentType, 'this', node.span, false); |
| 1498 ret.isSuper = true; | |
| 1499 return ret; | |
| 1508 } | 1500 } |
| 1509 | 1501 |
| 1510 _getOutermostMethod() { | 1502 _getOutermostMethod() { |
| 1511 var result = this; | 1503 var result = this; |
| 1512 while (result.enclosingMethod != null) { | 1504 while (result.enclosingMethod != null) { |
| 1513 result = result.enclosingMethod; | 1505 result = result.enclosingMethod; |
| 1514 } | 1506 } |
| 1515 return result; | 1507 return result; |
| 1516 } | 1508 } |
| 1517 | 1509 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 1529 /** | 1521 /** |
| 1530 * Creates a reference to the enclosing type ('this') that can be used within | 1522 * Creates a reference to the enclosing type ('this') that can be used within |
| 1531 * closures. | 1523 * closures. |
| 1532 */ | 1524 */ |
| 1533 Value _makeThisValue(Node node) { | 1525 Value _makeThisValue(Node node) { |
| 1534 if (enclosingMethod != null) { | 1526 if (enclosingMethod != null) { |
| 1535 var outermostMethod = _getOutermostMethod(); | 1527 var outermostMethod = _getOutermostMethod(); |
| 1536 outermostMethod._checkNonStatic(node); | 1528 outermostMethod._checkNonStatic(node); |
| 1537 outermostMethod.needsThis = true; | 1529 outermostMethod.needsThis = true; |
| 1538 return new Value(outermostMethod.method.declaringType, '\$this', | 1530 return new Value(outermostMethod.method.declaringType, '\$this', |
| 1539 node != null ? node.span : null, /*isSuper:*/false, /*needsTemp:*/false) ; | 1531 node != null ? node.span : null, /*needsTemp:*/false); |
| 1540 } else { | 1532 } else { |
| 1541 _checkNonStatic(node); | 1533 _checkNonStatic(node); |
| 1542 return new Value(method.declaringType, 'this', node != null ? node.span : null, | 1534 return new Value(method.declaringType, 'this', node != null ? node.span : null, |
| 1543 /*isSuper:*/false, /*needsTemp:*/false); | 1535 /*needsTemp:*/false); |
| 1544 } | 1536 } |
| 1545 } | 1537 } |
| 1546 | 1538 |
| 1547 // ******************* Expressions ******************* | 1539 // ******************* Expressions ******************* |
| 1548 visitLambdaExpression(LambdaExpression node) { | 1540 visitLambdaExpression(LambdaExpression node) { |
| 1549 var name = ''; | 1541 var name = ''; |
| 1550 if (node.func.name != null) { | 1542 if (node.func.name != null) { |
| 1551 name = world.toJsIdentifier(node.func.name.name); | 1543 name = world.toJsIdentifier(node.func.name.name); |
| 1552 } | 1544 } |
| 1553 | 1545 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1597 // TODO(jimhug): Ensure these have same semantics as JS! | 1589 // TODO(jimhug): Ensure these have same semantics as JS! |
| 1598 if (kind == TokenKind.AND || kind == TokenKind.OR) { | 1590 if (kind == TokenKind.AND || kind == TokenKind.OR) { |
| 1599 var x = visitValue(node.x); | 1591 var x = visitValue(node.x); |
| 1600 var y = visitValue(node.y); | 1592 var y = visitValue(node.y); |
| 1601 final code = '${x.code} ${node.op} ${y.code}'; | 1593 final code = '${x.code} ${node.op} ${y.code}'; |
| 1602 if (x.isConst && y.isConst) { | 1594 if (x.isConst && y.isConst) { |
| 1603 var value = (kind == TokenKind.AND) | 1595 var value = (kind == TokenKind.AND) |
| 1604 ? x.actualValue && y.actualValue : x.actualValue || y.actualValue; | 1596 ? x.actualValue && y.actualValue : x.actualValue || y.actualValue; |
| 1605 return new EvaluatedValue(x.type, value, '$value', node.span); | 1597 return new EvaluatedValue(x.type, value, '$value', node.span); |
| 1606 } | 1598 } |
| 1607 return new Value(null, code, node.span); | 1599 var ret = new Value(Type.union(x.type, y.type), code, node.span); |
| 1600 return ret.convertToNonNullBool(this, node); | |
| 1608 } else if (kind == TokenKind.EQ_STRICT || kind == TokenKind.NE_STRICT) { | 1601 } else if (kind == TokenKind.EQ_STRICT || kind == TokenKind.NE_STRICT) { |
| 1609 var x = visitValue(node.x); | 1602 var x = visitValue(node.x); |
| 1610 var y = visitValue(node.y); | 1603 var y = visitValue(node.y); |
| 1611 if (x.isConst && y.isConst) { | 1604 if (x.isConst && y.isConst) { |
| 1612 var value = kind == TokenKind.EQ_STRICT | 1605 var value = kind == TokenKind.EQ_STRICT |
| 1613 // Note: it is ok to use == and not === here since all of these | 1606 // Note: it is ok to use == and not === here since all of these |
| 1614 // constant comparisons are applied to doubles, bool, or strings. | 1607 // constant comparisons are applied to doubles, bool, or strings. |
| 1615 // We need it for the compile-time evaluator because | 1608 // We need it for the compile-time evaluator because |
| 1616 // (9).toDouble() === 9.0 is false in dartvm. | 1609 // (9).toDouble() === 9.0 is false in dartvm. |
| 1617 ? x.actualValue == y.actualValue : x.actualValue != y.actualValue; | 1610 ? x.actualValue == y.actualValue : x.actualValue != y.actualValue; |
| 1618 return new EvaluatedValue(world.boolType, value, "$value", node.span); | 1611 return new EvaluatedValue(world.boolType, value, "$value", node.span); |
| 1619 } | 1612 } |
| 1620 if (x.code == 'null' || y.code == 'null') { | 1613 if (x.code == 'null' || y.code == 'null') { |
| 1621 // Switching to == ensures that null and undefined are interchangable. | 1614 // Switching to == ensures that null and undefined are interchangable. |
| 1622 final op = node.op.toString().substring(0,2); | 1615 final op = node.op.toString().substring(0,2); |
| 1623 return new Value(null, '${x.code} $op ${y.code}', node.span); | 1616 return new Value(world.boolType, '${x.code} $op ${y.code}', node.span); |
|
Jennifer Messerly
2011/11/12 00:39:58
it's funny, I caught this one too. But it's now it
| |
| 1624 } else { | 1617 } else { |
| 1625 // TODO(jimhug): Resolve issue with undefined and null here. | 1618 // TODO(jimhug): Resolve issue with undefined and null here. |
| 1626 return new Value(null, '${x.code} ${node.op} ${y.code}', node.span); | 1619 return new Value(world.boolType, '${x.code} ${node.op} ${y.code}', |
| 1620 node.span); | |
| 1627 } | 1621 } |
| 1628 } | 1622 } |
| 1629 | 1623 |
| 1630 final assignKind = TokenKind.kindFromAssign(node.op.kind); | 1624 final assignKind = TokenKind.kindFromAssign(node.op.kind); |
| 1631 if (assignKind == -1) { | 1625 if (assignKind == -1) { |
| 1632 final x = visitValue(node.x); | 1626 final x = visitValue(node.x); |
| 1633 final y = visitValue(node.y); | 1627 final y = visitValue(node.y); |
| 1634 var name = TokenKind.binaryMethodName(node.op.kind); | 1628 var name = TokenKind.binaryMethodName(node.op.kind); |
| 1635 if (node.op.kind == TokenKind.NE) { | 1629 if (node.op.kind == TokenKind.NE) { |
| 1636 name = '\$ne'; | 1630 name = '\$ne'; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1682 | 1676 |
| 1683 if (x == null) { | 1677 if (x == null) { |
| 1684 // Look for a setter in the class | 1678 // Look for a setter in the class |
| 1685 var members = method.declaringType.resolveMember(name); | 1679 var members = method.declaringType.resolveMember(name); |
| 1686 if (members != null) { | 1680 if (members != null) { |
| 1687 x = _makeThisOrType(position.span); | 1681 x = _makeThisOrType(position.span); |
| 1688 if (kind == 0) { | 1682 if (kind == 0) { |
| 1689 return x.set_(this, name, position, y); | 1683 return x.set_(this, name, position, y); |
| 1690 } else if (!members.treatAsField || members.containsMethods) { | 1684 } else if (!members.treatAsField || members.containsMethods) { |
| 1691 var right = x.get_(this, name, position); | 1685 var right = x.get_(this, name, position); |
| 1692 //var right = members._get(this, position, x); | |
| 1693 right = captureOriginal(right); | 1686 right = captureOriginal(right); |
| 1694 y = right.invoke(this, TokenKind.binaryMethodName(kind), | 1687 y = right.invoke(this, TokenKind.binaryMethodName(kind), |
| 1695 position, new Arguments(null, [y])); | 1688 position, new Arguments(null, [y])); |
| 1696 return x.set_(this, name, position, y); | 1689 return x.set_(this, name, position, y); |
| 1697 } else { | 1690 } else { |
| 1698 x = x.get_(this, name, position); | 1691 x = x.get_(this, name, position); |
| 1699 } | 1692 } |
| 1700 } else { | 1693 } else { |
| 1701 // Look for a top-level setter | 1694 // Look for a top-level setter |
| 1702 final member = library.lookup(name, xn.name.span); | 1695 final member = library.lookup(name, xn.name.span); |
| (...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2045 final name = node.name.name; | 2038 final name = node.name.name; |
| 2046 | 2039 |
| 2047 // First check in block scopes. | 2040 // First check in block scopes. |
| 2048 var ret = _scope.lookup(name); | 2041 var ret = _scope.lookup(name); |
| 2049 if (ret != null) return ret; | 2042 if (ret != null) return ret; |
| 2050 | 2043 |
| 2051 return _makeThisOrType(node.span).get_(this, name, node); | 2044 return _makeThisOrType(node.span).get_(this, name, node); |
| 2052 } | 2045 } |
| 2053 | 2046 |
| 2054 _makeMissingValue(String name) { | 2047 _makeMissingValue(String name) { |
| 2055 // TODO(jimhug): Probably goes away to be fully replaced by doesNotUnder | 2048 // TODO(jimhug): Probably goes away to be fully replaced by noSuchMethod |
| 2056 return new Value(null, '$name()/*NotFound*/', null); | 2049 return new Value(world.varType, '$name()/*NotFound*/', null); |
| 2057 } | 2050 } |
| 2058 | 2051 |
| 2059 _makeThisOrType(SourceSpan span) { | 2052 _makeThisOrType(SourceSpan span) { |
| 2060 return new BareValue(this, _getOutermostMethod(), span); | 2053 return new BareValue(this, _getOutermostMethod(), span); |
| 2061 } | 2054 } |
| 2062 | 2055 |
| 2063 visitThisExpression(ThisExpression node) { | 2056 visitThisExpression(ThisExpression node) { |
| 2064 return _makeThisValue(node); | 2057 return _makeThisValue(node); |
| 2065 } | 2058 } |
| 2066 | 2059 |
| 2067 visitSuperExpression(SuperExpression node) { | 2060 visitSuperExpression(SuperExpression node) { |
| 2068 return _makeSuperValue(node); | 2061 return _makeSuperValue(node); |
| 2069 } | 2062 } |
| 2070 | 2063 |
| 2071 visitNullExpression(NullExpression node) { | 2064 visitNullExpression(NullExpression node) { |
| 2072 return new EvaluatedValue(null, null, 'null', null); | 2065 // TODO(jimhug): should be passing node.span |
| 2066 // TODO(jimhug): Can we do better than var for the type? | |
| 2067 return new EvaluatedValue(world.varType, null, 'null', null); | |
| 2073 } | 2068 } |
| 2074 | 2069 |
| 2075 visitLiteralExpression(LiteralExpression node) { | 2070 visitLiteralExpression(LiteralExpression node) { |
| 2076 // All Literal types are filled in at parse time, so no need to resolve. | 2071 // All Literal types are filled in at parse time, so no need to resolve. |
| 2077 var type = node.type.type; | 2072 var type = node.type.type; |
| 2078 assert(type != null); | 2073 assert(type != null); |
| 2079 | 2074 |
| 2080 if (node.value is List) { | 2075 if (node.value is List) { |
| 2081 var items = []; | 2076 var items = []; |
| 2082 for (var item in node.value) { | 2077 for (var item in node.value) { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2132 List<Value> values; | 2127 List<Value> values; |
| 2133 List<ArgumentNode> nodes; | 2128 List<ArgumentNode> nodes; |
| 2134 int _bareCount; | 2129 int _bareCount; |
| 2135 | 2130 |
| 2136 Arguments(this.nodes, this.values); | 2131 Arguments(this.nodes, this.values); |
| 2137 | 2132 |
| 2138 /** Constructs a bare list of arguments. */ | 2133 /** Constructs a bare list of arguments. */ |
| 2139 factory Arguments.bare(int arity) { | 2134 factory Arguments.bare(int arity) { |
| 2140 var values = []; | 2135 var values = []; |
| 2141 for (int i = 0; i < arity; i++) { | 2136 for (int i = 0; i < arity; i++) { |
| 2142 // TODO(jimhug): Need source locations. | 2137 // TODO(jimhug): Need a firm rule about null SourceSpans are allowed. |
| 2143 values.add(new Value(world.varType, '\$$i', null, false, /*needsTemp:*/fal se)); | 2138 values.add(new Value(world.varType, '\$$i', null, /*needsTemp:*/false)); |
| 2144 } | 2139 } |
| 2145 return new Arguments(null, values); | 2140 return new Arguments(null, values); |
| 2146 } | 2141 } |
| 2147 | 2142 |
| 2148 int get nameCount() => length - bareCount; | 2143 int get nameCount() => length - bareCount; |
| 2149 bool get hasNames() => bareCount < length; | 2144 bool get hasNames() => bareCount < length; |
| 2150 | 2145 |
| 2151 int get length() => values.length; | 2146 int get length() => values.length; |
| 2152 | 2147 |
| 2153 String getName(int i) => nodes[i].label.name; | 2148 String getName(int i) => nodes[i].label.name; |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2221 for (int i = bareCount; i < length; i++) { | 2216 for (int i = bareCount; i < length; i++) { |
| 2222 names.add(getName(i)); | 2217 names.add(getName(i)); |
| 2223 } | 2218 } |
| 2224 return names; | 2219 return names; |
| 2225 } | 2220 } |
| 2226 | 2221 |
| 2227 /** Gets the argument names used in a call stub; uses $0 $1 for bare args. */ | 2222 /** Gets the argument names used in a call stub; uses $0 $1 for bare args. */ |
| 2228 Arguments toCallStubArgs() { | 2223 Arguments toCallStubArgs() { |
| 2229 var result = []; | 2224 var result = []; |
| 2230 for (int i = 0; i < bareCount; i++) { | 2225 for (int i = 0; i < bareCount; i++) { |
| 2231 // TODO(jimhug): Need source locations. | 2226 result.add(new Value(world.varType, '\$$i', null, /*needsTemp:*/false)); |
| 2232 result.add(new Value(world.varType, '\$$i', null, false, /*needsTemp:*/fal se)); | |
| 2233 } | 2227 } |
| 2234 for (int i = bareCount; i < length; i++) { | 2228 for (int i = bareCount; i < length; i++) { |
| 2235 var name = getName(i); | 2229 var name = getName(i); |
| 2236 if (name == null) name = '\$$i'; | 2230 if (name == null) name = '\$$i'; |
| 2237 // TODO(jimhug): Need source locations. | 2231 result.add(new Value(world.varType, name, null, /*needsTemp:*/false)); |
| 2238 result.add(new Value(world.varType, name, null, false, /*needsTemp:*/false )); | |
| 2239 } | 2232 } |
| 2240 return new Arguments(nodes, result); | 2233 return new Arguments(nodes, result); |
| 2241 } | 2234 } |
| 2242 } | 2235 } |
| OLD | NEW |