Index: sdk/lib/_internal/compiler/implementation/native_handler.dart |
diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart |
index 038dc439bdeaddab2efba13b169ac7c874aeb819..3e37ed3ab500365a5be2f4c7f4fc45eea1ccfdd5 100644 |
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart |
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart |
@@ -35,15 +35,18 @@ class NativeEnqueuer { |
/// Initial entry point to native enqueuer. |
void processNativeClasses(Collection<LibraryElement> libraries) {} |
+ /// Notification of a main Enqueuer worklist element. For methods, adds |
+ /// information from metadata attributes, and computes types instantiated due |
+ /// to calling the method. |
void registerElement(Element element) {} |
- /// Method is a member of a native class. |
- void registerMethod(Element method) {} |
+ /// Notification of native field. Adds information from metadata attributes. |
+ void registerField(Element field) {} |
- /// Compute types instantiated due to getting a native field. |
+ /// Computes types instantiated due to getting a native field. |
void registerFieldLoad(Element field) {} |
- /// Compute types instantiated due to setting a native field. |
+ /// Computes types instantiated due to setting a native field. |
void registerFieldStore(Element field) {} |
/** |
@@ -93,6 +96,7 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
ClassElement _annotationCreatesClass; |
ClassElement _annotationReturnsClass; |
+ ClassElement _annotationJsNameClass; |
/// Subclasses of [NativeEnqueuerBase] are constructed by the backend. |
NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); |
@@ -122,16 +126,22 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
} |
ClassElement get annotationCreatesClass { |
- if (_annotationCreatesClass == null) findAnnotationClasses(); |
+ findAnnotationClasses(); |
return _annotationCreatesClass; |
} |
ClassElement get annotationReturnsClass { |
- if (_annotationReturnsClass == null) findAnnotationClasses(); |
+ findAnnotationClasses(); |
return _annotationReturnsClass; |
} |
+ ClassElement get annotationJsNameClass { |
+ findAnnotationClasses(); |
+ return _annotationJsNameClass; |
+ } |
+ |
void findAnnotationClasses() { |
+ if (_annotationCreatesClass != null) return; |
ClassElement find(name) { |
Element e = compiler.findHelper(name); |
if (e == null || e is! ClassElement) { |
@@ -141,8 +151,41 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
} |
_annotationCreatesClass = find(const SourceString('Creates')); |
_annotationReturnsClass = find(const SourceString('Returns')); |
+ _annotationJsNameClass = find(const SourceString('JSName')); |
} |
+ /// Returns the JSName annotation string or `null` if no JSName annotation is |
+ /// present. |
+ String findJsNameFromAnnotation(Element element) { |
+ String name = null; |
+ ClassElement annotationClass = annotationJsNameClass; |
+ for (Link<MetadataAnnotation> link = element.metadata; |
+ !link.isEmpty; |
+ link = link.tail) { |
+ MetadataAnnotation annotation = link.head.ensureResolved(compiler); |
+ var value = annotation.value; |
+ if (value is! ConstructedConstant) continue; |
+ if (value.type is! InterfaceType) continue; |
+ if (!identical(value.type.element, annotationClass)) continue; |
+ |
+ var fields = value.fields; |
+ // TODO(sra): Better validation of the constant. |
+ if (fields.length != 1 || fields[0] is! StringConstant) { |
+ PartialMetadataAnnotation partial = annotation; |
+ compiler.cancel( |
+ 'Annotations needs one string: ${partial.parseNode(compiler)}'); |
+ } |
+ String specString = fields[0].toDartString().slowToString(); |
+ if (name == null) { |
+ name = specString; |
+ } else { |
+ PartialMetadataAnnotation partial = annotation; |
+ compiler.cancel( |
+ 'Too many JSName annotations: ${partial.parseNode(compiler)}'); |
+ } |
+ } |
+ return name; |
+ } |
enqueueClass(ClassElement classElement, cause) { |
assert(unusedClasses.contains(classElement)); |
@@ -179,11 +222,20 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
} |
registerElement(Element element) { |
- if (element.isFunction()) return registerMethod(element); |
+ if (element.isFunction() || element.isGetter() || element.isSetter()) { |
+ return registerMethod(element); |
+ } |
+ } |
+ |
+ registerField(Element element) { |
+ if (element.enclosingElement.isNative()) { |
+ setNativeName(element); |
+ } |
} |
registerMethod(Element method) { |
if (isNativeMethod(method)) { |
+ setNativeName(method); |
processNativeBehavior( |
NativeBehavior.ofMethod(method, compiler), |
method); |
@@ -191,6 +243,14 @@ abstract class NativeEnqueuerBase implements NativeEnqueuer { |
} |
} |
+ /// Sets the native name of [element], either from an annotation, or |
+ /// defaulting to the Dart name. |
+ void setNativeName(Element element) { |
+ String name = findJsNameFromAnnotation(element); |
+ if (name == null) name = element.name.slowToString(); |
+ element.setNative(name); |
+ } |
+ |
bool isNativeMethod(Element element) { |
if (!element.getLibrary().canUseNative) return false; |
// Native method? |
@@ -667,15 +727,15 @@ Token handleNativeFunctionBody(ElementListener listener, Token token) { |
} |
SourceString checkForNativeClass(ElementListener listener) { |
- SourceString nativeName; |
+ SourceString nativeTagInfo; |
Node node = listener.nodes.head; |
if (node != null |
&& node.asIdentifier() != null |
&& node.asIdentifier().source.stringValue == 'native') { |
- nativeName = node.asIdentifier().token.next.value; |
+ nativeTagInfo = node.asIdentifier().token.next.value; |
listener.popNode(); |
} |
- return nativeName; |
+ return nativeTagInfo; |
} |
bool isOverriddenMethod(FunctionElement element, |
@@ -694,7 +754,6 @@ final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$'); |
void handleSsaNative(SsaBuilder builder, Expression nativeBody) { |
Compiler compiler = builder.compiler; |
FunctionElement element = builder.work.element; |
- element.setNative(); |
NativeEmitter nativeEmitter = builder.emitter.nativeEmitter; |
// If what we're compiling is a getter named 'typeName' and the native |
// class is named 'DOMType', we generate a call to the typeNameOf |
@@ -724,22 +783,23 @@ void handleSsaNative(SsaBuilder builder, Expression nativeBody) { |
} |
// Check which pattern this native method follows: |
- // 1) foo() native; hasBody = false, isRedirecting = false |
- // 2) foo() native "bar"; hasBody = false, isRedirecting = true |
- // 3) foo() native "return 42"; hasBody = true, isRedirecting = false |
+ // 1) foo() native; |
+ // hasBody = false, isRedirecting = false |
+ // 2) foo() native "bar"; |
+ // hasBody = false, isRedirecting = true, no longer supported. |
+ // 3) foo() native "return 42"; |
+ // hasBody = true, isRedirecting = false |
bool hasBody = false; |
- bool isRedirecting = false; |
- String nativeMethodName = element.name.slowToString(); |
+ assert(element.isNative()); |
+ String nativeMethodName = element.nativeName(); |
if (nativeBody != null) { |
LiteralString jsCode = nativeBody.asLiteralString(); |
String str = jsCode.dartString.slowToString(); |
if (nativeRedirectionRegExp.hasMatch(str)) { |
- nativeMethodName = str; |
- isRedirecting = true; |
- nativeEmitter.addRedirectingMethod(element, nativeMethodName); |
- } else { |
- hasBody = true; |
+ compiler.cancel("Deprecated syntax, use @JSName('name') instead.", |
+ node: nativeBody); |
} |
+ hasBody = true; |
} |
if (!hasBody) { |