| Index: pkg/compiler/lib/src/native/resolver.dart
|
| diff --git a/pkg/compiler/lib/src/native/resolver.dart b/pkg/compiler/lib/src/native/resolver.dart
|
| index ba302d652cbdc8e1b10d6ae3aea221909df7ac13..30e91708f14ef88f9c76f5b2a31b479219e19fa8 100644
|
| --- a/pkg/compiler/lib/src/native/resolver.dart
|
| +++ b/pkg/compiler/lib/src/native/resolver.dart
|
| @@ -29,16 +29,19 @@ import '../patch_parser.dart';
|
| import '../tree/tree.dart';
|
| import 'behavior.dart';
|
|
|
| +/// Interface for computing native members.
|
| +abstract class NativeMemberResolver {
|
| + /// Computes whether [element] is native or JsInterop and, if so, registers
|
| + /// its [NativeBehavior]s to [registry].
|
| + void resolveNativeMember(MemberEntity element, [NativeRegistry registry]);
|
| +}
|
| +
|
| /// Interface for computing native members and [NativeBehavior]s in member code
|
| /// based on the AST.
|
| -abstract class NativeDataResolver {
|
| +abstract class NativeDataResolver implements NativeMemberResolver {
|
| /// Returns `true` if [element] is a JsInterop member.
|
| bool isJsInteropMember(MemberElement element);
|
|
|
| - /// Computes whether [element] is native or JsInterop and, if so, registers
|
| - /// its [NativeBehavior]s to [registry].
|
| - void resolveNativeMember(MemberElement element, NativeRegistry registry);
|
| -
|
| /// Computes the [NativeBehavior] for a `JS` call, which can be an
|
| /// instantiation point for types.
|
| ///
|
| @@ -69,81 +72,65 @@ abstract class NativeDataResolver {
|
| NativeBehavior resolveJsBuiltinCall(Send node, ForeignResolver resolver);
|
| }
|
|
|
| -class NativeDataResolverImpl implements NativeDataResolver {
|
| +abstract class NativeMemberResolverBase implements NativeMemberResolver {
|
| static final RegExp _identifier = new RegExp(r'^[a-zA-Z_$][a-zA-Z0-9_$]*$');
|
|
|
| - final Compiler _compiler;
|
| + ElementEnvironment get elementEnvironment;
|
| + CommonElements get commonElements;
|
| + NativeBasicData get nativeBasicData;
|
| + NativeDataBuilder get nativeDataBuilder;
|
|
|
| - NativeDataResolverImpl(this._compiler);
|
| + bool isJsInteropMember(MemberEntity element);
|
| + bool isNativeMethod(FunctionEntity element);
|
|
|
| - JavaScriptBackend get _backend => _compiler.backend;
|
| - DiagnosticReporter get _reporter => _compiler.reporter;
|
| - NativeBasicData get _nativeBasicData => _backend.nativeBasicData;
|
| - NativeDataBuilder get _nativeDataBuilder => _backend.nativeDataBuilder;
|
| + NativeBehavior computeNativeMethodBehavior(FunctionEntity function,
|
| + {bool isJsInterop});
|
| + NativeBehavior computeNativeFieldLoadBehavior(FieldEntity field,
|
| + {bool isJsInterop});
|
| + NativeBehavior computeNativeFieldStoreBehavior(FieldEntity field);
|
|
|
| @override
|
| - bool isJsInteropMember(MemberElement element) {
|
| - // TODO(johnniwinther): Avoid computing this twice for external function;
|
| - // once from JavaScriptBackendTarget.resolveExternalFunction and once
|
| - // through JavaScriptBackendTarget.resolveNativeMember.
|
| - bool isJsInterop =
|
| - checkJsInteropMemberAnnotations(_compiler, element, _nativeDataBuilder);
|
| - // TODO(johnniwinther): Avoid this duplication of logic from
|
| - // NativeData.isJsInterop.
|
| - if (!isJsInterop && element is MethodElement && element.isExternal) {
|
| - if (element.enclosingClass != null) {
|
| - isJsInterop = _nativeBasicData.isJsInteropClass(element.enclosingClass);
|
| - } else {
|
| - isJsInterop = _nativeBasicData.isJsInteropLibrary(element.library);
|
| - }
|
| - }
|
| - return isJsInterop;
|
| - }
|
| -
|
| - void resolveNativeMember(MemberElement element, NativeRegistry registry) {
|
| + void resolveNativeMember(MemberEntity element, [NativeRegistry registry]) {
|
| bool isJsInterop = isJsInteropMember(element);
|
| if (element.isFunction ||
|
| element.isConstructor ||
|
| element.isGetter ||
|
| element.isSetter) {
|
| - MethodElement method = element;
|
| + FunctionEntity method = element;
|
| bool isNative = _processMethodAnnotations(method);
|
| if (isNative || isJsInterop) {
|
| - NativeBehavior behavior = NativeBehavior
|
| - .ofMethodElement(method, _compiler, isJsInterop: isJsInterop);
|
| - _nativeDataBuilder.setNativeMethodBehavior(method, behavior);
|
| - registry.registerNativeData(behavior);
|
| + NativeBehavior behavior =
|
| + computeNativeMethodBehavior(method, isJsInterop: isJsInterop);
|
| + nativeDataBuilder.setNativeMethodBehavior(method, behavior);
|
| + registry?.registerNativeData(behavior);
|
| }
|
| } else if (element.isField) {
|
| - FieldElement field = element;
|
| + FieldEntity field = element;
|
| bool isNative = _processFieldAnnotations(field);
|
| if (isNative || isJsInterop) {
|
| - NativeBehavior fieldLoadBehavior = NativeBehavior
|
| - .ofFieldElementLoad(field, _compiler, isJsInterop: isJsInterop);
|
| + NativeBehavior fieldLoadBehavior =
|
| + computeNativeFieldLoadBehavior(field, isJsInterop: isJsInterop);
|
| NativeBehavior fieldStoreBehavior =
|
| - NativeBehavior.ofFieldElementStore(field, _compiler);
|
| - _nativeDataBuilder.setNativeFieldLoadBehavior(field, fieldLoadBehavior);
|
| - _nativeDataBuilder.setNativeFieldStoreBehavior(
|
| + computeNativeFieldStoreBehavior(field);
|
| + nativeDataBuilder.setNativeFieldLoadBehavior(field, fieldLoadBehavior);
|
| + nativeDataBuilder.setNativeFieldStoreBehavior(
|
| field, fieldStoreBehavior);
|
|
|
| // TODO(sra): Process fields for storing separately.
|
| // We have to handle both loading and storing to the field because we
|
| // only get one look at each member and there might be a load or store
|
| // we have not seen yet.
|
| - registry.registerNativeData(fieldLoadBehavior);
|
| - registry.registerNativeData(fieldStoreBehavior);
|
| + registry?.registerNativeData(fieldLoadBehavior);
|
| + registry?.registerNativeData(fieldStoreBehavior);
|
| }
|
| }
|
| }
|
|
|
| /// Process the potentially native [field]. Adds information from metadata
|
| /// attributes. Returns `true` of [method] is native.
|
| - bool _processFieldAnnotations(Element element) {
|
| - if (_compiler.serialization.isDeserialized(element)) {
|
| - return false;
|
| - }
|
| + bool _processFieldAnnotations(FieldEntity element) {
|
| if (element.isInstanceMember &&
|
| - _backend.nativeBasicData.isNativeClass(element.enclosingClass)) {
|
| + nativeBasicData.isNativeClass(element.enclosingClass)) {
|
| // Exclude non-instance (static) fields - they are not really native and
|
| // are compiled as isolate globals. Access of a property of a constructor
|
| // function or a non-method property in the prototype chain, must be coded
|
| @@ -156,11 +143,8 @@ class NativeDataResolverImpl implements NativeDataResolver {
|
|
|
| /// Process the potentially native [method]. Adds information from metadata
|
| /// attributes. Returns `true` of [method] is native.
|
| - bool _processMethodAnnotations(Element method) {
|
| - if (_compiler.serialization.isDeserialized(method)) {
|
| - return false;
|
| - }
|
| - if (_isNativeMethod(method)) {
|
| + bool _processMethodAnnotations(FunctionEntity method) {
|
| + if (isNativeMethod(method)) {
|
| if (method.isStatic) {
|
| _setNativeNameForStaticMethod(method);
|
| } else {
|
| @@ -173,10 +157,10 @@ class NativeDataResolverImpl implements NativeDataResolver {
|
|
|
| /// Sets the native name of [element], either from an annotation, or
|
| /// defaulting to the Dart name.
|
| - void _setNativeName(MemberElement element) {
|
| + void _setNativeName(MemberEntity element) {
|
| String name = _findJsNameFromAnnotation(element);
|
| if (name == null) name = element.name;
|
| - _nativeDataBuilder.setNativeMemberName(element, name);
|
| + nativeDataBuilder.setNativeMemberName(element, name);
|
| }
|
|
|
| /// Sets the native name of the static native method [element], using the
|
| @@ -187,28 +171,97 @@ class NativeDataResolverImpl implements NativeDataResolver {
|
| /// use the declared @JSName as the expression
|
| /// 3. If [element] does not have a @JSName annotation, qualify the name of
|
| /// the method with the @Native name of the enclosing class.
|
| - void _setNativeNameForStaticMethod(MethodElement element) {
|
| + void _setNativeNameForStaticMethod(FunctionEntity element) {
|
| String name = _findJsNameFromAnnotation(element);
|
| if (name == null) name = element.name;
|
| if (_isIdentifier(name)) {
|
| List<String> nativeNames =
|
| - _nativeBasicData.getNativeTagsOfClass(element.enclosingClass);
|
| + nativeBasicData.getNativeTagsOfClass(element.enclosingClass);
|
| if (nativeNames.length != 1) {
|
| - _reporter.internalError(
|
| + throw new SpannableAssertionFailure(
|
| element,
|
| 'Unable to determine a native name for the enclosing class, '
|
| 'options: $nativeNames');
|
| }
|
| - _nativeDataBuilder.setNativeMemberName(
|
| - element, '${nativeNames[0]}.$name');
|
| + nativeDataBuilder.setNativeMemberName(element, '${nativeNames[0]}.$name');
|
| } else {
|
| - _nativeDataBuilder.setNativeMemberName(element, name);
|
| + nativeDataBuilder.setNativeMemberName(element, name);
|
| }
|
| }
|
|
|
| bool _isIdentifier(String s) => _identifier.hasMatch(s);
|
|
|
| - bool _isNativeMethod(FunctionElementX element) {
|
| + /// Returns the JSName annotation string or `null` if no JSName annotation is
|
| + /// present.
|
| + String _findJsNameFromAnnotation(MemberEntity element) {
|
| + String jsName = null;
|
| + for (ConstantValue value in elementEnvironment.getMemberMetadata(element)) {
|
| + String name = readAnnotationName(
|
| + element, value, commonElements.annotationJSNameClass);
|
| + if (jsName == null) {
|
| + jsName = name;
|
| + } else if (name != null) {
|
| + throw new SpannableAssertionFailure(
|
| + element, 'Too many JSName annotations: ${value.toDartText()}');
|
| + }
|
| + }
|
| + return jsName;
|
| + }
|
| +}
|
| +
|
| +class NativeDataResolverImpl extends NativeMemberResolverBase
|
| + implements NativeDataResolver {
|
| + final Compiler _compiler;
|
| +
|
| + NativeDataResolverImpl(this._compiler);
|
| +
|
| + JavaScriptBackend get _backend => _compiler.backend;
|
| + DiagnosticReporter get _reporter => _compiler.reporter;
|
| + ElementEnvironment get elementEnvironment => _compiler.elementEnvironment;
|
| + CommonElements get commonElements => _compiler.commonElements;
|
| + NativeBasicData get nativeBasicData => _backend.nativeBasicData;
|
| + NativeDataBuilder get nativeDataBuilder => _backend.nativeDataBuilder;
|
| +
|
| + @override
|
| + bool isJsInteropMember(MemberElement element) {
|
| + // TODO(johnniwinther): Avoid computing this twice for external function;
|
| + // once from JavaScriptBackendTarget.resolveExternalFunction and once
|
| + // through JavaScriptBackendTarget.resolveNativeMember.
|
| + bool isJsInterop =
|
| + checkJsInteropMemberAnnotations(_compiler, element, nativeDataBuilder);
|
| + // TODO(johnniwinther): Avoid this duplication of logic from
|
| + // NativeData.isJsInterop.
|
| + if (!isJsInterop && element is MethodElement && element.isExternal) {
|
| + if (element.enclosingClass != null) {
|
| + isJsInterop = nativeBasicData.isJsInteropClass(element.enclosingClass);
|
| + } else {
|
| + isJsInterop = nativeBasicData.isJsInteropLibrary(element.library);
|
| + }
|
| + }
|
| + return isJsInterop;
|
| + }
|
| +
|
| + @override
|
| + NativeBehavior computeNativeMethodBehavior(MethodElement function,
|
| + {bool isJsInterop}) {
|
| + return NativeBehavior.ofMethodElement(function, _compiler,
|
| + isJsInterop: isJsInterop);
|
| + }
|
| +
|
| + @override
|
| + NativeBehavior computeNativeFieldLoadBehavior(FieldElement field,
|
| + {bool isJsInterop}) {
|
| + return NativeBehavior.ofFieldElementLoad(field, _compiler,
|
| + isJsInterop: isJsInterop);
|
| + }
|
| +
|
| + @override
|
| + NativeBehavior computeNativeFieldStoreBehavior(FieldElement field) {
|
| + return NativeBehavior.ofFieldElementStore(field, _compiler);
|
| + }
|
| +
|
| + @override
|
| + bool isNativeMethod(FunctionElementX element) {
|
| if (!_backend.canLibraryUseNative(element.library)) return false;
|
| // Native method?
|
| return _reporter.withCurrentElement(element, () {
|
| @@ -222,24 +275,20 @@ class NativeDataResolverImpl implements NativeDataResolver {
|
| });
|
| }
|
|
|
| - /// Returns the JSName annotation string or `null` if no JSName annotation is
|
| - /// present.
|
| - String _findJsNameFromAnnotation(Element element) {
|
| - String jsName = null;
|
| - for (MetadataAnnotation annotation in element.implementation.metadata) {
|
| - annotation.ensureResolved(_compiler.resolution);
|
| - ConstantValue value =
|
| - _compiler.constants.getConstantValue(annotation.constant);
|
| - String name = readAnnotationName(
|
| - annotation, value, _compiler.commonElements.annotationJSNameClass);
|
| - if (jsName == null) {
|
| - jsName = name;
|
| - } else if (name != null) {
|
| - throw new SpannableAssertionFailure(
|
| - annotation, 'Too many JSName annotations: ${annotation}');
|
| - }
|
| + @override
|
| + bool _processMethodAnnotations(MethodElement method) {
|
| + if (_compiler.serialization.isDeserialized(method)) {
|
| + return false;
|
| }
|
| - return jsName;
|
| + return super._processMethodAnnotations(method);
|
| + }
|
| +
|
| + @override
|
| + bool _processFieldAnnotations(FieldElement element) {
|
| + if (_compiler.serialization.isDeserialized(element)) {
|
| + return false;
|
| + }
|
| + return super._processFieldAnnotations(element);
|
| }
|
|
|
| @override
|
|
|