| 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);
|
| + }
|
| }
|
|
|