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 | 6 |
| 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 8 import 'package:analyzer/dart/ast/ast.dart'; | 8 import 'package:analyzer/dart/ast/ast.dart'; |
| 9 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; | 9 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType; |
| 10 import 'package:analyzer/dart/element/element.dart'; | 10 import 'package:analyzer/dart/element/element.dart'; |
| (...skipping 2792 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2803 js.call('# != null ? # : #', [l, l, _visit(right)]) | 2803 js.call('# != null ? # : #', [l, l, _visit(right)]) |
| 2804 ]); | 2804 ]); |
| 2805 } | 2805 } |
| 2806 | 2806 |
| 2807 if (binaryOperationIsPrimitive(leftType, rightType) || | 2807 if (binaryOperationIsPrimitive(leftType, rightType) || |
| 2808 leftType == types.stringType && op.type == TokenType.PLUS) { | 2808 leftType == types.stringType && op.type == TokenType.PLUS) { |
| 2809 // special cases where we inline the operation | 2809 // special cases where we inline the operation |
| 2810 // these values are assumed to be non-null (determined by the checker) | 2810 // these values are assumed to be non-null (determined by the checker) |
| 2811 // TODO(jmesserly): it would be nice to just inline the method from core, | 2811 // TODO(jmesserly): it would be nice to just inline the method from core, |
| 2812 // instead of special cases here. | 2812 // instead of special cases here. |
| 2813 if (op.type == TokenType.TILDE_SLASH) { | 2813 JS.Expression binary(String code) { |
| 2814 // `a ~/ b` is equivalent to `(a / b).truncate()` | 2814 return js.call(code, [notNull(left), notNull(right)]); |
| 2815 var div = AstBuilder.binaryExpression(left, '/', right) | |
| 2816 ..staticType = node.staticType; | |
| 2817 return _emitSend(div, 'truncate', []); | |
| 2818 } else { | |
| 2819 // TODO(vsm): When do Dart ops not map to JS? | |
| 2820 code = '# $op #'; | |
| 2821 } | 2815 } |
| 2822 return js.call(code, [notNull(left), notNull(right)]); | 2816 |
| 2817 JS.Expression bitwise(String code) { | |
| 2818 return _coerceBitOperationResultToUnsigned(node, binary(code)); | |
| 2819 } | |
| 2820 | |
| 2821 switch (op.type) { | |
| 2822 case TokenType.TILDE_SLASH: | |
|
Jennifer Messerly
2016/04/28 20:08:52
this looks great, love the refactoring!
| |
| 2823 // `a ~/ b` is equivalent to `(a / b).truncate()` | |
| 2824 var div = AstBuilder.binaryExpression(left, '/', right) | |
| 2825 ..staticType = node.staticType; | |
| 2826 return _emitSend(div, 'truncate', []); | |
| 2827 | |
| 2828 case TokenType.PERCENT: | |
| 2829 // TODO(sra): We can generate `a % b + 0` if both are non-negative | |
| 2830 // (the `+ 0` is to coerce -0.0 to 0). | |
| 2831 return _emitSend(left, op.lexeme, [right]); | |
| 2832 | |
| 2833 case TokenType.AMPERSAND: | |
| 2834 return bitwise('# & #'); | |
| 2835 | |
| 2836 case TokenType.BAR: | |
| 2837 return bitwise('# | #'); | |
| 2838 | |
| 2839 case TokenType.CARET: | |
| 2840 return bitwise('# ^ #'); | |
| 2841 | |
| 2842 case TokenType.GT_GT: | |
| 2843 // TODO(sra): Detect when JS shift does the right thing. | |
| 2844 return _emitSend(left, op.lexeme, [right]); | |
| 2845 | |
| 2846 case TokenType.LT_LT: | |
| 2847 // TODO(sra): Detect when JS shift does the right thing. | |
| 2848 return _emitSend(left, op.lexeme, [right]); | |
| 2849 | |
| 2850 default: | |
| 2851 // TODO(vsm): When do Dart ops not map to JS? | |
| 2852 return binary('# $op #'); | |
| 2853 } | |
| 2823 } | 2854 } |
| 2824 | 2855 |
| 2825 return _emitSend(left, op.lexeme, [right]); | 2856 return _emitSend(left, op.lexeme, [right]); |
| 2826 } | 2857 } |
| 2827 | 2858 |
| 2859 JS.Expression _coerceBitOperationResultToUnsigned( | |
| 2860 Expression node, JS.Expression operation) { | |
| 2861 // Bit operations are coerced to values on [0, 2^32). The coercion changes | |
|
Jennifer Messerly
2016/04/28 20:08:52
move this to be a doc comment?
| |
| 2862 // the interpretation of the the 32-bit value from signed to unsigned. Most | |
| 2863 // JavaScript operations interpret their operands as signed and generate | |
| 2864 // signed results. | |
| 2865 return js.call('# >>> 0', operation); | |
| 2866 } | |
| 2867 | |
| 2828 /// If the type [t] is [int] or [double], or a type parameter | 2868 /// If the type [t] is [int] or [double], or a type parameter |
| 2829 /// bounded by [int], [double] or [num] returns [num]. | 2869 /// bounded by [int], [double] or [num] returns [num]. |
| 2830 /// Otherwise returns [t]. | 2870 /// Otherwise returns [t]. |
| 2831 DartType _canonicalizeNumTypes(DartType t) { | 2871 DartType _canonicalizeNumTypes(DartType t) { |
| 2832 var numType = types.numType; | 2872 var numType = types.numType; |
| 2833 if (rules.isSubtypeOf(t, numType)) return numType; | 2873 if (rules.isSubtypeOf(t, numType)) return numType; |
| 2834 return t; | 2874 return t; |
| 2835 } | 2875 } |
| 2836 | 2876 |
| 2837 bool _canUsePrimitiveEquality(Expression left, Expression right) { | 2877 bool _canUsePrimitiveEquality(Expression left, Expression right) { |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2994 return new JS.MetaLet(vars, body, statelessResult: true); | 3034 return new JS.MetaLet(vars, body, statelessResult: true); |
| 2995 } | 3035 } |
| 2996 | 3036 |
| 2997 @override | 3037 @override |
| 2998 JS.Expression visitPrefixExpression(PrefixExpression node) { | 3038 JS.Expression visitPrefixExpression(PrefixExpression node) { |
| 2999 var op = node.operator; | 3039 var op = node.operator; |
| 3000 var expr = node.operand; | 3040 var expr = node.operand; |
| 3001 | 3041 |
| 3002 var dispatchType = getStaticType(expr); | 3042 var dispatchType = getStaticType(expr); |
| 3003 if (unaryOperationIsPrimitive(dispatchType)) { | 3043 if (unaryOperationIsPrimitive(dispatchType)) { |
| 3044 if (op.lexeme == '~') { | |
| 3045 if (_isNumberInJS(dispatchType)) { | |
| 3046 JS.Expression jsExpr = js.call('~#', notNull(expr)); | |
| 3047 return _coerceBitOperationResultToUnsigned(node, jsExpr); | |
| 3048 } | |
| 3049 return _emitSend(expr, op.lexeme[0], []); | |
| 3050 } | |
| 3004 if (!isNullable(expr)) { | 3051 if (!isNullable(expr)) { |
| 3005 return js.call('$op#', _visit(expr)); | 3052 return js.call('$op#', _visit(expr)); |
| 3006 } else if (op.lexeme == '++' || op.lexeme == '--') { | 3053 } |
| 3054 if (op.lexeme == '++' || op.lexeme == '--') { | |
| 3007 // We need a null check, so the increment must be expanded out. | 3055 // We need a null check, so the increment must be expanded out. |
| 3008 var vars = <JS.MetaLetVariable, JS.Expression>{}; | 3056 var vars = <JS.MetaLetVariable, JS.Expression>{}; |
| 3009 var x = _bindLeftHandSide(vars, expr, context: expr); | 3057 var x = _bindLeftHandSide(vars, expr, context: expr); |
| 3010 | 3058 |
| 3011 var one = AstBuilder.integerLiteral(1)..staticType = types.intType; | 3059 var one = AstBuilder.integerLiteral(1)..staticType = types.intType; |
| 3012 var increment = AstBuilder.binaryExpression(x, op.lexeme[0], one) | 3060 var increment = AstBuilder.binaryExpression(x, op.lexeme[0], one) |
| 3013 ..staticElement = node.staticElement | 3061 ..staticElement = node.staticElement |
| 3014 ..staticType = getStaticType(expr); | 3062 ..staticType = getStaticType(expr); |
| 3015 | 3063 |
| 3016 return new JS.MetaLet(vars, [_emitSet(x, increment)]); | 3064 return new JS.MetaLet(vars, [_emitSet(x, increment)]); |
| 3017 } else { | |
| 3018 return js.call('$op#', notNull(expr)); | |
| 3019 } | 3065 } |
| 3066 return js.call('$op#', notNull(expr)); | |
| 3020 } | 3067 } |
| 3021 | 3068 |
| 3022 if (op.lexeme == '++' || op.lexeme == '--') { | 3069 if (op.lexeme == '++' || op.lexeme == '--') { |
| 3023 // Increment or decrement requires expansion. | 3070 // Increment or decrement requires expansion. |
| 3024 // Desugar `++x` as `x = x + 1`, ensuring that if `x` has subexpressions | 3071 // Desugar `++x` as `x = x + 1`, ensuring that if `x` has subexpressions |
| 3025 // (for example, x is IndexExpression) we evaluate those once. | 3072 // (for example, x is IndexExpression) we evaluate those once. |
| 3026 var one = AstBuilder.integerLiteral(1)..staticType = types.intType; | 3073 var one = AstBuilder.integerLiteral(1)..staticType = types.intType; |
| 3027 return _emitOpAssign(expr, one, op.lexeme[0], node.staticElement, | 3074 return _emitOpAssign(expr, one, op.lexeme[0], node.staticElement, |
| 3028 context: expr); | 3075 context: expr); |
| 3029 } | 3076 } |
| (...skipping 860 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3890 } | 3937 } |
| 3891 | 3938 |
| 3892 bool isLibraryPrefix(Expression node) => | 3939 bool isLibraryPrefix(Expression node) => |
| 3893 node is SimpleIdentifier && node.staticElement is PrefixElement; | 3940 node is SimpleIdentifier && node.staticElement is PrefixElement; |
| 3894 | 3941 |
| 3895 LibraryElement _getLibrary(AnalysisContext c, String uri) => | 3942 LibraryElement _getLibrary(AnalysisContext c, String uri) => |
| 3896 c.computeLibraryElement(c.sourceFactory.forUri(uri)); | 3943 c.computeLibraryElement(c.sourceFactory.forUri(uri)); |
| 3897 | 3944 |
| 3898 bool _isDartRuntime(LibraryElement l) => | 3945 bool _isDartRuntime(LibraryElement l) => |
| 3899 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; | 3946 l.isInSdk && l.source.uri.toString() == 'dart:_runtime'; |
| OLD | NEW |