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 483 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 494 // But probably harmless since we have to pay for the lookup anyway.) | 494 // But probably harmless since we have to pay for the lookup anyway.) |
| 495 final type = enclosingMethod.method.declaringType; | 495 final type = enclosingMethod.method.declaringType; |
| 496 if (type.library.lookup(name, null) != null) return true; | 496 if (type.library.lookup(name, null) != null) return true; |
| 497 | 497 |
| 498 // Nobody else needs this name. It's safe to reuse. | 498 // Nobody else needs this name. It's safe to reuse. |
| 499 return false; | 499 return false; |
| 500 } | 500 } |
| 501 | 501 |
| 502 | 502 |
| 503 Value create(String name, Type type, SourceSpan span, | 503 Value create(String name, Type type, SourceSpan span, |
| 504 [bool isParameter = false]) { | 504 [bool isFinal = false, bool isParameter = false]) { |
| 505 | 505 |
| 506 var jsName = world.toJsIdentifier(name); | 506 var jsName = world.toJsIdentifier(name); |
| 507 if (_vars.containsKey(name)) { | 507 if (_vars.containsKey(name)) { |
| 508 world.error('duplicate name "$name"', span); | 508 world.error('duplicate name "$name"', span); |
| 509 } | 509 } |
| 510 | 510 |
| 511 // Make sure variables don't shadow any names we might need to access. | 511 // Make sure variables don't shadow any names we might need to access. |
| 512 if (!isParameter) { | 512 if (!isParameter) { |
| 513 int index = 0; | 513 int index = 0; |
| 514 while (_isDefinedInParent(jsName)) { | 514 while (_isDefinedInParent(jsName)) { |
| 515 jsName = '$name${index++}'; | 515 jsName = '$name${index++}'; |
| 516 } | 516 } |
| 517 } | 517 } |
| 518 | 518 |
| 519 var ret = new Value(type, jsName, span, false); | 519 var ret = new Value(type, jsName, span, false); |
| 520 ret.isFinal = isFinal; | |
| 520 _vars[name] = ret; | 521 _vars[name] = ret; |
| 521 return ret; | 522 return ret; |
| 522 } | 523 } |
| 523 | 524 |
| 524 Value declareParameter(Parameter p) { | 525 Value declareParameter(Parameter p) { |
| 525 return create(p.name, p.type, p.definition.span, isParameter:true); | 526 return create(p.name, p.type, p.definition.span, isParameter:true); |
| 526 } | 527 } |
| 527 | 528 |
| 528 /** Declares a variable in the current scope for this identifier. */ | 529 /** Declares a variable in the current scope for this identifier. */ |
| 529 Value declare(DeclaredIdentifier id) { | 530 Value declare(DeclaredIdentifier id) { |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 575 if (enclosingMethod != null) { | 576 if (enclosingMethod != null) { |
| 576 _scope = new BlockScope(this, enclosingMethod._scope); | 577 _scope = new BlockScope(this, enclosingMethod._scope); |
| 577 captures = new Set(); | 578 captures = new Set(); |
| 578 } else { | 579 } else { |
| 579 _scope = new BlockScope(this, null); | 580 _scope = new BlockScope(this, null); |
| 580 } | 581 } |
| 581 // For named lambdas, add the name to this scope so we can call it | 582 // For named lambdas, add the name to this scope so we can call it |
| 582 // recursively. | 583 // recursively. |
| 583 if (enclosingMethod != null && method.name != '') { | 584 if (enclosingMethod != null && method.name != '') { |
| 584 MethodMember m = method; // lambdas must be MethodMembers | 585 MethodMember m = method; // lambdas must be MethodMembers |
| 585 _scope.create(m.name, m.functionType, m.definition.span); | 586 _scope.create(m.name, m.functionType, m.definition.span, isFinal:true); |
|
jimhug
2011/11/17 15:36:15
Nice use for lambdas - I'm a little sad this didn'
| |
| 586 } | 587 } |
| 587 _usedTemps = new Set(); | 588 _usedTemps = new Set(); |
| 588 _freeTemps = []; | 589 _freeTemps = []; |
| 589 } | 590 } |
| 590 | 591 |
| 591 Library get library() => method.library; | 592 Library get library() => method.library; |
| 592 | 593 |
| 593 // TODO(jimhug): Where does this really belong? | 594 // TODO(jimhug): Where does this really belong? |
| 594 MemberSet findMembers(String name) { | 595 MemberSet findMembers(String name) { |
| 595 return library._findMembers(name); | 596 return library._findMembers(name); |
| (...skipping 488 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1084 var value = visitValue(node.values[i]); | 1085 var value = visitValue(node.values[i]); |
| 1085 if (isFinal) { | 1086 if (isFinal) { |
| 1086 if (value == null) { | 1087 if (value == null) { |
| 1087 world.error('no value specified for final variable', node.span); | 1088 world.error('no value specified for final variable', node.span); |
| 1088 } else { | 1089 } else { |
| 1089 // TODO(jimhug): Mark inferred types as special for correct errors. | 1090 // TODO(jimhug): Mark inferred types as special for correct errors. |
| 1090 if (thisType.isVar) thisType = value.type; | 1091 if (thisType.isVar) thisType = value.type; |
| 1091 } | 1092 } |
| 1092 } | 1093 } |
| 1093 | 1094 |
| 1094 var val = _scope.create(name, thisType, node.names[i].span); | 1095 var val = _scope.create(name, thisType, node.names[i].span, isFinal); |
| 1095 | 1096 |
| 1096 if (value == null) { | 1097 if (value == null) { |
| 1097 if (_scope.reentrant) { | 1098 if (_scope.reentrant) { |
| 1098 // To preserve block scoping, we need to ensure the variable is | 1099 // To preserve block scoping, we need to ensure the variable is |
| 1099 // reinitialized each time the block is entered. | 1100 // reinitialized each time the block is entered. |
| 1100 writer.write('${val.code} = null'); | 1101 writer.write('${val.code} = null'); |
| 1101 } else { | 1102 } else { |
| 1102 writer.write('${val.code}'); | 1103 writer.write('${val.code}'); |
| 1103 } | 1104 } |
| 1104 } else { | 1105 } else { |
| 1105 value = value.convertTo(this, type, node.values[i]); | 1106 value = value.convertTo(this, type, node.values[i]); |
| 1106 writer.write('${val.code} = ${value.code}'); | 1107 writer.write('${val.code} = ${value.code}'); |
| 1107 } | 1108 } |
| 1108 } | 1109 } |
| 1109 writer.writeln(';'); | 1110 writer.writeln(';'); |
| 1110 return false; | 1111 return false; |
| 1111 | 1112 |
| 1112 } | 1113 } |
| 1113 | 1114 |
| 1114 bool visitFunctionDefinition(FunctionDefinition node) { | 1115 bool visitFunctionDefinition(FunctionDefinition node) { |
| 1115 var name = world.toJsIdentifier(node.name.name); | 1116 var name = world.toJsIdentifier(node.name.name); |
| 1116 | 1117 |
| 1117 var meth = _makeLambdaMethod(name, node); | 1118 var meth = _makeLambdaMethod(name, node); |
| 1118 | 1119 |
| 1119 // TODO(jimhug): Pass js name into writeDefinition? | 1120 // TODO(jimhug): Pass js name into writeDefinition? |
| 1120 var funcValue = | 1121 var funcValue = _scope.create(name, meth.functionType, |
| 1121 _scope.create(name, meth.functionType, method.definition.span); | 1122 method.definition.span, isFinal:true); |
|
jimhug
2011/11/17 15:36:15
This one really shocks me that it doesn't trigger
| |
| 1122 meth.generator.writeDefinition(writer, null); | 1123 meth.generator.writeDefinition(writer, null); |
| 1123 return false; | 1124 return false; |
| 1124 } | 1125 } |
| 1125 | 1126 |
| 1126 /** | 1127 /** |
| 1127 * Returns true indicating that normal control-flow is interrupted by | 1128 * Returns true indicating that normal control-flow is interrupted by |
| 1128 * this statement. (This could be a return, break, throw, or continue.) | 1129 * this statement. (This could be a return, break, throw, or continue.) |
| 1129 */ | 1130 */ |
| 1130 bool visitReturnStatement(ReturnStatement node) { | 1131 bool visitReturnStatement(ReturnStatement node) { |
| 1131 if (node.value == null) { | 1132 if (node.value == null) { |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1253 needsComma = true; | 1254 needsComma = true; |
| 1254 } | 1255 } |
| 1255 writer.write(') '); | 1256 writer.write(') '); |
| 1256 _pushBlock(/*reentrant:*/true); | 1257 _pushBlock(/*reentrant:*/true); |
| 1257 node.body.visit(this); | 1258 node.body.visit(this); |
| 1258 _popBlock(); | 1259 _popBlock(); |
| 1259 _popBlock(); | 1260 _popBlock(); |
| 1260 return false; | 1261 return false; |
| 1261 } | 1262 } |
| 1262 | 1263 |
| 1264 bool _isFinal(typeRef) { | |
| 1265 if (typeRef is GenericTypeReference) { | |
| 1266 typeRef = typeRef.baseType; | |
| 1267 } | |
| 1268 return typeRef != null && typeRef.isFinal; | |
| 1269 } | |
| 1270 | |
| 1263 bool visitForInStatement(ForInStatement node) { | 1271 bool visitForInStatement(ForInStatement node) { |
| 1264 // TODO(jimhug): visitValue and other cleanups here. | 1272 // TODO(jimhug): visitValue and other cleanups here. |
| 1265 var itemType = method.resolveType(node.item.type, false); | 1273 var itemType = method.resolveType(node.item.type, false); |
| 1266 var itemName = node.item.name.name; | 1274 var itemName = node.item.name.name; |
| 1267 var list = node.list.visit(this); | 1275 var list = node.list.visit(this); |
| 1268 _pushBlock(/*reentrant:*/true); | 1276 _pushBlock(/*reentrant:*/true); |
| 1269 // TODO(jimhug): Check that itemType matches list members... | 1277 // TODO(jimhug): Check that itemType matches list members... |
| 1270 var item = _scope.create(itemName, itemType, node.item.name.span); | 1278 bool isFinal = _isFinal(node.item.type); |
| 1279 var item = _scope.create(itemName, itemType, node.item.name.span, isFinal); | |
| 1271 Value listVar = list; | 1280 Value listVar = list; |
| 1272 if (list.needsTemp) { | 1281 if (list.needsTemp) { |
| 1273 listVar = _scope.create('\$list', list.type, null); | 1282 listVar = _scope.create('\$list', list.type, null); |
| 1274 writer.writeln('var ${listVar.code} = ${list.code};'); | 1283 writer.writeln('var ${listVar.code} = ${list.code};'); |
| 1275 } | 1284 } |
| 1276 | 1285 |
| 1277 // Special path for list for readability and perf optimization. | 1286 // Special path for list for readability and perf optimization. |
| 1278 if (list.type.isList) { | 1287 if (list.type.isList) { |
| 1279 var tmpi = _scope.create('\$i', world.numType, null); | 1288 var tmpi = _scope.create('\$i', world.numType, null); |
| 1280 writer.enterBlock('for (var ${tmpi.code} = 0;' + | 1289 writer.enterBlock('for (var ${tmpi.code} = 0;' + |
| (...skipping 462 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1743 return members._set(this, position, x, y); | 1752 return members._set(this, position, x, y); |
| 1744 } else { | 1753 } else { |
| 1745 x = members._get(this, position, x); | 1754 x = members._get(this, position, x); |
| 1746 } | 1755 } |
| 1747 } | 1756 } |
| 1748 | 1757 |
| 1749 // Otherwise treat it as a field. | 1758 // Otherwise treat it as a field. |
| 1750 // This makes for nicer code in the $op= case | 1759 // This makes for nicer code in the $op= case |
| 1751 } | 1760 } |
| 1752 | 1761 |
| 1753 // TODO(jimhug): Needs checks for final and other rules to enforce. | 1762 if (x.isFinal) { |
|
jimhug
2011/11/17 15:36:15
Yay!
| |
| 1763 world.error('final variable "${x.code}" is not assignable', | |
| 1764 position.span); | |
| 1765 } | |
| 1766 | |
| 1754 y = y.convertTo(this, x.type, yn); | 1767 y = y.convertTo(this, x.type, yn); |
| 1755 | 1768 |
| 1756 if (kind == 0) { | 1769 if (kind == 0) { |
| 1757 x = captureOriginal(x); | 1770 x = captureOriginal(x); |
| 1758 return new Value(y.type, '${x.code} = ${y.code}', position.span); | 1771 return new Value(y.type, '${x.code} = ${y.code}', position.span); |
| 1759 } else if (x.type.isNum && y.type.isNum && (kind != TokenKind.TRUNCDIV)) { | 1772 } else if (x.type.isNum && y.type.isNum && (kind != TokenKind.TRUNCDIV)) { |
| 1760 // Process everything but ~/ , which has no equivalent JS operator | 1773 // Process everything but ~/ , which has no equivalent JS operator |
| 1761 x = captureOriginal(x); | 1774 x = captureOriginal(x); |
| 1762 // Very localized optimization for numbers! | 1775 // Very localized optimization for numbers! |
| 1763 final op = TokenKind.kindToString(kind); | 1776 final op = TokenKind.kindToString(kind); |
| (...skipping 495 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2259 result.add(new Value(world.varType, '\$$i', null, /*needsTemp:*/false)); | 2272 result.add(new Value(world.varType, '\$$i', null, /*needsTemp:*/false)); |
| 2260 } | 2273 } |
| 2261 for (int i = bareCount; i < length; i++) { | 2274 for (int i = bareCount; i < length; i++) { |
| 2262 var name = getName(i); | 2275 var name = getName(i); |
| 2263 if (name == null) name = '\$$i'; | 2276 if (name == null) name = '\$$i'; |
| 2264 result.add(new Value(world.varType, name, null, /*needsTemp:*/false)); | 2277 result.add(new Value(world.varType, name, null, /*needsTemp:*/false)); |
| 2265 } | 2278 } |
| 2266 return new Arguments(nodes, result); | 2279 return new Arguments(nodes, result); |
| 2267 } | 2280 } |
| 2268 } | 2281 } |
| OLD | NEW |