| 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 HashSet, HashMap, SplayTreeSet; | 5 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
| 6 | 6 |
| 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 7 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 8 import 'package:analyzer/dart/ast/token.dart'; | 8 import 'package:analyzer/dart/ast/token.dart'; |
| 9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
| 10 import 'package:analyzer/src/generated/constant.dart'; | 10 import 'package:analyzer/src/generated/constant.dart'; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can | 49 // TODO(jmesserly): ideally we'd have a "dynamic call" dart library we can |
| 50 // import and generate calls to, rather than dart_runtime.js | 50 // import and generate calls to, rather than dart_runtime.js |
| 51 const DPUT = 'dput'; | 51 const DPUT = 'dput'; |
| 52 const DLOAD = 'dload'; | 52 const DLOAD = 'dload'; |
| 53 const DINDEX = 'dindex'; | 53 const DINDEX = 'dindex'; |
| 54 const DSETINDEX = 'dsetindex'; | 54 const DSETINDEX = 'dsetindex'; |
| 55 const DCALL = 'dcall'; | 55 const DCALL = 'dcall'; |
| 56 const DSEND = 'dsend'; | 56 const DSEND = 'dsend'; |
| 57 | 57 |
| 58 class JSCodegenVisitor extends GeneralizingAstVisitor | 58 class JSCodegenVisitor extends GeneralizingAstVisitor |
| 59 with ClosureAnnotator, JsTypeRefCodegen { | 59 with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference { |
| 60 final AbstractCompiler compiler; | 60 final AbstractCompiler compiler; |
| 61 final CodegenOptions options; | 61 final CodegenOptions options; |
| 62 final LibraryElement currentLibrary; | 62 final LibraryElement currentLibrary; |
| 63 final StrongTypeSystemImpl rules; | 63 final StrongTypeSystemImpl rules; |
| 64 | 64 |
| 65 /// The global extension type table. | 65 /// The global extension type table. |
| 66 final ExtensionTypeSet _extensionTypes; | 66 final ExtensionTypeSet _extensionTypes; |
| 67 | 67 |
| 68 /// Information that is precomputed for this library, indicates which fields | 68 /// Information that is precomputed for this library, indicates which fields |
| 69 /// need storage slots. | 69 /// need storage slots. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 104 ModuleItemLoadOrder _loader; | 104 ModuleItemLoadOrder _loader; |
| 105 | 105 |
| 106 /// _interceptors.JSArray<E>, used for List literals. | 106 /// _interceptors.JSArray<E>, used for List literals. |
| 107 ClassElement _jsArray; | 107 ClassElement _jsArray; |
| 108 | 108 |
| 109 /// The default value of the module object. See [visitLibraryDirective]. | 109 /// The default value of the module object. See [visitLibraryDirective]. |
| 110 String _jsModuleValue; | 110 String _jsModuleValue; |
| 111 | 111 |
| 112 bool _isDartRuntime; | 112 bool _isDartRuntime; |
| 113 | 113 |
| 114 NullableTypeInference _nullInference; | |
| 115 | |
| 116 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, | 114 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, |
| 117 this._extensionTypes, this._fieldsNeedingStorage) | 115 this._extensionTypes, this._fieldsNeedingStorage) |
| 118 : compiler = compiler, | 116 : compiler = compiler, |
| 119 options = compiler.options.codegenOptions, | 117 options = compiler.options.codegenOptions, |
| 120 _types = compiler.context.typeProvider { | 118 _types = compiler.context.typeProvider { |
| 121 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 119 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
| 122 | 120 |
| 123 var context = compiler.context; | 121 var context = compiler.context; |
| 124 var src = context.sourceFactory.forUri('dart:_interceptors'); | 122 var src = context.sourceFactory.forUri('dart:_interceptors'); |
| 125 var interceptors = context.computeLibraryElement(src); | 123 var interceptors = context.computeLibraryElement(src); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 148 // visit them. It has the ability to sort elements on demand, so | 146 // visit them. It has the ability to sort elements on demand, so |
| 149 // dependencies between top level items are handled with a minimal | 147 // dependencies between top level items are handled with a minimal |
| 150 // reordering of the user's input code. The loader will call back into | 148 // reordering of the user's input code. The loader will call back into |
| 151 // this visitor via [_emitModuleItem] when it's ready to visit the item | 149 // this visitor via [_emitModuleItem] when it's ready to visit the item |
| 152 // for real. | 150 // for real. |
| 153 _loader.collectElements(currentLibrary, units); | 151 _loader.collectElements(currentLibrary, units); |
| 154 | 152 |
| 155 // TODO(jmesserly): ideally we could do this at a smaller granularity. | 153 // TODO(jmesserly): ideally we could do this at a smaller granularity. |
| 156 // We'll need to be consistent about when we're generating functions, and | 154 // We'll need to be consistent about when we're generating functions, and |
| 157 // only run this on the outermost function. | 155 // only run this on the outermost function. |
| 158 _nullInference = | 156 inferNullableTypesInLibrary(units); |
| 159 new NullableTypeInference.forLibrary(_isPrimitiveType, units); | |
| 160 | 157 |
| 161 _constField = new ConstFieldVisitor(types, library.library.element.source); | 158 _constField = new ConstFieldVisitor(types, library.library.element.source); |
| 162 | 159 |
| 163 for (var unit in units) { | 160 for (var unit in units) { |
| 164 for (var decl in unit.declarations) { | 161 for (var decl in unit.declarations) { |
| 165 if (decl is TopLevelVariableDeclaration) { | 162 if (decl is TopLevelVariableDeclaration) { |
| 166 visitTopLevelVariableDeclaration(decl); | 163 visitTopLevelVariableDeclaration(decl); |
| 167 } else { | 164 } else { |
| 168 _loader.loadDeclaration(decl, decl.element); | 165 _loader.loadDeclaration(decl, decl.element); |
| 169 } | 166 } |
| (...skipping 2463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2633 var name = constructor.name; | 2630 var name = constructor.name; |
| 2634 var type = constructor.type.type; | 2631 var type = constructor.type.type; |
| 2635 return _emitInstanceCreationExpression( | 2632 return _emitInstanceCreationExpression( |
| 2636 element, type, name, node.argumentList, node.isConst); | 2633 element, type, name, node.argumentList, node.isConst); |
| 2637 } | 2634 } |
| 2638 | 2635 |
| 2639 /// True if this type is built-in to JS, and we use the values unwrapped. | 2636 /// True if this type is built-in to JS, and we use the values unwrapped. |
| 2640 /// For these types we generate a calling convention via static | 2637 /// For these types we generate a calling convention via static |
| 2641 /// "extension methods". This allows types to be extended without adding | 2638 /// "extension methods". This allows types to be extended without adding |
| 2642 /// extensions directly on the prototype. | 2639 /// extensions directly on the prototype. |
| 2643 bool _isPrimitiveType(DartType t) => | 2640 bool isPrimitiveType(DartType t) => |
| 2644 typeIsPrimitiveInJS(t) || t == _types.stringType; | 2641 typeIsPrimitiveInJS(t) || t == _types.stringType; |
| 2645 | 2642 |
| 2646 bool typeIsPrimitiveInJS(DartType t) => | 2643 bool typeIsPrimitiveInJS(DartType t) => |
| 2647 _isNumberInJS(t) || t == _types.boolType; | 2644 _isNumberInJS(t) || t == _types.boolType; |
| 2648 | 2645 |
| 2649 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 2646 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
| 2650 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 2647 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
| 2651 | 2648 |
| 2652 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 2649 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
| 2653 | 2650 |
| 2654 JS.Expression notNull(Expression expr) { | 2651 JS.Expression notNull(Expression expr) { |
| 2655 if (expr == null) return null; | 2652 if (expr == null) return null; |
| 2656 var jsExpr = _visit(expr); | 2653 var jsExpr = _visit(expr); |
| 2657 if (!_isNullable(expr)) return jsExpr; | 2654 if (!isNullable(expr)) return jsExpr; |
| 2658 return js.call('dart.notNull(#)', jsExpr); | 2655 return js.call('dart.notNull(#)', jsExpr); |
| 2659 } | 2656 } |
| 2660 | 2657 |
| 2661 /// Returns true if [expr] can be null, optionally using [localIsNullable] | |
| 2662 /// for locals. | |
| 2663 /// | |
| 2664 /// This analysis is conservative and incomplete, but it can optimize many | |
| 2665 /// common patterns. | |
| 2666 bool _isNullable(Expression expr) => _nullInference.isNullable(expr); | |
| 2667 | |
| 2668 @override | 2658 @override |
| 2669 JS.Expression visitBinaryExpression(BinaryExpression node) { | 2659 JS.Expression visitBinaryExpression(BinaryExpression node) { |
| 2670 var op = node.operator; | 2660 var op = node.operator; |
| 2671 var left = node.leftOperand; | 2661 var left = node.leftOperand; |
| 2672 var right = node.rightOperand; | 2662 var right = node.rightOperand; |
| 2673 | 2663 |
| 2674 var leftType = getStaticType(left); | 2664 var leftType = getStaticType(left); |
| 2675 var rightType = getStaticType(right); | 2665 var rightType = getStaticType(right); |
| 2676 | 2666 |
| 2677 var code; | 2667 var code; |
| 2678 if (op.type.isEqualityOperator) { | 2668 if (op.type.isEqualityOperator) { |
| 2679 // If we statically know LHS or RHS is null we can generate a clean check. | 2669 // If we statically know LHS or RHS is null we can generate a clean check. |
| 2680 // We can also do this if both sides are the same primitive type. | 2670 // We can also do this if both sides are the same primitive type. |
| 2681 if (_canUsePrimitiveEquality(left, right)) { | 2671 if (_canUsePrimitiveEquality(left, right)) { |
| 2682 code = op.type == TokenType.EQ_EQ ? '# == #' : '# != #'; | 2672 code = op.type == TokenType.EQ_EQ ? '# == #' : '# != #'; |
| 2683 } else if (left is SuperExpression) { | 2673 } else if (left is SuperExpression) { |
| 2684 return _emitSend(left, op.lexeme, [right]); | 2674 return _emitSend(left, op.lexeme, [right]); |
| 2685 } else { | 2675 } else { |
| 2686 var bang = op.type == TokenType.BANG_EQ ? '!' : ''; | 2676 var bang = op.type == TokenType.BANG_EQ ? '!' : ''; |
| 2687 code = '${bang}dart.equals(#, #)'; | 2677 code = '${bang}dart.equals(#, #)'; |
| 2688 } | 2678 } |
| 2689 return js.call(code, [_visit(left), _visit(right)]); | 2679 return js.call(code, [_visit(left), _visit(right)]); |
| 2690 } | 2680 } |
| 2691 | 2681 |
| 2692 if (op.type.lexeme == '??') { | 2682 if (op.type.lexeme == '??') { |
| 2693 // TODO(jmesserly): leave RHS for debugging? | 2683 // TODO(jmesserly): leave RHS for debugging? |
| 2694 // This should be a hint or warning for dead code. | 2684 // This should be a hint or warning for dead code. |
| 2695 if (!_isNullable(left)) return _visit(left); | 2685 if (!isNullable(left)) return _visit(left); |
| 2696 | 2686 |
| 2697 var vars = <String, JS.Expression>{}; | 2687 var vars = <String, JS.Expression>{}; |
| 2698 // Desugar `l ?? r` as `l != null ? l : r` | 2688 // Desugar `l ?? r` as `l != null ? l : r` |
| 2699 var l = _visit(_bindValue(vars, 'l', left, context: left)); | 2689 var l = _visit(_bindValue(vars, 'l', left, context: left)); |
| 2700 return new JS.MetaLet(vars, [ | 2690 return new JS.MetaLet(vars, [ |
| 2701 js.call('# != null ? # : #', [l, l, _visit(right)]) | 2691 js.call('# != null ? # : #', [l, l, _visit(right)]) |
| 2702 ]); | 2692 ]); |
| 2703 } | 2693 } |
| 2704 | 2694 |
| 2705 if (binaryOperationIsPrimitive(leftType, rightType) || | 2695 if (binaryOperationIsPrimitive(leftType, rightType) || |
| (...skipping 24 matching lines...) Expand all Loading... |
| 2730 var numType = types.numType; | 2720 var numType = types.numType; |
| 2731 if (rules.isSubtypeOf(t, numType)) return numType; | 2721 if (rules.isSubtypeOf(t, numType)) return numType; |
| 2732 return t; | 2722 return t; |
| 2733 } | 2723 } |
| 2734 | 2724 |
| 2735 bool _canUsePrimitiveEquality(Expression left, Expression right) { | 2725 bool _canUsePrimitiveEquality(Expression left, Expression right) { |
| 2736 if (_isNull(left) || _isNull(right)) return true; | 2726 if (_isNull(left) || _isNull(right)) return true; |
| 2737 | 2727 |
| 2738 var leftType = _canonicalizeNumTypes(getStaticType(left)); | 2728 var leftType = _canonicalizeNumTypes(getStaticType(left)); |
| 2739 var rightType = _canonicalizeNumTypes(getStaticType(right)); | 2729 var rightType = _canonicalizeNumTypes(getStaticType(right)); |
| 2740 return _isPrimitiveType(leftType) && leftType == rightType; | 2730 return isPrimitiveType(leftType) && leftType == rightType; |
| 2741 } | 2731 } |
| 2742 | 2732 |
| 2743 bool _isNull(Expression expr) => expr is NullLiteral; | 2733 bool _isNull(Expression expr) => expr is NullLiteral; |
| 2744 | 2734 |
| 2745 SimpleIdentifier _createTemporary(String name, DartType type, | 2735 SimpleIdentifier _createTemporary(String name, DartType type, |
| 2746 {bool nullable: true}) { | 2736 {bool nullable: true}) { |
| 2747 // We use an invalid source location to signal that this is a temporary. | 2737 // We use an invalid source location to signal that this is a temporary. |
| 2748 // See [_isTemporary]. | 2738 // See [_isTemporary]. |
| 2749 // TODO(jmesserly): alternatives are | 2739 // TODO(jmesserly): alternatives are |
| 2750 // * (ab)use Element.isSynthetic, which isn't currently used for | 2740 // * (ab)use Element.isSynthetic, which isn't currently used for |
| 2751 // LocalVariableElementImpl, so we could repurpose to mean "temp". | 2741 // LocalVariableElementImpl, so we could repurpose to mean "temp". |
| 2752 // * add a new property to LocalVariableElementImpl. | 2742 // * add a new property to LocalVariableElementImpl. |
| 2753 // * create a new subtype of LocalVariableElementImpl to mark a temp. | 2743 // * create a new subtype of LocalVariableElementImpl to mark a temp. |
| 2754 var id = | 2744 var id = |
| 2755 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); | 2745 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, -1)); |
| 2756 id.staticElement = new TemporaryVariableElement.forNode(id); | 2746 id.staticElement = new TemporaryVariableElement.forNode(id); |
| 2757 id.staticType = type; | 2747 id.staticType = type; |
| 2758 DynamicInvoke.set(id, type.isDynamic); | 2748 DynamicInvoke.set(id, type.isDynamic); |
| 2759 _nullInference.addVariable(id.staticElement, nullable: nullable); | 2749 addTemporaryVariable(id.staticElement, nullable: nullable); |
| 2760 return id; | 2750 return id; |
| 2761 } | 2751 } |
| 2762 | 2752 |
| 2763 JS.Expression _emitConst(JS.Expression expr()) { | 2753 JS.Expression _emitConst(JS.Expression expr()) { |
| 2764 // TODO(jmesserly): emit the constants at top level if possible. | 2754 // TODO(jmesserly): emit the constants at top level if possible. |
| 2765 // This wasn't quite working, so disabled for now. | 2755 // This wasn't quite working, so disabled for now. |
| 2766 return js.call('dart.const(#)', expr()); | 2756 return js.call('dart.const(#)', expr()); |
| 2767 } | 2757 } |
| 2768 | 2758 |
| 2769 /// Returns a new expression, which can be be used safely *once* on the | 2759 /// Returns a new expression, which can be be used safely *once* on the |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2856 /// | 2846 /// |
| 2857 /// The [JS.MetaLet] nodes automatically simplify themselves if they can. | 2847 /// The [JS.MetaLet] nodes automatically simplify themselves if they can. |
| 2858 /// For example, if the result value is not used, then `t` goes away. | 2848 /// For example, if the result value is not used, then `t` goes away. |
| 2859 @override | 2849 @override |
| 2860 JS.Expression visitPostfixExpression(PostfixExpression node) { | 2850 JS.Expression visitPostfixExpression(PostfixExpression node) { |
| 2861 var op = node.operator; | 2851 var op = node.operator; |
| 2862 var expr = node.operand; | 2852 var expr = node.operand; |
| 2863 | 2853 |
| 2864 var dispatchType = getStaticType(expr); | 2854 var dispatchType = getStaticType(expr); |
| 2865 if (unaryOperationIsPrimitive(dispatchType)) { | 2855 if (unaryOperationIsPrimitive(dispatchType)) { |
| 2866 if (!_isNullable(expr)) { | 2856 if (!isNullable(expr)) { |
| 2867 return js.call('#$op', _visit(expr)); | 2857 return js.call('#$op', _visit(expr)); |
| 2868 } | 2858 } |
| 2869 } | 2859 } |
| 2870 | 2860 |
| 2871 assert(op.lexeme == '++' || op.lexeme == '--'); | 2861 assert(op.lexeme == '++' || op.lexeme == '--'); |
| 2872 | 2862 |
| 2873 // Handle the left hand side, to ensure each of its subexpressions are | 2863 // Handle the left hand side, to ensure each of its subexpressions are |
| 2874 // evaluated only once. | 2864 // evaluated only once. |
| 2875 var vars = <String, JS.Expression>{}; | 2865 var vars = <String, JS.Expression>{}; |
| 2876 var left = _bindLeftHandSide(vars, expr, context: expr); | 2866 var left = _bindLeftHandSide(vars, expr, context: expr); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 2888 return new JS.MetaLet(vars, body, statelessResult: true); | 2878 return new JS.MetaLet(vars, body, statelessResult: true); |
| 2889 } | 2879 } |
| 2890 | 2880 |
| 2891 @override | 2881 @override |
| 2892 JS.Expression visitPrefixExpression(PrefixExpression node) { | 2882 JS.Expression visitPrefixExpression(PrefixExpression node) { |
| 2893 var op = node.operator; | 2883 var op = node.operator; |
| 2894 var expr = node.operand; | 2884 var expr = node.operand; |
| 2895 | 2885 |
| 2896 var dispatchType = getStaticType(expr); | 2886 var dispatchType = getStaticType(expr); |
| 2897 if (unaryOperationIsPrimitive(dispatchType)) { | 2887 if (unaryOperationIsPrimitive(dispatchType)) { |
| 2898 if (!_isNullable(expr)) { | 2888 if (!isNullable(expr)) { |
| 2899 return js.call('$op#', _visit(expr)); | 2889 return js.call('$op#', _visit(expr)); |
| 2900 } else if (op.lexeme == '++' || op.lexeme == '--') { | 2890 } else if (op.lexeme == '++' || op.lexeme == '--') { |
| 2901 // We need a null check, so the increment must be expanded out. | 2891 // We need a null check, so the increment must be expanded out. |
| 2902 var vars = <String, JS.Expression>{}; | 2892 var vars = <String, JS.Expression>{}; |
| 2903 var x = _bindLeftHandSide(vars, expr, context: expr); | 2893 var x = _bindLeftHandSide(vars, expr, context: expr); |
| 2904 | 2894 |
| 2905 var one = AstBuilder.integerLiteral(1)..staticType = types.intType; | 2895 var one = AstBuilder.integerLiteral(1)..staticType = types.intType; |
| 2906 var increment = AstBuilder.binaryExpression(x, op.lexeme[0], one) | 2896 var increment = AstBuilder.binaryExpression(x, op.lexeme[0], one) |
| 2907 ..staticElement = node.staticElement | 2897 ..staticElement = node.staticElement |
| 2908 ..staticType = getStaticType(expr); | 2898 ..staticType = getStaticType(expr); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2987 // This pattern has the benefit of preserving order, as well as minimizing | 2977 // This pattern has the benefit of preserving order, as well as minimizing |
| 2988 // code expansion: each `?.` becomes `, _ => _`, plus one helper call. | 2978 // code expansion: each `?.` becomes `, _ => _`, plus one helper call. |
| 2989 // | 2979 // |
| 2990 // TODO(jmesserly): we could desugar with MetaLet instead, which may | 2980 // TODO(jmesserly): we could desugar with MetaLet instead, which may |
| 2991 // lead to higher performing code, but at the cost of readability. | 2981 // lead to higher performing code, but at the cost of readability. |
| 2992 var tail = <JS.Expression>[]; | 2982 var tail = <JS.Expression>[]; |
| 2993 for (;;) { | 2983 for (;;) { |
| 2994 var op = _getOperator(node); | 2984 var op = _getOperator(node); |
| 2995 if (op != null && op.lexeme == '?.') { | 2985 if (op != null && op.lexeme == '?.') { |
| 2996 var nodeTarget = _getTarget(node); | 2986 var nodeTarget = _getTarget(node); |
| 2997 if (!_isNullable(nodeTarget)) { | 2987 if (!isNullable(nodeTarget)) { |
| 2998 node = _stripNullAwareOp(node, nodeTarget); | 2988 node = _stripNullAwareOp(node, nodeTarget); |
| 2999 break; | 2989 break; |
| 3000 } | 2990 } |
| 3001 | 2991 |
| 3002 var param = | 2992 var param = |
| 3003 _createTemporary('_', nodeTarget.staticType, nullable: false); | 2993 _createTemporary('_', nodeTarget.staticType, nullable: false); |
| 3004 var baseNode = _stripNullAwareOp(node, param); | 2994 var baseNode = _stripNullAwareOp(node, param); |
| 3005 tail.add(new JS.ArrowFun([_visit(param)], _visit(baseNode))); | 2995 tail.add(new JS.ArrowFun([_visit(param)], _visit(baseNode))); |
| 3006 node = nodeTarget; | 2996 node = nodeTarget; |
| 3007 } else { | 2997 } else { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 3024 return AstBuilder.propertyAccess(newTarget, node.propertyName); | 3014 return AstBuilder.propertyAccess(newTarget, node.propertyName); |
| 3025 } else { | 3015 } else { |
| 3026 var invoke = node as MethodInvocation; | 3016 var invoke = node as MethodInvocation; |
| 3027 return AstBuilder.methodInvoke( | 3017 return AstBuilder.methodInvoke( |
| 3028 newTarget, invoke.methodName, invoke.argumentList.arguments); | 3018 newTarget, invoke.methodName, invoke.argumentList.arguments); |
| 3029 } | 3019 } |
| 3030 } | 3020 } |
| 3031 | 3021 |
| 3032 bool _requiresStaticDispatch(Expression target, String memberName) { | 3022 bool _requiresStaticDispatch(Expression target, String memberName) { |
| 3033 var type = getStaticType(target); | 3023 var type = getStaticType(target); |
| 3034 if (!_isObjectProperty(memberName)) { | 3024 if (!isObjectProperty(memberName)) { |
| 3035 return false; | 3025 return false; |
| 3036 } | 3026 } |
| 3037 | 3027 |
| 3038 // If the target could be `null`, we need static dispatch. | 3028 // If the target could be `null`, we need static dispatch. |
| 3039 // If the target may be an extension type, we also use static dispatch | 3029 // If the target may be an extension type, we also use static dispatch |
| 3040 // as we don't symbolize object properties like hashCode. | 3030 // as we don't symbolize object properties like hashCode. |
| 3041 return _isNullable(target) || | 3031 return isNullable(target) || |
| 3042 (_extensionTypes.contains(type.element) && target is! SuperExpression); | 3032 (_extensionTypes.contains(type.element) && target is! SuperExpression); |
| 3043 } | 3033 } |
| 3044 | 3034 |
| 3045 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. | 3035 /// Shared code for [PrefixedIdentifier] and [PropertyAccess]. |
| 3046 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { | 3036 JS.Expression _emitGet(Expression target, SimpleIdentifier memberId) { |
| 3047 var member = memberId.staticElement; | 3037 var member = memberId.staticElement; |
| 3048 if (member is PropertyAccessorElement) { | 3038 if (member is PropertyAccessorElement) { |
| 3049 member = (member as PropertyAccessorElement).variable; | 3039 member = (member as PropertyAccessorElement).variable; |
| 3050 } | 3040 } |
| 3051 bool isStatic = member is ClassMemberElement && member.isStatic; | 3041 bool isStatic = member is ClassMemberElement && member.isStatic; |
| (...skipping 570 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3622 } | 3612 } |
| 3623 | 3613 |
| 3624 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 3614 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
| 3625 var baseType = type; | 3615 var baseType = type; |
| 3626 while (baseType is TypeParameterType) { | 3616 while (baseType is TypeParameterType) { |
| 3627 baseType = baseType.element.bound; | 3617 baseType = baseType.element.bound; |
| 3628 } | 3618 } |
| 3629 if (allowExtensions && | 3619 if (allowExtensions && |
| 3630 baseType != null && | 3620 baseType != null && |
| 3631 _extensionTypes.contains(baseType.element) && | 3621 _extensionTypes.contains(baseType.element) && |
| 3632 !_isObjectProperty(name)) { | 3622 !isObjectProperty(name)) { |
| 3633 return js.call('dartx.#', _propertyName(name)); | 3623 return js.call('dartx.#', _propertyName(name)); |
| 3634 } | 3624 } |
| 3635 | 3625 |
| 3636 return _propertyName(name); | 3626 return _propertyName(name); |
| 3637 } | 3627 } |
| 3638 | 3628 |
| 3639 bool _externalOrNative(node) => | 3629 bool _externalOrNative(node) => |
| 3640 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 3630 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
| 3641 | 3631 |
| 3642 FunctionBody _functionBody(node) => | 3632 FunctionBody _functionBody(node) => |
| (...skipping 28 matching lines...) Expand all Loading... |
| 3671 bool _isObjectGetter(String name) { | 3661 bool _isObjectGetter(String name) { |
| 3672 PropertyAccessorElement element = _types.objectType.element.getGetter(name); | 3662 PropertyAccessorElement element = _types.objectType.element.getGetter(name); |
| 3673 return (element != null && !element.isStatic); | 3663 return (element != null && !element.isStatic); |
| 3674 } | 3664 } |
| 3675 | 3665 |
| 3676 bool _isObjectMethod(String name) { | 3666 bool _isObjectMethod(String name) { |
| 3677 MethodElement element = _types.objectType.element.getMethod(name); | 3667 MethodElement element = _types.objectType.element.getMethod(name); |
| 3678 return (element != null && !element.isStatic); | 3668 return (element != null && !element.isStatic); |
| 3679 } | 3669 } |
| 3680 | 3670 |
| 3681 bool _isObjectProperty(String name) { | 3671 bool isObjectProperty(String name) { |
| 3682 return _isObjectGetter(name) || _isObjectMethod(name); | 3672 return _isObjectGetter(name) || _isObjectMethod(name); |
| 3683 } | 3673 } |
| 3684 | 3674 |
| 3685 // TODO(leafp): Various analyzer pieces computed similar things. | 3675 // TODO(leafp): Various analyzer pieces computed similar things. |
| 3686 // Share this logic somewhere? | 3676 // Share this logic somewhere? |
| 3687 DartType _getExpectedReturnType(ExecutableElement element) { | 3677 DartType _getExpectedReturnType(ExecutableElement element) { |
| 3688 FunctionType functionType = element.type; | 3678 FunctionType functionType = element.type; |
| 3689 if (functionType == null) { | 3679 if (functionType == null) { |
| 3690 return DynamicTypeImpl.instance; | 3680 return DynamicTypeImpl.instance; |
| 3691 } | 3681 } |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3861 | 3851 |
| 3862 /// A special kind of element created by the compiler, signifying a temporary | 3852 /// A special kind of element created by the compiler, signifying a temporary |
| 3863 /// variable. These objects use instance equality, and should be shared | 3853 /// variable. These objects use instance equality, and should be shared |
| 3864 /// everywhere in the tree where they are treated as the same variable. | 3854 /// everywhere in the tree where they are treated as the same variable. |
| 3865 class TemporaryVariableElement extends LocalVariableElementImpl { | 3855 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 3866 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3856 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 3867 | 3857 |
| 3868 int get hashCode => identityHashCode(this); | 3858 int get hashCode => identityHashCode(this); |
| 3869 bool operator ==(Object other) => identical(this, other); | 3859 bool operator ==(Object other) => identical(this, other); |
| 3870 } | 3860 } |
| OLD | NEW |