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 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 284 if (hide != null) { | 284 if (hide != null) { |
| 285 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); | 285 hiddenNames.addAll(hide.hiddenNames.map((i) => js.string(i.name, "'"))); |
| 286 } | 286 } |
| 287 args.add(new JS.ArrayInitializer(shownNames)); | 287 args.add(new JS.ArrayInitializer(shownNames)); |
| 288 args.add(new JS.ArrayInitializer(hiddenNames)); | 288 args.add(new JS.ArrayInitializer(hiddenNames)); |
| 289 } | 289 } |
| 290 _moduleItems.add(js.statement('dart.export(#);', [args])); | 290 _moduleItems.add(js.statement('dart.export(#);', [args])); |
| 291 } | 291 } |
| 292 | 292 |
| 293 JS.Identifier _initSymbol(JS.Identifier id) { | 293 JS.Identifier _initSymbol(JS.Identifier id) { |
| 294 var s = js.statement('let # = $_SYMBOL(#);', [id, js.string(id.name, "'")]); | 294 var s = js.statement('let # = Symbol(#);', [id, js.string(id.name, "'")]); |
| 295 _moduleItems.add(s); | 295 _moduleItems.add(s); |
| 296 return id; | 296 return id; |
| 297 } | 297 } |
| 298 | 298 |
| 299 // TODO(jmesserly): this is a temporary workaround for `Symbol` in core, | |
| 300 // until we have better name tracking. | |
| 301 String get _SYMBOL { | |
| 302 var name = currentLibrary.name; | |
| 303 if (name == 'dart.core' || name == 'dart._internal') return 'dart.JsSymbol'; | |
| 304 return 'Symbol'; | |
| 305 } | |
| 306 | |
| 307 bool isPublic(String name) => !name.startsWith('_'); | 299 bool isPublic(String name) => !name.startsWith('_'); |
| 308 | 300 |
| 309 @override | 301 @override |
| 310 visitAsExpression(AsExpression node) { | 302 visitAsExpression(AsExpression node) { |
| 311 var from = getStaticType(node.expression); | 303 var from = getStaticType(node.expression); |
| 312 var to = node.type.type; | 304 var to = node.type.type; |
| 313 | 305 |
| 314 // Skip the cast if it's not needed. | 306 // Skip the cast if it's not needed. |
| 315 if (rules.isSubTypeOf(from, to)) return _visit(node.expression); | 307 if (rules.isSubTypeOf(from, to)) return _visit(node.expression); |
| 316 | 308 |
| (...skipping 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 544 return js.statement( | 536 return js.statement( |
| 545 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', | 537 '{ #; dart.defineLazyClassGeneric(#, #, { get: # }); }', |
| 546 [genericDef, _exportsVar, _propertyName(name), genericName]); | 538 [genericDef, _exportsVar, _propertyName(name), genericName]); |
| 547 } | 539 } |
| 548 | 540 |
| 549 return js.statement( | 541 return js.statement( |
| 550 'dart.defineLazyClass(#, { get #() { #; return #; } });', | 542 'dart.defineLazyClass(#, { get #() { #; return #; } });', |
| 551 [_exportsVar, _propertyName(name), body, name]); | 543 [_exportsVar, _propertyName(name), body, name]); |
| 552 } | 544 } |
| 553 | 545 |
| 554 if (isPublic(name)) _addExport(name); | |
| 555 | |
| 556 if (genericDef != null) { | 546 if (genericDef != null) { |
| 557 var dynType = fillDynamicTypeArgs(type, types); | 547 var dynType = fillDynamicTypeArgs(type, types); |
| 558 var genericInst = _emitTypeName(dynType, lowerGeneric: true); | 548 var genericInst = _emitTypeName(dynType, lowerGeneric: true); |
| 559 return js.statement('{ #; let # = #; }', [genericDef, name, genericInst]); | 549 |
| 550 if (_isJsBuiltinName(name)) { | |
|
ochafik
2015/09/15 18:05:17
nit: store as a var above for reuse below?
| |
| 551 genericInst = js.statement('#.# = #;', | |
| 552 [_libraryName(type.element.library), name, genericInst]); | |
|
ochafik
2015/09/15 18:05:17
nit: isn't this guaranteed to be currentLibrary?
| |
| 553 } else { | |
| 554 if (isPublic(name)) _addExport(name); | |
| 555 genericInst = js.statement('let # = #;', [name, genericInst]); | |
| 556 } | |
| 557 | |
| 558 return new JS.Block([genericDef, genericInst]); | |
| 560 } | 559 } |
| 560 | |
| 561 if (_isJsBuiltinName(name)) { | |
| 562 var typeName = _emitTypeName(type) as JS.PropertyAccess; | |
| 563 | |
| 564 // TODO(jmesserly): we could generate something cleaner, but since it only | |
| 565 // affects 3 types, doesn't seem like a big concern. | |
| 566 var classExpr = new JS.Call( | |
| 567 new JS.ArrowFun([], js.statement('{ #; return #; }', [body, name])), | |
| 568 []); | |
| 569 return new JS.Assignment(typeName, classExpr).toStatement(); | |
| 570 } else if (isPublic(name)) { | |
| 571 _addExport(name); | |
| 572 } | |
| 573 | |
| 561 return body; | 574 return body; |
| 562 } | 575 } |
| 563 | 576 |
| 564 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { | 577 JS.Statement _emitGenericClassDef(ParameterizedType type, JS.Statement body) { |
| 565 var name = type.name; | 578 var name = type.name; |
| 566 var genericName = '$name\$'; | 579 var genericName = '$name\$'; |
| 567 var typeParams = type.typeParameters.map((p) => p.name); | 580 var typeParams = type.typeParameters.map((p) => p.name); |
| 568 if (isPublic(name)) _exports.add(genericName); | 581 if (isPublic(name)) _exports.add(genericName); |
| 569 return js.statement('let # = dart.generic(function(#) { #; return #; });', | 582 return js.statement('let # = dart.generic(function(#) { #; return #; });', |
| 570 [genericName, typeParams, body, name]); | 583 [genericName, typeParams, body, name]); |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 648 // Iterable, we know the adapter is already there, so we can skip it as a | 661 // Iterable, we know the adapter is already there, so we can skip it as a |
| 649 // simple code size optimization. | 662 // simple code size optimization. |
| 650 var parent = t.lookUpGetterInSuperclass('iterator', t.element.library); | 663 var parent = t.lookUpGetterInSuperclass('iterator', t.element.library); |
| 651 if (parent != null) return null; | 664 if (parent != null) return null; |
| 652 var parentType = findSupertype(t, _implementsIterable); | 665 var parentType = findSupertype(t, _implementsIterable); |
| 653 if (parentType != null) return null; | 666 if (parentType != null) return null; |
| 654 | 667 |
| 655 // Otherwise, emit the adapter method, which wraps the Dart iterator in | 668 // Otherwise, emit the adapter method, which wraps the Dart iterator in |
| 656 // an ES6 iterator. | 669 // an ES6 iterator. |
| 657 return new JS.Method( | 670 return new JS.Method( |
| 658 js.call('$_SYMBOL.iterator'), | 671 js.call('Symbol.iterator'), |
| 659 js.call('function() { return new dart.JsIterator(this.#); }', | 672 js.call('function() { return new dart.JsIterator(this.#); }', |
| 660 [_emitMemberName('iterator', type: t)]) as JS.Fun); | 673 [_emitMemberName('iterator', type: t)]) as JS.Fun); |
| 661 } | 674 } |
| 662 | 675 |
| 663 JS.Expression _instantiateAnnotation(Annotation node) { | 676 JS.Expression _instantiateAnnotation(Annotation node) { |
| 664 var element = node.element; | 677 var element = node.element; |
| 665 if (element is ConstructorElement) { | 678 if (element is ConstructorElement) { |
| 666 return _emitInstanceCreationExpression(element, element.returnType, | 679 return _emitInstanceCreationExpression(element, element.returnType, |
| 667 node.constructorName, node.arguments, true); | 680 node.constructorName, node.arguments, true); |
| 668 } else { | 681 } else { |
| (...skipping 979 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1648 return _maybeQualifiedName(element, _propertyName(name)); | 1661 return _maybeQualifiedName(element, _propertyName(name)); |
| 1649 } | 1662 } |
| 1650 | 1663 |
| 1651 JS.Expression _maybeQualifiedName(Element e, JS.Expression name, | 1664 JS.Expression _maybeQualifiedName(Element e, JS.Expression name, |
| 1652 [Map<Element, JS.MaybeQualifiedId> idTable]) { | 1665 [Map<Element, JS.MaybeQualifiedId> idTable]) { |
| 1653 var libName = _libraryName(e.library); | 1666 var libName = _libraryName(e.library); |
| 1654 if (idTable == null) idTable = _qualifiedIds; | 1667 if (idTable == null) idTable = _qualifiedIds; |
| 1655 | 1668 |
| 1656 // Mutable top-level fields should always be qualified. | 1669 // Mutable top-level fields should always be qualified. |
| 1657 bool mutableTopLevel = e is TopLevelVariableElement && !e.isConst; | 1670 bool mutableTopLevel = e is TopLevelVariableElement && !e.isConst; |
| 1658 if (e.library != currentLibrary || mutableTopLevel) { | 1671 if (e.library != currentLibrary || |
| 1672 mutableTopLevel || | |
| 1673 _isJsBuiltinName(e.name)) { | |
|
ochafik
2015/09/15 18:05:17
I think that check alone (in the context of "exten
| |
| 1659 return new JS.PropertyAccess(libName, name); | 1674 return new JS.PropertyAccess(libName, name); |
| 1660 } | 1675 } |
| 1661 | 1676 |
| 1662 return idTable.putIfAbsent(e, () => new JS.MaybeQualifiedId(libName, name)); | 1677 return idTable.putIfAbsent(e, () => new JS.MaybeQualifiedId(libName, name)); |
| 1663 } | 1678 } |
| 1664 | 1679 |
| 1680 /// Returns true if this name is Object, Error, or Symbol. | |
| 1681 /// | |
| 1682 /// We avoid using those names as locals so we don't shadow the global JS | |
| 1683 /// names. While we could technically deal with this, it confuses other tools | |
| 1684 /// like JS Compiler. | |
| 1685 bool _isJsBuiltinName(String name) { | |
| 1686 switch (name) { | |
| 1687 case 'Object': | |
| 1688 case 'Error': | |
| 1689 case 'Symbol': | |
|
ochafik
2015/09/15 16:58:33
Presumably also Map, Set, TypeError, RangeError, R
Jennifer Messerly
2015/09/15 17:06:09
Maybe I need to step back, I honestly don't have a
ochafik
2015/09/15 18:05:17
Right now it's just to avoid errors in Closure wit
Jennifer Messerly
2015/09/15 18:34:23
ah, we try and qualify the minimum possible. In fa
| |
| 1690 return true; | |
| 1691 } | |
| 1692 return false; | |
| 1693 } | |
| 1694 | |
| 1665 @override | 1695 @override |
| 1666 JS.Expression visitAssignmentExpression(AssignmentExpression node) { | 1696 JS.Expression visitAssignmentExpression(AssignmentExpression node) { |
| 1667 var left = node.leftHandSide; | 1697 var left = node.leftHandSide; |
| 1668 var right = node.rightHandSide; | 1698 var right = node.rightHandSide; |
| 1669 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); | 1699 if (node.operator.type == TokenType.EQ) return _emitSet(left, right); |
| 1670 var op = node.operator.lexeme; | 1700 var op = node.operator.lexeme; |
| 1671 assert(op.endsWith('=')); | 1701 assert(op.endsWith('=')); |
| 1672 op = op.substring(0, op.length - 1); // remove trailing '=' | 1702 op = op.substring(0, op.length - 1); // remove trailing '=' |
| 1673 return _emitOpAssign(left, right, op, node.staticElement, context: node); | 1703 return _emitOpAssign(left, right, op, node.staticElement, context: node); |
| 1674 } | 1704 } |
| (...skipping 1636 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3311 | 3341 |
| 3312 /// A special kind of element created by the compiler, signifying a temporary | 3342 /// A special kind of element created by the compiler, signifying a temporary |
| 3313 /// variable. These objects use instance equality, and should be shared | 3343 /// variable. These objects use instance equality, and should be shared |
| 3314 /// everywhere in the tree where they are treated as the same variable. | 3344 /// everywhere in the tree where they are treated as the same variable. |
| 3315 class TemporaryVariableElement extends LocalVariableElementImpl { | 3345 class TemporaryVariableElement extends LocalVariableElementImpl { |
| 3316 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3346 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); |
| 3317 | 3347 |
| 3318 int get hashCode => identityHashCode(this); | 3348 int get hashCode => identityHashCode(this); |
| 3319 bool operator ==(Object other) => identical(this, other); | 3349 bool operator ==(Object other) => identical(this, other); |
| 3320 } | 3350 } |
| OLD | NEW |