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; |
| 11 import 'package:analyzer/src/generated/constant.dart'; | 11 import 'package:analyzer/src/generated/constant.dart'; |
| 12 import 'package:analyzer/src/generated/element.dart'; | 12 import 'package:analyzer/src/generated/element.dart'; |
| 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; | 13 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
| 14 import 'package:analyzer/src/generated/scanner.dart' | 14 import 'package:analyzer/src/generated/scanner.dart' |
| 15 show StringToken, Token, TokenType; | 15 show StringToken, Token, TokenType; |
| 16 import 'package:analyzer/src/generated/type_system.dart' | |
| 17 show StrongTypeSystemImpl; | |
| 16 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; | 18 import 'package:analyzer/src/task/dart.dart' show PublicNamespaceBuilder; |
| 17 import 'package:analyzer/src/task/strong/rules.dart'; | |
| 18 | 19 |
| 19 import 'ast_builder.dart' show AstBuilder; | 20 import 'ast_builder.dart' show AstBuilder; |
| 20 import 'reify_coercions.dart' show CoercionReifier, Tuple2; | 21 import 'reify_coercions.dart' show CoercionReifier, Tuple2; |
| 21 | 22 |
| 22 // TODO(jmesserly): import from its own package | 23 // TODO(jmesserly): import from its own package |
| 23 import '../js/js_ast.dart' as JS; | 24 import '../js/js_ast.dart' as JS; |
| 24 import '../js/js_ast.dart' show js; | 25 import '../js/js_ast.dart' show js; |
| 25 | 26 |
| 26 import '../closure/closure_annotator.dart' show ClosureAnnotator; | 27 import '../closure/closure_annotator.dart' show ClosureAnnotator; |
| 27 import '../compiler.dart' show AbstractCompiler; | 28 import '../compiler.dart' show AbstractCompiler; |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 50 const DINDEX = 'dindex'; | 51 const DINDEX = 'dindex'; |
| 51 const DSETINDEX = 'dsetindex'; | 52 const DSETINDEX = 'dsetindex'; |
| 52 const DCALL = 'dcall'; | 53 const DCALL = 'dcall'; |
| 53 const DSEND = 'dsend'; | 54 const DSEND = 'dsend'; |
| 54 | 55 |
| 55 const ListEquality _listEquality = const ListEquality(); | 56 const ListEquality _listEquality = const ListEquality(); |
| 56 | 57 |
| 57 class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { | 58 class JSCodegenVisitor extends GeneralizingAstVisitor with ClosureAnnotator { |
| 58 final AbstractCompiler compiler; | 59 final AbstractCompiler compiler; |
| 59 final CodegenOptions options; | 60 final CodegenOptions options; |
| 60 final TypeRules rules; | |
| 61 final LibraryElement currentLibrary; | 61 final LibraryElement currentLibrary; |
| 62 final StrongTypeSystemImpl rules; | |
| 62 | 63 |
| 63 /// The global extension type table. | 64 /// The global extension type table. |
| 64 final HashSet<ClassElement> _extensionTypes; | 65 final HashSet<ClassElement> _extensionTypes; |
| 65 | 66 |
| 66 /// Information that is precomputed for this library, indicates which fields | 67 /// Information that is precomputed for this library, indicates which fields |
| 67 /// need storage slots. | 68 /// need storage slots. |
| 68 final HashSet<FieldElement> _fieldsNeedingStorage; | 69 final HashSet<FieldElement> _fieldsNeedingStorage; |
| 69 | 70 |
| 70 /// The variable for the target of the current `..` cascade expression. | 71 /// The variable for the target of the current `..` cascade expression. |
| 71 /// | 72 /// |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 103 ModuleItemLoadOrder _loader; | 104 ModuleItemLoadOrder _loader; |
| 104 | 105 |
| 105 /// _interceptors.JSArray<E>, used for List literals. | 106 /// _interceptors.JSArray<E>, used for List literals. |
| 106 ClassElement _jsArray; | 107 ClassElement _jsArray; |
| 107 | 108 |
| 108 /// The default value of the module object. See [visitLibraryDirective]. | 109 /// The default value of the module object. See [visitLibraryDirective]. |
| 109 String _jsModuleValue; | 110 String _jsModuleValue; |
| 110 | 111 |
| 111 bool _isDartUtils; | 112 bool _isDartUtils; |
| 112 | 113 |
| 113 Map<String, DartType> _objectMembers; | |
| 114 | |
| 115 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, | 114 JSCodegenVisitor(AbstractCompiler compiler, this.rules, this.currentLibrary, |
| 116 this._extensionTypes, this._fieldsNeedingStorage) | 115 this._extensionTypes, this._fieldsNeedingStorage) |
| 117 : compiler = compiler, | 116 : compiler = compiler, |
| 118 options = compiler.options.codegenOptions, | 117 options = compiler.options.codegenOptions, |
| 119 _types = compiler.context.typeProvider { | 118 _types = compiler.context.typeProvider { |
| 120 _loader = new ModuleItemLoadOrder(_emitModuleItem); | 119 _loader = new ModuleItemLoadOrder(_emitModuleItem); |
| 121 | 120 |
| 122 var context = compiler.context; | 121 var context = compiler.context; |
| 123 var src = context.sourceFactory.forUri('dart:_interceptors'); | 122 var src = context.sourceFactory.forUri('dart:_interceptors'); |
| 124 var interceptors = context.computeLibraryElement(src); | 123 var interceptors = context.computeLibraryElement(src); |
| 125 _jsArray = interceptors.getType('JSArray'); | 124 _jsArray = interceptors.getType('JSArray'); |
| 126 _isDartUtils = currentLibrary.source.uri.toString() == 'dart:_utils'; | 125 _isDartUtils = currentLibrary.source.uri.toString() == 'dart:_utils'; |
| 127 | |
| 128 _objectMembers = getObjectMemberMap(types); | |
| 129 } | 126 } |
| 130 | 127 |
| 131 TypeProvider get types => rules.provider; | 128 TypeProvider get types => _types; |
| 132 | 129 |
| 133 JS.Program emitLibrary(LibraryUnit library) { | 130 JS.Program emitLibrary(LibraryUnit library) { |
| 134 // Modify the AST to make coercions explicit. | 131 // Modify the AST to make coercions explicit. |
| 135 new CoercionReifier(library, rules).reify(); | 132 new CoercionReifier(library, rules).reify(); |
| 136 | 133 |
| 137 // Build the public namespace for this library. This allows us to do | 134 // Build the public namespace for this library. This allows us to do |
| 138 // constant time lookups (contrast with `Element.getChild(name)`). | 135 // constant time lookups (contrast with `Element.getChild(name)`). |
| 139 if (currentLibrary.publicNamespace == null) { | 136 if (currentLibrary.publicNamespace == null) { |
| 140 (currentLibrary as LibraryElementImpl).publicNamespace = | 137 (currentLibrary as LibraryElementImpl).publicNamespace = |
| 141 new PublicNamespaceBuilder().build(currentLibrary); | 138 new PublicNamespaceBuilder().build(currentLibrary); |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 326 bool isPublic(String name) => !name.startsWith('_'); | 323 bool isPublic(String name) => !name.startsWith('_'); |
| 327 | 324 |
| 328 @override | 325 @override |
| 329 visitAsExpression(AsExpression node) { | 326 visitAsExpression(AsExpression node) { |
| 330 var from = getStaticType(node.expression); | 327 var from = getStaticType(node.expression); |
| 331 var to = node.type.type; | 328 var to = node.type.type; |
| 332 | 329 |
| 333 var fromExpr = _visit(node.expression); | 330 var fromExpr = _visit(node.expression); |
| 334 | 331 |
| 335 // Skip the cast if it's not needed. | 332 // Skip the cast if it's not needed. |
| 336 if (rules.isSubTypeOf(from, to)) return fromExpr; | 333 if (rules.isSubtypeOf(from, to)) return fromExpr; |
| 337 | 334 |
| 338 // All Dart number types map to a JS double. | 335 // All Dart number types map to a JS double. |
| 339 if (_isNumberInJS(from) && _isNumberInJS(to)) { | 336 if (_isNumberInJS(from) && _isNumberInJS(to)) { |
| 340 // Make sure to check when converting to int. | 337 // Make sure to check when converting to int. |
| 341 if (from != _types.intType && to == _types.intType) { | 338 if (from != _types.intType && to == _types.intType) { |
| 342 return js.call('dart.asInt(#)', [fromExpr]); | 339 return js.call('dart.asInt(#)', [fromExpr]); |
| 343 } | 340 } |
| 344 | 341 |
| 345 // A no-op in JavaScript. | 342 // A no-op in JavaScript. |
| 346 return fromExpr; | 343 return fromExpr; |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 537 } | 534 } |
| 538 | 535 |
| 539 /// Given a class element and body, complete the class declaration. | 536 /// Given a class element and body, complete the class declaration. |
| 540 /// This handles generic type parameters, laziness (in library-cycle cases), | 537 /// This handles generic type parameters, laziness (in library-cycle cases), |
| 541 /// and ensuring dependencies are loaded first. | 538 /// and ensuring dependencies are loaded first. |
| 542 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { | 539 JS.Statement _finishClassDef(ParameterizedType type, JS.Statement body) { |
| 543 var name = type.name; | 540 var name = type.name; |
| 544 var genericName = '$name\$'; | 541 var genericName = '$name\$'; |
| 545 | 542 |
| 546 JS.Statement genericDef = null; | 543 JS.Statement genericDef = null; |
| 547 if (type.typeParameters.isNotEmpty) { | 544 if (_boundTypeParametersOf(type).isNotEmpty) { |
| 548 genericDef = _emitGenericClassDef(type, body); | 545 genericDef = _emitGenericClassDef(type, body); |
| 549 } | 546 } |
| 550 | |
| 551 // The base class and all mixins must be declared before this class. | 547 // The base class and all mixins must be declared before this class. |
| 552 if (!_loader.isLoaded(type.element)) { | 548 if (!_loader.isLoaded(type.element)) { |
| 553 // TODO(jmesserly): the lazy class def is a simple solution for now. | 549 // TODO(jmesserly): the lazy class def is a simple solution for now. |
| 554 // We may want to consider other options in the future. | 550 // We may want to consider other options in the future. |
| 555 | 551 |
| 556 if (genericDef != null) { | 552 if (genericDef != null) { |
| 557 return js.statement( | 553 return js.statement( |
| 558 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', | 554 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', |
| 559 [genericDef, _exportsVar, _propertyName(name), genericName]); | 555 [genericDef, _exportsVar, _propertyName(name), genericName]); |
| 560 } | 556 } |
| 561 | 557 |
| 562 return js.statement( | 558 return js.statement( |
| 563 'dart.defineLazyClass(#, { get #() { #; return #; } });', | 559 'dart.defineLazyClass(#, { get #() { #; return #; } });', |
| 564 [_exportsVar, _propertyName(name), body, name]); | 560 [_exportsVar, _propertyName(name), body, name]); |
| 565 } | 561 } |
| 566 | 562 |
| 567 if (isPublic(name)) _addExport(name); | 563 if (isPublic(name)) _addExport(name); |
| 568 | 564 |
| 569 if (genericDef != null) { | 565 if (genericDef != null) { |
| 570 var dynType = fillDynamicTypeArgs(type, types); | 566 var dynType = fillDynamicTypeArgs(type, types); |
| 571 var genericInst = _emitTypeName(dynType, lowerGeneric: true); | 567 var genericInst = _emitTypeName(dynType, lowerGeneric: true); |
| 572 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | 568 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); |
| 573 } | 569 } |
| 574 return body; | 570 return body; |
| 575 } | 571 } |
| 576 | 572 |
| 577 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { | 573 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { |
| 578 var name = type.name; | 574 var name = type.name; |
| 579 var genericName = '$name\$'; | 575 var genericName = '$name\$'; |
| 580 var typeParams = type.typeParameters.map((p) => p.name); | 576 var typeParams = _boundTypeParametersOf(type).map((p) => p.name); |
| 581 if (isPublic(name)) _exports.add(genericName); | 577 if (isPublic(name)) _exports.add(genericName); |
| 582 return js.statement('const # = dart.generic(function(#) { #; return #; });', | 578 return js.statement('const # = dart.generic(function(#) { #; return #; });', |
| 583 [genericName, typeParams, body, name]); | 579 [genericName, typeParams, body, name]); |
| 584 } | 580 } |
| 585 | 581 |
| 586 final _hasDeferredSupertype = new HashSet<ClassElement>(); | 582 final _hasDeferredSupertype = new HashSet<ClassElement>(); |
| 587 | 583 |
| 588 bool _deferIfNeeded(DartType type, ClassElement current) { | 584 bool _deferIfNeeded(DartType type, ClassElement current) { |
| 589 if (type is ParameterizedType) { | 585 if (type is ParameterizedType) { |
| 590 var typeArguments = type.typeArguments; | 586 var typeArguments = type.typeArguments; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 604 if (type.isObject) return null; | 600 if (type.isObject) return null; |
| 605 | 601 |
| 606 // Assume we can load eagerly, until proven otherwise. | 602 // Assume we can load eagerly, until proven otherwise. |
| 607 _loader.startTopLevel(element); | 603 _loader.startTopLevel(element); |
| 608 | 604 |
| 609 // Find the super type | 605 // Find the super type |
| 610 JS.Expression heritage; | 606 JS.Expression heritage; |
| 611 var supertype = type.superclass; | 607 var supertype = type.superclass; |
| 612 if (_deferIfNeeded(supertype, element)) { | 608 if (_deferIfNeeded(supertype, element)) { |
| 613 // Fall back to raw type. | 609 // Fall back to raw type. |
| 614 supertype = fillDynamicTypeArgs(supertype.element.type, rules.provider); | 610 supertype = fillDynamicTypeArgs(supertype.element.type, _types); |
| 615 _hasDeferredSupertype.add(element); | 611 _hasDeferredSupertype.add(element); |
| 616 } | 612 } |
| 617 heritage = _emitTypeName(supertype); | 613 heritage = _emitTypeName(supertype); |
| 618 | 614 |
| 619 if (type.mixins.isNotEmpty) { | 615 if (type.mixins.isNotEmpty) { |
| 620 var mixins = type.mixins.map(_emitTypeName).toList(); | 616 var mixins = type.mixins.map(_emitTypeName).toList(); |
| 621 mixins.insert(0, heritage); | 617 mixins.insert(0, heritage); |
| 622 heritage = js.call('dart.mixin(#)', [mixins]); | 618 heritage = js.call('dart.mixin(#)', [mixins]); |
| 623 } | 619 } |
| 624 | 620 |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 781 { | 777 { |
| 782 var tStatics = <JS.Property>[]; | 778 var tStatics = <JS.Property>[]; |
| 783 var tMethods = <JS.Property>[]; | 779 var tMethods = <JS.Property>[]; |
| 784 var sNames = <JS.Expression>[]; | 780 var sNames = <JS.Expression>[]; |
| 785 for (MethodDeclaration node in methods) { | 781 for (MethodDeclaration node in methods) { |
| 786 if (!(node.isSetter || node.isGetter || node.isAbstract)) { | 782 if (!(node.isSetter || node.isGetter || node.isAbstract)) { |
| 787 var name = node.name.name; | 783 var name = node.name.name; |
| 788 var element = node.element; | 784 var element = node.element; |
| 789 var inheritedElement = | 785 var inheritedElement = |
| 790 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); | 786 classElem.lookUpInheritedConcreteMethod(name, currentLibrary); |
| 791 if (inheritedElement != null && | 787 if (inheritedElement != null && inheritedElement.type == element.type) |
| 792 inheritedElement.type == element.type) continue; | 788 continue; |
| 793 var memberName = _elementMemberName(element); | 789 var memberName = _elementMemberName(element); |
| 794 var parts = _emitFunctionTypeParts(element.type); | 790 var parts = _emitFunctionTypeParts(element.type); |
| 795 var property = | 791 var property = |
| 796 new JS.Property(memberName, new JS.ArrayInitializer(parts)); | 792 new JS.Property(memberName, new JS.ArrayInitializer(parts)); |
| 797 if (node.isStatic) { | 793 if (node.isStatic) { |
| 798 tStatics.add(property); | 794 tStatics.add(property); |
| 799 sNames.add(memberName); | 795 sNames.add(memberName); |
| 800 } else { | 796 } else { |
| 801 tMethods.add(property); | 797 tMethods.add(property); |
| 802 } | 798 } |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 914 /// `C() : super() {}`. | 910 /// `C() : super() {}`. |
| 915 JS.Method _emitImplicitConstructor( | 911 JS.Method _emitImplicitConstructor( |
| 916 ClassDeclaration node, List<FieldDeclaration> fields) { | 912 ClassDeclaration node, List<FieldDeclaration> fields) { |
| 917 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); | 913 assert(_hasUnnamedConstructor(node.element) == fields.isNotEmpty); |
| 918 | 914 |
| 919 // If we don't have a method body, skip this. | 915 // If we don't have a method body, skip this. |
| 920 var superCall = _superConstructorCall(node.element); | 916 var superCall = _superConstructorCall(node.element); |
| 921 if (fields.isEmpty && superCall == null) return null; | 917 if (fields.isEmpty && superCall == null) return null; |
| 922 | 918 |
| 923 dynamic body = _initializeFields(node, fields); | 919 dynamic body = _initializeFields(node, fields); |
| 924 if (superCall != null) body = [ | 920 if (superCall != null) |
| 921 body = [ | |
| 925 [body, superCall] | 922 [body, superCall] |
| 926 ]; | 923 ]; |
| 927 var name = _constructorName(node.element.unnamedConstructor); | 924 var name = _constructorName(node.element.unnamedConstructor); |
| 928 return annotateDefaultConstructor( | 925 return annotateDefaultConstructor( |
| 929 new JS.Method(name, js.call('function() { #; }', body) as JS.Fun), | 926 new JS.Method(name, js.call('function() { #; }', body) as JS.Fun), |
| 930 node.element); | 927 node.element); |
| 931 } | 928 } |
| 932 | 929 |
| 933 JS.Method _emitConstructor(ConstructorDeclaration node, InterfaceType type, | 930 JS.Method _emitConstructor(ConstructorDeclaration node, InterfaceType type, |
| 934 List<FieldDeclaration> fields, bool isObject) { | 931 List<FieldDeclaration> fields, bool isObject) { |
| (...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1228 ])); | 1225 ])); |
| 1229 } | 1226 } |
| 1230 } else if (param.kind == ParameterKind.POSITIONAL) { | 1227 } else if (param.kind == ParameterKind.POSITIONAL) { |
| 1231 body.add(js.statement('if (# === void 0) # = #;', | 1228 body.add(js.statement('if (# === void 0) # = #;', |
| 1232 [jsParam, jsParam, _defaultParamValue(param)])); | 1229 [jsParam, jsParam, _defaultParamValue(param)])); |
| 1233 } | 1230 } |
| 1234 | 1231 |
| 1235 // TODO(jmesserly): various problems here, see: | 1232 // TODO(jmesserly): various problems here, see: |
| 1236 // https://github.com/dart-lang/dev_compiler/issues/161 | 1233 // https://github.com/dart-lang/dev_compiler/issues/161 |
| 1237 var paramType = param.element.type; | 1234 var paramType = param.element.type; |
| 1238 if (!constructor && _hasTypeParameter(paramType)) { | 1235 if (!constructor && _hasUnsoundTypeParameter(paramType)) { |
| 1239 body.add(js.statement( | 1236 body.add(js |
| 1240 'dart.as(#, #);', [jsParam, _emitTypeName(paramType)])); | 1237 .statement('dart.as(#, #);', [jsParam, _emitTypeName(paramType)])); |
| 1241 } | 1238 } |
| 1242 } | 1239 } |
| 1243 return body.isEmpty ? null : _statement(body); | 1240 return body.isEmpty ? null : _statement(body); |
| 1244 } | 1241 } |
| 1245 | 1242 |
| 1246 bool _hasTypeParameter(DartType t) => t is TypeParameterType || | 1243 bool _isUnsoundTypeParameter(DartType t) => |
| 1247 t is ParameterizedType && t.typeArguments.any(_hasTypeParameter); | 1244 t is TypeParameterType && t.element.enclosingElement is ClassElement; |
| 1245 | |
| 1246 bool _hasUnsoundTypeParameter(DartType t) => | |
| 1247 _isUnsoundTypeParameter(t) || | |
| 1248 t is ParameterizedType && t.typeArguments.any(_hasUnsoundTypeParameter); | |
| 1248 | 1249 |
| 1249 JS.Expression _defaultParamValue(FormalParameter param) { | 1250 JS.Expression _defaultParamValue(FormalParameter param) { |
| 1250 if (param is DefaultFormalParameter && param.defaultValue != null) { | 1251 if (param is DefaultFormalParameter && param.defaultValue != null) { |
| 1251 return _visit(param.defaultValue); | 1252 return _visit(param.defaultValue); |
| 1252 } else { | 1253 } else { |
| 1253 return new JS.LiteralNull(); | 1254 return new JS.LiteralNull(); |
| 1254 } | 1255 } |
| 1255 } | 1256 } |
| 1256 | 1257 |
| 1257 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { | 1258 JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1372 | 1373 |
| 1373 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { | 1374 JS.Method _emitTopLevelProperty(FunctionDeclaration node) { |
| 1374 var name = node.name.name; | 1375 var name = node.name.name; |
| 1375 return annotate( | 1376 return annotate( |
| 1376 new JS.Method(_propertyName(name), _visit(node.functionExpression), | 1377 new JS.Method(_propertyName(name), _visit(node.functionExpression), |
| 1377 isGetter: node.isGetter, isSetter: node.isSetter), | 1378 isGetter: node.isGetter, isSetter: node.isSetter), |
| 1378 node.element); | 1379 node.element); |
| 1379 } | 1380 } |
| 1380 | 1381 |
| 1381 bool _executesAtTopLevel(AstNode node) { | 1382 bool _executesAtTopLevel(AstNode node) { |
| 1382 var ancestor = node.getAncestor((n) => n is FunctionBody || | 1383 var ancestor = node.getAncestor((n) => |
| 1384 n is FunctionBody || | |
| 1383 (n is FieldDeclaration && n.staticKeyword == null) || | 1385 (n is FieldDeclaration && n.staticKeyword == null) || |
| 1384 (n is ConstructorDeclaration && n.constKeyword == null)); | 1386 (n is ConstructorDeclaration && n.constKeyword == null)); |
| 1385 return ancestor == null; | 1387 return ancestor == null; |
| 1386 } | 1388 } |
| 1387 | 1389 |
| 1388 bool _typeIsLoaded(DartType type) { | 1390 bool _typeIsLoaded(DartType type) { |
| 1389 if (type is FunctionType && (type.name == '' || type.name == null)) { | 1391 if (type is FunctionType && (type.name == '' || type.name == null)) { |
| 1390 return (_typeIsLoaded(type.returnType) && | 1392 return (_typeIsLoaded(type.returnType) && |
| 1391 type.optionalParameterTypes.every(_typeIsLoaded) && | 1393 type.optionalParameterTypes.every(_typeIsLoaded) && |
| 1392 type.namedParameterTypes.values.every(_typeIsLoaded) && | 1394 type.namedParameterTypes.values.every(_typeIsLoaded) && |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1513 } else { | 1515 } else { |
| 1514 _asyncStarController = null; | 1516 _asyncStarController = null; |
| 1515 jsParams = params; | 1517 jsParams = params; |
| 1516 } | 1518 } |
| 1517 JS.Expression gen = new JS.Fun(jsParams, _visit(body), isGenerator: true); | 1519 JS.Expression gen = new JS.Fun(jsParams, _visit(body), isGenerator: true); |
| 1518 if (JS.This.foundIn(gen)) { | 1520 if (JS.This.foundIn(gen)) { |
| 1519 gen = js.call('#.bind(this)', gen); | 1521 gen = js.call('#.bind(this)', gen); |
| 1520 } | 1522 } |
| 1521 _asyncStarController = savedController; | 1523 _asyncStarController = savedController; |
| 1522 | 1524 |
| 1523 var T = _emitTypeName(rules.getExpectedReturnType(body)); | 1525 var T = _emitTypeName(_getExpectedReturnType(body)); |
| 1524 return js.call('dart.#(#)', [ | 1526 return js.call('dart.#(#)', [ |
| 1525 kind, | 1527 kind, |
| 1526 [gen, T]..addAll(params) | 1528 [gen, T]..addAll(params) |
| 1527 ]); | 1529 ]); |
| 1528 } | 1530 } |
| 1529 | 1531 |
| 1530 @override | 1532 @override |
| 1531 JS.Statement visitFunctionDeclarationStatement( | 1533 JS.Statement visitFunctionDeclarationStatement( |
| 1532 FunctionDeclarationStatement node) { | 1534 FunctionDeclarationStatement node) { |
| 1533 var func = node.functionDeclaration; | 1535 var func = node.functionDeclaration; |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 1559 var accessor = node.staticElement; | 1561 var accessor = node.staticElement; |
| 1560 if (accessor == null) { | 1562 if (accessor == null) { |
| 1561 return js.commentExpression( | 1563 return js.commentExpression( |
| 1562 'Unimplemented unknown name', new JS.Identifier(node.name)); | 1564 'Unimplemented unknown name', new JS.Identifier(node.name)); |
| 1563 } | 1565 } |
| 1564 | 1566 |
| 1565 // Get the original declaring element. If we had a property accessor, this | 1567 // Get the original declaring element. If we had a property accessor, this |
| 1566 // indirects back to a (possibly synthetic) field. | 1568 // indirects back to a (possibly synthetic) field. |
| 1567 var element = accessor; | 1569 var element = accessor; |
| 1568 if (accessor is PropertyAccessorElement) element = accessor.variable; | 1570 if (accessor is PropertyAccessorElement) element = accessor.variable; |
| 1571 if (accessor is FunctionMember) element = accessor.baseElement; | |
| 1569 | 1572 |
| 1570 _loader.declareBeforeUse(element); | 1573 _loader.declareBeforeUse(element); |
| 1571 | 1574 |
| 1572 var name = element.name; | 1575 var name = element.name; |
| 1573 | 1576 |
| 1574 // type literal | 1577 // type literal |
| 1575 if (element is ClassElement || | 1578 if (element is ClassElement || |
| 1576 element is DynamicElementImpl || | 1579 element is DynamicElementImpl || |
| 1577 element is FunctionTypeAliasElement) { | 1580 element is FunctionTypeAliasElement) { |
| 1578 return _emitTypeName( | 1581 return _emitTypeName( |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1715 | 1718 |
| 1716 // TODO(jmesserly): like constants, should we hoist function types out of | 1719 // TODO(jmesserly): like constants, should we hoist function types out of |
| 1717 // methods? Similar issue with generic types. For all of these, we may want | 1720 // methods? Similar issue with generic types. For all of these, we may want |
| 1718 // to canonicalize them too, at least when inside the same library. | 1721 // to canonicalize them too, at least when inside the same library. |
| 1719 var name = type.name; | 1722 var name = type.name; |
| 1720 var element = type.element; | 1723 var element = type.element; |
| 1721 if (name == '' || name == null || lowerTypedef) { | 1724 if (name == '' || name == null || lowerTypedef) { |
| 1722 var parts = _emitFunctionTypeParts(type as FunctionType); | 1725 var parts = _emitFunctionTypeParts(type as FunctionType); |
| 1723 return js.call('dart.functionType(#)', [parts]); | 1726 return js.call('dart.functionType(#)', [parts]); |
| 1724 } | 1727 } |
| 1728 // For now, reify generic method parameters as dynamic | |
| 1729 bool _isGenericTypeParameter(DartType type) => | |
| 1730 (type is TypeParameterType) && | |
| 1731 !(type.element.enclosingElement is ClassElement || | |
| 1732 type.element.enclosingElement is FunctionTypeAliasElement); | |
| 1733 | |
| 1734 if (_isGenericTypeParameter(type)) { | |
| 1735 return js.call('dart.dynamic'); | |
| 1736 } | |
| 1725 | 1737 |
| 1726 if (type is TypeParameterType) { | 1738 if (type is TypeParameterType) { |
| 1727 return new JS.Identifier(name); | 1739 return new JS.Identifier(name); |
| 1728 } | 1740 } |
| 1729 | 1741 |
| 1730 if (type is ParameterizedType) { | 1742 if (type is ParameterizedType) { |
| 1731 var args = type.typeArguments; | 1743 var args = type.typeArguments; |
| 1732 var isCurrentClass = | 1744 var isCurrentClass = |
| 1733 args.isNotEmpty && _loader.isCurrentElement(type.element); | 1745 args.isNotEmpty && _loader.isCurrentElement(type.element); |
| 1734 Iterable jsArgs = null; | 1746 Iterable jsArgs = null; |
| 1735 if (args.any((a) => a != types.dynamicType)) { | 1747 if (args |
| 1748 .any((a) => a != types.dynamicType && !_isGenericTypeParameter(a))) { | |
| 1736 jsArgs = args.map(_emitTypeName); | 1749 jsArgs = args.map(_emitTypeName); |
| 1737 } else if (lowerGeneric || isCurrentClass) { | 1750 } else if (lowerGeneric || isCurrentClass) { |
| 1738 // When creating a `new S<dynamic>` we try and use the raw form | 1751 // When creating a `new S<dynamic>` we try and use the raw form |
| 1739 // `new S()`, but this does not work if we're inside the same class, | 1752 // `new S()`, but this does not work if we're inside the same class, |
| 1740 // because `S` refers to the current S<T> we are generating. | 1753 // because `S` refers to the current S<T> we are generating. |
| 1741 jsArgs = []; | 1754 jsArgs = []; |
| 1742 } | 1755 } |
| 1743 if (jsArgs != null) { | 1756 if (jsArgs != null) { |
| 1744 var genericName = _maybeQualifiedName(element, '$name\$'); | 1757 var genericName = _maybeQualifiedName(element, '$name\$'); |
| 1745 return js.call('#(#)', [genericName, jsArgs]); | 1758 return js.call('#(#)', [genericName, jsArgs]); |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1807 var inc = AstBuilder.binaryExpression(lhs, op, right); | 1820 var inc = AstBuilder.binaryExpression(lhs, op, right); |
| 1808 inc.staticElement = element; | 1821 inc.staticElement = element; |
| 1809 inc.staticType = getStaticType(left); | 1822 inc.staticType = getStaticType(left); |
| 1810 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); | 1823 return new JS.MetaLet(vars, [_emitSet(lhs, inc)]); |
| 1811 } | 1824 } |
| 1812 | 1825 |
| 1813 JS.Expression _emitSet(Expression lhs, Expression rhs) { | 1826 JS.Expression _emitSet(Expression lhs, Expression rhs) { |
| 1814 if (lhs is IndexExpression) { | 1827 if (lhs is IndexExpression) { |
| 1815 var target = _getTarget(lhs); | 1828 var target = _getTarget(lhs); |
| 1816 if (_useNativeJsIndexer(target.staticType)) { | 1829 if (_useNativeJsIndexer(target.staticType)) { |
| 1817 return js.call( | 1830 return js |
| 1818 '#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]); | 1831 .call('#[#] = #', [_visit(target), _visit(lhs.index), _visit(rhs)]); |
| 1819 } | 1832 } |
| 1820 return _emitSend(target, '[]=', [lhs.index, rhs]); | 1833 return _emitSend(target, '[]=', [lhs.index, rhs]); |
| 1821 } | 1834 } |
| 1822 | 1835 |
| 1823 Expression target = null; | 1836 Expression target = null; |
| 1824 SimpleIdentifier id; | 1837 SimpleIdentifier id; |
| 1825 if (lhs is PropertyAccess) { | 1838 if (lhs is PropertyAccess) { |
| 1826 if (lhs.operator.lexeme == '?.') { | 1839 if (lhs.operator.lexeme == '?.') { |
| 1827 return _emitNullSafeSet(lhs, rhs); | 1840 return _emitNullSafeSet(lhs, rhs); |
| 1828 } | 1841 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1894 var result = _emitForeignJS(node); | 1907 var result = _emitForeignJS(node); |
| 1895 if (result != null) return result; | 1908 if (result != null) return result; |
| 1896 | 1909 |
| 1897 String code; | 1910 String code; |
| 1898 if (target == null || isLibraryPrefix(target)) { | 1911 if (target == null || isLibraryPrefix(target)) { |
| 1899 if (DynamicInvoke.get(node.methodName)) { | 1912 if (DynamicInvoke.get(node.methodName)) { |
| 1900 code = 'dart.$DCALL(#, #)'; | 1913 code = 'dart.$DCALL(#, #)'; |
| 1901 } else { | 1914 } else { |
| 1902 code = '#(#)'; | 1915 code = '#(#)'; |
| 1903 } | 1916 } |
| 1904 return js.call( | 1917 return js |
| 1905 code, [_visit(node.methodName), _visit(node.argumentList)]); | 1918 .call(code, [_visit(node.methodName), _visit(node.argumentList)]); |
| 1906 } | 1919 } |
| 1907 | 1920 |
| 1908 var type = getStaticType(target); | 1921 var type = getStaticType(target); |
| 1909 var name = node.methodName.name; | 1922 var name = node.methodName.name; |
| 1910 var element = node.methodName.staticElement; | 1923 var element = node.methodName.staticElement; |
| 1911 bool isStatic = element is ExecutableElement && element.isStatic; | 1924 bool isStatic = element is ExecutableElement && element.isStatic; |
| 1912 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); | 1925 var memberName = _emitMemberName(name, type: type, isStatic: isStatic); |
| 1913 | 1926 |
| 1914 if (DynamicInvoke.get(target)) { | 1927 if (DynamicInvoke.get(target)) { |
| 1915 code = 'dart.$DSEND(#, #, #)'; | 1928 code = 'dart.$DSEND(#, #, #)'; |
| 1916 } else if (DynamicInvoke.get(node.methodName)) { | 1929 } else if (DynamicInvoke.get(node.methodName)) { |
| 1917 // This is a dynamic call to a statically known target. For example: | 1930 // This is a dynamic call to a statically known target. For example: |
| 1918 // class Foo { Function bar; } | 1931 // class Foo { Function bar; } |
| 1919 // new Foo().bar(); // dynamic call | 1932 // new Foo().bar(); // dynamic call |
| 1920 code = 'dart.$DCALL(#.#, #)'; | 1933 code = 'dart.$DCALL(#.#, #)'; |
| 1921 } else if (_requiresStaticDispatch(target, name)) { | 1934 } else if (_requiresStaticDispatch(target, name)) { |
| 1922 assert(rules.objectMembers[name] is FunctionType); | |
| 1923 // Object methods require a helper for null checks. | 1935 // Object methods require a helper for null checks. |
| 1924 return js.call('dart.#(#, #)', | 1936 return js.call('dart.#(#, #)', |
| 1925 [memberName, _visit(target), _visit(node.argumentList)]); | 1937 [memberName, _visit(target), _visit(node.argumentList)]); |
| 1926 } else { | 1938 } else { |
| 1927 code = '#.#(#)'; | 1939 code = '#.#(#)'; |
| 1928 } | 1940 } |
| 1929 | 1941 |
| 1930 return js.call( | 1942 return js |
| 1931 code, [_visit(target), memberName, _visit(node.argumentList)]); | 1943 .call(code, [_visit(target), memberName, _visit(node.argumentList)]); |
| 1932 } | 1944 } |
| 1933 | 1945 |
| 1934 /// Emits code for the `JS(...)` builtin. | 1946 /// Emits code for the `JS(...)` builtin. |
| 1935 _emitForeignJS(MethodInvocation node) { | 1947 _emitForeignJS(MethodInvocation node) { |
| 1936 var e = node.methodName.staticElement; | 1948 var e = node.methodName.staticElement; |
| 1937 if (isInlineJS(e)) { | 1949 if (isInlineJS(e)) { |
| 1938 var args = node.argumentList.arguments; | 1950 var args = node.argumentList.arguments; |
| 1939 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` | 1951 // arg[0] is static return type, used in `RestrictedStaticTypeAnalyzer` |
| 1940 var code = args[1]; | 1952 var code = args[1]; |
| 1941 var templateArgs; | 1953 var templateArgs; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2007 _propertyName(node.name.label.name), _visit(node.expression)); | 2019 _propertyName(node.name.label.name), _visit(node.expression)); |
| 2008 } | 2020 } |
| 2009 | 2021 |
| 2010 bool _isNamedParam(FormalParameter param) => | 2022 bool _isNamedParam(FormalParameter param) => |
| 2011 param.kind == ParameterKind.NAMED; | 2023 param.kind == ParameterKind.NAMED; |
| 2012 | 2024 |
| 2013 /// We cannot destructure named params that clash with JS reserved names: | 2025 /// We cannot destructure named params that clash with JS reserved names: |
| 2014 /// see discussion in https://github.com/dart-lang/dev_compiler/issues/392. | 2026 /// see discussion in https://github.com/dart-lang/dev_compiler/issues/392. |
| 2015 bool _isDestructurableNamedParam(FormalParameter param) => | 2027 bool _isDestructurableNamedParam(FormalParameter param) => |
| 2016 _isNamedParam(param) && | 2028 _isNamedParam(param) && |
| 2017 !invalidVariableName(param.identifier.name) && | 2029 !invalidVariableName(param.identifier.name) && |
| 2018 options.destructureNamedParams; | 2030 options.destructureNamedParams; |
| 2019 | 2031 |
| 2020 @override | 2032 @override |
| 2021 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => | 2033 List<JS.Parameter> visitFormalParameterList(FormalParameterList node) => |
| 2022 _emitFormalParameterList(node); | 2034 _emitFormalParameterList(node); |
| 2023 | 2035 |
| 2024 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, | 2036 List<JS.Parameter> _emitFormalParameterList(FormalParameterList node, |
| 2025 {bool allowDestructuring: true}) { | 2037 {bool allowDestructuring: true}) { |
| 2026 var result = <JS.Parameter>[]; | 2038 var result = <JS.Parameter>[]; |
| 2027 | 2039 |
| 2028 var namedVars = <JS.DestructuredVariable>[]; | 2040 var namedVars = <JS.DestructuredVariable>[]; |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2180 } | 2192 } |
| 2181 | 2193 |
| 2182 @override | 2194 @override |
| 2183 visitVariableDeclaration(VariableDeclaration node) { | 2195 visitVariableDeclaration(VariableDeclaration node) { |
| 2184 if (node.element is PropertyInducingElement) return _emitStaticField(node); | 2196 if (node.element is PropertyInducingElement) return _emitStaticField(node); |
| 2185 | 2197 |
| 2186 var name = new JS.Identifier(node.name.name); | 2198 var name = new JS.Identifier(node.name.name); |
| 2187 return new JS.VariableInitialization(name, _visitInitializer(node)); | 2199 return new JS.VariableInitialization(name, _visitInitializer(node)); |
| 2188 } | 2200 } |
| 2189 | 2201 |
| 2190 bool _isFinalJSDecl(AstNode field) => field is VariableDeclaration && | 2202 bool _isFinalJSDecl(AstNode field) => |
| 2203 field is VariableDeclaration && | |
| 2191 field.isFinal && | 2204 field.isFinal && |
| 2192 _isJSInvocation(field.initializer); | 2205 _isJSInvocation(field.initializer); |
| 2193 | 2206 |
| 2194 /// Emits a static or top-level field. | 2207 /// Emits a static or top-level field. |
| 2195 JS.Statement _emitStaticField(VariableDeclaration field) { | 2208 JS.Statement _emitStaticField(VariableDeclaration field) { |
| 2196 PropertyInducingElement element = field.element; | 2209 PropertyInducingElement element = field.element; |
| 2197 assert(element.isStatic); | 2210 assert(element.isStatic); |
| 2198 | 2211 |
| 2199 bool eagerInit; | 2212 bool eagerInit; |
| 2200 JS.Expression jsInit; | 2213 JS.Expression jsInit; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2282 _findAccessor(element, getter: false))); | 2295 _findAccessor(element, getter: false))); |
| 2283 } | 2296 } |
| 2284 } | 2297 } |
| 2285 | 2298 |
| 2286 JS.Expression objExpr = _exportsVar; | 2299 JS.Expression objExpr = _exportsVar; |
| 2287 var target = _lazyFields[0].element.enclosingElement; | 2300 var target = _lazyFields[0].element.enclosingElement; |
| 2288 if (target is ClassElement) { | 2301 if (target is ClassElement) { |
| 2289 objExpr = new JS.Identifier(target.type.name); | 2302 objExpr = new JS.Identifier(target.type.name); |
| 2290 } | 2303 } |
| 2291 | 2304 |
| 2292 return js.statement( | 2305 return js |
| 2293 'dart.defineLazyProperties(#, { # });', [objExpr, methods]); | 2306 .statement('dart.defineLazyProperties(#, { # });', [objExpr, methods]); |
| 2294 } | 2307 } |
| 2295 | 2308 |
| 2296 PropertyAccessorElement _findAccessor(VariableElement element, | 2309 PropertyAccessorElement _findAccessor(VariableElement element, |
| 2297 {bool getter}) { | 2310 {bool getter}) { |
| 2298 var parent = element.enclosingElement; | 2311 var parent = element.enclosingElement; |
| 2299 if (parent is ClassElement) { | 2312 if (parent is ClassElement) { |
| 2300 return getter | 2313 return getter |
| 2301 ? parent.getGetter(element.name) | 2314 ? parent.getGetter(element.name) |
| 2302 : parent.getSetter(element.name); | 2315 : parent.getSetter(element.name); |
| 2303 } | 2316 } |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2503 } else { | 2516 } else { |
| 2504 // TODO(vsm): When do Dart ops not map to JS? | 2517 // TODO(vsm): When do Dart ops not map to JS? |
| 2505 code = '# $op #'; | 2518 code = '# $op #'; |
| 2506 } | 2519 } |
| 2507 return js.call(code, [notNull(left), notNull(right)]); | 2520 return js.call(code, [notNull(left), notNull(right)]); |
| 2508 } | 2521 } |
| 2509 | 2522 |
| 2510 return _emitSend(left, op.lexeme, [right]); | 2523 return _emitSend(left, op.lexeme, [right]); |
| 2511 } | 2524 } |
| 2512 | 2525 |
| 2513 /// If the type [t] is [int] or [double], returns [num]. | 2526 /// If the type [t] is [int] or [double], or a type parameter |
| 2527 /// bounded by [int], [double] or [num] returns [num]. | |
| 2514 /// Otherwise returns [t]. | 2528 /// Otherwise returns [t]. |
| 2515 DartType _canonicalizeNumTypes(DartType t) { | 2529 DartType _canonicalizeNumTypes(DartType t) { |
| 2516 var numType = types.numType; | 2530 var numType = types.numType; |
| 2517 if (t is InterfaceType && t.superclass == numType) return numType; | 2531 if (rules.isSubtypeOf(t, numType)) return numType; |
| 2518 return t; | 2532 return t; |
| 2519 } | 2533 } |
| 2520 | 2534 |
| 2521 bool _canUsePrimitiveEquality(Expression left, Expression right) { | 2535 bool _canUsePrimitiveEquality(Expression left, Expression right) { |
| 2522 if (_isNull(left) || _isNull(right)) return true; | 2536 if (_isNull(left) || _isNull(right)) return true; |
| 2523 | 2537 |
| 2524 var leftType = _canonicalizeNumTypes(getStaticType(left)); | 2538 var leftType = _canonicalizeNumTypes(getStaticType(left)); |
| 2525 var rightType = _canonicalizeNumTypes(getStaticType(right)); | 2539 var rightType = _canonicalizeNumTypes(getStaticType(right)); |
| 2526 return _isJSBuiltinType(leftType) && leftType == rightType; | 2540 return _isJSBuiltinType(leftType) && leftType == rightType; |
| 2527 } | 2541 } |
| (...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2808 return AstBuilder.propertyAccess(newTarget, node.propertyName); | 2822 return AstBuilder.propertyAccess(newTarget, node.propertyName); |
| 2809 } else { | 2823 } else { |
| 2810 var invoke = node as MethodInvocation; | 2824 var invoke = node as MethodInvocation; |
| 2811 return AstBuilder.methodInvoke( | 2825 return AstBuilder.methodInvoke( |
| 2812 newTarget, invoke.methodName, invoke.argumentList.arguments); | 2826 newTarget, invoke.methodName, invoke.argumentList.arguments); |
| 2813 } | 2827 } |
| 2814 } | 2828 } |
| 2815 | 2829 |
| 2816 bool _requiresStaticDispatch(Expression target, String memberName) { | 2830 bool _requiresStaticDispatch(Expression target, String memberName) { |
| 2817 var type = getStaticType(target); | 2831 var type = getStaticType(target); |
| 2818 if (!rules.objectMembers.containsKey(memberName)) { | 2832 if (!_isObjectProperty(memberName)) { |
| 2819 return false; | 2833 return false; |
| 2820 } | 2834 } |
| 2821 if (!type.isObject && | 2835 if (!type.isObject && |
| 2822 !_isJSBuiltinType(type) && | 2836 !_isJSBuiltinType(type) && |
| 2823 _isNonNullableExpression(target)) { | 2837 _isNonNullableExpression(target)) { |
| 2824 return false; | 2838 return false; |
| 2825 } | 2839 } |
| 2826 return true; | 2840 return true; |
| 2827 } | 2841 } |
| 2828 | 2842 |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3019 var createStreamIter = _emitInstanceCreationExpression( | 3033 var createStreamIter = _emitInstanceCreationExpression( |
| 3020 StreamIterator_T.element.unnamedConstructor, | 3034 StreamIterator_T.element.unnamedConstructor, |
| 3021 StreamIterator_T, | 3035 StreamIterator_T, |
| 3022 null, | 3036 null, |
| 3023 AstBuilder.argumentList([node.iterable]), | 3037 AstBuilder.argumentList([node.iterable]), |
| 3024 false); | 3038 false); |
| 3025 var iter = _visit(_createTemporary('it', StreamIterator_T)); | 3039 var iter = _visit(_createTemporary('it', StreamIterator_T)); |
| 3026 | 3040 |
| 3027 var init = _visit(node.identifier); | 3041 var init = _visit(node.identifier); |
| 3028 if (init == null) { | 3042 if (init == null) { |
| 3029 init = js.call( | 3043 init = js |
| 3030 'let # = #.current', [node.loopVariable.identifier.name, iter]); | 3044 .call('let # = #.current', [node.loopVariable.identifier.name, iter]); |
| 3031 } else { | 3045 } else { |
| 3032 init = js.call('# = #.current', [init, iter]); | 3046 init = js.call('# = #.current', [init, iter]); |
| 3033 } | 3047 } |
| 3034 return js.statement( | 3048 return js.statement( |
| 3035 '{' | 3049 '{' |
| 3036 ' let # = #;' | 3050 ' let # = #;' |
| 3037 ' try {' | 3051 ' try {' |
| 3038 ' while (#) { #; #; }' | 3052 ' while (#) { #; #; }' |
| 3039 ' } finally { #; }' | 3053 ' } finally { #; }' |
| 3040 '}', | 3054 '}', |
| 3041 [ | 3055 [ |
| 3042 iter, | 3056 iter, |
| 3043 createStreamIter, | 3057 createStreamIter, |
| 3044 new JS.Yield(js.call('#.moveNext()', iter)), | 3058 new JS.Yield(js.call('#.moveNext()', iter)), |
| 3045 init, | 3059 init, |
| 3046 _visit(node.body), | 3060 _visit(node.body), |
| 3047 new JS.Yield(js.call('#.cancel()', iter)) | 3061 new JS.Yield(js.call('#.cancel()', iter)) |
| 3048 ]); | 3062 ]); |
| 3049 } | 3063 } |
| 3050 | 3064 |
| 3051 @override | 3065 @override |
| 3052 visitBreakStatement(BreakStatement node) { | 3066 visitBreakStatement(BreakStatement node) { |
| 3053 var label = node.label; | 3067 var label = node.label; |
| 3054 return new JS.Break(label?.name); | 3068 return new JS.Break(label?.name); |
| 3055 } | 3069 } |
| 3056 | 3070 |
| 3057 @override | 3071 @override |
| 3058 visitContinueStatement(ContinueStatement node) { | 3072 visitContinueStatement(ContinueStatement node) { |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3117 /// Visits the catch clause body. This skips the exception type guard, if any. | 3131 /// Visits the catch clause body. This skips the exception type guard, if any. |
| 3118 /// That is handled in [_visitCatch]. | 3132 /// That is handled in [_visitCatch]. |
| 3119 @override | 3133 @override |
| 3120 JS.Statement visitCatchClause(CatchClause node) { | 3134 JS.Statement visitCatchClause(CatchClause node) { |
| 3121 var body = <JS.Statement>[]; | 3135 var body = <JS.Statement>[]; |
| 3122 | 3136 |
| 3123 var savedCatch = _catchParameter; | 3137 var savedCatch = _catchParameter; |
| 3124 if (node.catchKeyword != null) { | 3138 if (node.catchKeyword != null) { |
| 3125 var name = node.exceptionParameter; | 3139 var name = node.exceptionParameter; |
| 3126 if (name != null && name != _catchParameter) { | 3140 if (name != null && name != _catchParameter) { |
| 3127 body.add(js.statement( | 3141 body.add(js |
| 3128 'let # = #;', [_visit(name), _visit(_catchParameter)])); | 3142 .statement('let # = #;', [_visit(name), _visit(_catchParameter)])); |
| 3129 _catchParameter = name; | 3143 _catchParameter = name; |
| 3130 } | 3144 } |
| 3131 if (node.stackTraceParameter != null) { | 3145 if (node.stackTraceParameter != null) { |
| 3132 var stackVar = node.stackTraceParameter.name; | 3146 var stackVar = node.stackTraceParameter.name; |
| 3133 body.add(js.statement( | 3147 body.add(js.statement( |
| 3134 'let # = dart.stackTrace(#);', [stackVar, _visit(name)])); | 3148 'let # = dart.stackTrace(#);', [stackVar, _visit(name)])); |
| 3135 } | 3149 } |
| 3136 } | 3150 } |
| 3137 | 3151 |
| 3138 body.add( | 3152 body.add( |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3306 return result; | 3320 return result; |
| 3307 } | 3321 } |
| 3308 | 3322 |
| 3309 /// Visits a list of expressions, creating a comma expression if needed in JS. | 3323 /// Visits a list of expressions, creating a comma expression if needed in JS. |
| 3310 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { | 3324 JS.Expression _visitListToBinary(List<Expression> nodes, String operator) { |
| 3311 if (nodes == null || nodes.isEmpty) return null; | 3325 if (nodes == null || nodes.isEmpty) return null; |
| 3312 return new JS.Expression.binary( | 3326 return new JS.Expression.binary( |
| 3313 _visitList(nodes) as List<JS.Expression>, operator); | 3327 _visitList(nodes) as List<JS.Expression>, operator); |
| 3314 } | 3328 } |
| 3315 | 3329 |
| 3330 /// Return the bound type parameters for a ParameterizedType | |
| 3331 List<TypeParameterElement> _boundTypeParametersOf(ParameterizedType type) { | |
| 3332 return (type is FunctionType) | |
| 3333 ? type.boundTypeParameters | |
| 3334 : type.typeParameters; | |
| 3335 } | |
| 3336 | |
| 3316 /// Like [_emitMemberName], but for declaration sites. | 3337 /// Like [_emitMemberName], but for declaration sites. |
| 3317 /// | 3338 /// |
| 3318 /// Unlike call sites, we always have an element available, so we can use it | 3339 /// Unlike call sites, we always have an element available, so we can use it |
| 3319 /// directly rather than computing the relevant options for [_emitMemberName]. | 3340 /// directly rather than computing the relevant options for [_emitMemberName]. |
| 3320 JS.Expression _elementMemberName(ExecutableElement e, | 3341 JS.Expression _elementMemberName(ExecutableElement e, |
| 3321 {bool allowExtensions: true}) { | 3342 {bool allowExtensions: true}) { |
| 3322 String name; | 3343 String name; |
| 3323 if (e is PropertyAccessorElement) { | 3344 if (e is PropertyAccessorElement) { |
| 3324 name = e.variable.name; | 3345 name = e.variable.name; |
| 3325 } else { | 3346 } else { |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3394 name = 'unary-'; | 3415 name = 'unary-'; |
| 3395 } else if (name == 'constructor' || name == 'prototype') { | 3416 } else if (name == 'constructor' || name == 'prototype') { |
| 3396 // This uses an illegal (in Dart) character for a member, avoiding the | 3417 // This uses an illegal (in Dart) character for a member, avoiding the |
| 3397 // conflict. We could use practically any character for this. | 3418 // conflict. We could use practically any character for this. |
| 3398 name = '+$name'; | 3419 name = '+$name'; |
| 3399 } | 3420 } |
| 3400 | 3421 |
| 3401 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 3422 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
| 3402 if (allowExtensions && | 3423 if (allowExtensions && |
| 3403 _extensionTypes.contains(type.element) && | 3424 _extensionTypes.contains(type.element) && |
| 3404 !_objectMembers.containsKey(name)) { | 3425 !_isObjectProperty(name)) { |
| 3405 return js.call('dartx.#', _propertyName(name)); | 3426 return js.call('dartx.#', _propertyName(name)); |
| 3406 } | 3427 } |
| 3407 | 3428 |
| 3408 return _propertyName(name); | 3429 return _propertyName(name); |
| 3409 } | 3430 } |
| 3410 | 3431 |
| 3411 bool _externalOrNative(node) => | 3432 bool _externalOrNative(node) => |
| 3412 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 3433 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
| 3413 | 3434 |
| 3414 FunctionBody _functionBody(node) => | 3435 FunctionBody _functionBody(node) => |
| 3415 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 3436 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
| 3416 | 3437 |
| 3417 /// Choose a canonical name from the library element. | 3438 /// Choose a canonical name from the library element. |
| 3418 /// This never uses the library's name (the identifier in the `library` | 3439 /// This never uses the library's name (the identifier in the `library` |
| 3419 /// declaration) as it doesn't have any meaningful rules enforced. | 3440 /// declaration) as it doesn't have any meaningful rules enforced. |
| 3420 JS.Identifier _libraryName(LibraryElement library) { | 3441 JS.Identifier _libraryName(LibraryElement library) { |
| 3421 if (library == currentLibrary) return _exportsVar; | 3442 if (library == currentLibrary) return _exportsVar; |
| 3422 return _imports.putIfAbsent( | 3443 return _imports.putIfAbsent( |
| 3423 library, () => new JS.TemporaryId(jsLibraryName(library))); | 3444 library, () => new JS.TemporaryId(jsLibraryName(library))); |
| 3424 } | 3445 } |
| 3425 | 3446 |
| 3426 DartType getStaticType(Expression e) => rules.getStaticType(e); | 3447 DartType getStaticType(Expression e) => |
| 3448 e.staticType ?? DynamicTypeImpl.instance; | |
| 3427 | 3449 |
| 3428 @override | 3450 @override |
| 3429 String getQualifiedName(TypeDefiningElement type) { | 3451 String getQualifiedName(TypeDefiningElement type) { |
| 3430 JS.TemporaryId id = _imports[type.library]; | 3452 JS.TemporaryId id = _imports[type.library]; |
| 3431 return id == null ? type.name : '${id.name}.${type.name}'; | 3453 return id == null ? type.name : '${id.name}.${type.name}'; |
| 3432 } | 3454 } |
| 3433 | 3455 |
| 3434 JS.Node annotate(JS.Node method, ExecutableElement e) => | 3456 JS.Node annotate(JS.Node method, ExecutableElement e) => |
| 3435 options.closure && e != null | 3457 options.closure && e != null |
| 3436 ? method.withClosureAnnotation( | 3458 ? method.withClosureAnnotation( |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 3452 options.closure && e != null | 3474 options.closure && e != null |
| 3453 ? node.withClosureAnnotation(closureAnnotationForTypeDef(e)) | 3475 ? node.withClosureAnnotation(closureAnnotationForTypeDef(e)) |
| 3454 : node; | 3476 : node; |
| 3455 | 3477 |
| 3456 /// Returns true if this is any kind of object represented by `Number` in JS. | 3478 /// Returns true if this is any kind of object represented by `Number` in JS. |
| 3457 /// | 3479 /// |
| 3458 /// In practice, this is 4 types: num, int, double, and JSNumber. | 3480 /// In practice, this is 4 types: num, int, double, and JSNumber. |
| 3459 /// | 3481 /// |
| 3460 /// JSNumber is the type that actually "implements" all numbers, hence it's | 3482 /// JSNumber is the type that actually "implements" all numbers, hence it's |
| 3461 /// a subtype of int and double (and num). It's in our "dart:_interceptors". | 3483 /// a subtype of int and double (and num). It's in our "dart:_interceptors". |
| 3462 bool _isNumberInJS(DartType t) => rules.isSubTypeOf(t, _types.numType); | 3484 bool _isNumberInJS(DartType t) => rules.isSubtypeOf(t, _types.numType); |
| 3485 | |
| 3486 bool _isObjectGetter(String name) { | |
| 3487 PropertyAccessorElement element = _types.objectType.element.getGetter(name); | |
| 3488 return (element != null && !element.isStatic); | |
| 3489 } | |
| 3490 | |
| 3491 bool _isObjectMethod(String name) { | |
| 3492 MethodElement element = _types.objectType.element.getMethod(name); | |
| 3493 return (element != null && !element.isStatic); | |
| 3494 } | |
| 3495 | |
| 3496 bool _isObjectProperty(String name) { | |
| 3497 return _isObjectGetter(name) || _isObjectMethod(name); | |
| 3498 } | |
| 3499 | |
| 3500 // TODO(leafp): This is copied out of the analyzer code checker. Should | |
| 3501 // be shared somewhere. | |
|
Jennifer Messerly
2016/01/04 20:48:40
yeah, agree. Another idea is we could just store i
Leaf
2016/01/04 21:34:47
Acknowledged.
| |
| 3502 DartType _getExpectedReturnType(FunctionBody body, {bool yieldStar: false}) { | |
| 3503 FunctionType functionType; | |
| 3504 var parent = body.parent; | |
| 3505 if (parent is Declaration) { | |
| 3506 functionType = (parent.element as dynamic)?.type; | |
| 3507 } else { | |
| 3508 assert(parent is FunctionExpression); | |
| 3509 functionType = parent.staticType; | |
| 3510 } | |
| 3511 if (functionType == null) { | |
| 3512 return DynamicTypeImpl.instance; | |
| 3513 } | |
| 3514 var type = functionType.returnType; | |
| 3515 | |
| 3516 InterfaceType expectedType = null; | |
| 3517 if (body.isAsynchronous) { | |
| 3518 if (body.isGenerator) { | |
| 3519 // Stream<T> -> T | |
| 3520 expectedType = _types.streamType; | |
| 3521 } else { | |
| 3522 // Future<T> -> T | |
| 3523 // TODO(vsm): Revisit with issue #228. | |
| 3524 expectedType = _types.futureType; | |
| 3525 } | |
| 3526 } else { | |
| 3527 if (body.isGenerator) { | |
| 3528 // Iterable<T> -> T | |
| 3529 expectedType = _types.iterableType; | |
| 3530 } else { | |
| 3531 // T -> T | |
| 3532 return type; | |
| 3533 } | |
| 3534 } | |
| 3535 if (type.isDynamic) { | |
| 3536 return type; | |
| 3537 } else if (type is InterfaceType && type.element == expectedType.element) { | |
| 3538 return type.typeArguments[0]; | |
| 3539 } else { | |
| 3540 // TODO(leafp): The above only handles the case where the return type | |
| 3541 // is exactly Future/Stream/Iterable. Handle the subtype case. | |
| 3542 return DynamicTypeImpl.instance; | |
| 3543 } | |
| 3544 } | |
| 3463 } | 3545 } |
| 3464 | 3546 |
| 3465 class JSGenerator extends CodeGenerator { | 3547 class JSGenerator extends CodeGenerator { |
| 3466 final _extensionTypes = new HashSet<ClassElement>(); | 3548 final _extensionTypes = new HashSet<ClassElement>(); |
| 3467 | 3549 final TypeProvider _types; |
| 3468 JSGenerator(AbstractCompiler compiler) : super(compiler) { | 3550 JSGenerator(AbstractCompiler compiler) |
| 3551 : _types = compiler.context.typeProvider, | |
| 3552 super(compiler) { | |
| 3469 // TODO(jacobr): determine the the set of types with extension methods from | 3553 // TODO(jacobr): determine the the set of types with extension methods from |
| 3470 // the annotations rather than hard coding the list once the analyzer | 3554 // the annotations rather than hard coding the list once the analyzer |
| 3471 // supports summaries. | 3555 // supports summaries. |
| 3472 var context = compiler.context; | 3556 var context = compiler.context; |
| 3473 var src = context.sourceFactory.forUri('dart:_interceptors'); | 3557 var src = context.sourceFactory.forUri('dart:_interceptors'); |
| 3474 var interceptors = context.computeLibraryElement(src); | 3558 var interceptors = context.computeLibraryElement(src); |
| 3475 for (var t in ['JSArray', 'JSString', 'JSNumber', 'JSBool']) { | 3559 for (var t in ['JSArray', 'JSString', 'JSNumber', 'JSBool']) { |
| 3476 _addExtensionType(interceptors.getType(t).type); | 3560 _addExtensionType(interceptors.getType(t).type); |
| 3477 } | 3561 } |
| 3478 // TODO(jmesserly): manually add `int` and `double` | 3562 // TODO(jmesserly): manually add `int` and `double` |
| 3479 // Unfortunately our current analyzer rejects "implements int". | 3563 // Unfortunately our current analyzer rejects "implements int". |
| 3480 // Fix was landed, so we can remove this hack once we're updated: | 3564 // Fix was landed, so we can remove this hack once we're updated: |
| 3481 // https://github.com/dart-lang/sdk/commit/d7cd11f86a02f55269fc8d9843e7758eb eeb81c8 | 3565 // https://github.com/dart-lang/sdk/commit/d7cd11f86a02f55269fc8d9843e7758eb eeb81c8 |
| 3482 _addExtensionType(context.typeProvider.intType); | 3566 _addExtensionType(_types.intType); |
| 3483 _addExtensionType(context.typeProvider.doubleType); | 3567 _addExtensionType(_types.doubleType); |
| 3484 } | 3568 } |
| 3485 | 3569 |
| 3486 void _addExtensionType(InterfaceType t) { | 3570 void _addExtensionType(InterfaceType t) { |
| 3487 if (t.isObject || !_extensionTypes.add(t.element)) return; | 3571 if (t.isObject || !_extensionTypes.add(t.element)) return; |
| 3488 t = fillDynamicTypeArgs(t, rules.provider) as InterfaceType; | 3572 t = fillDynamicTypeArgs(t, _types) as InterfaceType; |
| 3489 t.interfaces.forEach(_addExtensionType); | 3573 t.interfaces.forEach(_addExtensionType); |
| 3490 t.mixins.forEach(_addExtensionType); | 3574 t.mixins.forEach(_addExtensionType); |
| 3491 _addExtensionType(t.superclass); | 3575 _addExtensionType(t.superclass); |
| 3492 } | 3576 } |
| 3493 | 3577 |
| 3494 String generateLibrary(LibraryUnit unit) { | 3578 String generateLibrary(LibraryUnit unit) { |
| 3495 // Clone the AST first, so we can mutate it. | 3579 // Clone the AST first, so we can mutate it. |
| 3496 unit = unit.clone(); | 3580 unit = unit.clone(); |
| 3497 var library = unit.library.element.library; | 3581 var library = unit.library.element.library; |
| 3498 var fields = findFieldsNeedingStorage(unit, _extensionTypes); | 3582 var fields = findFieldsNeedingStorage(unit, _extensionTypes); |
| 3583 var rules = new StrongTypeSystemImpl(); | |
| 3499 var codegen = | 3584 var codegen = |
| 3500 new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields); | 3585 new JSCodegenVisitor(compiler, rules, library, _extensionTypes, fields); |
| 3501 var module = codegen.emitLibrary(unit); | 3586 var module = codegen.emitLibrary(unit); |
| 3502 var out = compiler.getOutputPath(library.source.uri); | 3587 var out = compiler.getOutputPath(library.source.uri); |
| 3503 return writeJsLibrary(module, out, | 3588 return writeJsLibrary(module, out, |
| 3504 emitSourceMaps: options.emitSourceMaps, | 3589 emitSourceMaps: options.emitSourceMaps, |
| 3505 arrowFnBindThisWorkaround: options.arrowFnBindThisWorkaround); | 3590 arrowFnBindThisWorkaround: options.arrowFnBindThisWorkaround); |
| 3506 } | 3591 } |
| 3507 } | 3592 } |
| 3508 | 3593 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 3524 | 3609 |
| 3525 /// A special kind of element created by the compiler, signifying a temporary | 3610 /// A special kind of element created by the compiler, signifying a temporary |
| 3526 /// variable. These objects use instance equality, and should be shared | 3611 /// variable. These objects use instance equality, and should be shared |
| 3527 /// everywhere in the tree where they are treated as the same variable. | 3612 /// everywhere in the tree where they are treated as the same variable. |
| 3528 class TemporaryVariableElement extends LocalVariableElementImpl { | 3613 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 3529 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3614 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 3530 | 3615 |
| 3531 int get hashCode => identityHashCode(this); | 3616 int get hashCode => identityHashCode(this); |
| 3532 bool operator ==(Object other) => identical(this, other); | 3617 bool operator ==(Object other) => identical(this, other); |
| 3533 } | 3618 } |
| OLD | NEW |