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 | 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'; |
(...skipping 26 matching lines...) Expand all Loading... |
37 import '../js_ast/js_ast.dart' as JS; | 37 import '../js_ast/js_ast.dart' as JS; |
38 import '../js_ast/js_ast.dart' show js; | 38 import '../js_ast/js_ast.dart' show js; |
39 import 'ast_builder.dart' show AstBuilder; | 39 import 'ast_builder.dart' show AstBuilder; |
40 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile; | 40 import 'compiler.dart' show BuildUnit, CompilerOptions, JSModuleFile; |
41 import 'element_helpers.dart'; | 41 import 'element_helpers.dart'; |
42 import 'extension_types.dart' show ExtensionTypeSet; | 42 import 'extension_types.dart' show ExtensionTypeSet; |
43 import 'js_interop.dart'; | 43 import 'js_interop.dart'; |
44 import 'js_metalet.dart' as JS; | 44 import 'js_metalet.dart' as JS; |
45 import 'js_names.dart' as JS; | 45 import 'js_names.dart' as JS; |
46 import 'js_typeref_codegen.dart' show JsTypeRefCodegen; | 46 import 'js_typeref_codegen.dart' show JsTypeRefCodegen; |
47 import 'js_typerep.dart' show JSTypeRep; | 47 import 'js_typerep.dart' show JSTypeRep, JSType; |
48 import 'module_builder.dart' show pathToJSIdentifier; | 48 import 'module_builder.dart' show pathToJSIdentifier; |
49 import 'nullable_type_inference.dart' show NullableTypeInference; | 49 import 'nullable_type_inference.dart' show NullableTypeInference; |
50 import 'property_model.dart'; | 50 import 'property_model.dart'; |
51 import 'reify_coercions.dart' show CoercionReifier; | 51 import 'reify_coercions.dart' show CoercionReifier; |
52 import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless; | 52 import 'side_effect_analysis.dart' show ConstFieldVisitor, isStateless; |
53 import 'type_utilities.dart'; | 53 import 'type_utilities.dart'; |
54 | 54 |
55 /// The code generator for Dart Dev Compiler. | 55 /// The code generator for Dart Dev Compiler. |
56 /// | 56 /// |
57 /// Takes as input resolved Dart ASTs for every compilation unit in every | 57 /// Takes as input resolved Dart ASTs for every compilation unit in every |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 | 128 |
129 final LibraryElement dartCoreLibrary; | 129 final LibraryElement dartCoreLibrary; |
130 final LibraryElement dartJSLibrary; | 130 final LibraryElement dartJSLibrary; |
131 | 131 |
132 /// The dart:async `StreamIterator<>` type. | 132 /// The dart:async `StreamIterator<>` type. |
133 final InterfaceType _asyncStreamIterator; | 133 final InterfaceType _asyncStreamIterator; |
134 | 134 |
135 /// The dart:core `identical` element. | 135 /// The dart:core `identical` element. |
136 final FunctionElement _coreIdentical; | 136 final FunctionElement _coreIdentical; |
137 | 137 |
138 /// The dart:_interceptors JSArray element. | 138 /// The dart:_interceptors implementation elements. |
139 final ClassElement _jsArray; | 139 final ClassElement _jsArray; |
| 140 final ClassElement _jsBool; |
| 141 final ClassElement _jsNumber; |
| 142 final ClassElement _jsString; |
140 | 143 |
141 final ClassElement boolClass; | 144 final ClassElement boolClass; |
142 final ClassElement intClass; | 145 final ClassElement intClass; |
143 final ClassElement interceptorClass; | 146 final ClassElement interceptorClass; |
144 final ClassElement nullClass; | 147 final ClassElement nullClass; |
145 final ClassElement numClass; | 148 final ClassElement numClass; |
146 final ClassElement objectClass; | 149 final ClassElement objectClass; |
147 final ClassElement stringClass; | 150 final ClassElement stringClass; |
148 final ClassElement functionClass; | 151 final ClassElement functionClass; |
149 final ClassElement privateSymbolClass; | 152 final ClassElement privateSymbolClass; |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 CodeGenerator( | 201 CodeGenerator( |
199 AnalysisContext c, this.summaryData, this.options, this._extensionTypes) | 202 AnalysisContext c, this.summaryData, this.options, this._extensionTypes) |
200 : context = c, | 203 : context = c, |
201 rules = new StrongTypeSystemImpl(c.typeProvider), | 204 rules = new StrongTypeSystemImpl(c.typeProvider), |
202 types = c.typeProvider, | 205 types = c.typeProvider, |
203 _asyncStreamIterator = | 206 _asyncStreamIterator = |
204 _getLibrary(c, 'dart:async').getType('StreamIterator').type, | 207 _getLibrary(c, 'dart:async').getType('StreamIterator').type, |
205 _coreIdentical = | 208 _coreIdentical = |
206 _getLibrary(c, 'dart:core').publicNamespace.get('identical'), | 209 _getLibrary(c, 'dart:core').publicNamespace.get('identical'), |
207 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), | 210 _jsArray = _getLibrary(c, 'dart:_interceptors').getType('JSArray'), |
| 211 _jsBool = _getLibrary(c, 'dart:_interceptors').getType('JSBool'), |
| 212 _jsString = _getLibrary(c, 'dart:_interceptors').getType('JSString'), |
| 213 _jsNumber = _getLibrary(c, 'dart:_interceptors').getType('JSNumber'), |
208 interceptorClass = | 214 interceptorClass = |
209 _getLibrary(c, 'dart:_interceptors').getType('Interceptor'), | 215 _getLibrary(c, 'dart:_interceptors').getType('Interceptor'), |
210 dartCoreLibrary = _getLibrary(c, 'dart:core'), | 216 dartCoreLibrary = _getLibrary(c, 'dart:core'), |
211 boolClass = _getLibrary(c, 'dart:core').getType('bool'), | 217 boolClass = _getLibrary(c, 'dart:core').getType('bool'), |
212 intClass = _getLibrary(c, 'dart:core').getType('int'), | 218 intClass = _getLibrary(c, 'dart:core').getType('int'), |
213 numClass = _getLibrary(c, 'dart:core').getType('num'), | 219 numClass = _getLibrary(c, 'dart:core').getType('num'), |
214 nullClass = _getLibrary(c, 'dart:core').getType('Null'), | 220 nullClass = _getLibrary(c, 'dart:core').getType('Null'), |
215 objectClass = _getLibrary(c, 'dart:core').getType('Object'), | 221 objectClass = _getLibrary(c, 'dart:core').getType('Object'), |
216 stringClass = _getLibrary(c, 'dart:core').getType('String'), | 222 stringClass = _getLibrary(c, 'dart:core').getType('String'), |
217 functionClass = _getLibrary(c, 'dart:core').getType('Function'), | 223 functionClass = _getLibrary(c, 'dart:core').getType('Function'), |
(...skipping 2207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2425 body.add(js.statement('if (# === void 0) # = #;', | 2431 body.add(js.statement('if (# === void 0) # = #;', |
2426 [jsParam, jsParam, _defaultParamValue(param)])); | 2432 [jsParam, jsParam, _defaultParamValue(param)])); |
2427 } | 2433 } |
2428 } | 2434 } |
2429 | 2435 |
2430 var paramElement = resolutionMap.elementDeclaredByFormalParameter(param); | 2436 var paramElement = resolutionMap.elementDeclaredByFormalParameter(param); |
2431 if (_isCovariant(paramElement)) { | 2437 if (_isCovariant(paramElement)) { |
2432 var castType = _emitType(paramElement.type); | 2438 var castType = _emitType(paramElement.type); |
2433 body.add(js.statement('#._check(#);', [castType, jsParam])); | 2439 body.add(js.statement('#._check(#);', [castType, jsParam])); |
2434 } | 2440 } |
| 2441 if (_annotatedNullCheck(paramElement)) { |
| 2442 body.add(nullParameterCheck(jsParam)); |
| 2443 } |
2435 } | 2444 } |
2436 return body.isEmpty ? null : _statement(body); | 2445 return body.isEmpty ? null : _statement(body); |
2437 } | 2446 } |
2438 | 2447 |
2439 bool _isCovariant(ParameterElement p) { | 2448 bool _isCovariant(ParameterElement p) { |
2440 if (p.isCovariant) return true; | 2449 if (p.isCovariant) return true; |
2441 var covariantParams = _classProperties?.covariantParameters; | 2450 var covariantParams = _classProperties?.covariantParameters; |
2442 return covariantParams != null && covariantParams.contains(p); | 2451 return covariantParams != null && covariantParams.contains(p); |
2443 } | 2452 } |
2444 | 2453 |
(...skipping 815 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3260 var t = _bindValue(vars, 't', x, context: x); | 3269 var t = _bindValue(vars, 't', x, context: x); |
3261 return new JS.MetaLet(vars, [ | 3270 return new JS.MetaLet(vars, [ |
3262 js.call('# == null ? # : #', [_visit(t), _emitSet(x, right), _visit(t)]) | 3271 js.call('# == null ? # : #', [_visit(t), _emitSet(x, right), _visit(t)]) |
3263 ]); | 3272 ]); |
3264 } | 3273 } |
3265 | 3274 |
3266 // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions | 3275 // Desugar `x += y` as `x = x + y`, ensuring that if `x` has subexpressions |
3267 // (for example, x is IndexExpression) we evaluate those once. | 3276 // (for example, x is IndexExpression) we evaluate those once. |
3268 var vars = <JS.MetaLetVariable, JS.Expression>{}; | 3277 var vars = <JS.MetaLetVariable, JS.Expression>{}; |
3269 var lhs = _bindLeftHandSide(vars, left, context: context); | 3278 var lhs = _bindLeftHandSide(vars, left, context: context); |
| 3279 // TODO(leafp): The element for lhs here will be the setter element |
| 3280 // instead of the getter element if lhs is a property access. This |
| 3281 // interferes with nullability analysis. |
3270 Expression inc = AstBuilder.binaryExpression(lhs, op, right) | 3282 Expression inc = AstBuilder.binaryExpression(lhs, op, right) |
3271 ..staticElement = element | 3283 ..staticElement = element |
3272 ..staticType = getStaticType(lhs); | 3284 ..staticType = getStaticType(lhs); |
3273 | 3285 |
3274 var castTo = getImplicitOperationCast(left); | 3286 var castTo = getImplicitOperationCast(left); |
3275 if (castTo != null) inc = CoercionReifier.castExpression(inc, castTo); | 3287 if (castTo != null) inc = CoercionReifier.castExpression(inc, castTo); |
3276 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); | 3288 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); |
3277 } | 3289 } |
3278 | 3290 |
3279 JS.Expression _emitSet(Expression left, Expression right) { | 3291 JS.Expression _emitSet(Expression left, Expression right) { |
(...skipping 763 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4043 JS.Statement visitVariableDeclarationStatement( | 4055 JS.Statement visitVariableDeclarationStatement( |
4044 VariableDeclarationStatement node) { | 4056 VariableDeclarationStatement node) { |
4045 // Special case a single variable with an initializer. | 4057 // Special case a single variable with an initializer. |
4046 // This helps emit cleaner code for things like: | 4058 // This helps emit cleaner code for things like: |
4047 // var result = []..add(1)..add(2); | 4059 // var result = []..add(1)..add(2); |
4048 var variables = node.variables.variables; | 4060 var variables = node.variables.variables; |
4049 if (variables.length == 1) { | 4061 if (variables.length == 1) { |
4050 var v = variables[0]; | 4062 var v = variables[0]; |
4051 if (v.initializer != null) { | 4063 if (v.initializer != null) { |
4052 var name = new JS.Identifier(v.name.name); | 4064 var name = new JS.Identifier(v.name.name); |
4053 return _visit<JS.Expression>(v.initializer).toVariableDeclaration(name); | 4065 var value = _annotatedNullCheck(v.element) |
| 4066 ? notNull(v.initializer) |
| 4067 : _visit<JS.Expression>(v.initializer); |
| 4068 return value.toVariableDeclaration(name); |
4054 } | 4069 } |
4055 } | 4070 } |
4056 return _visit<JS.Expression>(node.variables).toStatement(); | 4071 return _visit<JS.Expression>(node.variables).toStatement(); |
4057 } | 4072 } |
4058 | 4073 |
4059 @override | 4074 @override |
4060 visitVariableDeclarationList(VariableDeclarationList node) { | 4075 visitVariableDeclarationList(VariableDeclarationList node) { |
4061 return new JS.VariableDeclarationList('let', _visitList(node.variables)); | 4076 return new JS.VariableDeclarationList('let', _visitList(node.variables)); |
4062 } | 4077 } |
4063 | 4078 |
(...skipping 22 matching lines...) Expand all Loading... |
4086 for (var field in fields) { | 4101 for (var field in fields) { |
4087 _moduleItems.add(annotate( | 4102 _moduleItems.add(annotate( |
4088 js.statement('# = #;', | 4103 js.statement('# = #;', |
4089 [_emitTopLevelName(field.element), _visitInitializer(field)]), | 4104 [_emitTopLevelName(field.element), _visitInitializer(field)]), |
4090 field, | 4105 field, |
4091 field.element)); | 4106 field.element)); |
4092 } | 4107 } |
4093 } | 4108 } |
4094 | 4109 |
4095 JS.Expression _visitInitializer(VariableDeclaration node) { | 4110 JS.Expression _visitInitializer(VariableDeclaration node) { |
4096 var value = _visit(node.initializer); | 4111 var value = _annotatedNullCheck(node.element) |
| 4112 ? notNull(node.initializer) |
| 4113 : _visit(node.initializer); |
4097 // explicitly initialize to null, to avoid getting `undefined`. | 4114 // explicitly initialize to null, to avoid getting `undefined`. |
4098 // TODO(jmesserly): do this only for vars that aren't definitely assigned. | 4115 // TODO(jmesserly): do this only for vars that aren't definitely assigned. |
4099 return value ?? new JS.LiteralNull(); | 4116 return value ?? new JS.LiteralNull(); |
4100 } | 4117 } |
4101 | 4118 |
4102 JS.Statement _emitLazyFields( | 4119 JS.Statement _emitLazyFields( |
4103 Element target, List<VariableDeclaration> fields) { | 4120 Element target, List<VariableDeclaration> fields) { |
4104 var methods = []; | 4121 var methods = []; |
4105 for (var node in fields) { | 4122 for (var node in fields) { |
4106 var name = node.name.name; | 4123 var name = node.name.name; |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4247 : new JS.LiteralNull(); | 4264 : new JS.LiteralNull(); |
4248 } | 4265 } |
4249 throw new StateError('failed to evaluate $node'); | 4266 throw new StateError('failed to evaluate $node'); |
4250 } | 4267 } |
4251 return _emitInstanceCreationExpression( | 4268 return _emitInstanceCreationExpression( |
4252 element, type, name, node.argumentList, node.isConst); | 4269 element, type, name, node.argumentList, node.isConst); |
4253 } | 4270 } |
4254 | 4271 |
4255 bool isPrimitiveType(DartType t) => typeRep.isPrimitive(t); | 4272 bool isPrimitiveType(DartType t) => typeRep.isPrimitive(t); |
4256 | 4273 |
| 4274 /// Given a Dart type return the known implementation type, if any. |
| 4275 /// Given `bool`, `String`, or `num`/`int`/`double`, |
| 4276 /// returns the corresponding type in `dart:_interceptors`: |
| 4277 /// `JSBool`, `JSString`, and `JSNumber` respectively, otherwise null. |
| 4278 InterfaceType getImplementationType(DartType t) { |
| 4279 JSType rep = typeRep.typeFor(t); |
| 4280 // Number, String, and Bool are final |
| 4281 if (rep == JSType.jsNumber) return _jsNumber.type; |
| 4282 if (rep == JSType.jsBoolean) return _jsBool.type; |
| 4283 if (rep == JSType.jsString) return _jsString.type; |
| 4284 return null; |
| 4285 } |
| 4286 |
| 4287 JS.Statement nullParameterCheck(JS.Expression param) { |
| 4288 var call = _callHelper('argumentError((#))', [param]); |
| 4289 return js.statement('if (# == null) #;', [param, call]); |
| 4290 } |
| 4291 |
4257 JS.Expression notNull(Expression expr) { | 4292 JS.Expression notNull(Expression expr) { |
4258 if (expr == null) return null; | 4293 if (expr == null) return null; |
4259 var jsExpr = _visit(expr); | 4294 var jsExpr = _visit(expr); |
4260 if (!isNullable(expr)) return jsExpr; | 4295 if (!isNullable(expr)) return jsExpr; |
4261 return _callHelper('notNull(#)', jsExpr); | 4296 return _callHelper('notNull(#)', jsExpr); |
4262 } | 4297 } |
4263 | 4298 |
4264 JS.Expression _emitEqualityOperator(BinaryExpression node, Token op) { | 4299 JS.Expression _emitEqualityOperator(BinaryExpression node, Token op) { |
4265 var left = node.leftOperand; | 4300 var left = node.leftOperand; |
4266 var right = node.rightOperand; | 4301 var right = node.rightOperand; |
(...skipping 842 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5109 return new JS.Do(_visitScope(node.body), _visitTest(node.condition)); | 5144 return new JS.Do(_visitScope(node.body), _visitTest(node.condition)); |
5110 } | 5145 } |
5111 | 5146 |
5112 @override | 5147 @override |
5113 JS.Statement visitForEachStatement(ForEachStatement node) { | 5148 JS.Statement visitForEachStatement(ForEachStatement node) { |
5114 if (node.awaitKeyword != null) { | 5149 if (node.awaitKeyword != null) { |
5115 return _emitAwaitFor(node); | 5150 return _emitAwaitFor(node); |
5116 } | 5151 } |
5117 | 5152 |
5118 var init = _visit(node.identifier); | 5153 var init = _visit(node.identifier); |
| 5154 var iterable = _visit(node.iterable); |
| 5155 var body = _visitScope(node.body); |
5119 if (init == null) { | 5156 if (init == null) { |
5120 init = js.call('let #', node.loopVariable.identifier.name); | 5157 var name = node.loopVariable.identifier.name; |
| 5158 init = js.call('let #', name); |
| 5159 if (_annotatedNullCheck(node.loopVariable.element)) { |
| 5160 body = |
| 5161 new JS.Block([nullParameterCheck(new JS.Identifier(name)), body]); |
| 5162 } |
5121 } | 5163 } |
5122 return new JS.ForOf(init, _visit(node.iterable), _visitScope(node.body)); | 5164 return new JS.ForOf(init, iterable, body); |
5123 } | 5165 } |
5124 | 5166 |
5125 JS.Statement _emitAwaitFor(ForEachStatement node) { | 5167 JS.Statement _emitAwaitFor(ForEachStatement node) { |
5126 // Emits `await for (var value in stream) ...`, which desugars as: | 5168 // Emits `await for (var value in stream) ...`, which desugars as: |
5127 // | 5169 // |
5128 // var iter = new StreamIterator(stream); | 5170 // var iter = new StreamIterator(stream); |
5129 // try { | 5171 // try { |
5130 // while (await iter.moveNext()) { | 5172 // while (await iter.moveNext()) { |
5131 // var value = iter.current; | 5173 // var value = iter.current; |
5132 // ... | 5174 // ... |
(...skipping 859 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5992 if (targetIdentifier.staticElement is! PrefixElement) return false; | 6034 if (targetIdentifier.staticElement is! PrefixElement) return false; |
5993 var prefix = targetIdentifier.staticElement as PrefixElement; | 6035 var prefix = targetIdentifier.staticElement as PrefixElement; |
5994 | 6036 |
5995 // The library the prefix is referring to must come from a deferred import. | 6037 // The library the prefix is referring to must come from a deferred import. |
5996 var containingLibrary = resolutionMap | 6038 var containingLibrary = resolutionMap |
5997 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) | 6039 .elementDeclaredByCompilationUnit(target.root as CompilationUnit) |
5998 .library; | 6040 .library; |
5999 var imports = containingLibrary.getImportsWithPrefix(prefix); | 6041 var imports = containingLibrary.getImportsWithPrefix(prefix); |
6000 return imports.length == 1 && imports[0].isDeferred; | 6042 return imports.length == 1 && imports[0].isDeferred; |
6001 } | 6043 } |
| 6044 |
| 6045 bool _annotatedNullCheck(Element e) => |
| 6046 e != null && findAnnotation(e, isNullCheckAnnotation) != null; |
OLD | NEW |