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

Side by Side Diff: lib/src/codegen/js_codegen.dart

Issue 1069493002: implement opassign, fix bugs in pre/postfix, introduce a let* helper (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 years, 8 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
« no previous file with comments | « lib/src/codegen/ast_builder.dart ('k') | lib/src/codegen/js_metalet.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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 library dev_compiler.src.codegen.js_codegen; 5 library dev_compiler.src.codegen.js_codegen;
6 6
7 import 'dart:collection' show HashSet, HashMap; 7 import 'dart:collection' show HashSet, HashMap;
8 import 'dart:io' show Directory, File; 8 import 'dart:io' show Directory, File;
9 9
10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 10 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
(...skipping 13 matching lines...) Expand all
24 import 'package:dev_compiler/src/js/js_ast.dart' as JS; 24 import 'package:dev_compiler/src/js/js_ast.dart' as JS;
25 import 'package:dev_compiler/src/js/js_ast.dart' show js; 25 import 'package:dev_compiler/src/js/js_ast.dart' show js;
26 26
27 import 'package:dev_compiler/src/checker/rules.dart'; 27 import 'package:dev_compiler/src/checker/rules.dart';
28 import 'package:dev_compiler/src/info.dart'; 28 import 'package:dev_compiler/src/info.dart';
29 import 'package:dev_compiler/src/options.dart'; 29 import 'package:dev_compiler/src/options.dart';
30 import 'package:dev_compiler/src/utils.dart'; 30 import 'package:dev_compiler/src/utils.dart';
31 31
32 import 'code_generator.dart'; 32 import 'code_generator.dart';
33 import 'js_names.dart'; 33 import 'js_names.dart';
34 import 'js_metalet.dart';
34 35
35 bool _isAnnotationType(Annotation m, String name) => m.name.name == name; 36 bool _isAnnotationType(Annotation m, String name) => m.name.name == name;
36 37
37 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata 38 Annotation _getAnnotation(AnnotatedNode node, String name) => node.metadata
38 .firstWhere((annotation) => _isAnnotationType(annotation, name), 39 .firstWhere((annotation) => _isAnnotationType(annotation, name),
39 orElse: () => null); 40 orElse: () => null);
40 41
41 Annotation _getJsNameAnnotation(AnnotatedNode node) => 42 Annotation _getJsNameAnnotation(AnnotatedNode node) =>
42 _getAnnotation(node, "JsName"); 43 _getAnnotation(node, "JsName");
43 44
(...skipping 1037 matching lines...) Expand 10 before | Expand all | Expand 10 after
1081 } 1082 }
1082 1083
1083 // initializing formal parameter, e.g. `Point(this.x)` 1084 // initializing formal parameter, e.g. `Point(this.x)`
1084 if (e is ParameterElement && e.isInitializingFormal && e.isPrivate) { 1085 if (e is ParameterElement && e.isInitializingFormal && e.isPrivate) {
1085 /// Rename private names so they don't shadow the private field symbol. 1086 /// Rename private names so they don't shadow the private field symbol.
1086 /// The renamer would handle this, but it would prefer to rename the 1087 /// The renamer would handle this, but it would prefer to rename the
1087 /// temporary used for the private symbol. Instead rename the parameter. 1088 /// temporary used for the private symbol. Instead rename the parameter.
1088 return new JSTemporary('${name.substring(1)}'); 1089 return new JSTemporary('${name.substring(1)}');
1089 } 1090 }
1090 1091
1091 if (_isTemporary(e)) return new JSTemporary(e.name); 1092 if (_isTemporary(e)) {
1093 if (name[0] == '#') {
1094 return new JS.InterpolatedExpression(name.substring(1));
1095 } else {
1096 return new JSTemporary(e.name);
1097 }
1098 }
1092 1099
1093 return new JS.Identifier(name); 1100 return new JS.Identifier(name);
1094 } 1101 }
1095 1102
1096 JS.Expression _emitTypeName(DartType type) { 1103 JS.Expression _emitTypeName(DartType type) {
1097 var name = type.name; 1104 var name = type.name;
1098 var element = type.element; 1105 var element = type.element;
1099 if (name == '') { 1106 if (name == '') {
1100 // TODO(jmesserly): remove when we're using coercion reifier. 1107 // TODO(jmesserly): remove when we're using coercion reifier.
1101 return _unimplementedCall('Unimplemented type $type'); 1108 return _unimplementedCall('Unimplemented type $type');
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
1143 _visit(target), 1150 _visit(target),
1144 js.string(id.name, "'"), 1151 js.string(id.name, "'"),
1145 _visit(rhs) 1152 _visit(rhs)
1146 ]); 1153 ]);
1147 } else { 1154 } else {
1148 return null; 1155 return null;
1149 } 1156 }
1150 } 1157 }
1151 1158
1152 @override 1159 @override
1153 JS.Node visitAssignmentExpression(AssignmentExpression node) { 1160 JS.Expression visitAssignmentExpression(AssignmentExpression node) {
1154 var lhs = node.leftHandSide; 1161 var left = node.leftHandSide;
1155 var rhs = node.rightHandSide; 1162 var right = node.rightHandSide;
1156 return _emitSet(lhs, rhs, node.parent); 1163 if (node.operator.type == TokenType.EQ) return _emitSet(left, right);
1164 return _emitOpAssign(left, right, node.operator.lexeme[0], context: node);
1157 } 1165 }
1158 1166
1159 JS.Node _emitSet(Expression lhs, Expression rhs, [AstNode parent]) { 1167 JSMetaLet _emitOpAssign(Expression left, Expression right, String op,
1168 {Expression context}) {
1169 // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions
1170 // (for example, x is IndexExpression) we evaluate those once.
1171 var vars = {};
1172 var lhs = _bindLeftHandSide(vars, left, context: context);
1173 var inc = AstBuilder.binaryExpression(lhs, op, right);
1174 inc.staticType = rules.getStaticType(left);
1175 return new JSMetaLet(vars, [_emitSet(lhs, inc)]);
1176 }
1177
1178 JS.Expression _emitSet(Expression lhs, Expression rhs) {
1160 if (lhs is IndexExpression) { 1179 if (lhs is IndexExpression) {
1161 String code; 1180 String code;
1162 var target = _getTarget(lhs); 1181 var target = _getTarget(lhs);
1163 if (rules.isDynamicTarget(target)) { 1182 if (rules.isDynamicTarget(target)) {
1164 code = 'dart.dsetindex(#, #, #)'; 1183 code = 'dart.dsetindex(#, #, #)';
1165 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]); 1184 return js.call(code, [_visit(target), _visit(lhs.index), _visit(rhs)]);
1166 } 1185 }
1167 return js.call('#.#(#, #)', [ 1186 return js.call('#.#(#, #)', [
1168 _visit(target), 1187 _visit(target),
1169 _emitMemberName('[]=', target: target), 1188 _emitMemberName('[]=', target: target),
1170 _visit(lhs.index), 1189 _visit(lhs.index),
1171 _visit(rhs) 1190 _visit(rhs)
1172 ]); 1191 ]);
1173 } 1192 }
1174 1193
1175 if (lhs is PropertyAccess) { 1194 if (lhs is PropertyAccess) {
1176 var result = _emitDSetIfDynamic(_getTarget(lhs), lhs.propertyName, rhs); 1195 var result = _emitDSetIfDynamic(_getTarget(lhs), lhs.propertyName, rhs);
1177 if (result != null) return result; 1196 if (result != null) return result;
1178 } else if (lhs is PrefixedIdentifier) { 1197 } else if (lhs is PrefixedIdentifier) {
1179 // TODO(vsm): Is this the right code if the prefix is a library? 1198 // TODO(vsm): Is this the right code if the prefix is a library?
1180 var result = _emitDSetIfDynamic(lhs.prefix, lhs.identifier, rhs); 1199 var result = _emitDSetIfDynamic(lhs.prefix, lhs.identifier, rhs);
1181 if (result != null) return result; 1200 if (result != null) return result;
1182 } 1201 }
1183 1202 return _visit(rhs).toAssignExpression(_visit(lhs));
1184 if (parent is ExpressionStatement &&
1185 rhs is CascadeExpression &&
1186 _isStateless(lhs, rhs)) {
1187 // Special case: cascade assignment to a variable in a statement.
1188 // We can reuse the variable to desugar it:
1189 // result = []..length = length;
1190 // becomes:
1191 // result = [];
1192 // result.length = length;
1193 var savedCascadeTemp = _cascadeTarget;
1194 _cascadeTarget = lhs;
1195
1196 var body = [];
1197 body.add(js.statement('# = #;', [_visit(lhs), _visit(rhs.target)]));
1198 for (var section in rhs.cascadeSections) {
1199 body.add(new JS.ExpressionStatement(_visit(section)));
1200 }
1201
1202 _cascadeTarget = savedCascadeTemp;
1203 return _statement(body);
1204 }
1205
1206 return js.call('# = #', [_visit(lhs), _visit(rhs)]);
1207 } 1203 }
1208 1204
1209 @override 1205 @override
1210 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) { 1206 JS.Block visitExpressionFunctionBody(ExpressionFunctionBody node) {
1211 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent)); 1207 var initArgs = _emitArgumentInitializers(_parametersOf(node.parent));
1212 var ret = new JS.Return(_visit(node.expression)); 1208 var ret = new JS.Return(_visit(node.expression));
1213 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]); 1209 return new JS.Block(initArgs != null ? [initArgs, ret] : [ret]);
1214 } 1210 }
1215 1211
1216 @override 1212 @override
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
1326 result.add(_namedArgTemp); 1322 result.add(_namedArgTemp);
1327 break; 1323 break;
1328 } 1324 }
1329 result.add(_visit(param)); 1325 result.add(_visit(param));
1330 } 1326 }
1331 return result; 1327 return result;
1332 } 1328 }
1333 1329
1334 @override 1330 @override
1335 JS.Statement visitExpressionStatement(ExpressionStatement node) => 1331 JS.Statement visitExpressionStatement(ExpressionStatement node) =>
1336 _expressionStatement(_visit(node.expression)); 1332 _visit(node.expression).toStatement();
1337
1338 // Some expressions may choose to generate themselves as JS statements
1339 // if their parent is in a statement context.
1340 // TODO(jmesserly): refactor so we handle the special cases here, and
1341 // can use better return types on the expression visit methods.
1342 JS.Statement _expressionStatement(expr) =>
1343 expr is JS.Statement ? expr : new JS.ExpressionStatement(expr);
1344 1333
1345 @override 1334 @override
1346 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) => 1335 JS.EmptyStatement visitEmptyStatement(EmptyStatement node) =>
1347 new JS.EmptyStatement(); 1336 new JS.EmptyStatement();
1348 1337
1349 @override 1338 @override
1350 JS.Statement visitAssertStatement(AssertStatement node) => 1339 JS.Statement visitAssertStatement(AssertStatement node) =>
1351 // TODO(jmesserly): only emit in checked mode. 1340 // TODO(jmesserly): only emit in checked mode.
1352 js.statement('dart.assert(#);', _visit(node.condition)); 1341 js.statement('dart.assert(#);', _visit(node.condition));
1353 1342
1354 @override 1343 @override
1355 JS.Return visitReturnStatement(ReturnStatement node) => 1344 JS.Statement visitReturnStatement(ReturnStatement node) {
1356 new JS.Return(_visit(node.expression)); 1345 var e = node.expression;
1346 if (e == null) return new JS.Return();
1347 return _visit(e).toReturn();
1348 }
1357 1349
1358 @override 1350 @override
1359 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { 1351 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
1360 var body = <JS.Statement>[]; 1352 var body = <JS.Statement>[];
1361 1353
1362 for (var field in node.variables.variables) { 1354 for (var field in node.variables.variables) {
1363 if (field.isConst) { 1355 if (field.isConst) {
1364 // constant fields don't change, so we can generate them as `let` 1356 // constant fields don't change, so we can generate them as `let`
1365 // but add them to the module's exports 1357 // but add them to the module's exports
1366 var name = field.name.name; 1358 var name = field.name.name;
1367 body.add(js.statement( 1359 body.add(js.statement(
1368 'let # = #;', [new JS.Identifier(name), _visitInitializer(field)])); 1360 'let # = #;', [new JS.Identifier(name), _visitInitializer(field)]));
1369 if (isPublic(name)) _addExport(name); 1361 if (isPublic(name)) _addExport(name);
1370 } else if (_isFieldInitConstant(field)) { 1362 } else if (_isFieldInitConstant(field)) {
1371 body.add(js.statement( 1363 body.add(js.statement(
1372 '# = #;', [_visit(field.name), _visitInitializer(field)])); 1364 '# = #;', [_visit(field.name), _visitInitializer(field)]));
1373 } else { 1365 } else {
1374 _lazyFields.add(field); 1366 _lazyFields.add(field);
1375 } 1367 }
1376 } 1368 }
1377 1369
1378 return _statement(body); 1370 return _statement(body);
1379 } 1371 }
1380 1372
1381 _addExport(String name) { 1373 _addExport(String name) {
1382 if (!_exports.add(name)) throw 'Duplicate top level name found: $name'; 1374 if (!_exports.add(name)) throw 'Duplicate top level name found: $name';
1383 } 1375 }
1384 1376
1385 @override 1377 @override
1378 JS.Statement visitVariableDeclarationStatement(
1379 VariableDeclarationStatement node) {
1380 // Special case a single variable with an initializer.
1381 // This helps emit cleaner code for things like:
1382 // var result = []..add(1)..add(2);
1383 if (node.variables.variables.length == 1) {
1384 var v = node.variables.variables.single;
1385 if (v.initializer != null) {
1386 var name = new JS.Identifier(v.name.name);
1387 return _visit(v.initializer).toVariableDeclaration(name);
1388 }
1389 }
1390 return _visit(node.variables).toStatement();
1391 }
1392
1393 @override
1386 visitVariableDeclarationList(VariableDeclarationList node) { 1394 visitVariableDeclarationList(VariableDeclarationList node) {
1387 var last = node.variables.last; 1395 return new JS.VariableDeclarationList('let', _visitList(node.variables));
1388 var lastInitializer = last.initializer;
1389
1390 List<JS.VariableInitialization> variables;
1391 if (lastInitializer is CascadeExpression &&
1392 node.parent is VariableDeclarationStatement) {
1393 // Special case: cascade as variable initializer
1394 //
1395 // We can reuse the variable to desugar it:
1396 // var result = []..length = length;
1397 // becomes:
1398 // var result = [];
1399 // result.length = length;
1400 var savedCascadeTemp = _cascadeTarget;
1401 _cascadeTarget = last.name;
1402
1403 variables = _visitList(node.variables.take(node.variables.length - 1));
1404 variables.add(new JS.VariableInitialization(
1405 new JS.Identifier(last.name.name), _visit(lastInitializer.target)));
1406
1407 var result =
1408 <JS.Expression>[new JS.VariableDeclarationList('let', variables)];
1409 result.addAll(_visitList(lastInitializer.cascadeSections));
1410 _cascadeTarget = savedCascadeTemp;
1411 return _statement(result.map((e) => new JS.ExpressionStatement(e)));
1412 } else {
1413 variables = _visitList(node.variables);
1414 }
1415
1416 return new JS.VariableDeclarationList('let', variables);
1417 } 1396 }
1418 1397
1419 @override 1398 @override
1420 JS.VariableInitialization visitVariableDeclaration(VariableDeclaration node) { 1399 JS.VariableInitialization visitVariableDeclaration(VariableDeclaration node) {
1421 var name = new JS.Identifier(node.name.name); 1400 var name = new JS.Identifier(node.name.name);
1422 return new JS.VariableInitialization(name, _visitInitializer(node)); 1401 return new JS.VariableInitialization(name, _visitInitializer(node));
1423 } 1402 }
1424 1403
1425 JS.Expression _visitInitializer(VariableDeclaration node) { 1404 JS.Expression _visitInitializer(VariableDeclaration node) {
1426 var value = _visit(node.initializer); 1405 var value = _visit(node.initializer);
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1460 void _flushLibraryProperties(List<JS.Statement> body) { 1439 void _flushLibraryProperties(List<JS.Statement> body) {
1461 if (_properties.isEmpty) return; 1440 if (_properties.isEmpty) return;
1462 body.add(js.statement('dart.copyProperties(#, { # });', [ 1441 body.add(js.statement('dart.copyProperties(#, { # });', [
1463 _exportsVar, 1442 _exportsVar,
1464 _properties.map(_emitTopLevelProperty) 1443 _properties.map(_emitTopLevelProperty)
1465 ])); 1444 ]));
1466 _properties.clear(); 1445 _properties.clear();
1467 } 1446 }
1468 1447
1469 @override 1448 @override
1470 JS.Statement visitVariableDeclarationStatement(
1471 VariableDeclarationStatement node) =>
1472 _expressionStatement(_visit(node.variables));
1473
1474 @override
1475 visitConstructorName(ConstructorName node) { 1449 visitConstructorName(ConstructorName node) {
1476 var typeName = _visit(node.type); 1450 var typeName = _visit(node.type);
1477 if (node.name != null) { 1451 if (node.name != null) {
1478 return js.call( 1452 return js.call(
1479 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]); 1453 '#.#', [typeName, _emitMemberName(node.name.name, isStatic: true)]);
1480 } 1454 }
1481 return typeName; 1455 return typeName;
1482 } 1456 }
1483 1457
1484 @override 1458 @override
(...skipping 29 matching lines...) Expand all
1514 1488
1515 // TODO(vsm): Revisit whether we really need this when we get 1489 // TODO(vsm): Revisit whether we really need this when we get
1516 // better non-nullability in the type system. 1490 // better non-nullability in the type system.
1517 1491
1518 if (expr is Literal && expr is! NullLiteral) { 1492 if (expr is Literal && expr is! NullLiteral) {
1519 return true; 1493 return true;
1520 } 1494 }
1521 if (expr is ParenthesizedExpression) { 1495 if (expr is ParenthesizedExpression) {
1522 return _isNonNullableExpression(expr.expression); 1496 return _isNonNullableExpression(expr.expression);
1523 } 1497 }
1498 if (expr is Conversion) {
1499 return _isNonNullableExpression(expr.expression);
1500 }
1524 DartType type = null; 1501 DartType type = null;
1525 if (expr is BinaryExpression) { 1502 if (expr is BinaryExpression) {
1526 type = rules.getStaticType(expr.leftOperand); 1503 type = rules.getStaticType(expr.leftOperand);
1527 } else if (expr is PrefixExpression) { 1504 } else if (expr is PrefixExpression) {
1528 type = rules.getStaticType(expr.operand); 1505 type = rules.getStaticType(expr.operand);
1529 } else if (expr is PostfixExpression) { 1506 } else if (expr is PostfixExpression) {
1530 type = rules.getStaticType(expr.operand); 1507 type = rules.getStaticType(expr.operand);
1531 } 1508 }
1532 if (type != null && typeIsPrimitiveInJS(type)) { 1509 if (type != null && typeIsPrimitiveInJS(type)) {
1533 return true; 1510 return true;
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
1656 // * create a new subtype of LocalVariableElementImpl to mark a temp. 1633 // * create a new subtype of LocalVariableElementImpl to mark a temp.
1657 var id = 1634 var id =
1658 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); 1635 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1));
1659 id.staticElement = new LocalVariableElementImpl.forNode(id); 1636 id.staticElement = new LocalVariableElementImpl.forNode(id);
1660 id.staticType = type; 1637 id.staticType = type;
1661 return id; 1638 return id;
1662 } 1639 }
1663 1640
1664 bool _isTemporary(Element node) => node.nameOffset == -1; 1641 bool _isTemporary(Element node) => node.nameOffset == -1;
1665 1642
1666 JS.Expression _emitPostfixIncrement(Expression expr, Token op) { 1643 /// Desugars postfix increment.
1644 ///
1645 /// In the general case [expr] can be one of [IndexExpression],
1646 /// [PrefixExpression] or [PropertyAccess] and we need to
1647 /// ensure sub-expressions are evaluated once.
1648 ///
1649 /// We also need to ensure we can return the original value of the expression,
1650 /// and that it is only evaluated once.
1651 ///
1652 /// We desugar this using let*.
1653 ///
1654 /// For example, `expr1[expr2]++` can be transformed to this:
1655 ///
1656 /// // psuedocode mix of Scheme and JS:
1657 /// (let* (x1=expr1, x2=expr2, t=expr1[expr2]) { x1[x2] = t + 1; t })
1658 ///
1659 /// The [JSMetaLet] nodes automatically simplify themselves if they can.
1660 /// For example, if the result value is not used, then `t` goes away.
1661 JSMetaLet _emitPostfixIncrement(Expression expr, Token op) {
1667 var type = rules.getStaticType(expr); 1662 var type = rules.getStaticType(expr);
1668 assert(type != null); 1663 assert(type != null);
1669 var tmp = _createTemporary('x', type);
1670 1664
1671 // Increment and write 1665 // Handle the left hand side, to ensure each of its subexpressions are
1666 // evaluated only once.
1667 var vars = {};
1668 var left = _bindLeftHandSide(vars, expr, context: expr);
1669
1670 // Desugar `x++` as `(x1 = x0 + 1, x0)` where `x0` is the original value
1671 // and `x1` is the new value for `x`.
1672 var x = _bindValue(vars, 'x', left, context: expr);
1673
1672 var one = AstBuilder.integerLiteral(1); 1674 var one = AstBuilder.integerLiteral(1);
1673 one.staticType = rules.provider.intType; 1675 one.staticType = rules.provider.intType;
1674 var increment = AstBuilder.binaryExpression(tmp, op.lexeme[0], one); 1676 var increment = AstBuilder.binaryExpression(x, op.lexeme[0], one);
1675 increment.staticType = type; 1677 increment.staticType = type;
1676 var write = _emitSet(expr, increment);
1677 1678
1678 return js.call( 1679 var body = [_emitSet(left, increment), _visit(x)];
1679 "((#) => (#, #))(#)", [_visit(tmp), write, _visit(tmp), _visit(expr)]); 1680 return new JSMetaLet(vars, body, statelessResult: true);
1681 }
1682
1683 /// Returns a new expression, which can be be used safely *once* on the
1684 /// left hand side, and *once* on the right side of an assignment.
1685 /// For example: `expr1[expr2] += y` can be compiled as
1686 /// `expr1[expr2] = expr1[expr2] + y`.
1687 ///
1688 /// The temporary scope will ensure `expr1` and `expr2` are only evaluated
1689 /// once: `((x1, x2) => x1[x2] = x1[x2] + y)(expr1, expr2)`.
1690 ///
1691 /// If the expression does not end up using `x1` or `x2` more than once, or
1692 /// if those expressions can be treated as stateless (e.g. they are
1693 /// non-mutated variables), then the resulting code will be simplified
1694 /// automatically.
1695 ///
1696 /// [scope] can be mutated to contain any new temporaries that were created,
1697 /// unless [expr] is a SimpleIdentifier, in which case a temporary is not
1698 /// needed.
1699 Expression _bindLeftHandSide(
1700 Map<String, JS.Expression> scope, Expression expr, {Expression context}) {
1701 if (expr is IndexExpression) {
1702 IndexExpression index = expr;
1703 return new IndexExpression.forTarget(
1704 _bindValue(scope, 'o', index.target, context: context),
1705 index.leftBracket,
1706 _bindValue(scope, 'i', index.index, context: context),
1707 index.rightBracket)..staticType = expr.staticType;
1708 } else if (expr is PropertyAccess) {
1709 PropertyAccess prop = expr;
1710 return new PropertyAccess(
1711 _bindValue(scope, 'o', prop.target, context: context), prop.operator,
1712 prop.propertyName)..staticType = expr.staticType;
1713 } else if (expr is PrefixedIdentifier) {
1714 PrefixedIdentifier ident = expr;
1715 return new PrefixedIdentifier(
1716 _bindValue(scope, 'o', ident.prefix, context: context), ident.period,
1717 ident.identifier)..staticType = expr.staticType;
1718 }
1719 return expr as SimpleIdentifier;
1720 }
1721
1722 /// Creates a temporary to contain the value of [expr]. The temporary can be
1723 /// used multiple times in the resulting expression. For example:
1724 /// `expr ** 2` could be compiled as `expr * expr`. The temporary scope will
1725 /// ensure `expr` is only evaluated once: `(x => x * x)(expr)`.
1726 ///
1727 /// If the expression does not end up using `x` more than once, or if those
1728 /// expressions can be treated as stateless (e.g. they are non-mutated
1729 /// variables), then the resulting code will be simplified automatically.
1730 ///
1731 /// [scope] will be mutated to contain the new temporary's initialization.
1732 Expression _bindValue(
1733 Map<String, JS.Expression> scope, String name, Expression expr,
1734 {Expression context}) {
1735 // No need to do anything for stateless expressions.
1736 if (_isStateless(expr, context)) return expr;
1737
1738 var t = _createTemporary('#$name', expr.staticType);
1739 scope[name] = _visit(expr);
1740 return t;
1680 } 1741 }
1681 1742
1682 @override 1743 @override
1683 JS.Expression visitPostfixExpression(PostfixExpression node) { 1744 JS.Expression visitPostfixExpression(PostfixExpression node) {
1684 var op = node.operator; 1745 var op = node.operator;
1685 var expr = node.operand; 1746 var expr = node.operand;
1686 1747
1687 if (node.parent is Statement) {
1688 // Prefix code is simpler. If the expr result isn't used, fall to that.
1689 return _emitPrefixExpression(op, expr);
1690 }
1691
1692 var dispatchType = rules.getStaticType(expr); 1748 var dispatchType = rules.getStaticType(expr);
1693 if (unaryOperationIsPrimitive(dispatchType)) { 1749 if (unaryOperationIsPrimitive(dispatchType)) {
1694 if (_isNonNullableExpression(expr)) { 1750 if (_isNonNullableExpression(expr)) {
1695 return js.call('#$op', _visit(expr)); 1751 return js.call('#$op', _visit(expr));
1696 } 1752 }
1697 } 1753 }
1698 1754
1699 assert(op.lexeme == '++' || op.lexeme == '--'); 1755 assert(op.lexeme == '++' || op.lexeme == '--');
1700 return _emitPostfixIncrement(expr, op); 1756 return _emitPostfixIncrement(expr, op);
1701 } 1757 }
1702 1758
1703 JS.Expression _emitPrefixIncrement(Token op, Expression expr) {
1704 var one = AstBuilder.integerLiteral(1);
1705 one.staticType = rules.provider.intType;
1706 var increment = AstBuilder.binaryExpression(expr, op.lexeme[0], one);
1707 return _emitSet(expr, increment);
1708 }
1709
1710 @override 1759 @override
1711 JS.Expression visitPrefixExpression(PrefixExpression node) { 1760 JS.Expression visitPrefixExpression(PrefixExpression node) {
1712 return _emitPrefixExpression(node.operator, node.operand); 1761 return _emitPrefixExpression(node.operator, node.operand);
1713 } 1762 }
1714 1763
1715 JS.Expression _emitPrefixExpression(Token op, Expression expr) { 1764 JS.Expression _emitPrefixExpression(Token op, Expression expr) {
1716 var dispatchType = rules.getStaticType(expr); 1765 var dispatchType = rules.getStaticType(expr);
1717 if (unaryOperationIsPrimitive(dispatchType)) { 1766 if (unaryOperationIsPrimitive(dispatchType)) {
1718 if (_isNonNullableExpression(expr)) { 1767 if (_isNonNullableExpression(expr)) {
1719 return js.call('$op#', _visit(expr)); 1768 return js.call('$op#', _visit(expr));
1720 } else if (op.lexeme == '++' || op.lexeme == '--') { 1769 } else if (op.lexeme == '++' || op.lexeme == '--') {
1721 // We need a null check, so the increment must be expanded out. 1770 // We need a null check, so the increment must be expanded out.
1722 var mathop = op.lexeme[0]; 1771 var mathop = op.lexeme[0];
1723 return js.call('# = # $mathop 1', [_visit(expr), notNull(expr)]); 1772 var vars = {};
1773 var x = _bindLeftHandSide(vars, expr, context: expr);
1774 var body = js.call('# = # $mathop 1', [_visit(x), notNull(x)]);
1775 return new JSMetaLet(vars, [body]);
1724 } else { 1776 } else {
1725 return js.call('$op#', notNull(expr)); 1777 return js.call('$op#', notNull(expr));
1726 } 1778 }
1727 } else { 1779 }
1780
1781 if (op.lexeme == '++' || op.lexeme == '--') {
1728 // Increment or decrement requires expansion. 1782 // Increment or decrement requires expansion.
1729 if (op.lexeme == '++' || op.lexeme == '--') { 1783 // Desugar `++x` as `x = x + 1`, ensuring that if `x` has subexpressions
1730 return _emitPrefixIncrement(op, expr); 1784 // (for example, x is IndexExpression) we evaluate those once.
1731 } 1785 var one = AstBuilder.integerLiteral(1)
1786 ..staticType = rules.provider.intType;
1787 return _emitOpAssign(expr, one, op.lexeme[0], context: expr);
1732 } 1788 }
1733 1789
1734 // Call the operator 1790 // Call the operator
1735 var opString = _emitMemberName(op.lexeme, unary: true); 1791 var opString = _emitMemberName(op.lexeme, unary: true);
1736 if (rules.isDynamicTarget(expr)) { 1792 if (rules.isDynamicTarget(expr)) {
1737 // dynamic dispatch 1793 // dynamic dispatch
1738 return js.call('dart.dunary(#, #)', [opString, _visit(expr)]); 1794 return js.call('dart.dunary(#, #)', [opString, _visit(expr)]);
1739 } else if (_isJSBuiltinType(dispatchType)) { 1795 } else if (_isJSBuiltinType(dispatchType)) {
1740 return js.call( 1796 return js.call(
1741 '#.#(#)', [_emitTypeName(dispatchType), opString, _visit(expr)]); 1797 '#.#(#)', [_emitTypeName(dispatchType), opString, _visit(expr)]);
1742 } else { 1798 } else {
1743 // Generic static-dispatch, user-defined operator code path. 1799 // Generic static-dispatch, user-defined operator code path.
1744 return js.call('#.#()', [_visit(expr), opString]); 1800 return js.call('#.#()', [_visit(expr), opString]);
1745 } 1801 }
1746 } 1802 }
1747 1803
1748 // Cascades can contain [IndexExpression], [MethodInvocation] and 1804 // Cascades can contain [IndexExpression], [MethodInvocation] and
1749 // [PropertyAccess]. The code generation for those is handled in their 1805 // [PropertyAccess]. The code generation for those is handled in their
1750 // respective visit methods. 1806 // respective visit methods.
1751 @override 1807 @override
1752 JS.Node visitCascadeExpression(CascadeExpression node) { 1808 JS.Node visitCascadeExpression(CascadeExpression node) {
1753 var savedCascadeTemp = _cascadeTarget; 1809 var savedCascadeTemp = _cascadeTarget;
1754 1810
1755 var parent = node.parent; 1811 var vars = {};
1756 JS.Node result; 1812 _cascadeTarget = _bindValue(vars, '_', node.target, context: node);
1757 if (_isStateless(node.target, node)) { 1813 var sections = _visitList(node.cascadeSections);
1758 // Special case: target is stateless, so we can just reuse it. 1814 sections.add(_visit(_cascadeTarget));
1759 _cascadeTarget = node.target; 1815 var result = new JSMetaLet(vars, sections, statelessResult: true);
1760
1761 if (parent is ExpressionStatement) {
1762 var sections = _visitList(node.cascadeSections);
1763 result = _statement(sections.map((e) => new JS.ExpressionStatement(e)));
1764 } else {
1765 // Use comma expression. For example:
1766 // (sb.write(1), sb.write(2), sb)
1767 var sections = _visitListToBinary(node.cascadeSections, ',');
1768 result = new JS.Binary(',', sections, _visit(_cascadeTarget));
1769 }
1770 } else {
1771 // In the general case we need to capture the target expression into
1772 // a temporary. This uses a lambda to get a temporary scope, and it also
1773 // remains valid in an expression context.
1774 _cascadeTarget = _createTemporary('_', node.target.staticType);
1775
1776 var body = _visitList(node.cascadeSections);
1777 if (node.parent is! ExpressionStatement) {
1778 body.add(js.statement('return #;', _visit(_cascadeTarget)));
1779 }
1780
1781 result = js.call('((#) => { # })(#)', [
1782 _visit(_cascadeTarget),
1783 body,
1784 _visit(node.target)
1785 ]);
1786 }
1787
1788 _cascadeTarget = savedCascadeTemp; 1816 _cascadeTarget = savedCascadeTemp;
1789 return result; 1817 return result;
1790 } 1818 }
1791 1819
1792 /// True is the expression can be evaluated multiple times without causing
1793 /// code execution. This is true for final fields. This can be true for local
1794 /// variables, if:
1795 ///
1796 /// * they are not assigned within the [context] scope.
1797 /// * they are not assigned in a function closure anywhere.
1798 ///
1799 /// This method is used to avoid creating temporaries in cases where we know
1800 /// we can safely re-evaluate [node] multiple times in [context]. This lets
1801 /// us generate prettier code.
1802 ///
1803 /// This method is conservative: it should never return `true` unless it is
1804 /// certain the [node] is stateless, because generated code may rely on the
1805 /// correctness of a `true` value. However it may return `false` for things
1806 /// that are in fact, stateless.
1807 bool _isStateless(Expression node, [AstNode context]) {
1808 if (node is SimpleIdentifier) {
1809 var e = node.staticElement;
1810 if (e is PropertyAccessorElement) e = e.variable;
1811 if (e is VariableElement && !e.isSynthetic) {
1812 if (e.isFinal) return true;
1813
1814 // TODO(jmesserly): remove this when isPotentiallyMutated* is available
1815 // without the implementation class. Technically we shouldn't hit the
1816 // ParameterMember case based on current usage of _isStateless, but this
1817 // makes it clear we shouldn't rely on *Impl class.
1818 if (e is Member) e = e.baseElement;
1819
1820 if (e is LocalVariableElementImpl || e is ParameterElementImpl) {
1821 // make sure the local isn't mutated in the context.
1822 return !_isPotentiallyMutated(e, context);
1823 }
1824 }
1825 }
1826 return false;
1827 }
1828
1829 @override 1820 @override
1830 visitParenthesizedExpression(ParenthesizedExpression node) => 1821 visitParenthesizedExpression(ParenthesizedExpression node) =>
1831 // The printer handles precedence so we don't need to. 1822 // The printer handles precedence so we don't need to.
1832 _visit(node.expression); 1823 _visit(node.expression);
1833 1824
1834 @override 1825 @override
1835 visitFormalParameter(FormalParameter node) => 1826 visitFormalParameter(FormalParameter node) =>
1836 visitSimpleIdentifier(node.identifier); 1827 visitSimpleIdentifier(node.identifier);
1837 1828
1838 @override 1829 @override
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1923 @override 1914 @override
1924 JS.If visitIfStatement(IfStatement node) { 1915 JS.If visitIfStatement(IfStatement node) {
1925 return new JS.If(_visit(node.condition), _visit(node.thenStatement), 1916 return new JS.If(_visit(node.condition), _visit(node.thenStatement),
1926 _visit(node.elseStatement)); 1917 _visit(node.elseStatement));
1927 } 1918 }
1928 1919
1929 @override 1920 @override
1930 JS.For visitForStatement(ForStatement node) { 1921 JS.For visitForStatement(ForStatement node) {
1931 var init = _visit(node.initialization); 1922 var init = _visit(node.initialization);
1932 if (init == null) init = _visit(node.variables); 1923 if (init == null) init = _visit(node.variables);
1933 return new JS.For(init, _visit(node.condition), 1924 var update = _visitListToBinary(node.updaters, ',');
1934 _visitListToBinary(node.updaters, ','), _visit(node.body)); 1925 if (update != null) update = update.toVoidExpression();
1926 return new JS.For(init, _visit(node.condition), update, _visit(node.body));
1935 } 1927 }
1936 1928
1937 @override 1929 @override
1938 JS.While visitWhileStatement(WhileStatement node) { 1930 JS.While visitWhileStatement(WhileStatement node) {
1939 return new JS.While(_visit(node.condition), _visit(node.body)); 1931 return new JS.While(_visit(node.condition), _visit(node.body));
1940 } 1932 }
1941 1933
1942 @override 1934 @override
1943 JS.Do visitDoStatement(DoStatement node) { 1935 JS.Do visitDoStatement(DoStatement node) {
1944 return new JS.Do(_visit(node.body), _visit(node.condition)); 1936 return new JS.Do(_visit(node.body), _visit(node.condition));
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after
2206 List _visitList(Iterable<AstNode> nodes) { 2198 List _visitList(Iterable<AstNode> nodes) {
2207 if (nodes == null) return null; 2199 if (nodes == null) return null;
2208 var result = []; 2200 var result = [];
2209 for (var node in nodes) result.add(_visit(node)); 2201 for (var node in nodes) result.add(_visit(node));
2210 return result; 2202 return result;
2211 } 2203 }
2212 2204
2213 /// Visits a list of expressions, creating a comma expression if needed in JS. 2205 /// Visits a list of expressions, creating a comma expression if needed in JS.
2214 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { 2206 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) {
2215 if (nodes == null || nodes.isEmpty) return null; 2207 if (nodes == null || nodes.isEmpty) return null;
2216 2208 return new JS.Expression.binary(_visitList(nodes), operator);
2217 JS.Expression result = null;
2218 for (var node in nodes) {
2219 var jsExpr = _visit(node);
2220 if (result == null) {
2221 result = jsExpr;
2222 } else {
2223 result = new JS.Binary(operator, result, jsExpr);
2224 }
2225 }
2226 return result;
2227 } 2209 }
2228 2210
2229 /// This handles member renaming for private names and operators. 2211 /// This handles member renaming for private names and operators.
2230 /// 2212 ///
2231 /// Private names are generated using ES6 symbols: 2213 /// Private names are generated using ES6 symbols:
2232 /// 2214 ///
2233 /// // At the top of the module: 2215 /// // At the top of the module:
2234 /// let _x = Symbol('_x'); 2216 /// let _x = Symbol('_x');
2235 /// let _y = Symbol('_y'); 2217 /// let _y = Symbol('_y');
2236 /// ... 2218 /// ...
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
2312 JS.Identifier _libraryName(LibraryElement library) { 2294 JS.Identifier _libraryName(LibraryElement library) {
2313 if (library == libraryInfo.library) return _exportsVar; 2295 if (library == libraryInfo.library) return _exportsVar;
2314 return new JS.Identifier(jsLibraryName(library)); 2296 return new JS.Identifier(jsLibraryName(library));
2315 } 2297 }
2316 2298
2317 static bool _needsImplicitThis(Element e) => 2299 static bool _needsImplicitThis(Element e) =>
2318 e is PropertyAccessorElement && !e.variable.isStatic || 2300 e is PropertyAccessorElement && !e.variable.isStatic ||
2319 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement; 2301 e is ClassMemberElement && !e.isStatic && e is! ConstructorElement;
2320 } 2302 }
2321 2303
2322 /// Returns true if the local variable is potentially mutated within [context].
2323 /// This accounts for closures that may have been created outside of [context].
2324 // TODO(jmesserly): change type annotation to not be *Impl once
2325 // isPotentiallyMutated is available on VariableElement.
2326 bool _isPotentiallyMutated(VariableElementImpl e, [AstNode context]) {
2327 if (e.isPotentiallyMutatedInClosure) {
2328 // TODO(jmesserly): this returns true incorrectly in some cases, because
2329 // VariableResolverVisitor only checks that enclosingElement is not the
2330 // function element, but enclosingElement can be something else in some
2331 // cases (the block scope?). So it's more conservative than it could be.
2332 return true;
2333 }
2334 if (e.isPotentiallyMutatedInScope) {
2335 // Need to visit the context looking for assignment to this local.
2336 if (context != null) {
2337 var visitor = new _AssignmentFinder(e);
2338 context.accept(visitor);
2339 return visitor._potentiallyMutated;
2340 }
2341 return true;
2342 }
2343 return false;
2344 }
2345
2346 /// Adapted from VariableResolverVisitor. Finds an assignment to a given
2347 /// local variable.
2348 class _AssignmentFinder extends RecursiveAstVisitor {
2349 final VariableElementImpl _variable;
2350 bool _potentiallyMutated = false;
2351
2352 _AssignmentFinder(this._variable);
2353
2354 @override
2355 visitSimpleIdentifier(SimpleIdentifier node) {
2356 // Ignore if qualified.
2357 AstNode parent = node.parent;
2358 if (parent is PrefixedIdentifier &&
2359 identical(parent.identifier, node)) return;
2360 if (parent is PropertyAccess &&
2361 identical(parent.propertyName, node)) return;
2362 if (parent is MethodInvocation &&
2363 identical(parent.methodName, node)) return;
2364 if (parent is ConstructorName) return;
2365 if (parent is Label) return;
2366
2367 if (node.inSetterContext() && node.staticElement == _variable) {
2368 _potentiallyMutated = true;
2369 }
2370 }
2371 }
2372
2373 class JSGenerator extends CodeGenerator { 2304 class JSGenerator extends CodeGenerator {
2374 final JSCodeOptions options; 2305 final JSCodeOptions options;
2375 2306
2376 JSGenerator(String outDir, Uri root, TypeRules rules, this.options) 2307 JSGenerator(String outDir, Uri root, TypeRules rules, this.options)
2377 : super(outDir, root, rules); 2308 : super(outDir, root, rules);
2378 2309
2379 String generateLibrary(LibraryUnit unit, LibraryInfo info) { 2310 String generateLibrary(LibraryUnit unit, LibraryInfo info) {
2380 var jsTree = new JSCodegenVisitor(info, rules).emitLibrary(unit); 2311 var jsTree = new JSCodegenVisitor(info, rules).emitLibrary(unit);
2381 2312
2382 var outputPath = path.join(outDir, jsOutputPath(info, root)); 2313 var outputPath = path.join(outDir, jsOutputPath(info, root));
(...skipping 17 matching lines...) Expand all
2400 return computeHash(text); 2331 return computeHash(text);
2401 } 2332 }
2402 } 2333 }
2403 } 2334 }
2404 2335
2405 void _writeNode(JS.JavaScriptPrintingContext context, JS.Node node) { 2336 void _writeNode(JS.JavaScriptPrintingContext context, JS.Node node) {
2406 var opts = new JS.JavaScriptPrintingOptions(allowKeywordsInProperties: true); 2337 var opts = new JS.JavaScriptPrintingOptions(allowKeywordsInProperties: true);
2407 node.accept(new JS.Printer(opts, context, localNamer: new JSNamer(node))); 2338 node.accept(new JS.Printer(opts, context, localNamer: new JSNamer(node)));
2408 } 2339 }
2409 2340
2410 /// This is a debugging helper to print a JS node.
2411 String jsNodeToString(JS.Node node) { 2341 String jsNodeToString(JS.Node node) {
2412 var context = new JS.SimpleJavaScriptPrintingContext(); 2342 var context = new JS.SimpleJavaScriptPrintingContext();
2413 _writeNode(context, node); 2343 _writeNode(context, node);
2414 return context.getText(); 2344 return context.getText();
2415 } 2345 }
2416 2346
2417 /// Choose a canonical name from the library element. 2347 /// Choose a canonical name from the library element.
2418 /// This never uses the library's name (the identifier in the `library` 2348 /// This never uses the library's name (the identifier in the `library`
2419 /// declaration) as it doesn't have any meaningful rules enforced. 2349 /// declaration) as it doesn't have any meaningful rules enforced.
2420 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library); 2350 String jsLibraryName(LibraryElement library) => canonicalLibraryName(library);
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
2494 2424
2495 // TODO(jmesserly): in many cases marking the end will be unncessary. 2425 // TODO(jmesserly): in many cases marking the end will be unncessary.
2496 printer.mark(_location(node.end)); 2426 printer.mark(_location(node.end));
2497 } 2427 }
2498 2428
2499 String _getIdentifier(AstNode node) { 2429 String _getIdentifier(AstNode node) {
2500 if (node is SimpleIdentifier) return node.name; 2430 if (node is SimpleIdentifier) return node.name;
2501 return null; 2431 return null;
2502 } 2432 }
2503 } 2433 }
2434
2435 /// True is the expression can be evaluated multiple times without causing
2436 /// code execution. This is true for final fields. This can be true for local
2437 /// variables, if:
2438 /// * they are not assigned within the [context].
2439 /// * they are not assigned in a function closure anywhere.
2440 /// True is the expression can be evaluated multiple times without causing
2441 /// code execution. This is true for final fields. This can be true for local
2442 /// variables, if:
2443 ///
2444 /// * they are not assigned within the [context] scope.
2445 /// * they are not assigned in a function closure anywhere.
2446 ///
2447 /// This method is used to avoid creating temporaries in cases where we know
2448 /// we can safely re-evaluate [node] multiple times in [context]. This lets
2449 /// us generate prettier code.
2450 ///
2451 /// This method is conservative: it should never return `true` unless it is
2452 /// certain the [node] is stateless, because generated code may rely on the
2453 /// correctness of a `true` value. However it may return `false` for things
2454 /// that are in fact, stateless.
2455 bool _isStateless(Expression node, [AstNode context]) {
2456 if (node is SimpleIdentifier) {
2457 var e = node.staticElement;
2458 if (e is PropertyAccessorElement) e = e.variable;
2459 if (e is VariableElement && !e.isSynthetic) {
2460 if (e.isFinal) return true;
2461
2462 // TODO(jmesserly): remove this when isPotentiallyMutated* is available
2463 // without the implementation class. Technically we shouldn't hit the
2464 // ParameterMember case based on current usage of _isStateless, but this
2465 // makes it clear we shouldn't rely on *Impl class.
2466 if (e is Member) e = e.baseElement;
2467
2468 if (e is LocalVariableElementImpl || e is ParameterElementImpl) {
2469 // make sure the local isn't mutated in the context.
2470 return !_isPotentiallyMutated(e, context);
2471 }
2472 }
2473 }
2474 return false;
2475 }
2476
2477 /// Returns true if the local variable is potentially mutated within [context].
2478 /// This accounts for closures that may have been created outside of [context].
2479 bool _isPotentiallyMutated(VariableElementImpl e, [AstNode context]) {
2480 if (e.isPotentiallyMutatedInClosure) {
2481 // TODO(jmesserly): this returns true incorrectly in some cases, because
2482 // VariableResolverVisitor only checks that enclosingElement is not the
2483 // function element, but enclosingElement can be something else in some
2484 // cases (the block scope?). So it's more conservative than it could be.
2485 return true;
2486 }
2487 if (e.isPotentiallyMutatedInScope) {
2488 // Need to visit the context looking for assignment to this local.
2489 if (context != null) {
2490 var visitor = new _AssignmentFinder(e);
2491 context.accept(visitor);
2492 return visitor._potentiallyMutated;
2493 }
2494 return true;
2495 }
2496 return false;
2497 }
2498
2499 /// Adapted from VariableResolverVisitor. Finds an assignment to a given
2500 /// local variable.
2501 // TODO(jmesserly): change type annotation to not be *Impl once
2502 // isPotentiallyMutated is available on VariableElement.
2503 class _AssignmentFinder extends RecursiveAstVisitor {
2504 final VariableElementImpl _variable;
2505 bool _potentiallyMutated = false;
2506
2507 _AssignmentFinder(this._variable);
2508
2509 @override
2510 visitSimpleIdentifier(SimpleIdentifier node) {
2511 // Ignore if qualified.
2512 AstNode parent = node.parent;
2513 if (parent is PrefixedIdentifier &&
2514 identical(parent.identifier, node)) return;
2515 if (parent is PropertyAccess &&
2516 identical(parent.propertyName, node)) return;
2517 if (parent is MethodInvocation &&
2518 identical(parent.methodName, node)) return;
2519 if (parent is ConstructorName) return;
2520 if (parent is Label) return;
2521
2522 if (node.inSetterContext() && node.staticElement == _variable) {
2523 _potentiallyMutated = true;
2524 }
2525 }
2526 }
OLDNEW
« no previous file with comments | « lib/src/codegen/ast_builder.dart ('k') | lib/src/codegen/js_metalet.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698