| 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 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 304 return 'Symbol'; | 304 return 'Symbol'; |
| 305 } | 305 } |
| 306 | 306 |
| 307 bool isPublic(String name) => !name.startsWith('_'); | 307 bool isPublic(String name) => !name.startsWith('_'); |
| 308 | 308 |
| 309 @override | 309 @override |
| 310 visitAsExpression(AsExpression node) { | 310 visitAsExpression(AsExpression node) { |
| 311 var from = getStaticType(node.expression); | 311 var from = getStaticType(node.expression); |
| 312 var to = node.type.type; | 312 var to = node.type.type; |
| 313 | 313 |
| 314 var fromExpr = _visit(node.expression); |
| 315 |
| 314 // Skip the cast if it's not needed. | 316 // Skip the cast if it's not needed. |
| 315 if (rules.isSubTypeOf(from, to)) return _visit(node.expression); | 317 if (rules.isSubTypeOf(from, to)) return fromExpr; |
| 316 | 318 |
| 317 // All Dart number types map to a JS double. | 319 // All Dart number types map to a JS double. |
| 318 if (rules.isNumType(from) && | 320 if (rules.isNumberInJS(from) && rules.isNumberInJS(to)) { |
| 319 (rules.isIntType(to) || rules.isDoubleType(to))) { | 321 // Make sure to check when converting to int. |
| 320 // TODO(jmesserly): a lot of these checks are meaningless, as people use | 322 if (!rules.isIntType(from) && rules.isIntType(to)) { |
| 321 // `num` to mean "any kind of number" rather than "could be null". | 323 return js.call('dart.asInt(#)', [fromExpr]); |
| 322 // The core libraries especially suffer from this problem, with many of | 324 } |
| 323 // the `num` methods returning `num`. | 325 |
| 324 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { | 326 if (!rules.isNonNullableType(from) && rules.isNonNullableType(to)) { |
| 325 // Converting from a nullable number to a non-nullable number | 327 // Converting from a nullable number to a non-nullable number |
| 326 // only requires a null check. | 328 // only requires a null check. |
| 327 return js.call('dart.notNull(#)', _visit(node.expression)); | 329 // TODO(jmesserly): a lot of these checks are meaningless, as people use |
| 330 // `num` to mean "any kind of number" rather than "could be null". |
| 331 // The core libraries especially suffer from this problem, with many of |
| 332 // the `num` methods returning `num`. |
| 333 return js.call('dart.notNull(#)', fromExpr); |
| 328 } else { | 334 } else { |
| 329 // A no-op in JavaScript. | 335 // A no-op in JavaScript. |
| 330 return _visit(node.expression); | 336 return fromExpr; |
| 331 } | 337 } |
| 332 } | 338 } |
| 333 | 339 |
| 334 return _emitCast(node.expression, to); | 340 return js.call('dart.as(#, #)', [fromExpr, _emitTypeName(to)]); |
| 335 } | 341 } |
| 336 | 342 |
| 337 _emitCast(Expression node, DartType type) => js.call('dart.as(#)', [ | |
| 338 [_visit(node), _emitTypeName(type)] | |
| 339 ]); | |
| 340 | |
| 341 @override | 343 @override |
| 342 visitIsExpression(IsExpression node) { | 344 visitIsExpression(IsExpression node) { |
| 343 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. | 345 // Generate `is` as `dart.is` or `typeof` depending on the RHS type. |
| 344 JS.Expression result; | 346 JS.Expression result; |
| 345 var type = node.type.type; | 347 var type = node.type.type; |
| 346 var lhs = _visit(node.expression); | 348 var lhs = _visit(node.expression); |
| 347 var typeofName = _jsTypeofName(type); | 349 var typeofName = _jsTypeofName(type); |
| 348 if (typeofName != null) { | 350 if (typeofName != null) { |
| 349 result = js.call('typeof # == #', [lhs, js.string(typeofName, "'")]); | 351 result = js.call('typeof # == #', [lhs, js.string(typeofName, "'")]); |
| 350 } else { | 352 } else { |
| 351 // Always go through a runtime helper, because implicit interfaces. | 353 // Always go through a runtime helper, because implicit interfaces. |
| 352 result = js.call('dart.is(#, #)', [lhs, _emitTypeName(type)]); | 354 result = js.call('dart.is(#, #)', [lhs, _emitTypeName(type)]); |
| 353 } | 355 } |
| 354 | 356 |
| 355 if (node.notOperator != null) { | 357 if (node.notOperator != null) { |
| 356 return js.call('!#', result); | 358 return js.call('!#', result); |
| 357 } | 359 } |
| 358 return result; | 360 return result; |
| 359 } | 361 } |
| 360 | 362 |
| 361 String _jsTypeofName(DartType t) { | 363 String _jsTypeofName(DartType t) { |
| 362 if (rules.isIntType(t) || rules.isDoubleType(t)) return 'number'; | 364 if (rules.isNumberInJS(t)) return 'number'; |
| 363 if (rules.isStringType(t)) return 'string'; | 365 if (rules.isStringType(t)) return 'string'; |
| 364 if (rules.isBoolType(t)) return 'boolean'; | 366 if (rules.isBoolType(t)) return 'boolean'; |
| 365 return null; | 367 return null; |
| 366 } | 368 } |
| 367 | 369 |
| 368 @override | 370 @override |
| 369 visitFunctionTypeAlias(FunctionTypeAlias node) { | 371 visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 370 var element = node.element; | 372 var element = node.element; |
| 371 var type = element.type; | 373 var type = element.type; |
| 372 var name = element.name; | 374 var name = element.name; |
| (...skipping 1786 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2159 element, type, name, node.argumentList, node.isConst); | 2161 element, type, name, node.argumentList, node.isConst); |
| 2160 } | 2162 } |
| 2161 | 2163 |
| 2162 /// True if this type is built-in to JS, and we use the values unwrapped. | 2164 /// True if this type is built-in to JS, and we use the values unwrapped. |
| 2163 /// For these types we generate a calling convention via static | 2165 /// For these types we generate a calling convention via static |
| 2164 /// "extension methods". This allows types to be extended without adding | 2166 /// "extension methods". This allows types to be extended without adding |
| 2165 /// extensions directly on the prototype. | 2167 /// extensions directly on the prototype. |
| 2166 bool _isJSBuiltinType(DartType t) => | 2168 bool _isJSBuiltinType(DartType t) => |
| 2167 typeIsPrimitiveInJS(t) || rules.isStringType(t); | 2169 typeIsPrimitiveInJS(t) || rules.isStringType(t); |
| 2168 | 2170 |
| 2169 bool typeIsPrimitiveInJS(DartType t) => rules.isIntType(t) || | 2171 bool typeIsPrimitiveInJS(DartType t) => |
| 2170 rules.isDoubleType(t) || | 2172 rules.isNumberInJS(t) || rules.isBoolType(t); |
| 2171 rules.isBoolType(t) || | |
| 2172 rules.isNumType(t); | |
| 2173 | 2173 |
| 2174 bool typeIsNonNullablePrimitiveInJS(DartType t) => | 2174 bool typeIsNonNullablePrimitiveInJS(DartType t) => |
| 2175 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); | 2175 typeIsPrimitiveInJS(t) && rules.isNonNullableType(t); |
| 2176 | 2176 |
| 2177 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => | 2177 bool binaryOperationIsPrimitive(DartType leftT, DartType rightT) => |
| 2178 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); | 2178 typeIsPrimitiveInJS(leftT) && typeIsPrimitiveInJS(rightT); |
| 2179 | 2179 |
| 2180 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); | 2180 bool unaryOperationIsPrimitive(DartType t) => typeIsPrimitiveInJS(t); |
| 2181 | 2181 |
| 2182 bool _isNonNullableExpression(Expression expr) { | 2182 bool _isNonNullableExpression(Expression expr) { |
| (...skipping 1073 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3256 class JSGenerator extends CodeGenerator { | 3256 class JSGenerator extends CodeGenerator { |
| 3257 final _extensionTypes = new HashSet<ClassElement>(); | 3257 final _extensionTypes = new HashSet<ClassElement>(); |
| 3258 | 3258 |
| 3259 JSGenerator(AbstractCompiler compiler) : super(compiler) { | 3259 JSGenerator(AbstractCompiler compiler) : super(compiler) { |
| 3260 // TODO(jacobr): determine the the set of types with extension methods from | 3260 // TODO(jacobr): determine the the set of types with extension methods from |
| 3261 // the annotations rather than hard coding the list once the analyzer | 3261 // the annotations rather than hard coding the list once the analyzer |
| 3262 // supports summaries. | 3262 // supports summaries. |
| 3263 var context = compiler.context; | 3263 var context = compiler.context; |
| 3264 var src = context.sourceFactory.forUri('dart:_interceptors'); | 3264 var src = context.sourceFactory.forUri('dart:_interceptors'); |
| 3265 var interceptors = context.computeLibraryElement(src); | 3265 var interceptors = context.computeLibraryElement(src); |
| 3266 for (var t in ['JSArray', 'JSString', 'JSInt', 'JSDouble', 'JSBool']) { | 3266 for (var t in ['JSArray', 'JSString', 'JSNumber', 'JSBool']) { |
| 3267 _addExtensionType(interceptors.getType(t).type); | 3267 _addExtensionType(interceptors.getType(t).type); |
| 3268 } | 3268 } |
| 3269 // TODO(jmesserly): manually add `int` and `double` |
| 3270 // Unfortunately our current analyzer rejects "implements int". |
| 3271 // Fix was landed, so we can remove this hack once we're updated: |
| 3272 // https://github.com/dart-lang/sdk/commit/d7cd11f86a02f55269fc8d9843e7758eb
eeb81c8 |
| 3273 _addExtensionType(context.typeProvider.intType); |
| 3274 _addExtensionType(context.typeProvider.doubleType); |
| 3269 } | 3275 } |
| 3270 | 3276 |
| 3271 void _addExtensionType(InterfaceType t) { | 3277 void _addExtensionType(InterfaceType t) { |
| 3272 if (t.isObject || !_extensionTypes.add(t.element)) return; | 3278 if (t.isObject || !_extensionTypes.add(t.element)) return; |
| 3273 t = fillDynamicTypeArgs(t, rules.provider) as InterfaceType; | 3279 t = fillDynamicTypeArgs(t, rules.provider) as InterfaceType; |
| 3274 t.interfaces.forEach(_addExtensionType); | 3280 t.interfaces.forEach(_addExtensionType); |
| 3275 t.mixins.forEach(_addExtensionType); | 3281 t.mixins.forEach(_addExtensionType); |
| 3276 _addExtensionType(t.superclass); | 3282 _addExtensionType(t.superclass); |
| 3277 } | 3283 } |
| 3278 | 3284 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3315 | 3321 |
| 3316 /// A special kind of element created by the compiler, signifying a temporary | 3322 /// A special kind of element created by the compiler, signifying a temporary |
| 3317 /// variable. These objects use instance equality, and should be shared | 3323 /// variable. These objects use instance equality, and should be shared |
| 3318 /// everywhere in the tree where they are treated as the same variable. | 3324 /// everywhere in the tree where they are treated as the same variable. |
| 3319 class TemporaryVariableElement extends LocalVariableElementImpl { | 3325 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 3320 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3326 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 3321 | 3327 |
| 3322 int get hashCode => identityHashCode(this); | 3328 int get hashCode => identityHashCode(this); |
| 3323 bool operator ==(Object other) => identical(this, other); | 3329 bool operator ==(Object other) => identical(this, other); |
| 3324 } | 3330 } |
| OLD | NEW |