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

Side by Side Diff: pkg/dev_compiler/lib/src/compiler/code_generator.dart

Issue 2926613003: Generate better code for '=='. (Closed)
Patch Set: Address comments Created 3 years, 5 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 | « pkg/dev_compiler/lib/sdk/ddc_sdk.sum ('k') | pkg/dev_compiler/lib/src/compiler/js_typerep.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 2
3 // for details. All rights reserved. Use of this source code is governed by a 3 // for details. All rights reserved. Use of this source code is governed by a
4 // BSD-style license that can be found in the LICENSE file. 4 // BSD-style license that can be found in the LICENSE file.
5 5
6 import 'dart:collection' show HashMap, HashSet; 6 import 'dart:collection' show HashMap, HashSet;
7 import 'dart:math' show min, max; 7 import 'dart:math' show min, max;
8 8
9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator;
10 import 'package:analyzer/dart/ast/ast.dart'; 10 import 'package:analyzer/dart/ast/ast.dart';
11 import 'package:analyzer/dart/ast/standard_ast_factory.dart'; 11 import 'package:analyzer/dart/ast/standard_ast_factory.dart';
12 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; 12 import 'package:analyzer/dart/ast/standard_resolution_map.dart';
13 import 'package:analyzer/dart/ast/token.dart' show TokenType; 13 import 'package:analyzer/dart/ast/token.dart' show Token, TokenType;
14 import 'package:analyzer/dart/element/element.dart'; 14 import 'package:analyzer/dart/element/element.dart';
15 import 'package:analyzer/dart/element/type.dart'; 15 import 'package:analyzer/dart/element/type.dart';
16 import 'package:analyzer/src/dart/ast/token.dart' show StringToken; 16 import 'package:analyzer/src/dart/ast/token.dart' show StringToken;
17 import 'package:analyzer/src/dart/element/element.dart' 17 import 'package:analyzer/src/dart/element/element.dart'
18 show FieldElementImpl, LocalVariableElementImpl; 18 show FieldElementImpl, LocalVariableElementImpl;
19 import 'package:analyzer/src/dart/element/type.dart' show DynamicTypeImpl; 19 import 'package:analyzer/src/dart/element/type.dart' show DynamicTypeImpl;
20 import 'package:analyzer/src/dart/sdk/sdk.dart'; 20 import 'package:analyzer/src/dart/sdk/sdk.dart';
21 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; 21 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
22 import 'package:analyzer/src/generated/resolver.dart' 22 import 'package:analyzer/src/generated/resolver.dart'
23 show TypeProvider, NamespaceBuilder; 23 show TypeProvider, NamespaceBuilder;
(...skipping 15 matching lines...) Expand all
39 import '../js_ast/js_ast.dart' as JS; 39 import '../js_ast/js_ast.dart' as JS;
40 import '../js_ast/js_ast.dart' show js; 40 import '../js_ast/js_ast.dart' show js;
41 import 'ast_builder.dart' show AstBuilder; 41 import 'ast_builder.dart' show AstBuilder;
42 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile; 42 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile;
43 import 'element_helpers.dart'; 43 import 'element_helpers.dart';
44 import 'extension_types.dart' show ExtensionTypeSet; 44 import 'extension_types.dart' show ExtensionTypeSet;
45 import 'js_interop.dart'; 45 import 'js_interop.dart';
46 import 'js_metalet.dart' as JS; 46 import 'js_metalet.dart' as JS;
47 import 'js_names.dart' as JS; 47 import 'js_names.dart' as JS;
48 import 'js_typeref_codegen.dart' show JsTypeRefCodegen; 48 import 'js_typeref_codegen.dart' show JsTypeRefCodegen;
49 import 'js_typerep.dart' show JSTypeRep;
49 import 'module_builder.dart' show pathToJSIdentifier; 50 import 'module_builder.dart' show pathToJSIdentifier;
50 import 'nullable_type_inference.dart' show NullableTypeInference; 51 import 'nullable_type_inference.dart' show NullableTypeInference;
51 import 'property_model.dart'; 52 import 'property_model.dart';
52 import 'reify_coercions.dart' show CoercionReifier; 53 import 'reify_coercions.dart' show CoercionReifier;
53 import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless; 54 import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless;
54 import 'type_utilities.dart'; 55 import 'type_utilities.dart';
55 56
56 /// The code generator for Dart Dev Compiler. 57 /// The code generator for Dart Dev Compiler.
57 /// 58 ///
58 /// Takes as input resolved Dart ASTs for every compilation unit in every 59 /// Takes as input resolved Dart ASTs for every compilation unit in every
59 /// library in the module. Produces a single JavaScript AST for the module as 60 /// library in the module. Produces a single JavaScript AST for the module as
60 /// output, along with its source map. 61 // output, along with its source map.
61 /// 62 ///
62 /// This class attempts to preserve identifier names and structure of the input 63 /// This class attempts to preserve identifier names and structure of the input
63 /// Dart code, whenever this is possible to do in the generated code. 64 /// Dart code, whenever this is possible to do in the generated code.
64 // 65 ///
65 // TODO(jmesserly): we should use separate visitors for statements and 66 // TODO(jmesserly): we should use separate visitors for statements and
66 // expressions. Declarations are handled directly, and many minor component 67 // expressions. Declarations are handled directly, and many minor component
67 // AST nodes aren't visited, so the visitor pattern isn't helping except for 68 // AST nodes aren't visited, so the visitor pattern isn't helping except for
68 // expressions (which result in JS.Expression) and statements 69 // expressions (which result in JS.Expression) and statements
69 // (which result in (JS.Statement). 70 // (which result in (JS.Statement).
70 class CodeGenerator extends Object 71 class CodeGenerator extends Object
71 with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference 72 with ClosureAnnotator, JsTypeRefCodegen, NullableTypeInference
72 implements AstVisitor<JS.Node> { 73 implements AstVisitor<JS.Node> {
73 final AnalysisContext context; 74 final AnalysisContext context;
74 final SummaryDataStore summaryData; 75 final SummaryDataStore summaryData;
75 76
76 final CompilerOptions options; 77 final CompilerOptions options;
77 final StrongTypeSystemImpl rules; 78 final StrongTypeSystemImpl rules;
79 JSTypeRep typeRep;
78 80
79 /// The set of libraries we are currently compiling, and the temporaries used 81 /// The set of libraries we are currently compiling, and the temporaries used
80 /// to refer to them. 82 /// to refer to them.
81 /// 83 ///
82 /// We sometimes special case codegen for a single library, as it simplifies 84 /// We sometimes special case codegen for a single library, as it simplifies
83 /// name scoping requirements. 85 /// name scoping requirements.
84 final _libraries = new Map<LibraryElement, JS.Identifier>(); 86 final _libraries = new Map<LibraryElement, JS.Identifier>();
85 87
86 /// Imported libraries, and the temporaries used to refer to them. 88 /// Imported libraries, and the temporaries used to refer to them.
87 final _imports = new Map<LibraryElement, JS.TemporaryId>(); 89 final _imports = new Map<LibraryElement, JS.TemporaryId>();
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 127
126 /// The type provider from the current Analysis [context]. 128 /// The type provider from the current Analysis [context].
127 final TypeProvider types; 129 final TypeProvider types;
128 130
129 final LibraryElement dartCoreLibrary; 131 final LibraryElement dartCoreLibrary;
130 final LibraryElement dartJSLibrary; 132 final LibraryElement dartJSLibrary;
131 133
132 /// The dart:async `StreamIterator<>` type. 134 /// The dart:async `StreamIterator<>` type.
133 final InterfaceType _asyncStreamIterator; 135 final InterfaceType _asyncStreamIterator;
134 136
137 /// The dart:core `identical` element.
138 final FunctionElement _coreIdentical;
139
135 /// The dart:_interceptors JSArray element. 140 /// The dart:_interceptors JSArray element.
136 final ClassElement _jsArray; 141 final ClassElement _jsArray;
137 142
138 final ClassElement boolClass; 143 final ClassElement boolClass;
139 final ClassElement intClass; 144 final ClassElement intClass;
140 final ClassElement interceptorClass; 145 final ClassElement interceptorClass;
141 final ClassElement nullClass; 146 final ClassElement nullClass;
142 final ClassElement numClass; 147 final ClassElement numClass;
143 final ClassElement objectClass; 148 final ClassElement objectClass;
144 final ClassElement stringClass; 149 final ClassElement stringClass;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 /// unit. 195 /// unit.
191 final virtualFields = new VirtualFieldModel(); 196 final virtualFields = new VirtualFieldModel();
192 197
193 CodeGenerator( 198 CodeGenerator(
194 AnalysisContext c, this.summaryData, this.options, this._extensionTypes) 199 AnalysisContext c, this.summaryData, this.options, this._extensionTypes)
195 : context = c, 200 : context = c,
196 rules = new StrongTypeSystemImpl(c.typeProvider), 201 rules = new StrongTypeSystemImpl(c.typeProvider),
197 types = c.typeProvider, 202 types = c.typeProvider,
198 _asyncStreamIterator = 203 _asyncStreamIterator =
199 _getLibrary(c, 'dart:async').getType('StreamIterator').type, 204 _getLibrary(c, 'dart:async').getType('StreamIterator').type,
205 _coreIdentical =
206 _getLibrary(c, 'dart:core').publicNamespace.get('identical'),
200 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), 207 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'),
201 interceptorClass = 208 interceptorClass =
202 _getLibrary(c, 'dart:_interceptors').getType('Interceptor'), 209 _getLibrary(c, 'dart:_interceptors').getType('Interceptor'),
203 dartCoreLibrary = _getLibrary(c, 'dart:core'), 210 dartCoreLibrary = _getLibrary(c, 'dart:core'),
204 boolClass = _getLibrary(c, 'dart:core').getType('bool'), 211 boolClass = _getLibrary(c, 'dart:core').getType('bool'),
205 intClass = _getLibrary(c, 'dart:core').getType('int'), 212 intClass = _getLibrary(c, 'dart:core').getType('int'),
206 numClass = _getLibrary(c, 'dart:core').getType('num'), 213 numClass = _getLibrary(c, 'dart:core').getType('num'),
207 nullClass = _getLibrary(c, 'dart:core').getType('Null'), 214 nullClass = _getLibrary(c, 'dart:core').getType('Null'),
208 objectClass = _getLibrary(c, 'dart:core').getType('Object'), 215 objectClass = _getLibrary(c, 'dart:core').getType('Object'),
209 stringClass = _getLibrary(c, 'dart:core').getType('String'), 216 stringClass = _getLibrary(c, 'dart:core').getType('String'),
210 functionClass = _getLibrary(c, 'dart:core').getType('Function'), 217 functionClass = _getLibrary(c, 'dart:core').getType('Function'),
211 privateSymbolClass = 218 privateSymbolClass =
212 _getLibrary(c, 'dart:_internal').getType('PrivateSymbol'), 219 _getLibrary(c, 'dart:_internal').getType('PrivateSymbol'),
213 dartJSLibrary = _getLibrary(c, 'dart:js'); 220 dartJSLibrary = _getLibrary(c, 'dart:js') {
221 typeRep = new JSTypeRep(rules, types);
222 }
214 223
215 Element get currentElement => _currentElements.last; 224 Element get currentElement => _currentElements.last;
216 225
217 LibraryElement get currentLibrary => currentElement.library; 226 LibraryElement get currentLibrary => currentElement.library;
218 227
219 /// The main entry point to JavaScript code generation. 228 /// The main entry point to JavaScript code generation.
220 /// 229 ///
221 /// Takes the metadata for the build unit, as well as resolved trees and 230 /// Takes the metadata for the build unit, as well as resolved trees and
222 /// errors, and computes the output module code and optionally the source map. 231 /// errors, and computes the output module code and optionally the source map.
223 JSModuleFile compile(BuildUnit unit, List<CompilationUnit> compilationUnits, 232 JSModuleFile compile(BuildUnit unit, List<CompilationUnit> compilationUnits,
(...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after
690 Expression fromExpr = node.expression; 699 Expression fromExpr = node.expression;
691 var from = getStaticType(fromExpr); 700 var from = getStaticType(fromExpr);
692 var to = node.type.type; 701 var to = node.type.type;
693 702
694 JS.Expression jsFrom = _visit(fromExpr); 703 JS.Expression jsFrom = _visit(fromExpr);
695 704
696 // Skip the cast if it's not needed. 705 // Skip the cast if it's not needed.
697 if (rules.isSubtypeOf(from, to)) return jsFrom; 706 if (rules.isSubtypeOf(from, to)) return jsFrom;
698 707
699 // All Dart number types map to a JS double. 708 // All Dart number types map to a JS double.
700 if (_isNumberInJS(from) && _isNumberInJS(to)) { 709 if (typeRep.isNumber(from) && typeRep.isNumber(to)) {
701 // Make sure to check when converting to int. 710 // Make sure to check when converting to int.
702 if (from != types.intType && to == types.intType) { 711 if (from != types.intType && to == types.intType) {
703 // TODO(jmesserly): fuse this with notNull check. 712 // TODO(jmesserly): fuse this with notNull check.
704 return _callHelper('asInt(#)', jsFrom); 713 return _callHelper('asInt(#)', jsFrom);
705 } 714 }
706 715
707 // A no-op in JavaScript. 716 // A no-op in JavaScript.
708 return jsFrom; 717 return jsFrom;
709 } 718 }
710 719
(...skipping 27 matching lines...) Expand all
738 result = js.call('#.is(#)', [castType, lhs]); 747 result = js.call('#.is(#)', [castType, lhs]);
739 } 748 }
740 749
741 if (node.notOperator != null) { 750 if (node.notOperator != null) {
742 return js.call('!#', result); 751 return js.call('!#', result);
743 } 752 }
744 return result; 753 return result;
745 } 754 }
746 755
747 String _jsTypeofName(DartType t) { 756 String _jsTypeofName(DartType t) {
748 if (_isNumberInJS(t)) return 'number'; 757 if (typeRep.isNumber(t)) return 'number';
749 if (t == types.stringType) return 'string'; 758 if (t == types.stringType) return 'string';
750 if (t == types.boolType) return 'boolean'; 759 if (t == types.boolType) return 'boolean';
751 return null; 760 return null;
752 } 761 }
753 762
754 @override 763 @override
755 visitFunctionTypeAlias(FunctionTypeAlias node) => _emitTypedef(node); 764 visitFunctionTypeAlias(FunctionTypeAlias node) => _emitTypedef(node);
756 765
757 @override 766 @override
758 visitGenericTypeAlias(GenericTypeAlias node) => _emitTypedef(node); 767 visitGenericTypeAlias(GenericTypeAlias node) => _emitTypedef(node);
(...skipping 2762 matching lines...) Expand 10 before | Expand all | Expand 10 after
3521 InvocationExpression node, JS.Expression fn, List<JS.Expression> args) { 3530 InvocationExpression node, JS.Expression fn, List<JS.Expression> args) {
3522 var typeArgs = _emitInvokeTypeArguments(node); 3531 var typeArgs = _emitInvokeTypeArguments(node);
3523 if (typeArgs != null) { 3532 if (typeArgs != null) {
3524 return _callHelper( 3533 return _callHelper(
3525 'dgcall(#, #, #)', [fn, new JS.ArrayInitializer(typeArgs), args]); 3534 'dgcall(#, #, #)', [fn, new JS.ArrayInitializer(typeArgs), args]);
3526 } else { 3535 } else {
3527 return _callHelper('dcall(#, #)', [fn, args]); 3536 return _callHelper('dcall(#, #)', [fn, args]);
3528 } 3537 }
3529 } 3538 }
3530 3539
3540 bool _doubleEqIsIdentity(Expression left, Expression right) {
3541 // If we statically know LHS or RHS is null we can use ==.
3542 if (_isNull(left) || _isNull(right)) return true;
3543 // If the representation of the two types will not induce conversion in
3544 // JS then we can use == .
3545 return !typeRep.equalityMayConvert(left.staticType, right.staticType);
3546 }
3547
3548 bool _tripleEqIsIdentity(Expression left, Expression right) {
3549 // If either is non-nullable, then we don't need to worry about
3550 // equating null and undefined, and so we can use triple equals.
3551 return !isNullable(left) || !isNullable(right);
3552 }
3553
3554 bool _isCoreIdentical(Expression node) {
3555 return node is Identifier && node.staticElement == _coreIdentical;
3556 }
3557
3558 JS.Expression _emitJSDoubleEq(List<JS.Expression> args,
3559 {bool negated = false}) {
3560 var op = negated ? '# != #' : '# == #';
3561 return js.call(op, args);
3562 }
3563
3564 JS.Expression _emitJSTripleEq(List<JS.Expression> args,
3565 {bool negated = false}) {
3566 var op = negated ? '# !== #' : '# === #';
3567 return js.call(op, args);
3568 }
3569
3570 JS.Expression _emitCoreIdenticalCall(List<Expression> arguments,
3571 {bool negated = false}) {
3572 if (arguments.length != 2) {
3573 // Shouldn't happen in typechecked code
3574 return _callHelper(
3575 'throw(Error("compile error: calls to `identical` require 2 args")');
3576 }
3577 var left = arguments[0];
3578 var right = arguments[1];
3579 var args = [_visit(left), _visit(right)];
3580 if (_tripleEqIsIdentity(left, right)) {
3581 return _emitJSTripleEq(args, negated: negated);
3582 }
3583 if (_doubleEqIsIdentity(left, right)) {
3584 return _emitJSDoubleEq(args, negated: negated);
3585 }
3586 var bang = negated ? '!' : '';
3587 return js.call(
3588 "${bang}#", new JS.Call(_emitTopLevelName(_coreIdentical), args));
3589 }
3590
3531 /// Emits a function call, to a top-level function, local function, or 3591 /// Emits a function call, to a top-level function, local function, or
3532 /// an expression. 3592 /// an expression.
3533 JS.Expression _emitFunctionCall(InvocationExpression node, 3593 JS.Expression _emitFunctionCall(InvocationExpression node,
3534 [Expression function]) { 3594 [Expression function]) {
3535 if (function == null) { 3595 if (function == null) {
3536 function = node.function; 3596 function = node.function;
3537 } 3597 }
3598 if (_isCoreIdentical(function)) {
3599 return _emitCoreIdenticalCall(node.argumentList.arguments);
3600 }
3538 var fn = _visit(function); 3601 var fn = _visit(function);
3539 var args = _emitArgumentList(node.argumentList); 3602 var args = _emitArgumentList(node.argumentList);
3540 if (isDynamicInvoke(function)) { 3603 if (isDynamicInvoke(function)) {
3541 return _emitDynamicInvoke(node, fn, args); 3604 return _emitDynamicInvoke(node, fn, args);
3542 } else {
3543 return new JS.Call(_applyInvokeTypeArguments(fn, node), args);
3544 } 3605 }
3606 return new JS.Call(_applyInvokeTypeArguments(fn, node), args);
3545 } 3607 }
3546 3608
3547 JS.Expression _applyInvokeTypeArguments( 3609 JS.Expression _applyInvokeTypeArguments(
3548 JS.Expression target, InvocationExpression node) { 3610 JS.Expression target, InvocationExpression node) {
3549 var typeArgs = _emitInvokeTypeArguments(node); 3611 var typeArgs = _emitInvokeTypeArguments(node);
3550 if (typeArgs == null) return target; 3612 if (typeArgs == null) return target;
3551 return new JS.Call(target, typeArgs); 3613 return new JS.Call(target, typeArgs);
3552 } 3614 }
3553 3615
3554 List<JS.Expression> _emitInvokeTypeArguments(InvocationExpression node) { 3616 List<JS.Expression> _emitInvokeTypeArguments(InvocationExpression node) {
(...skipping 525 matching lines...) Expand 10 before | Expand all | Expand 10 after
4080 return stringValue != null 4142 return stringValue != null
4081 ? js.escapedString(stringValue) 4143 ? js.escapedString(stringValue)
4082 : new JS.LiteralNull(); 4144 : new JS.LiteralNull();
4083 } 4145 }
4084 throw new StateError('failed to evaluate $node'); 4146 throw new StateError('failed to evaluate $node');
4085 } 4147 }
4086 return _emitInstanceCreationExpression( 4148 return _emitInstanceCreationExpression(
4087 element, type, name, node.argumentList, node.isConst); 4149 element, type, name, node.argumentList, node.isConst);
4088 } 4150 }
4089 4151
4090 /// True if this type is built-in to JS, and we use the values unwrapped. 4152 bool isPrimitiveType(DartType t) => typeRep.isPrimitive(t);
4091 /// For these types we generate a calling convention via static
4092 /// "extension methods". This allows types to be extended without adding
4093 /// extensions directly on the prototype.
4094 bool isPrimitiveType(DartType t) =>
4095 typeIsPrimitiveInJS(t) || t == types.stringType;
4096
4097 bool typeIsPrimitiveInJS(DartType t) =>
4098 _isNumberInJS(t) || t == types.boolType;
4099
4100 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) =>
4101 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT);
4102
4103 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t);
4104 4153
4105 JS.Expression notNull(Expression expr) { 4154 JS.Expression notNull(Expression expr) {
4106 if (expr == null) return null; 4155 if (expr == null) return null;
4107 var jsExpr = _visit(expr); 4156 var jsExpr = _visit(expr);
4108 if (!isNullable(expr)) return jsExpr; 4157 if (!isNullable(expr)) return jsExpr;
4109 return _callHelper('notNull(#)', jsExpr); 4158 return _callHelper('notNull(#)', jsExpr);
4110 } 4159 }
4111 4160
4161 JS.Expression _emitEqualityOperator(BinaryExpression node, Token op) {
4162 var left = node.leftOperand;
4163 var right = node.rightOperand;
4164 var leftType = left.staticType;
4165 var negated = op.type == TokenType.BANG_EQ;
4166
4167 if (left is SuperExpression) {
4168 return _emitSend(left, op.lexeme, [right]);
4169 }
4170
4171 // Equality on enums and primitives is identity.
4172 // TODO(leafp): Walk the class hierarchy and check to see if == was
4173 // overridden
4174 var isEnum = leftType is InterfaceType && leftType.element.isEnum;
4175 var usesIdentity = typeRep.isPrimitive(leftType) ||
4176 isEnum ||
4177 _isNull(left) ||
4178 _isNull(right);
4179
4180 // If we know that the left type uses identity for equality, we can
4181 // sometimes emit better code.
4182 if (usesIdentity) {
4183 return _emitCoreIdenticalCall([left, right], negated: negated);
4184 }
4185
4186 var bang = op.type == TokenType.BANG_EQ ? '!' : '';
4187 var leftElement = leftType.element;
4188
4189 // If either is null, we can use simple equality.
4190 // We need to equate null and undefined, so if both are nullable
4191 // (but not known to be null), we cannot directly use JS ==
4192 // unless we know that conversion will not happen.
4193 // Functions may or may not have an [dartx[`==`]] method attached.
4194 // - If they are tearoffs they will, otherwise they won't and equality is
4195 // identity.
4196 // TODO(leafp): consider fixing this.
4197 //
4198 // Native types may not have equality on the prototype.
4199 // If left is not nullable, then we don't need to worry about
4200 // null/undefined.
4201 // TODO(leafp): consider using (left || dart.EQ)['=='](right))
4202 // when left is nullable but not falsey
4203 if ((leftElement is ClassElement && _isJSNative(leftElement)) ||
4204 typeRep.isUnknown(leftType) ||
4205 leftType is FunctionType ||
4206 isNullable(left)) {
4207 // Fall back to equality for now.
4208 var code = '${bang}#.equals(#, #)';
4209 return js.call(code, [_runtimeModule, _visit(left), _visit(right)]);
4210 }
4211
4212 var name = _emitMemberName('==', type: leftType);
4213 var code = '${bang}#[#](#)';
4214 return js.call(code, [_visit(left), name, _visit(right)]);
4215 }
4216
4112 @override 4217 @override
4113 JS.Expression visitBinaryExpression(BinaryExpression node) { 4218 JS.Expression visitBinaryExpression(BinaryExpression node) {
4114 var op = node.operator; 4219 var op = node.operator;
4115 4220
4116 // The operands of logical boolean operators are subject to boolean 4221 // The operands of logical boolean operators are subject to boolean
4117 // conversion. 4222 // conversion.
4118 if (op.type == TokenType.BAR_BAR || 4223 if (op.type == TokenType.BAR_BAR ||
4119 op.type == TokenType.AMPERSAND_AMPERSAND) { 4224 op.type == TokenType.AMPERSAND_AMPERSAND) {
4120 return _visitTest(node); 4225 return _visitTest(node);
4121 } 4226 }
4122 4227
4228 if (op.type.isEqualityOperator) return _emitEqualityOperator(node, op);
4229
4123 var left = node.leftOperand; 4230 var left = node.leftOperand;
4124 var right = node.rightOperand; 4231 var right = node.rightOperand;
4125 4232
4126 var leftType = getStaticType(left);
4127 var rightType = getStaticType(right);
4128
4129 var code;
4130 if (op.type.isEqualityOperator) {
4131 // If we statically know LHS or RHS is null we can generate a clean check.
4132 // We can also do this if both sides are the same primitive type.
4133 if (_canUsePrimitiveEquality(left, right)) {
4134 code = op.type == TokenType.EQ_EQ ? '# == #' : '# != #';
4135 } else if (left is SuperExpression) {
4136 return _emitSend(left, op.lexeme, [right]);
4137 } else {
4138 var bang = op.type == TokenType.BANG_EQ ? '!' : '';
4139 code = '${bang}#.equals(#, #)';
4140 return js.call(code, [_runtimeModule, _visit(left), _visit(right)]);
4141 }
4142 return js.call(code, [_visit(left), _visit(right)]);
4143 }
4144
4145 if (op.type.lexeme == '??') { 4233 if (op.type.lexeme == '??') {
4146 // TODO(jmesserly): leave RHS for debugging? 4234 // TODO(jmesserly): leave RHS for debugging?
4147 // This should be a hint or warning for dead code. 4235 // This should be a hint or warning for dead code.
4148 if (!isNullable(left)) return _visit(left); 4236 if (!isNullable(left)) return _visit(left);
4149 4237
4150 var vars = <JS.MetaLetVariable, JS.Expression>{}; 4238 var vars = <JS.MetaLetVariable, JS.Expression>{};
4151 // Desugar `l ?? r` as `l != null ? l : r` 4239 // Desugar `l ?? r` as `l != null ? l : r`
4152 var l = _visit(_bindValue(vars, 'l', left, context: left)); 4240 var l = _visit(_bindValue(vars, 'l', left, context: left));
4153 return new JS.MetaLet(vars, [ 4241 return new JS.MetaLet(vars, [
4154 js.call('# != null ? # : #', [l, l, _visit(right)]) 4242 js.call('# != null ? # : #', [l, l, _visit(right)])
4155 ]); 4243 ]);
4156 } 4244 }
4157 4245
4158 if (binaryOperationIsPrimitive(leftType, rightType) || 4246 var leftType = getStaticType(left);
4247 var rightType = getStaticType(right);
4248
4249 if (typeRep.binaryOperationIsPrimitive(leftType, rightType) ||
4159 leftType == types.stringType && op.type == TokenType.PLUS) { 4250 leftType == types.stringType && op.type == TokenType.PLUS) {
4160 // special cases where we inline the operation 4251 // special cases where we inline the operation
4161 // these values are assumed to be non-null (determined by the checker) 4252 // these values are assumed to be non-null (determined by the checker)
4162 // TODO(jmesserly): it would be nice to just inline the method from core, 4253 // TODO(jmesserly): it would be nice to just inline the method from core,
4163 // instead of special cases here. 4254 // instead of special cases here.
4164 JS.Expression binary(String code) { 4255 JS.Expression binary(String code) {
4165 return js.call(code, [notNull(left), notNull(right)]); 4256 return js.call(code, [notNull(left), notNull(right)]);
4166 } 4257 }
4167 4258
4168 JS.Expression bitwise(String code) { 4259 JS.Expression bitwise(String code) {
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
4383 } 4474 }
4384 } 4475 }
4385 int value = _asIntInRange(expr, 0, 0x7fffffff); 4476 int value = _asIntInRange(expr, 0, 0x7fffffff);
4386 if (value != null) return value.bitLength; 4477 if (value != null) return value.bitLength;
4387 return MAX; 4478 return MAX;
4388 } 4479 }
4389 4480
4390 return bitWidth(expr, 0) < 32; 4481 return bitWidth(expr, 0) < 32;
4391 } 4482 }
4392 4483
4393 /// If the type [t] is [int] or [double], or a type parameter 4484 bool _isNull(Expression expr) =>
4394 /// bounded by [int], [double] or [num] returns [num]. 4485 expr is NullLiteral || getStaticType(expr).isDartCoreNull;
4395 /// Otherwise returns [t].
4396 DartType _canonicalizeNumTypes(DartType t) {
4397 var numType = types.numType;
4398 if (rules.isSubtypeOf(t, numType)) return numType;
4399 return t;
4400 }
4401
4402 bool _canUsePrimitiveEquality(Expression left, Expression right) {
4403 if (_isNull(left) || _isNull(right)) return true;
4404
4405 var leftType = _canonicalizeNumTypes(getStaticType(left));
4406 var rightType = _canonicalizeNumTypes(getStaticType(right));
4407 return isPrimitiveType(leftType) && leftType == rightType;
4408 }
4409
4410 bool _isNull(Expression expr) => expr is NullLiteral;
4411 4486
4412 SimpleIdentifier _createTemporary(String name, DartType type, 4487 SimpleIdentifier _createTemporary(String name, DartType type,
4413 {bool nullable: true, JS.Expression variable, bool dynamicInvoke}) { 4488 {bool nullable: true, JS.Expression variable, bool dynamicInvoke}) {
4414 // We use an invalid source location to signal that this is a temporary. 4489 // We use an invalid source location to signal that this is a temporary.
4415 // See [_isTemporary]. 4490 // See [_isTemporary].
4416 // TODO(jmesserly): alternatives are 4491 // TODO(jmesserly): alternatives are
4417 // * (ab)use Element.isSynthetic, which isn't currently used for 4492 // * (ab)use Element.isSynthetic, which isn't currently used for
4418 // LocalVariableElementImpl, so we could repurpose to mean "temp". 4493 // LocalVariableElementImpl, so we could repurpose to mean "temp".
4419 // * add a new property to LocalVariableElementImpl. 4494 // * add a new property to LocalVariableElementImpl.
4420 // * create a new subtype of LocalVariableElementImpl to mark a temp. 4495 // * create a new subtype of LocalVariableElementImpl to mark a temp.
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
4545 /// (let* (x1=expr1, x2=expr2, t=expr1[expr2]) { x1[x2] = t + 1; t }) 4620 /// (let* (x1=expr1, x2=expr2, t=expr1[expr2]) { x1[x2] = t + 1; t })
4546 /// 4621 ///
4547 /// The [JS.MetaLet] nodes automatically simplify themselves if they can. 4622 /// The [JS.MetaLet] nodes automatically simplify themselves if they can.
4548 /// For example, if the result value is not used, then `t` goes away. 4623 /// For example, if the result value is not used, then `t` goes away.
4549 @override 4624 @override
4550 JS.Expression visitPostfixExpression(PostfixExpression node) { 4625 JS.Expression visitPostfixExpression(PostfixExpression node) {
4551 var op = node.operator; 4626 var op = node.operator;
4552 var expr = node.operand; 4627 var expr = node.operand;
4553 4628
4554 var dispatchType = getStaticType(expr); 4629 var dispatchType = getStaticType(expr);
4555 if (unaryOperationIsPrimitive(dispatchType)) { 4630 if (typeRep.unaryOperationIsPrimitive(dispatchType)) {
4556 if (!isNullable(expr)) { 4631 if (!isNullable(expr)) {
4557 return js.call('#$op', _visit(expr)); 4632 return js.call('#$op', _visit(expr));
4558 } 4633 }
4559 } 4634 }
4560 4635
4561 assert(op.lexeme == '++' || op.lexeme == '--'); 4636 assert(op.lexeme == '++' || op.lexeme == '--');
4562 4637
4563 // Handle the left hand side, to ensure each of its subexpressions are 4638 // Handle the left hand side, to ensure each of its subexpressions are
4564 // evaluated only once. 4639 // evaluated only once.
4565 var vars = <JS.MetaLetVariable, JS.Expression>{}; 4640 var vars = <JS.MetaLetVariable, JS.Expression>{};
(...skipping 16 matching lines...) Expand all
4582 JS.Expression visitPrefixExpression(PrefixExpression node) { 4657 JS.Expression visitPrefixExpression(PrefixExpression node) {
4583 var op = node.operator; 4658 var op = node.operator;
4584 4659
4585 // Logical negation, `!e`, is a boolean conversion context since it is 4660 // Logical negation, `!e`, is a boolean conversion context since it is
4586 // defined as `e ? false : true`. 4661 // defined as `e ? false : true`.
4587 if (op.lexeme == '!') return _visitTest(node); 4662 if (op.lexeme == '!') return _visitTest(node);
4588 4663
4589 var expr = node.operand; 4664 var expr = node.operand;
4590 4665
4591 var dispatchType = getStaticType(expr); 4666 var dispatchType = getStaticType(expr);
4592 if (unaryOperationIsPrimitive(dispatchType)) { 4667 if (typeRep.unaryOperationIsPrimitive(dispatchType)) {
4593 if (op.lexeme == '~') { 4668 if (op.lexeme == '~') {
4594 if (_isNumberInJS(dispatchType)) { 4669 if (typeRep.isNumber(dispatchType)) {
4595 JS.Expression jsExpr = js.call('~#', notNull(expr)); 4670 JS.Expression jsExpr = js.call('~#', notNull(expr));
4596 return _coerceBitOperationResultToUnsigned(node, jsExpr); 4671 return _coerceBitOperationResultToUnsigned(node, jsExpr);
4597 } 4672 }
4598 return _emitSend(expr, op.lexeme[0], []); 4673 return _emitSend(expr, op.lexeme[0], []);
4599 } 4674 }
4600 if (!isNullable(expr)) { 4675 if (!isNullable(expr)) {
4601 return js.call('$op#', _visit(expr)); 4676 return js.call('$op#', _visit(expr));
4602 } 4677 }
4603 if (op.lexeme == '++' || op.lexeme == '--') { 4678 if (op.lexeme == '++' || op.lexeme == '--') {
4604 // We need a null check, so the increment must be expanded out. 4679 // We need a null check, so the increment must be expanded out.
(...skipping 692 matching lines...) Expand 10 before | Expand all | Expand 10 after
5297 /// etc.), where conversions and null checks are implemented via `dart.test` 5372 /// etc.), where conversions and null checks are implemented via `dart.test`
5298 /// to give a more helpful message. 5373 /// to give a more helpful message.
5299 // TODO(sra): When nullablility is available earlier, it would be cleaner to 5374 // TODO(sra): When nullablility is available earlier, it would be cleaner to
5300 // build an input AST where the boolean conversion is a single AST node. 5375 // build an input AST where the boolean conversion is a single AST node.
5301 JS.Expression _visitTest(Expression node) { 5376 JS.Expression _visitTest(Expression node) {
5302 JS.Expression finish(JS.Expression result) { 5377 JS.Expression finish(JS.Expression result) {
5303 return annotate(result, node); 5378 return annotate(result, node);
5304 } 5379 }
5305 5380
5306 if (node is PrefixExpression && node.operator.lexeme == '!') { 5381 if (node is PrefixExpression && node.operator.lexeme == '!') {
5382 // TODO(leafp): consider a peephole opt for identical
5383 // and == here.
5307 return finish(js.call('!#', _visitTest(node.operand))); 5384 return finish(js.call('!#', _visitTest(node.operand)));
5308 } 5385 }
5309 if (node is ParenthesizedExpression) { 5386 if (node is ParenthesizedExpression) {
5310 return finish(_visitTest(node.expression)); 5387 return finish(_visitTest(node.expression));
5311 } 5388 }
5312 if (node is BinaryExpression) { 5389 if (node is BinaryExpression) {
5313 JS.Expression shortCircuit(String code) { 5390 JS.Expression shortCircuit(String code) {
5314 return finish(js.call(code, 5391 return finish(js.call(code,
5315 [_visitTest(node.leftOperand), _visitTest(node.rightOperand)])); 5392 [_visitTest(node.leftOperand), _visitTest(node.rightOperand)]));
5316 } 5393 }
5317 5394
5318 var op = node.operator.type.lexeme; 5395 var op = node.operator.type.lexeme;
5319 if (op == '&&') return shortCircuit('# && #'); 5396 if (op == '&&') return shortCircuit('# && #');
5320 if (op == '||') return shortCircuit('# || #'); 5397 if (op == '||') return shortCircuit('# || #');
5321 } 5398 }
5322 if (node is AsExpression && CoercionReifier.isImplicitCast(node)) { 5399 if (node is AsExpression && CoercionReifier.isImplicitCast(node)) {
5323 assert(node.staticType == types.boolType); 5400 assert(node.staticType == types.boolType);
5324 return _callHelper('test(#)', _visit(node.expression)); 5401 return _callHelper('dtest(#)', _visit(node.expression));
5325 } 5402 }
5326 JS.Expression result = _visit(node); 5403 JS.Expression result = _visit(node);
5327 if (isNullable(node)) result = _callHelper('test(#)', result); 5404 if (isNullable(node)) result = _callHelper('test(#)', result);
5328 return result; 5405 return result;
5329 } 5406 }
5330 5407
5331 /// Like [_emitMemberName], but for declaration sites. 5408 /// Like [_emitMemberName], but for declaration sites.
5332 /// 5409 ///
5333 /// Unlike call sites, we always have an element available, so we can use it 5410 /// Unlike call sites, we always have an element available, so we can use it
5334 /// directly rather than computing the relevant options for [_emitMemberName]. 5411 /// directly rather than computing the relevant options for [_emitMemberName].
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after
5515 } 5592 }
5516 5593
5517 T annotate<T extends JS.Node>(T node, AstNode original, [Element element]) { 5594 T annotate<T extends JS.Node>(T node, AstNode original, [Element element]) {
5518 if (options.closure && element != null) { 5595 if (options.closure && element != null) {
5519 node.closureAnnotation = 5596 node.closureAnnotation =
5520 closureAnnotationFor(node, original, element, namedArgumentTemp.name); 5597 closureAnnotationFor(node, original, element, namedArgumentTemp.name);
5521 } 5598 }
5522 return node..sourceInformation = original; 5599 return node..sourceInformation = original;
5523 } 5600 }
5524 5601
5525 /// Returns true if this is any kind of object represented by `Number` in JS.
5526 ///
5527 /// In practice, this is 4 types: num, int, double, and JSNumber.
5528 ///
5529 /// JSNumber is the type that actually "implements" all numbers, hence it's
5530 /// a subtype of int and double (and num). It's in our "dart:_interceptors".
5531 bool _isNumberInJS(DartType t) =>
5532 rules.isSubtypeOf(t, types.numType) &&
5533 !rules.isSubtypeOf(t, types.nullType);
5534
5535 /// Return true if this is one of the methods/properties on all Dart Objects 5602 /// Return true if this is one of the methods/properties on all Dart Objects
5536 /// (toString, hashCode, noSuchMethod, runtimeType). 5603 /// (toString, hashCode, noSuchMethod, runtimeType).
5537 /// 5604 ///
5538 /// Operator == is excluded, as it is handled as part of the equality binary 5605 /// Operator == is excluded, as it is handled as part of the equality binary
5539 /// operator. 5606 /// operator.
5540 bool isObjectMember(String name) { 5607 bool isObjectMember(String name) {
5541 // We could look these up on Object, but we have hard coded runtime helpers 5608 // We could look these up on Object, but we have hard coded runtime helpers
5542 // so it's not really providing any benefit. 5609 // so it's not really providing any benefit.
5543 switch (name) { 5610 switch (name) {
5544 case 'hashCode': 5611 case 'hashCode':
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after
5828 if (targetIdentifier.staticElement is! PrefixElement) return false; 5895 if (targetIdentifier.staticElement is! PrefixElement) return false;
5829 var prefix = targetIdentifier.staticElement as PrefixElement; 5896 var prefix = targetIdentifier.staticElement as PrefixElement;
5830 5897
5831 // The library the prefix is referring to must come from a deferred import. 5898 // The library the prefix is referring to must come from a deferred import.
5832 var containingLibrary = resolutionMap 5899 var containingLibrary = resolutionMap
5833 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) 5900 .elementDeclaredByCompilationUnit(target.root as CompilationUnit)
5834 .library; 5901 .library;
5835 var imports = containingLibrary.getImportsWithPrefix(prefix); 5902 var imports = containingLibrary.getImportsWithPrefix(prefix);
5836 return imports.length == 1 && imports[0].isDeferred; 5903 return imports.length == 1 && imports[0].isDeferred;
5837 } 5904 }
OLDNEW
« no previous file with comments | « pkg/dev_compiler/lib/sdk/ddc_sdk.sum ('k') | pkg/dev_compiler/lib/src/compiler/js_typerep.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698