Index: lib/src/codegen/js_codegen.dart |
diff --git a/lib/src/codegen/js_codegen.dart b/lib/src/codegen/js_codegen.dart |
index 64ab40352f67e3b22ff90d10b74a69ce7f324d7a..b41607f168acc516553c6ebae38e6797394e9a16 100644 |
--- a/lib/src/codegen/js_codegen.dart |
+++ b/lib/src/codegen/js_codegen.dart |
@@ -10,6 +10,7 @@ import 'package:analyzer/analyzer.dart' hide ConstantEvaluator; |
import 'package:analyzer/src/generated/ast.dart' hide ConstantEvaluator; |
import 'package:analyzer/src/generated/constant.dart'; |
import 'package:analyzer/src/generated/element.dart'; |
+import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; |
import 'package:analyzer/src/generated/resolver.dart' show TypeProvider; |
import 'package:analyzer/src/generated/scanner.dart' |
show StringToken, Token, TokenType; |
@@ -218,10 +219,7 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
// String scriptTag = null; |
// if (library.library.scriptTag != null) scriptTag = '/usr/bin/env $jsBin'; |
return moduleBuilder.build( |
- currentModuleName, |
- _jsModuleValue, |
- _exportsVar, |
- items); |
+ currentModuleName, _jsModuleValue, _exportsVar, items); |
} |
void _emitModuleItem(AstNode node) { |
@@ -439,9 +437,17 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
String jsPeerName; |
var jsPeer = findAnnotation(classElem, isJsPeerInterface); |
+ // Only look at "Native" annotations on registered extension types. |
+ // E.g., we're current ignoring the ones in dart:html. |
+ if (jsPeer == null && _extensionTypes.contains(classElem)) { |
+ jsPeer = findAnnotation(classElem, isNativeAnnotation); |
+ } |
if (jsPeer != null) { |
jsPeerName = |
getConstantField(jsPeer, 'name', types.stringType)?.toStringValue(); |
+ if (jsPeerName.contains(',')) { |
+ jsPeerName = jsPeerName.split(',')[0]; |
+ } |
} |
var body = _finishClassMembers(classElem, classExpr, ctors, fields, |
@@ -1322,17 +1328,49 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
} |
} |
+ JS.Fun _emitNativeFunctionBody( |
+ List<JS.Parameter> params, MethodDeclaration node) { |
+ if (node.isStatic) { |
+ // TODO(vsm): Do we need to handle this case? |
+ return null; |
+ } |
+ |
+ String name = node.name.name; |
+ var annotation = findAnnotation(node.element, isJsName); |
+ if (annotation != null) { |
+ name = getConstantField(annotation, 'name', types.stringType) |
+ ?.toStringValue(); |
+ } |
+ if (node.isGetter) { |
+ return new JS.Fun(params, js.statement('{ return this.#; }', [name])); |
+ } else if (node.isSetter) { |
+ return new JS.Fun( |
+ params, js.statement('{ this.# = #; }', [name, params.last])); |
+ } else { |
+ return new JS.Fun( |
+ params, js.statement('{ return this.#(#); }', [name, params])); |
+ } |
+ } |
+ |
JS.Method _emitMethodDeclaration(DartType type, MethodDeclaration node) { |
- if (node.isAbstract || _externalOrNative(node)) { |
+ if (node.isAbstract) { |
return null; |
} |
var params = _visit(node.parameters) as List<JS.Parameter>; |
if (params == null) params = <JS.Parameter>[]; |
- var typeParams = _emitTypeParams(node.element).toList(); |
- var returnType = emitTypeRef(node.element.returnType); |
- JS.Fun fn = _emitFunctionBody(params, node.body, typeParams, returnType); |
+ JS.Fun fn; |
+ if (_externalOrNative(node)) { |
+ fn = _emitNativeFunctionBody(params, node); |
+ // TODO(vsm): Remove if / when we handle the static case above. |
+ if (fn == null) return null; |
+ } else { |
+ var typeParams = _emitTypeParams(node.element).toList(); |
+ var returnType = emitTypeRef(node.element.returnType); |
+ fn = _emitFunctionBody(params, node.body, typeParams, returnType); |
+ } |
+ |
if (node.operatorKeyword != null && |
node.name.name == '[]=' && |
params.isNotEmpty) { |
@@ -2931,7 +2969,10 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
if (!_isObjectProperty(memberName)) { |
return false; |
} |
- if (!type.isObject && !_isJSBuiltinType(type) && !_isNullable(target)) { |
+ if (!type.isObject && |
+ !_isJSBuiltinType(type) && |
+ !_extensionTypes.contains(type.element) && |
+ !_isNullable(target)) { |
return false; |
} |
return true; |
@@ -3625,27 +3666,18 @@ class JSCodegenVisitor extends GeneralizingAstVisitor |
} |
} |
-class JSGenerator extends CodeGenerator { |
- final _extensionTypes = new HashSet<ClassElement>(); |
+class _ExtensionFinder extends GeneralizingElementVisitor { |
+ final AnalysisContext _context; |
+ final HashSet<ClassElement> _extensionTypes; |
final TypeProvider _types; |
- JSGenerator(AbstractCompiler compiler) |
- : _types = compiler.context.typeProvider, |
- super(compiler) { |
- // TODO(jacobr): determine the the set of types with extension methods from |
- // the annotations rather than hard coding the list once the analyzer |
- // supports summaries. |
- var context = compiler.context; |
- var src = context.sourceFactory.forUri('dart:_interceptors'); |
- var interceptors = context.computeLibraryElement(src); |
- for (var t in ['JSArray', 'JSString', 'JSNumber', 'JSBool']) { |
- _addExtensionType(interceptors.getType(t).type); |
+ |
+ _ExtensionFinder(this._context, this._extensionTypes, this._types); |
+ |
+ visitClassElement(ClassElement element) { |
+ if (findAnnotation(element, isJsPeerInterface) != null || |
+ findAnnotation(element, isNativeAnnotation) != null) { |
+ _addExtensionType(element.type); |
} |
- // TODO(jmesserly): manually add `int` and `double` |
- // Unfortunately our current analyzer rejects "implements int". |
- // Fix was landed, so we can remove this hack once we're updated: |
- // https://github.com/dart-lang/sdk/commit/d7cd11f86a02f55269fc8d9843e7758ebeeb81c8 |
- _addExtensionType(_types.intType); |
- _addExtensionType(_types.doubleType); |
} |
void _addExtensionType(InterfaceType t) { |
@@ -3656,6 +3688,35 @@ class JSGenerator extends CodeGenerator { |
_addExtensionType(t.superclass); |
} |
+ void _addExtensionTypes(String libraryUri) { |
+ var sourceFactory = _context.sourceFactory.forUri(libraryUri); |
+ var library = _context.computeLibraryElement(sourceFactory); |
+ visitLibraryElement(library); |
+ } |
+} |
+ |
+class JSGenerator extends CodeGenerator { |
+ final _extensionTypes = new HashSet<ClassElement>(); |
+ final TypeProvider _types; |
+ |
+ JSGenerator(AbstractCompiler compiler) |
+ : _types = compiler.context.typeProvider, |
+ super(compiler) { |
+ // TODO(vsm): Eventually, we want to make this extensible - i.e., find |
+ // annotations in user code as well. It would need to be summarized in |
+ // the element model - not searched this way on every compile. |
+ var finder = new _ExtensionFinder(context, _extensionTypes, _types); |
+ finder._addExtensionTypes('dart:_interceptors'); |
+ finder._addExtensionTypes('dart:_native_typed_data'); |
+ |
+ // TODO(vsm): If we're analyzing against the main SDK, those |
+ // types are not explicitly annotated. |
+ finder._addExtensionType(_types.intType); |
+ finder._addExtensionType(_types.doubleType); |
+ finder._addExtensionType(_types.boolType); |
+ finder._addExtensionType(_types.stringType); |
+ } |
+ |
String generateLibrary(LibraryUnit unit) { |
// Clone the AST first, so we can mutate it. |
unit = unit.clone(); |