| 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 library dev_compiler.src.codegen.js_codegen; | 5 library dev_compiler.src.codegen.js_codegen; |
| 6 | 6 |
| 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; | 7 import 'dart:collection' show HashSet, HashMap, SplayTreeSet; |
| 8 | 8 |
| 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; | 9 import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
| 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; | 10 import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 84 final _temps = new HashMap<Element, JS.TemporaryId>(); | 84 final _temps = new HashMap<Element, JS.TemporaryId>(); |
| 85 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); | 85 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); |
| 86 | 86 |
| 87 /// The name for the library's exports inside itself. | 87 /// The name for the library's exports inside itself. |
| 88 /// `exports` was chosen as the most similar to ES module patterns. | 88 /// `exports` was chosen as the most similar to ES module patterns. |
| 89 final _dartxVar = new JS.Identifier('dartx'); | 89 final _dartxVar = new JS.Identifier('dartx'); |
| 90 final _exportsVar = new JS.TemporaryId('exports'); | 90 final _exportsVar = new JS.TemporaryId('exports'); |
| 91 final _runtimeLibVar = new JS.Identifier('dart'); | 91 final _runtimeLibVar = new JS.Identifier('dart'); |
| 92 final _namedArgTemp = new JS.TemporaryId('opts'); | 92 final _namedArgTemp = new JS.TemporaryId('opts'); |
| 93 | 93 |
| 94 final TypeProvider _types; |
| 95 |
| 94 ConstFieldVisitor _constField; | 96 ConstFieldVisitor _constField; |
| 95 | 97 |
| 96 ModuleItemLoadOrder _loader; | 98 ModuleItemLoadOrder _loader; |
| 97 | 99 |
| 98 /// _interceptors.JSArray<E>, used for List literals. | 100 /// _interceptors.JSArray<E>, used for List literals. |
| 99 ClassElement _jsArray; | 101 ClassElement _jsArray; |
| 100 | 102 |
| 101 /// The default value of the module object. See [visitLibraryDirective]. | 103 /// The default value of the module object. See [visitLibraryDirective]. |
| 102 String _jsModuleValue; | 104 String _jsModuleValue; |
| 103 | 105 |
| 104 Map<String, DartType> _objectMembers; | 106 Map<String, DartType> _objectMembers; |
| 105 | 107 |
| 106 JSCodegenVisitor(AbstractCompiler compiler, this.currentLibrary, | 108 JSCodegenVisitor(AbstractCompiler compiler, this.currentLibrary, |
| 107 this._extensionTypes, this._fieldsNeedingStorage) | 109 this._extensionTypes, this._fieldsNeedingStorage) |
| 108 : compiler = compiler, | 110 : compiler = compiler, |
| 109 options = compiler.options.codegenOptions, | 111 options = compiler.options.codegenOptions, |
| 110 rules = compiler.rules { | 112 rules = compiler.rules, |
| 113 _types = compiler.context.typeProvider { |
| 111 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 114 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
| 112 | 115 |
| 113 var context = compiler.context; | 116 var context = compiler.context; |
| 114 var src = context.sourceFactory.forUri('dart:_interceptors'); | 117 var src = context.sourceFactory.forUri('dart:_interceptors'); |
| 115 var interceptors = context.computeLibraryElement(src); | 118 var interceptors = context.computeLibraryElement(src); |
| 116 _jsArray = interceptors.getType('JSArray'); | 119 _jsArray = interceptors.getType('JSArray'); |
| 117 | 120 |
| 118 _objectMembers = getObjectMemberMap(types); | 121 _objectMembers = getObjectMemberMap(types); |
| 119 } | 122 } |
| 120 | 123 |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 309 visitAsExpression(AsExpression node) { | 312 visitAsExpression(AsExpression node) { |
| 310 var from = getStaticType(node.expression); | 313 var from = getStaticType(node.expression); |
| 311 var to = node.type.type; | 314 var to = node.type.type; |
| 312 | 315 |
| 313 var fromExpr = _visit(node.expression); | 316 var fromExpr = _visit(node.expression); |
| 314 | 317 |
| 315 // Skip the cast if it's not needed. | 318 // Skip the cast if it's not needed. |
| 316 if (rules.isSubTypeOf(from, to)) return fromExpr; | 319 if (rules.isSubTypeOf(from, to)) return fromExpr; |
| 317 | 320 |
| 318 // All Dart number types map to a JS double. | 321 // All Dart number types map to a JS double. |
| 319 if (rules.isNumberInJS(from) && rules.isNumberInJS(to)) { | 322 if (_isNumberInJS(from) && _isNumberInJS(to)) { |
| 320 // Make sure to check when converting to int. | 323 // Make sure to check when converting to int. |
| 321 if (!rules.isIntType(from) && rules.isIntType(to)) { | 324 if (from != _types.intType && to == _types.intType) { |
| 322 return js.call('dart.asInt(#)', [fromExpr]); | 325 return js.call('dart.asInt(#)', [fromExpr]); |
| 323 } | 326 } |
| 324 | 327 |
| 325 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { | 328 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { |
| 326 // Converting from a nullable number to a non-nullable number | 329 // Converting from a nullable number to a non-nullable number |
| 327 // only requires a null check. | 330 // only requires a null check. |
| 328 // TODO(jmesserly): a lot of these checks are meaningless, as people use | 331 // TODO(jmesserly): a lot of these checks are meaningless, as people use |
| 329 // `num` to mean "any kind of number" rather than "could be null". | 332 // `num` to mean "any kind of number" rather than "could be null". |
| 330 // The core libraries especially suffer from this problem, with many of | 333 // The core libraries especially suffer from this problem, with many of |
| 331 // the `num` methods returning `num`. | 334 // the `num` methods returning `num`. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 353 result = js.call('dart.is(#, #)', [lhs, _emitTypeName(type)]); | 356 result = js.call('dart.is(#, #)', [lhs, _emitTypeName(type)]); |
| 354 } | 357 } |
| 355 | 358 |
| 356 if (node.notOperator != null) { | 359 if (node.notOperator != null) { |
| 357 return js.call('!#', result); | 360 return js.call('!#', result); |
| 358 } | 361 } |
| 359 return result; | 362 return result; |
| 360 } | 363 } |
| 361 | 364 |
| 362 String _jsTypeofName(DartType t) { | 365 String _jsTypeofName(DartType t) { |
| 363 if (rules.isNumberInJS(t)) return 'number'; | 366 if (_isNumberInJS(t)) return 'number'; |
| 364 if (rules.isStringType(t)) return 'string'; | 367 if (t == _types.stringType) return 'string'; |
| 365 if (rules.isBoolType(t)) return 'boolean'; | 368 if (t == _types.boolType) return 'boolean'; |
| 366 return null; | 369 return null; |
| 367 } | 370 } |
| 368 | 371 |
| 369 @override | 372 @override |
| 370 visitFunctionTypeAlias(FunctionTypeAlias node) { | 373 visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 371 var element = node.element; | 374 var element = node.element; |
| 372 var type = element.type; | 375 var type = element.type; |
| 373 var name = element.name; | 376 var name = element.name; |
| 374 | 377 |
| 375 var fnType = annotateTypeDef( | 378 var fnType = annotateTypeDef( |
| (...skipping 1784 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2160 var type = constructor.type.type; | 2163 var type = constructor.type.type; |
| 2161 return _emitInstanceCreationExpression( | 2164 return _emitInstanceCreationExpression( |
| 2162 element, type, name, node.argumentList, node.isConst); | 2165 element, type, name, node.argumentList, node.isConst); |
| 2163 } | 2166 } |
| 2164 | 2167 |
| 2165 /// True if this type is built-in to JS, and we use the values unwrapped. | 2168 /// True if this type is built-in to JS, and we use the values unwrapped. |
| 2166 /// For these types we generate a calling convention via static | 2169 /// For these types we generate a calling convention via static |
| 2167 /// "extension methods". This allows types to be extended without adding | 2170 /// "extension methods". This allows types to be extended without adding |
| 2168 /// extensions directly on the prototype. | 2171 /// extensions directly on the prototype. |
| 2169 bool _isJSBuiltinType(DartType t) => | 2172 bool _isJSBuiltinType(DartType t) => |
| 2170 typeIsPrimitiveInJS(t) || rules.isStringType(t); | 2173 typeIsPrimitiveInJS(t) || t == _types.stringType; |
| 2171 | 2174 |
| 2172 bool typeIsPrimitiveInJS(DartType t) => | 2175 bool typeIsPrimitiveInJS(DartType t) => |
| 2173 rules.isNumberInJS(t) || rules.isBoolType(t); | 2176 _isNumberInJS(t) || t == _types.boolType; |
| 2174 | 2177 |
| 2175 bool typeIsNonNullablePrimitiveInJS(DartType t) => | 2178 bool typeIsNonNullablePrimitiveInJS(DartType t) => |
| 2176 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); | 2179 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); |
| 2177 | 2180 |
| 2178 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 2181 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
| 2179 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 2182 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
| 2180 | 2183 |
| 2181 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 2184 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
| 2182 | 2185 |
| 2183 bool _isNonNullableExpression(Expression expr) { | 2186 bool _isNonNullableExpression(Expression expr) { |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2287 | 2290 |
| 2288 var vars = <String, JS.Expression>{}; | 2291 var vars = <String, JS.Expression>{}; |
| 2289 // Desugar `l ?? r` as `l != null ? l : r` | 2292 // Desugar `l ?? r` as `l != null ? l : r` |
| 2290 var l = _visit(_bindValue(vars, 'l', left, context: left)); | 2293 var l = _visit(_bindValue(vars, 'l', left, context: left)); |
| 2291 return new JS.MetaLet(vars, [ | 2294 return new JS.MetaLet(vars, [ |
| 2292 js.call('# != null ? # : #', [l, l, _visit(right)]) | 2295 js.call('# != null ? # : #', [l, l, _visit(right)]) |
| 2293 ]); | 2296 ]); |
| 2294 } | 2297 } |
| 2295 | 2298 |
| 2296 if (binaryOperationIsPrimitive(leftType, rightType) || | 2299 if (binaryOperationIsPrimitive(leftType, rightType) || |
| 2297 rules.isStringType(leftType) && op.type == TokenType.PLUS) { | 2300 leftType == _types.stringType && op.type == TokenType.PLUS) { |
| 2298 // special cases where we inline the operation | 2301 // special cases where we inline the operation |
| 2299 // these values are assumed to be non-null (determined by the checker) | 2302 // these values are assumed to be non-null (determined by the checker) |
| 2300 // TODO(jmesserly): it would be nice to just inline the method from core, | 2303 // TODO(jmesserly): it would be nice to just inline the method from core, |
| 2301 // instead of special cases here. | 2304 // instead of special cases here. |
| 2302 if (op.type == TokenType.TILDE_SLASH) { | 2305 if (op.type == TokenType.TILDE_SLASH) { |
| 2303 // `a ~/ b` is equivalent to `(a / b).truncate()` | 2306 // `a ~/ b` is equivalent to `(a / b).truncate()` |
| 2304 var div = AstBuilder.binaryExpression(left, '/', right) | 2307 var div = AstBuilder.binaryExpression(left, '/', right) |
| 2305 ..staticType = node.staticType; | 2308 ..staticType = node.staticType; |
| 2306 return _emitSend(div, 'truncate', []); | 2309 return _emitSend(div, 'truncate', []); |
| 2307 } else { | 2310 } else { |
| (...skipping 937 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3245 | 3248 |
| 3246 JS.Node annotateVariable(JS.Node node, VariableElement e) => | 3249 JS.Node annotateVariable(JS.Node node, VariableElement e) => |
| 3247 options.closure && e != null | 3250 options.closure && e != null |
| 3248 ? node.withClosureAnnotation(closureAnnotationForVariable(e)) | 3251 ? node.withClosureAnnotation(closureAnnotationForVariable(e)) |
| 3249 : node; | 3252 : node; |
| 3250 | 3253 |
| 3251 JS.Node annotateTypeDef(JS.Node node, FunctionTypeAliasElement e) => | 3254 JS.Node annotateTypeDef(JS.Node node, FunctionTypeAliasElement e) => |
| 3252 options.closure && e != null | 3255 options.closure && e != null |
| 3253 ? node.withClosureAnnotation(closureAnnotationForTypeDef(e)) | 3256 ? node.withClosureAnnotation(closureAnnotationForTypeDef(e)) |
| 3254 : node; | 3257 : node; |
| 3258 |
| 3259 /// Returns true if this is any kind of object represented by `Number` in JS. |
| 3260 /// |
| 3261 /// In practice, this is 4 types: num, int, double, and JSNumber. |
| 3262 /// |
| 3263 /// JSNumber is the type that actually "implements" all numbers, hence it's |
| 3264 /// a subtype of int and double (and num). It's in our "dart:_interceptors". |
| 3265 bool _isNumberInJS(DartType t) => rules.isSubTypeOf(t, _types.numType); |
| 3255 } | 3266 } |
| 3256 | 3267 |
| 3257 class JSGenerator extends CodeGenerator { | 3268 class JSGenerator extends CodeGenerator { |
| 3258 final _extensionTypes = new HashSet<ClassElement>(); | 3269 final _extensionTypes = new HashSet<ClassElement>(); |
| 3259 | 3270 |
| 3260 JSGenerator(AbstractCompiler compiler) : super(compiler) { | 3271 JSGenerator(AbstractCompiler compiler) : super(compiler) { |
| 3261 // TODO(jacobr): determine the the set of types with extension methods from | 3272 // TODO(jacobr): determine the the set of types with extension methods from |
| 3262 // the annotations rather than hard coding the list once the analyzer | 3273 // the annotations rather than hard coding the list once the analyzer |
| 3263 // supports summaries. | 3274 // supports summaries. |
| 3264 var context = compiler.context; | 3275 var context = compiler.context; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3322 | 3333 |
| 3323 /// A special kind of element created by the compiler, signifying a temporary | 3334 /// A special kind of element created by the compiler, signifying a temporary |
| 3324 /// variable. These objects use instance equality, and should be shared | 3335 /// variable. These objects use instance equality, and should be shared |
| 3325 /// everywhere in the tree where they are treated as the same variable. | 3336 /// everywhere in the tree where they are treated as the same variable. |
| 3326 class TemporaryVariableElement extends LocalVariableElementImpl { | 3337 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 3327 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3338 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 3328 | 3339 |
| 3329 int get hashCode => identityHashCode(this); | 3340 int get hashCode => identityHashCode(this); |
| 3330 bool operator ==(Object other) => identical(this, other); | 3341 bool operator ==(Object other) => identical(this, other); |
| 3331 } | 3342 } |
| OLD | NEW |