Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 import 'dart:collection' show HashMap, HashSet; | 5 import 'dart:collection' show HashMap, HashSet; |
| 6 import 'dart:math' show min, max; | 6 import 'dart:math' show min, max; |
| 7 | 7 |
| 8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 8 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 10 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
| (...skipping 438 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 449 if (export is ClassElement && export.typeParameters.isNotEmpty) { | 449 if (export is ClassElement && export.typeParameters.isNotEmpty) { |
| 450 // Export the generic name as well. | 450 // Export the generic name as well. |
| 451 // TODO(jmesserly): revisit generic classes | 451 // TODO(jmesserly): revisit generic classes |
| 452 emitExport(export, suffix: r'$'); | 452 emitExport(export, suffix: r'$'); |
| 453 } | 453 } |
| 454 emitExport(export); | 454 emitExport(export); |
| 455 } | 455 } |
| 456 } | 456 } |
| 457 | 457 |
| 458 @override | 458 @override |
| 459 visitAsExpression(AsExpression node) => | 459 visitAsExpression(AsExpression node) { |
| 460 _emitCast(node.expression, to: node.type.type); | 460 Expression fromExpr = node.expression; |
| 461 var from = getStaticType(fromExpr); | |
| 462 var to = node.type.type; | |
| 461 | 463 |
| 462 /// Emits a cast and/or a null check (i.e. a cast to a non-null type). | |
| 463 JS.Expression _emitCast(Expression fromExpr, | |
| 464 {DartType to, bool checkNull: false}) { | |
| 465 var jsFrom = _visit(fromExpr); | 464 var jsFrom = _visit(fromExpr); |
| 466 var from = getStaticType(fromExpr); | |
| 467 | |
| 468 JS.Expression maybeCheckNull(JS.Expression jsExpr) { | |
| 469 if (checkNull && isNullable(fromExpr)) { | |
| 470 return js.call('dart.notNull(#)', jsExpr); | |
| 471 } | |
| 472 return jsExpr; | |
| 473 } | |
| 474 | 465 |
| 475 // Skip the cast if it's not needed. | 466 // Skip the cast if it's not needed. |
| 476 if (to == null || rules.isSubtypeOf(from, to)) { | 467 if (rules.isSubtypeOf(from, to)) return jsFrom; |
| 477 return maybeCheckNull(jsFrom); | |
| 478 } | |
| 479 | 468 |
| 480 // All Dart number types map to a JS double. | 469 // All Dart number types map to a JS double. |
| 481 if (_isNumberInJS(from) && _isNumberInJS(to)) { | 470 if (_isNumberInJS(from) && _isNumberInJS(to)) { |
| 482 // Make sure to check when converting to int. | 471 // Make sure to check when converting to int. |
| 483 if (from != types.intType && to == types.intType) { | 472 if (from != types.intType && to == types.intType) { |
| 484 // TODO(jmesserly): fuse this with notNull check. | 473 return js.call('dart.asInt(#)', jsFrom); |
|
Jennifer Messerly
2016/05/19 17:45:38
Why remove this TODO? We still want to be smarter
sra1
2016/05/19 18:57:56
I'll add it back.
| |
| 485 return maybeCheckNull(js.call('dart.asInt(#)', [jsFrom])); | |
| 486 } | 474 } |
| 487 | 475 |
| 488 // A no-op in JavaScript. | 476 // A no-op in JavaScript. |
| 489 return maybeCheckNull(jsFrom); | 477 return jsFrom; |
| 490 } | 478 } |
| 491 | 479 |
| 492 if (to == types.boolType && checkNull) { | 480 return js.call('dart.as(#, #)', [jsFrom, _emitType(to)]); |
| 493 return js.call('dart.test(#)', _visit(fromExpr)); | |
| 494 } | |
| 495 | |
| 496 return maybeCheckNull(js.call('dart.as(#, #)', [jsFrom, _emitType(to)])); | |
| 497 } | 481 } |
| 498 | 482 |
| 499 @override | 483 @override |
| 500 visitIsExpression(IsExpression node) { | 484 visitIsExpression(IsExpression node) { |
| 501 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. | 485 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. |
| 502 JS.Expression result; | 486 JS.Expression result; |
| 503 var type = node.type.type; | 487 var type = node.type.type; |
| 504 var lhs = _visit(node.expression); | 488 var lhs = _visit(node.expression); |
| 505 var typeofName = _jsTypeofName(type); | 489 var typeofName = _jsTypeofName(type); |
| 506 if (typeofName != null) { | 490 if (typeofName != null) { |
| (...skipping 2614 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3121 bool typeIsPrimitiveInJS(DartType t) => | 3105 bool typeIsPrimitiveInJS(DartType t) => |
| 3122 _isNumberInJS(t) || t == types.boolType; | 3106 _isNumberInJS(t) || t == types.boolType; |
| 3123 | 3107 |
| 3124 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 3108 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
| 3125 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 3109 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
| 3126 | 3110 |
| 3127 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 3111 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
| 3128 | 3112 |
| 3129 JS.Expression notNull(Expression expr) { | 3113 JS.Expression notNull(Expression expr) { |
| 3130 if (expr == null) return null; | 3114 if (expr == null) return null; |
| 3131 if (expr is AsExpression) { | 3115 var jsExpr = _visit(expr); |
| 3132 return _emitCast(expr.expression, to: expr.type.type, checkNull: true); | 3116 if (!isNullable(expr)) return jsExpr; |
| 3133 } | 3117 return js.call('dart.notNull(#)', jsExpr); |
| 3134 return _emitCast(expr, checkNull: true); | |
| 3135 } | 3118 } |
| 3136 | 3119 |
| 3137 @override | 3120 @override |
| 3138 JS.Expression visitBinaryExpression(BinaryExpression node) { | 3121 JS.Expression visitBinaryExpression(BinaryExpression node) { |
| 3139 var op = node.operator; | 3122 var op = node.operator; |
| 3140 var left = node.leftOperand; | 3123 var left = node.leftOperand; |
| 3141 var right = node.rightOperand; | 3124 var right = node.rightOperand; |
| 3142 | 3125 |
| 3143 var leftType = getStaticType(left); | 3126 var leftType = getStaticType(left); |
| 3144 var rightType = getStaticType(right); | 3127 var rightType = getStaticType(right); |
| (...skipping 687 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3832 Expression _getTarget(node) { | 3815 Expression _getTarget(node) { |
| 3833 assert(node is IndexExpression || | 3816 assert(node is IndexExpression || |
| 3834 node is PropertyAccess || | 3817 node is PropertyAccess || |
| 3835 node is MethodInvocation); | 3818 node is MethodInvocation); |
| 3836 return node.isCascaded ? _cascadeTarget : node.target; | 3819 return node.isCascaded ? _cascadeTarget : node.target; |
| 3837 } | 3820 } |
| 3838 | 3821 |
| 3839 @override | 3822 @override |
| 3840 visitConditionalExpression(ConditionalExpression node) { | 3823 visitConditionalExpression(ConditionalExpression node) { |
| 3841 return js.call('# ? # : #', [ | 3824 return js.call('# ? # : #', [ |
| 3842 notNull(node.condition), | 3825 _visitTest(node.condition), |
| 3843 _visit(node.thenExpression), | 3826 _visit(node.thenExpression), |
| 3844 _visit(node.elseExpression) | 3827 _visit(node.elseExpression) |
| 3845 ]); | 3828 ]); |
| 3846 } | 3829 } |
| 3847 | 3830 |
| 3848 @override | 3831 @override |
| 3849 visitThrowExpression(ThrowExpression node) { | 3832 visitThrowExpression(ThrowExpression node) { |
| 3850 var expr = _visit(node.expression); | 3833 var expr = _visit(node.expression); |
| 3851 if (node.parent is ExpressionStatement) { | 3834 if (node.parent is ExpressionStatement) { |
| 3852 return js.statement('dart.throw(#);', expr); | 3835 return js.statement('dart.throw(#);', expr); |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 3874 var result = _visit(stmt); | 3857 var result = _visit(stmt); |
| 3875 if (result is JS.ExpressionStatement && | 3858 if (result is JS.ExpressionStatement && |
| 3876 result.expression is JS.VariableDeclarationList) { | 3859 result.expression is JS.VariableDeclarationList) { |
| 3877 return new JS.Block([result]); | 3860 return new JS.Block([result]); |
| 3878 } | 3861 } |
| 3879 return result; | 3862 return result; |
| 3880 } | 3863 } |
| 3881 | 3864 |
| 3882 @override | 3865 @override |
| 3883 JS.If visitIfStatement(IfStatement node) { | 3866 JS.If visitIfStatement(IfStatement node) { |
| 3884 return new JS.If(notNull(node.condition), _visitScope(node.thenStatement), | 3867 return new JS.If(_visitTest(node.condition), |
| 3885 _visitScope(node.elseStatement)); | 3868 _visitScope(node.thenStatement), _visitScope(node.elseStatement)); |
| 3886 } | 3869 } |
| 3887 | 3870 |
| 3888 @override | 3871 @override |
| 3889 JS.For visitForStatement(ForStatement node) { | 3872 JS.For visitForStatement(ForStatement node) { |
| 3890 var init = _visit(node.initialization); | 3873 var init = _visit(node.initialization); |
| 3891 if (init == null) init = _visit(node.variables); | 3874 if (init == null) init = _visit(node.variables); |
| 3892 var update = _visitListToBinary(node.updaters, ','); | 3875 var update = _visitListToBinary(node.updaters, ','); |
| 3893 if (update != null) update = update.toVoidExpression(); | 3876 if (update != null) update = update.toVoidExpression(); |
| 3894 return new JS.For( | 3877 var condition = node.condition == null ? null : _visitTest(node.condition); |
| 3895 init, notNull(node.condition), update, _visitScope(node.body)); | 3878 return new JS.For(init, condition, update, _visitScope(node.body)); |
| 3896 } | 3879 } |
| 3897 | 3880 |
| 3898 @override | 3881 @override |
| 3899 JS.While visitWhileStatement(WhileStatement node) { | 3882 JS.While visitWhileStatement(WhileStatement node) { |
| 3900 return new JS.While(notNull(node.condition), _visitScope(node.body)); | 3883 return new JS.While(_visitTest(node.condition), _visitScope(node.body)); |
| 3901 } | 3884 } |
| 3902 | 3885 |
| 3903 @override | 3886 @override |
| 3904 JS.Do visitDoStatement(DoStatement node) { | 3887 JS.Do visitDoStatement(DoStatement node) { |
| 3905 return new JS.Do(_visitScope(node.body), notNull(node.condition)); | 3888 return new JS.Do(_visitScope(node.body), _visitTest(node.condition)); |
| 3906 } | 3889 } |
| 3907 | 3890 |
| 3908 @override | 3891 @override |
| 3909 JS.Statement visitForEachStatement(ForEachStatement node) { | 3892 JS.Statement visitForEachStatement(ForEachStatement node) { |
| 3910 if (node.awaitKeyword != null) { | 3893 if (node.awaitKeyword != null) { |
| 3911 return _emitAwaitFor(node); | 3894 return _emitAwaitFor(node); |
| 3912 } | 3895 } |
| 3913 | 3896 |
| 3914 var init = _visit(node.identifier); | 3897 var init = _visit(node.identifier); |
| 3915 if (init == null) { | 3898 if (init == null) { |
| (...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4232 return result; | 4215 return result; |
| 4233 } | 4216 } |
| 4234 | 4217 |
| 4235 /// Visits a list of expressions, creating a comma expression if needed in JS. | 4218 /// Visits a list of expressions, creating a comma expression if needed in JS. |
| 4236 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 4219 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
| 4237 if (nodes == null || nodes.isEmpty) return null; | 4220 if (nodes == null || nodes.isEmpty) return null; |
| 4238 return new JS.Expression.binary( | 4221 return new JS.Expression.binary( |
| 4239 _visitList(nodes) as List<JS.Expression>, operator); | 4222 _visitList(nodes) as List<JS.Expression>, operator); |
| 4240 } | 4223 } |
| 4241 | 4224 |
| 4225 /// Generates an expression for a boolean conversion context (if, while, &&, | |
| 4226 /// etc.), where conversions and null checks are implemented via `dart.test` | |
| 4227 /// to give a more helpful message. | |
| 4228 // TODO(sra): When nullablility is available earlier, it would be cleaner to | |
| 4229 // build an input AST where the boolean conversion is a single AST node. | |
| 4230 JS.Expression _visitTest(Expression node) { | |
| 4231 JS.Expression finish(JS.Expression result) { | |
| 4232 return annotate(result, node); | |
| 4233 } | |
| 4234 if (node is PrefixExpression && node.operator.lexeme == '!') { | |
| 4235 return finish(js.call('!#', _visitTest(node.operand))); | |
| 4236 } | |
| 4237 if (node is BinaryExpression) { | |
| 4238 JS.Expression shortCircuit(String code) { | |
| 4239 return finish(js.call(code, | |
| 4240 [_visitTest(node.leftOperand), _visitTest(node.rightOperand)])); | |
| 4241 } | |
| 4242 var op = node.operator.type.lexeme; | |
| 4243 if (op == '&&') return shortCircuit('# && #'); | |
| 4244 if (op == '||') return shortCircuit('# || #'); | |
| 4245 } | |
| 4246 // Offset 0 from start of file is syntactically impossible for normal code. | |
| 4247 // TODO(sra): Find a better way to recognize reified coercion, since we | |
| 4248 // can't set the isSynthetic attribute. | |
|
Jennifer Messerly
2016/05/19 17:45:38
FYI -- we can send a patch to Analyzer if we need
sra1
2016/05/19 18:57:56
Acknowledged.
| |
| 4249 if (node is AsExpression && node.asOperator.offset == 0) { | |
| 4250 assert(node.staticType == types.boolType); | |
| 4251 return js.call('dart.test(#)', _visit(node.expression)); | |
| 4252 } | |
| 4253 JS.Expression result = _visit(node); | |
| 4254 if (isNullable(node)) result = js.call('dart.test(#)', result); | |
| 4255 return result; | |
| 4256 } | |
| 4257 | |
| 4242 /// Like [_emitMemberName], but for declaration sites. | 4258 /// Like [_emitMemberName], but for declaration sites. |
| 4243 /// | 4259 /// |
| 4244 /// Unlike call sites, we always have an element available, so we can use it | 4260 /// Unlike call sites, we always have an element available, so we can use it |
| 4245 /// directly rather than computing the relevant options for [_emitMemberName]. | 4261 /// directly rather than computing the relevant options for [_emitMemberName]. |
| 4246 JS.Expression _elementMemberName(ExecutableElement e, {bool useExtension}) { | 4262 JS.Expression _elementMemberName(ExecutableElement e, {bool useExtension}) { |
| 4247 String name; | 4263 String name; |
| 4248 if (e is PropertyAccessorElement) { | 4264 if (e is PropertyAccessorElement) { |
| 4249 name = e.variable.name; | 4265 name = e.variable.name; |
| 4250 } else { | 4266 } else { |
| 4251 name = e.name; | 4267 name = e.name; |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4489 } | 4505 } |
| 4490 | 4506 |
| 4491 bool isLibraryPrefix(Expression node) => | 4507 bool isLibraryPrefix(Expression node) => |
| 4492 node is SimpleIdentifier && node.staticElement is PrefixElement; | 4508 node is SimpleIdentifier && node.staticElement is PrefixElement; |
| 4493 | 4509 |
| 4494 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 4510 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
| 4495 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 4511 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
| 4496 | 4512 |
| 4497 bool _isDartRuntime(LibraryElement l) => | 4513 bool _isDartRuntime(LibraryElement l) => |
| 4498 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 4514 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
| OLD | NEW |