Index: pkg/compiler/lib/src/resolution/resolution_strategy.dart |
diff --git a/pkg/compiler/lib/src/resolution/resolution_strategy.dart b/pkg/compiler/lib/src/resolution/resolution_strategy.dart |
index b71bd4c056d0f2f2ce23a81b10703ac096c2ae92..32800d8307c086e9fb321063992df02492b017f5 100644 |
--- a/pkg/compiler/lib/src/resolution/resolution_strategy.dart |
+++ b/pkg/compiler/lib/src/resolution/resolution_strategy.dart |
@@ -10,6 +10,7 @@ import '../common/backend_api.dart'; |
import '../common/names.dart'; |
import '../common/resolution.dart'; |
import '../common/tasks.dart'; |
+import '../constants/values.dart'; |
import '../compiler.dart'; |
import '../elements/elements.dart'; |
import '../elements/entities.dart'; |
@@ -431,6 +432,8 @@ class _ElementAnnotationProcessor implements AnnotationProcessor { |
_ElementAnnotationProcessor(this._compiler); |
+ CommonElements get _commonElements => _compiler.commonElements; |
+ |
/// Check whether [cls] has a `@Native(...)` annotation, and if so, set its |
/// native name from the annotation. |
void extractNativeAnnotations( |
@@ -462,4 +465,150 @@ class _ElementAnnotationProcessor implements AnnotationProcessor { |
} |
}); |
} |
+ |
+ void processJsInteropAnnotations( |
+ NativeBasicData nativeBasicData, NativeDataBuilder nativeDataBuilder) { |
+ if (_commonElements.jsAnnotationClass == null) return; |
+ |
+ ClassElement cls = _commonElements.jsAnnotationClass; |
+ FieldElement nameField = cls.lookupMember('name'); |
+ |
+ /// Resolves the metadata of [element] and returns the name of the `JS(...)` |
+ /// annotation for js interop, if found. |
+ String processJsInteropAnnotation(Element element) { |
+ for (MetadataAnnotation annotation in element.implementation.metadata) { |
+ // TODO(johnniwinther): Avoid processing unresolved elements. |
+ if (annotation.constant == null) continue; |
+ ConstantValue constant = |
+ _compiler.constants.getConstantValue(annotation.constant); |
+ if (constant == null || constant is! ConstructedConstantValue) continue; |
+ ConstructedConstantValue constructedConstant = constant; |
+ if (constructedConstant.type.element == |
+ _commonElements.jsAnnotationClass) { |
+ ConstantValue value = constructedConstant.fields[nameField]; |
+ String name; |
+ if (value.isString) { |
+ StringConstantValue stringValue = value; |
+ name = stringValue.primitiveValue.slowToString(); |
+ } else { |
+ // TODO(jacobr): report a warning if the value is not a String. |
+ name = ''; |
+ } |
+ return name; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ void checkFunctionParameters(MethodElement fn) { |
+ if (fn.hasFunctionSignature && |
+ fn.functionSignature.optionalParametersAreNamed) { |
+ _compiler.reporter.reportErrorMessage( |
+ fn, |
+ MessageKind.JS_INTEROP_METHOD_WITH_NAMED_ARGUMENTS, |
+ {'method': fn.name}); |
+ } |
+ } |
+ |
+ bool hasAnonymousAnnotation(Element element) { |
+ if (_commonElements.jsAnonymousClass == null) return false; |
+ return element.metadata.any((MetadataAnnotation annotation) { |
+ ConstantValue constant = |
+ _compiler.constants.getConstantValue(annotation.constant); |
+ if (constant == null || constant is! ConstructedConstantValue) |
+ return false; |
+ ConstructedConstantValue constructedConstant = constant; |
+ return constructedConstant.type.element == |
+ _commonElements.jsAnonymousClass; |
+ }); |
+ } |
+ |
+ void processJsInteropAnnotationsInLibrary(LibraryElement library) { |
+ String libraryName = processJsInteropAnnotation(library); |
+ if (libraryName != null) { |
+ nativeDataBuilder.setJsInteropLibraryName(library, libraryName); |
+ } |
+ library.implementation.forEachLocalMember((Element element) { |
+ if (element is MemberElement) { |
+ String memberName = processJsInteropAnnotation(element); |
+ if (memberName != null) { |
+ nativeDataBuilder.setJsInteropMemberName(element, memberName); |
+ if (element is MethodElement) { |
+ checkFunctionParameters(element); |
+ } |
+ } |
+ } |
+ |
+ if (!element.isClass) return; |
+ |
+ ClassElement classElement = element; |
+ String className = processJsInteropAnnotation(classElement); |
+ if (className != null) { |
+ nativeDataBuilder.setJsInteropClassName(classElement, className); |
+ } |
+ if (!nativeBasicData.isJsInteropClass(classElement)) return; |
+ |
+ bool isAnonymous = hasAnonymousAnnotation(classElement); |
+ if (isAnonymous) { |
+ nativeDataBuilder.markJsInteropClassAsAnonymous(classElement); |
+ } |
+ |
+ // Skip classes that are completely unreachable. This should only happen |
+ // when all of jsinterop types are unreachable from main. |
+ if (!_compiler.resolutionWorldBuilder.isImplemented(classElement)) { |
+ return; |
+ } |
+ |
+ if (!classElement |
+ .implementsInterface(_commonElements.jsJavaScriptObjectClass)) { |
+ _compiler.reporter.reportErrorMessage(classElement, |
+ MessageKind.JS_INTEROP_CLASS_CANNOT_EXTEND_DART_CLASS, { |
+ 'cls': classElement.name, |
+ 'superclass': classElement.superclass.name |
+ }); |
+ } |
+ |
+ classElement |
+ .forEachMember((ClassElement classElement, MemberElement member) { |
+ String memberName = processJsInteropAnnotation(member); |
+ if (memberName != null) { |
+ nativeDataBuilder.setJsInteropMemberName(member, memberName); |
+ } |
+ |
+ if (!member.isSynthesized && |
+ nativeBasicData.isJsInteropClass(classElement) && |
+ member is MethodElement) { |
+ MethodElement fn = member; |
+ if (!fn.isExternal && |
+ !fn.isAbstract && |
+ !fn.isConstructor && |
+ !fn.isStatic) { |
+ _compiler.reporter.reportErrorMessage( |
+ fn, |
+ MessageKind.JS_INTEROP_CLASS_NON_EXTERNAL_MEMBER, |
+ {'cls': classElement.name, 'member': member.name}); |
+ } |
+ |
+ if (fn.isFactoryConstructor && isAnonymous) { |
+ fn.functionSignature |
+ .orderedForEachParameter((ParameterElement parameter) { |
+ if (!parameter.isNamed) { |
+ _compiler.reporter.reportErrorMessage( |
+ parameter, |
+ MessageKind |
+ .JS_OBJECT_LITERAL_CONSTRUCTOR_WITH_POSITIONAL_ARGUMENTS, |
+ {'parameter': parameter.name, 'cls': classElement.name}); |
+ } |
+ }); |
+ } else { |
+ checkFunctionParameters(fn); |
+ } |
+ } |
+ }); |
+ }); |
+ } |
+ |
+ _compiler.libraryLoader.libraries |
+ .forEach(processJsInteropAnnotationsInLibrary); |
+ } |
} |