 Chromium Code Reviews
 Chromium Code Reviews Issue 1580413002:
  Add a `@JSExportName` annotation for internal use in the runtime (use it to export dart.assert inst…  (Closed) 
  Base URL: git@github.com:dart-lang/dev_compiler.git@master
    
  
    Issue 1580413002:
  Add a `@JSExportName` annotation for internal use in the runtime (use it to export dart.assert inst…  (Closed) 
  Base URL: git@github.com:dart-lang/dev_compiler.git@master| 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 1276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1287 } | 1287 } | 
| 1288 | 1288 | 
| 1289 return annotate( | 1289 return annotate( | 
| 1290 new JS.Method(_elementMemberName(node.element), fn, | 1290 new JS.Method(_elementMemberName(node.element), fn, | 
| 1291 isGetter: node.isGetter, | 1291 isGetter: node.isGetter, | 
| 1292 isSetter: node.isSetter, | 1292 isSetter: node.isSetter, | 
| 1293 isStatic: node.isStatic), | 1293 isStatic: node.isStatic), | 
| 1294 node.element); | 1294 node.element); | 
| 1295 } | 1295 } | 
| 1296 | 1296 | 
| 1297 /// Returns the name value of the `JSExportName` annotation (when compiling | |
| 1298 /// the SDK), or `null` if there's none. This is used to control the name | |
| 1299 /// under which functions are compiled and exported. | |
| 1300 String _getJSExportName(Element e) { | |
| 1301 if (e is! FunctionElement || !currentLibrary.source.isInSystemLibrary) { | |
| 1302 return null; | |
| 1303 } | |
| 1304 var jsName = findAnnotation(e, isJSExportNameAnnotation); | |
| 1305 return getConstantField(jsName, 'name', types.stringType)?.toStringValue(); | |
| 1306 } | |
| 1307 | |
| 1297 @override | 1308 @override | 
| 1298 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 1309 JS.Statement visitFunctionDeclaration(FunctionDeclaration node) { | 
| 1299 assert(node.parent is CompilationUnit); | 1310 assert(node.parent is CompilationUnit); | 
| 1300 | 1311 | 
| 1301 if (_externalOrNative(node)) return null; | 1312 if (_externalOrNative(node)) return null; | 
| 1302 | 1313 | 
| 1303 if (node.isGetter || node.isSetter) { | 1314 if (node.isGetter || node.isSetter) { | 
| 1304 // Add these later so we can use getter/setter syntax. | 1315 // Add these later so we can use getter/setter syntax. | 
| 1305 _properties.add(node); | 1316 _properties.add(node); | 
| 1306 return null; | 1317 return null; | 
| 1307 } | 1318 } | 
| 1308 | 1319 | 
| 1309 var body = <JS.Statement>[]; | 1320 var body = <JS.Statement>[]; | 
| 1310 _flushLibraryProperties(body); | 1321 _flushLibraryProperties(body); | 
| 1311 | 1322 | 
| 1312 var name = node.name.name; | 1323 var name = _getJSExportName(node.element) ?? node.name.name; | 
| 1313 | 1324 | 
| 1314 var fn = _visit(node.functionExpression); | 1325 var fn = _visit(node.functionExpression); | 
| 1315 bool needsTagging = true; | 1326 bool needsTagging = true; | 
| 1316 | 1327 | 
| 1317 if (currentLibrary.source.isInSystemLibrary && | 1328 if (currentLibrary.source.isInSystemLibrary && | 
| 1318 _isInlineJSFunction(node.functionExpression)) { | 1329 _isInlineJSFunction(node.functionExpression)) { | 
| 1319 fn = _simplifyPassThroughArrowFunCallBody(fn); | 1330 fn = _simplifyPassThroughArrowFunCallBody(fn); | 
| 1320 needsTagging = !_isDartUtils; | 1331 needsTagging = !_isDartUtils; | 
| 1321 } | 1332 } | 
| 1322 | 1333 | 
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1568 } | 1579 } | 
| 1569 | 1580 | 
| 1570 // Get the original declaring element. If we had a property accessor, this | 1581 // Get the original declaring element. If we had a property accessor, this | 
| 1571 // indirects back to a (possibly synthetic) field. | 1582 // indirects back to a (possibly synthetic) field. | 
| 1572 var element = accessor; | 1583 var element = accessor; | 
| 1573 if (accessor is PropertyAccessorElement) element = accessor.variable; | 1584 if (accessor is PropertyAccessorElement) element = accessor.variable; | 
| 1574 if (accessor is FunctionMember) element = accessor.baseElement; | 1585 if (accessor is FunctionMember) element = accessor.baseElement; | 
| 1575 | 1586 | 
| 1576 _loader.declareBeforeUse(element); | 1587 _loader.declareBeforeUse(element); | 
| 1577 | 1588 | 
| 1578 var name = element.name; | |
| 1579 | |
| 1580 // type literal | 1589 // type literal | 
| 1581 if (element is ClassElement || | 1590 if (element is ClassElement || | 
| 1582 element is DynamicElementImpl || | 1591 element is DynamicElementImpl || | 
| 1583 element is FunctionTypeAliasElement) { | 1592 element is FunctionTypeAliasElement) { | 
| 1584 return _emitTypeName( | 1593 return _emitTypeName( | 
| 1585 fillDynamicTypeArgs((element as dynamic).type, types)); | 1594 fillDynamicTypeArgs((element as dynamic).type, types)); | 
| 1586 } | 1595 } | 
| 1587 | 1596 | 
| 1588 // library member | 1597 // library member | 
| 1589 if (element.enclosingElement is CompilationUnitElement) { | 1598 if (element.enclosingElement is CompilationUnitElement) { | 
| 1590 return _maybeQualifiedName(element); | 1599 return _emitTopLevelName(element); | 
| 
Jennifer Messerly
2016/01/13 17:43:59
nice! it's funny how a name change makes such a di
 
ochafik
2016/01/13 18:51:29
Yeah, *sounds* so much more deterministic now ;-)
 | |
| 1591 } | 1600 } | 
| 1592 | 1601 | 
| 1602 var name = element.name; | |
| 1603 | |
| 1593 // Unqualified class member. This could mean implicit-this, or implicit | 1604 // Unqualified class member. This could mean implicit-this, or implicit | 
| 1594 // call to a static from the same class. | 1605 // call to a static from the same class. | 
| 1595 if (element is ClassMemberElement && element is! ConstructorElement) { | 1606 if (element is ClassMemberElement && element is! ConstructorElement) { | 
| 1596 bool isStatic = element.isStatic; | 1607 bool isStatic = element.isStatic; | 
| 1597 var type = element.enclosingElement.type; | 1608 var type = element.enclosingElement.type; | 
| 1598 var member = _emitMemberName(name, isStatic: isStatic, type: type); | 1609 var member = _emitMemberName(name, isStatic: isStatic, type: type); | 
| 1599 | 1610 | 
| 1600 // For static methods, we add the raw type name, without generics or | 1611 // For static methods, we add the raw type name, without generics or | 
| 1601 // library prefix. We don't need those because static calls can't use | 1612 // library prefix. We don't need those because static calls can't use | 
| 1602 // the generic type. | 1613 // the generic type. | 
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1750 if (args | 1761 if (args | 
| 1751 .any((a) => a != types.dynamicType && !_isGenericTypeParameter(a))) { | 1762 .any((a) => a != types.dynamicType && !_isGenericTypeParameter(a))) { | 
| 1752 jsArgs = args.map(_emitTypeName); | 1763 jsArgs = args.map(_emitTypeName); | 
| 1753 } else if (lowerGeneric || isCurrentClass) { | 1764 } else if (lowerGeneric || isCurrentClass) { | 
| 1754 // When creating a `new S<dynamic>` we try and use the raw form | 1765 // When creating a `new S<dynamic>` we try and use the raw form | 
| 1755 // `new S()`, but this does not work if we're inside the same class, | 1766 // `new S()`, but this does not work if we're inside the same class, | 
| 1756 // because `S` refers to the current S<T> we are generating. | 1767 // because `S` refers to the current S<T> we are generating. | 
| 1757 jsArgs = []; | 1768 jsArgs = []; | 
| 1758 } | 1769 } | 
| 1759 if (jsArgs != null) { | 1770 if (jsArgs != null) { | 
| 1760 var genericName = _maybeQualifiedName(element, '$name\$'); | 1771 var genericName = _emitTopLevelName(element, '$name\$'); | 
| 1761 return js.call('#(#)', [genericName, jsArgs]); | 1772 return js.call('#(#)', [genericName, jsArgs]); | 
| 1762 } | 1773 } | 
| 1763 } | 1774 } | 
| 1764 | 1775 | 
| 1765 return _maybeQualifiedName(element); | 1776 return _emitTopLevelName(element); | 
| 1766 } | 1777 } | 
| 1767 | 1778 | 
| 1768 JS.Expression _maybeQualifiedName(Element e, [String name]) { | 1779 JS.Expression _emitTopLevelName(Element e, [String name]) { | 
| 1769 var libName = _libraryName(e.library); | 1780 var libName = _libraryName(e.library); | 
| 1770 var nameExpr = _propertyName(name ?? e.name); | 1781 var nameExpr = _propertyName(name ?? _getJSExportName(e) ?? e.name); | 
| 
Jennifer Messerly
2016/01/13 17:44:00
oh wow, the [name] optional parameter--which was p
 
ochafik
2016/01/13 18:51:29
Cool! Done.
 | |
| 1771 | 1782 | 
| 1772 // Always qualify: | 1783 // Always qualify: | 
| 1773 // * mutable top-level fields | 1784 // * mutable top-level fields | 
| 1774 // * elements from other libraries | 1785 // * elements from other libraries | 
| 1775 bool mutableTopLevel = e is TopLevelVariableElement && | 1786 bool mutableTopLevel = e is TopLevelVariableElement && | 
| 1776 !e.isConst && | 1787 !e.isConst && | 
| 1777 !_isFinalJSDecl(e.computeNode()); | 1788 !_isFinalJSDecl(e.computeNode()); | 
| 1778 bool fromAnotherLibrary = e.library != currentLibrary; | 1789 bool fromAnotherLibrary = e.library != currentLibrary; | 
| 1779 if (mutableTopLevel || fromAnotherLibrary) { | 1790 if (mutableTopLevel || fromAnotherLibrary) { | 
| 1780 return new JS.PropertyAccess(libName, nameExpr); | 1791 return new JS.PropertyAccess(libName, nameExpr); | 
| (...skipping 442 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2223 } else { | 2234 } else { | 
| 2224 jsInit = _visitInitializer(field); | 2235 jsInit = _visitInitializer(field); | 
| 2225 eagerInit = false; | 2236 eagerInit = false; | 
| 2226 } | 2237 } | 
| 2227 | 2238 | 
| 2228 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile | 2239 // Treat `final x = JS('', '...')` as a const (non-lazy) to help compile | 
| 2229 // runtime helpers. | 2240 // runtime helpers. | 
| 2230 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); | 2241 var isJSTopLevel = field.isFinal && _isFinalJSDecl(field); | 
| 2231 if (isJSTopLevel) eagerInit = true; | 2242 if (isJSTopLevel) eagerInit = true; | 
| 2232 | 2243 | 
| 2233 var fieldName = field.name.name; | 2244 var fieldName = _getJSExportName(element) ?? field.name.name; | 
| 
Jennifer Messerly
2016/01/13 17:43:59
So for top-level fields, I think this works.
I wo
 
ochafik
2016/01/13 18:51:29
Aaaah, absolutely, thanks!
 | |
| 2234 if ((field.isConst && eagerInit && element is TopLevelVariableElement) || | 2245 if ((field.isConst && eagerInit && element is TopLevelVariableElement) || | 
| 2235 isJSTopLevel) { | 2246 isJSTopLevel) { | 
| 2236 // constant fields don't change, so we can generate them as `let` | 2247 // constant fields don't change, so we can generate them as `let` | 
| 2237 // but add them to the module's exports. However, make sure we generate | 2248 // but add them to the module's exports. However, make sure we generate | 
| 2238 // anything they depend on first. | 2249 // anything they depend on first. | 
| 2239 | 2250 | 
| 2240 if (isPublic(fieldName)) _addExport(fieldName); | 2251 if (isPublic(fieldName)) _addExport(fieldName); | 
| 2241 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; | 2252 var declKeyword = field.isConst || field.isFinal ? 'const' : 'let'; | 
| 2242 return annotateVariable( | 2253 return annotateVariable( | 
| 2243 js.statement( | 2254 js.statement( | 
| (...skipping 1376 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3620 | 3631 | 
| 3621 /// A special kind of element created by the compiler, signifying a temporary | 3632 /// A special kind of element created by the compiler, signifying a temporary | 
| 3622 /// variable. These objects use instance equality, and should be shared | 3633 /// variable. These objects use instance equality, and should be shared | 
| 3623 /// everywhere in the tree where they are treated as the same variable. | 3634 /// everywhere in the tree where they are treated as the same variable. | 
| 3624 class TemporaryVariableElement extends LocalVariableElementImpl { | 3635 class TemporaryVariableElement extends LocalVariableElementImpl { | 
| 3625 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 3636 TemporaryVariableElement.forNode(Identifier name) : super.forNode(name); | 
| 3626 | 3637 | 
| 3627 int get hashCode => identityHashCode(this); | 3638 int get hashCode => identityHashCode(this); | 
| 3628 bool operator ==(Object other) => identical(this, other); | 3639 bool operator ==(Object other) => identical(this, other); | 
| 3629 } | 3640 } | 
| OLD | NEW |