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