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 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
79 JS.TemporaryId _asyncStarController; | 79 JS.TemporaryId _asyncStarController; |
80 | 80 |
81 /// Imported libraries, and the temporaries used to refer to them. | 81 /// Imported libraries, and the temporaries used to refer to them. |
82 final _imports = new Map<LibraryElement, JS.TemporaryId>(); | 82 final _imports = new Map<LibraryElement, JS.TemporaryId>(); |
83 final _exports = new Set<String>(); | 83 final _exports = new Set<String>(); |
84 final _properties = <FunctionDeclaration>[]; | 84 final _properties = <FunctionDeclaration>[]; |
85 final _privateNames = new HashMap<String, JS.TemporaryId>(); | 85 final _privateNames = new HashMap<String, JS.TemporaryId>(); |
86 final _moduleItems = <JS.Statement>[]; | 86 final _moduleItems = <JS.Statement>[]; |
87 final _temps = new HashMap<Element, JS.TemporaryId>(); | 87 final _temps = new HashMap<Element, JS.TemporaryId>(); |
88 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); | 88 final _qualifiedIds = new List<Tuple2<Element, JS.MaybeQualifiedId>>(); |
89 final _topLevelExtensionNames = new Set<String>(); | |
89 | 90 |
90 /// The name for the library's exports inside itself. | 91 /// The name for the library's exports inside itself. |
91 /// `exports` was chosen as the most similar to ES module patterns. | 92 /// `exports` was chosen as the most similar to ES module patterns. |
92 final _dartxVar = new JS.Identifier('dartx'); | 93 final _dartxVar = new JS.Identifier('dartx'); |
93 final _exportsVar = new JS.TemporaryId('exports'); | 94 final _exportsVar = new JS.TemporaryId('exports'); |
94 final _runtimeLibVar = new JS.Identifier('dart'); | 95 final _runtimeLibVar = new JS.Identifier('dart'); |
95 final _namedArgTemp = new JS.TemporaryId('opts'); | 96 final _namedArgTemp = new JS.TemporaryId('opts'); |
96 | 97 |
97 final TypeProvider _types; | 98 final TypeProvider _types; |
98 | 99 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
152 if (decl is TopLevelVariableDeclaration) { | 153 if (decl is TopLevelVariableDeclaration) { |
153 visitTopLevelVariableDeclaration(decl); | 154 visitTopLevelVariableDeclaration(decl); |
154 } else { | 155 } else { |
155 _loader.loadDeclaration(decl, decl.element); | 156 _loader.loadDeclaration(decl, decl.element); |
156 } | 157 } |
157 } | 158 } |
158 } | 159 } |
159 | 160 |
160 // Flush any unwritten fields/properties. | 161 // Flush any unwritten fields/properties. |
161 _flushLibraryProperties(_moduleItems); | 162 _flushLibraryProperties(_moduleItems); |
163 if (_topLevelExtensionNames.isNotEmpty) { | |
Jennifer Messerly
2016/02/02 00:22:56
I don't think this should be needed at the top-lev
ochafik
2016/02/03 19:58:49
Great point! Leaving todo for follow up refactorin
| |
164 _moduleItems.add(_emitExtensionNamesDeclaration( | |
165 _topLevelExtensionNames.map(js.string).toList())); | |
166 } | |
162 | 167 |
163 // Mark all qualified names as qualified or not, depending on if they need | 168 // Mark all qualified names as qualified or not, depending on if they need |
164 // to be loaded lazily or not. | 169 // to be loaded lazily or not. |
165 for (var elementIdPairs in _qualifiedIds) { | 170 for (var elementIdPairs in _qualifiedIds) { |
166 var element = elementIdPairs.e0; | 171 var element = elementIdPairs.e0; |
167 var id = elementIdPairs.e1; | 172 var id = elementIdPairs.e1; |
168 id.setQualified(!_loader.isLoaded(element)); | 173 id.setQualified(!_loader.isLoaded(element)); |
169 } | 174 } |
170 | 175 |
171 var moduleBuilder = new ModuleBuilder(options.moduleFormat); | 176 var moduleBuilder = new ModuleBuilder(options.moduleFormat); |
(...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
651 JS.Expression _instantiateAnnotation(Annotation node) { | 656 JS.Expression _instantiateAnnotation(Annotation node) { |
652 var element = node.element; | 657 var element = node.element; |
653 if (element is ConstructorElement) { | 658 if (element is ConstructorElement) { |
654 return _emitInstanceCreationExpression(element, element.returnType, | 659 return _emitInstanceCreationExpression(element, element.returnType, |
655 node.constructorName, node.arguments, true); | 660 node.constructorName, node.arguments, true); |
656 } else { | 661 } else { |
657 return _visit(node.name); | 662 return _visit(node.name); |
658 } | 663 } |
659 } | 664 } |
660 | 665 |
666 JS.Statement _emitExtensionNamesDeclaration(List<JS.Expression> names) => | |
667 js.statement('dart.defineExtensionNames(#)', | |
668 [new JS.ArrayInitializer(names.toList(), multiline: true)]); | |
669 | |
670 JS.Expression _emitExtensionName(String name) => | |
671 js.call('dartx.#', _propertyName(name)); | |
672 | |
661 /// Emit class members that need to come after the class declaration, such | 673 /// Emit class members that need to come after the class declaration, such |
662 /// as static fields. See [_emitClassMethods] for things that are emitted | 674 /// as static fields. See [_emitClassMethods] for things that are emitted |
663 /// inside the ES6 `class { ... }` node. | 675 /// inside the ES6 `class { ... }` node. |
664 JS.Statement _finishClassMembers( | 676 JS.Statement _finishClassMembers( |
665 ClassElement classElem, | 677 ClassElement classElem, |
666 JS.ClassExpression cls, | 678 JS.ClassExpression cls, |
667 List<ConstructorDeclaration> ctors, | 679 List<ConstructorDeclaration> ctors, |
668 List<FieldDeclaration> fields, | 680 List<FieldDeclaration> fields, |
669 List<FieldDeclaration> staticFields, | 681 List<FieldDeclaration> staticFields, |
670 List<MethodDeclaration> methods, | 682 List<MethodDeclaration> methods, |
671 List<Annotation> metadata, | 683 List<Annotation> metadata, |
672 String jsPeerName) { | 684 String jsPeerName) { |
673 var name = classElem.name; | 685 var name = classElem.name; |
674 var body = <JS.Statement>[]; | 686 var body = <JS.Statement>[]; |
675 | 687 |
676 if (_extensionTypes.contains(classElem)) { | 688 if (_extensionTypes.contains(classElem)) { |
677 var dartxNames = <JS.Expression>[]; | 689 var dartxNames = <JS.Expression>[]; |
678 for (var m in methods) { | 690 for (var m in methods) { |
679 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { | 691 if (!m.isAbstract && !m.isStatic && m.element.isPublic) { |
680 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); | 692 dartxNames.add(_elementMemberName(m.element, allowExtensions: false)); |
681 } | 693 } |
682 } | 694 } |
683 if (dartxNames.isNotEmpty) { | 695 if (dartxNames.isNotEmpty) { |
684 body.add(js.statement('dart.defineExtensionNames(#)', | 696 body.add(_emitExtensionNamesDeclaration(dartxNames)); |
685 [new JS.ArrayInitializer(dartxNames, multiline: true)])); | |
686 } | 697 } |
687 } | 698 } |
688 | 699 |
689 body.add(new JS.ClassDeclaration(cls)); | 700 body.add(new JS.ClassDeclaration(cls)); |
690 | 701 |
691 // TODO(jmesserly): we should really just extend native Array. | 702 // TODO(jmesserly): we should really just extend native Array. |
692 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { | 703 if (jsPeerName != null && classElem.typeParameters.isNotEmpty) { |
693 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', | 704 body.add(js.statement('dart.setBaseClass(#, dart.global.#);', |
694 [classElem.name, _propertyName(jsPeerName)])); | 705 [classElem.name, _propertyName(jsPeerName)])); |
695 } | 706 } |
(...skipping 2688 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3384 /// for this transformation to happen, otherwise binary minus is assumed. | 3395 /// for this transformation to happen, otherwise binary minus is assumed. |
3385 /// | 3396 /// |
3386 /// Equality is a bit special, it is generated via the Dart `equals` runtime | 3397 /// Equality is a bit special, it is generated via the Dart `equals` runtime |
3387 /// helper, that checks for null. The user defined method is called '=='. | 3398 /// helper, that checks for null. The user defined method is called '=='. |
3388 /// | 3399 /// |
3389 JS.Expression _emitMemberName(String name, | 3400 JS.Expression _emitMemberName(String name, |
3390 {DartType type, | 3401 {DartType type, |
3391 bool unary: false, | 3402 bool unary: false, |
3392 bool isStatic: false, | 3403 bool isStatic: false, |
3393 bool allowExtensions: true}) { | 3404 bool allowExtensions: true}) { |
3394 // Static members skip the rename steps. | 3405 // Static members skip the rename steps, except for Function properties. |
3395 if (isStatic) return _propertyName(name); | 3406 if (isStatic) { |
3407 if (options.closure) { | |
3408 // Avoid colliding with Function properties, which might be desirable | |
3409 // in general but is critical to work around a Closure ES6->ES5 bug | |
3410 // (https://github.com/google/closure-compiler/issues/1460). | |
3411 if (invalidStaticFieldName(name, closureCompiler: true)) { | |
Jennifer Messerly
2016/01/28 20:03:36
I still don't understand why were are using extens
Jennifer Messerly
2016/02/02 00:22:56
We chatted about this, decided extension method sy
ochafik
2016/02/03 19:58:49
Done.
ochafik
2016/02/03 19:58:49
Acknowledged.
| |
3412 _topLevelExtensionNames.add(name); | |
3413 return _emitExtensionName(name); | |
3414 } | |
3415 } | |
3416 return _propertyName(name); | |
3417 } | |
3396 | 3418 |
3397 if (name.startsWith('_')) { | 3419 if (name.startsWith('_')) { |
3398 return _privateNames.putIfAbsent( | 3420 return _privateNames.putIfAbsent( |
3399 name, () => _initSymbol(new JS.TemporaryId(name)) as JS.TemporaryId); | 3421 name, () => _initSymbol(new JS.TemporaryId(name)) as JS.TemporaryId); |
3400 } | 3422 } |
3401 | 3423 |
3402 if (name == '[]') { | 3424 if (name == '[]') { |
3403 name = 'get'; | 3425 name = 'get'; |
3404 } else if (name == '[]=') { | 3426 } else if (name == '[]=') { |
3405 name = 'set'; | 3427 name = 'set'; |
3406 } else if (name == '-' && unary) { | 3428 } else if (name == '-' && unary) { |
3407 name = 'unary-'; | 3429 name = 'unary-'; |
3408 } else if (name == 'constructor' || name == 'prototype') { | 3430 } else if (name == 'constructor' || name == 'prototype') { |
3409 // This uses an illegal (in Dart) character for a member, avoiding the | 3431 // This uses an illegal (in Dart) character for a member, avoiding the |
3410 // conflict. We could use practically any character for this. | 3432 // conflict. We could use practically any character for this. |
3411 name = '+$name'; | 3433 name = '+$name'; |
3412 } | 3434 } |
3413 | 3435 |
3414 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. | 3436 // Dart "extension" methods. Used for JS Array, Boolean, Number, String. |
3415 var baseType = type; | 3437 var baseType = type; |
3416 while (baseType is TypeParameterType) { | 3438 while (baseType is TypeParameterType) { |
3417 baseType = baseType.element.bound; | 3439 baseType = baseType.element.bound; |
3418 } | 3440 } |
3419 if (allowExtensions && | 3441 if (allowExtensions && |
3420 _extensionTypes.contains(baseType.element) && | 3442 _extensionTypes.contains(baseType.element) && |
3421 !_isObjectProperty(name)) { | 3443 !_isObjectProperty(name)) { |
3422 return js.call('dartx.#', _propertyName(name)); | 3444 return _emitExtensionName(name); |
3423 } | 3445 } |
3424 | 3446 |
3425 return _propertyName(name); | 3447 return _propertyName(name); |
3426 } | 3448 } |
3427 | 3449 |
3428 bool _externalOrNative(node) => | 3450 bool _externalOrNative(node) => |
3429 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; | 3451 node.externalKeyword != null || _functionBody(node) is NativeFunctionBody; |
3430 | 3452 |
3431 FunctionBody _functionBody(node) => | 3453 FunctionBody _functionBody(node) => |
3432 node is FunctionDeclaration ? node.functionExpression.body : node.body; | 3454 node is FunctionDeclaration ? node.functionExpression.body : node.body; |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3610 | 3632 |
3611 /// A special kind of element created by the compiler, signifying a temporary | 3633 /// A special kind of element created by the compiler, signifying a temporary |
3612 /// variable. These objects use instance equality, and should be shared | 3634 /// variable. These objects use instance equality, and should be shared |
3613 /// everywhere in the tree where they are treated as the same variable. | 3635 /// everywhere in the tree where they are treated as the same variable. |
3614 class TemporaryVariableElement extends LocalVariableElementImpl { | 3636 class TemporaryVariableElement extends LocalVariableElementImpl { |
3615 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3637 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
3616 | 3638 |
3617 int get hashCode => identityHashCode(this); | 3639 int get hashCode => identityHashCode(this); |
3618 bool operator ==(Object other) => identical(this, other); | 3640 bool operator ==(Object other) => identical(this, other); |
3619 } | 3641 } |
OLD | NEW |