| Index: pkg/compiler/lib/src/js_backend/backend.dart
|
| diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
|
| index 8370d133e74297d38e8c84d243f91f693d62260f..e748d8bdba931a68f5e0779a5c3d474b9b840306 100644
|
| --- a/pkg/compiler/lib/src/js_backend/backend.dart
|
| +++ b/pkg/compiler/lib/src/js_backend/backend.dart
|
| @@ -690,7 +690,7 @@ class JavaScriptBackend extends Backend {
|
| }
|
|
|
| FunctionElement resolveExternalFunction(FunctionElement element) {
|
| - if (isForeign(element) || element.isJsInterop) return element;
|
| + if (isForeign(element) || isJsInterop(element)) return element;
|
| return patchResolverTask.measure(() {
|
| return patchResolverTask.resolveExternalFunction(element);
|
| });
|
| @@ -755,7 +755,7 @@ class JavaScriptBackend extends Backend {
|
|
|
| bool isInterceptorClass(ClassElement element) {
|
| if (element == null) return false;
|
| - if (Elements.isNativeOrExtendsNative(element)) return true;
|
| + if (isNativeOrExtendsNative(element)) return true;
|
| if (interceptedClasses.contains(element)) return true;
|
| if (classesMixedIntoInterceptedClasses.contains(element)) return true;
|
| return false;
|
| @@ -798,10 +798,175 @@ class JavaScriptBackend extends Backend {
|
| return aliasedSuperMembers.contains(member);
|
| }
|
|
|
| + /// The JavaScript names for elements implemented via typed JavaScript
|
| + /// interop.
|
| + Map<Element, String> jsInteropNames = <Element, String>{};
|
| +
|
| + /// The JavaScript names for native JavaScript elements implemented.
|
| + Map<Element, String> nativeMemberName = <Element, String>{};
|
| +
|
| + /// Tag info for native JavaScript classes names. See
|
| + /// [setNativeClassTagInfo].
|
| + Map<ClassElement, String> nativeClassTagInfo = <ClassElement, String>{};
|
| +
|
| + /// Returns `true` if [element] is explicitly marked as part of JsInterop.
|
| + bool _isJsInterop(Element element) {
|
| + return jsInteropNames.containsKey(element.declaration);
|
| + }
|
| +
|
| + /// Returns [element] as an explicit part of JsInterop. The js interop name is
|
| + /// expected to be computed later.
|
| + void markAsJsInterop(Element element) {
|
| + jsInteropNames[element.declaration] = null;
|
| + }
|
| +
|
| + /// Sets the explicit js interop [name] for [element].
|
| + void setJsInteropName(Element element, String name) {
|
| + assert(invariant(element,
|
| + isJsInterop(element),
|
| + message:
|
| + 'Element $element is not js interop but given a js interop name.'));
|
| + jsInteropNames[element.declaration] = name;
|
| + }
|
| +
|
| + /// Returns the explicit js interop name for [element].
|
| + String getJsInteropName(Element element) {
|
| + return jsInteropNames[element.declaration];
|
| + }
|
| +
|
| + /// Returns `true` if [element] is part of JsInterop.
|
| + @override
|
| + bool isJsInterop(Element element) {
|
| + // An function is part of JsInterop in the following cases:
|
| + // * It has a jsInteropName annotation
|
| + // * It is external member of a class or library tagged as JsInterop.
|
| + if (element.isFunction || element.isConstructor || element.isAccessor) {
|
| + FunctionElement function = element;
|
| + if (!function.isExternal) return false;
|
| +
|
| + if (_isJsInterop(function)) return true;
|
| + if (function.isClassMember) return isJsInterop(function.contextClass);
|
| + if (function.isTopLevel) return isJsInterop(function.library);
|
| + return false;
|
| + } else {
|
| + return _isJsInterop(element);
|
| + }
|
| + }
|
| +
|
| + /// Returns `true` if the name of [element] is fixed for the generated
|
| + /// JavaScript.
|
| + bool hasFixedBackendName(Element element) {
|
| + return isJsInterop(element) ||
|
| + nativeMemberName.containsKey(element.declaration);
|
| + }
|
| +
|
| + String _jsNameHelper(Element element) {
|
| + String jsInteropName = jsInteropNames[element.declaration];
|
| + assert(invariant(element,
|
| + !(_isJsInterop(element) && jsInteropName == null),
|
| + message:
|
| + 'Element $element is js interop but js interop name has not yet '
|
| + 'been computed.'));
|
| + if (jsInteropName != null && jsInteropName.isNotEmpty) {
|
| + return jsInteropName;
|
| + }
|
| + return element.isLibrary ? 'self' : element.name;
|
| + }
|
| +
|
| + /// Computes the name for [element] to use in the generated JavaScript. This
|
| + /// is either given through a native annotation or a js interop annotation.
|
| + String getFixedBackendName(Element element) {
|
| + String name = nativeMemberName[element.declaration];
|
| + if (name == null && isJsInterop(element)) {
|
| + // If an element isJsInterop but _isJsInterop is false that means it is
|
| + // considered interop as the parent class is interop.
|
| + name = _jsNameHelper(
|
| + element.isConstructor ? element.enclosingClass : element);
|
| + nativeMemberName[element.declaration] = name;
|
| + }
|
| + return name;
|
| + }
|
| +
|
| + /// Whether [element] corresponds to a native JavaScript construct either
|
| + /// through the native mechanism (`@Native(...)` or the `native` pseudo
|
| + /// keyword) which is only allowed for internal libraries or via the typed
|
| + /// JavaScriptInterop mechanism which is allowed for user libraries.
|
| + @override
|
| + bool isNative(Element element) {
|
| + if (isJsInterop(element)) return true;
|
| + if (element.isClass) {
|
| + return nativeClassTagInfo.containsKey(element.declaration);
|
| + } else {
|
| + return nativeMemberName.containsKey(element.declaration);
|
| + }
|
| + }
|
| +
|
| + /// Sets the native [name] for the member [element]. This name is used for
|
| + /// [element] in the generated JavaScript.
|
| + void setNativeMemberName(MemberElement element, String name) {
|
| + // TODO(johnniwinther): Avoid setting this more than once. The enqueuer
|
| + // might enqueue [element] several times (before processing it) and computes
|
| + // name on each call to `internalAddToWorkList`.
|
| + assert(invariant(element,
|
| + nativeMemberName[element.declaration] == null ||
|
| + nativeMemberName[element.declaration] == name,
|
| + message:
|
| + "Native member name set inconsistently on $element: "
|
| + "Existing name '${nativeMemberName[element.declaration]}', "
|
| + "new name '$name'."));
|
| + nativeMemberName[element.declaration] = name;
|
| + }
|
| +
|
| + /// Sets the native tag info for [cls].
|
| + ///
|
| + /// The tag info string contains comma-separated 'words' which are either
|
| + /// dispatch tags (having JavaScript identifier syntax) and directives that
|
| + /// begin with `!`.
|
| + void setNativeClassTagInfo(ClassElement cls, String tagInfo) {
|
| + // TODO(johnniwinther): Assert that this is only called once. The memory
|
| + // compiler copies pre-processed elements into a new compiler through
|
| + // [Compiler.onLibraryScanned] and thereby causes multiple calls to this
|
| + // method.
|
| + assert(invariant(cls,
|
| + nativeClassTagInfo[cls.declaration] == null ||
|
| + nativeClassTagInfo[cls.declaration] == tagInfo,
|
| + message:
|
| + "Native tag info set inconsistently on $cls: "
|
| + "Existing tag info '${nativeClassTagInfo[cls.declaration]}', "
|
| + "new tag info '$tagInfo'."));
|
| + nativeClassTagInfo[cls.declaration] = tagInfo;
|
| + }
|
| +
|
| + /// Returns the list of native tag words for [cls].
|
| + List<String> getNativeTagsOfClassRaw(ClassElement cls) {
|
| + String quotedName = nativeClassTagInfo[cls.declaration];
|
| + return quotedName.substring(1, quotedName.length - 1).split(',');
|
| + }
|
| +
|
| + /// Returns the list of non-directive native tag words for [cls].
|
| + List<String> getNativeTagsOfClass(ClassElement cls) {
|
| + return getNativeTagsOfClassRaw(cls).where(
|
| + (s) => !s.startsWith('!')).toList();
|
| + }
|
| +
|
| + /// Returns `true` if [cls] has a `!nonleaf` tag word.
|
| + bool hasNativeTagsForcedNonLeaf(ClassElement cls) {
|
| + return getNativeTagsOfClassRaw(cls).contains('!nonleaf');
|
| + }
|
| +
|
| + bool isNativeOrExtendsNative(ClassElement element) {
|
| + if (element == null) return false;
|
| + if (isNative(element) || isJsInterop(element)) {
|
| + return true;
|
| + }
|
| + assert(element.isResolved);
|
| + return isNativeOrExtendsNative(element.superclass);
|
| + }
|
| +
|
| bool isInterceptedMethod(Element element) {
|
| if (!element.isInstanceMember) return false;
|
| if (element.isGenerativeConstructorBody) {
|
| - return Elements.isNativeOrExtendsNative(element.enclosingClass);
|
| + return isNativeOrExtendsNative(element.enclosingClass);
|
| }
|
| return interceptedElements[element.name] != null;
|
| }
|
| @@ -866,7 +1031,7 @@ class JavaScriptBackend extends Backend {
|
| Set<ClassElement> result = new Set<ClassElement>();
|
| for (Element element in intercepted) {
|
| ClassElement classElement = element.enclosingClass;
|
| - if (Elements.isNativeOrExtendsNative(classElement)
|
| + if (isNativeOrExtendsNative(classElement)
|
| || interceptedClasses.contains(classElement)) {
|
| result.add(classElement);
|
| }
|
| @@ -887,7 +1052,7 @@ class JavaScriptBackend extends Backend {
|
| for (MixinApplicationElement use in uses) {
|
| Iterable<ClassElement> subclasses = classWorld.strictSubclassesOf(use);
|
| for (ClassElement subclass in subclasses) {
|
| - if (Elements.isNativeOrExtendsNative(subclass)) {
|
| + if (isNativeOrExtendsNative(subclass)) {
|
| if (result == null) result = new Set<ClassElement>();
|
| result.add(subclass);
|
| }
|
| @@ -1090,7 +1255,7 @@ class JavaScriptBackend extends Backend {
|
| } else if (cls == boundClosureClass) {
|
| // TODO(johnniwinther): Is this a noop?
|
| enqueueClass(enqueuer, boundClosureClass, registry);
|
| - } else if (Elements.isNativeOrExtendsNative(cls)) {
|
| + } else if (isNativeOrExtendsNative(cls)) {
|
| enqueue(enqueuer, getNativeInterceptorMethod, registry);
|
| enqueueClass(enqueuer, jsInterceptorClass, compiler.globalDependencies);
|
| enqueueClass(enqueuer, jsJavaScriptObjectClass, registry);
|
| @@ -1180,7 +1345,7 @@ class JavaScriptBackend extends Backend {
|
| addInterceptors(jsUnknownJavaScriptObjectClass, enqueuer, registry);
|
| } else if (cls == jsJavaScriptFunctionClass) {
|
| addInterceptors(jsJavaScriptFunctionClass, enqueuer, registry);
|
| - } else if (Elements.isNativeOrExtendsNative(cls)) {
|
| + } else if (isNativeOrExtendsNative(cls)) {
|
| addInterceptorsForNativeClassMembers(cls, enqueuer);
|
| } else if (cls == jsIndexingBehaviorInterface) {
|
| // These two helpers are used by the emitter and the codegen.
|
| @@ -1349,7 +1514,7 @@ class JavaScriptBackend extends Backend {
|
| if (!type.treatAsRaw || type.containsTypeVariables) {
|
| enqueueClass(world, compiler.listClass, registry);
|
| }
|
| - if (type.element != null && type.element.isNative) {
|
| + if (type.element != null && isNative(type.element)) {
|
| // We will neeed to add the "$is" and "$as" properties on the
|
| // JavaScript object prototype, so we make sure
|
| // [:defineProperty:] is compiled.
|
| @@ -1603,9 +1768,9 @@ class JavaScriptBackend extends Backend {
|
| }
|
|
|
| ClassElement defaultSuperclass(ClassElement element) {
|
| - if (element.isJsInterop) return jsJavaScriptObjectClass;
|
| + if (isJsInterop(element)) return jsJavaScriptObjectClass;
|
| // Native classes inherit from Interceptor.
|
| - return element.isNative ? jsInterceptorClass : compiler.objectClass;
|
| + return isNative(element) ? jsInterceptorClass : compiler.objectClass;
|
| }
|
|
|
| /**
|
| @@ -2534,7 +2699,7 @@ class JavaScriptBackend extends Backend {
|
| }
|
|
|
| LibraryElement library = element.library;
|
| - if (!library.isPlatformLibrary && !library.canUseNative) return;
|
| + if (!library.isPlatformLibrary && !canLibraryUseNative(library)) return;
|
| bool hasNoInline = false;
|
| bool hasForceInline = false;
|
| bool hasNoThrows = false;
|
| @@ -3035,7 +3200,7 @@ class JavaScriptResolutionCallbacks extends ResolutionCallbacks {
|
| if (type is FunctionType) {
|
| registerBackendImpact(transformed, impacts.functionTypeCheck);
|
| }
|
| - if (type.element != null && type.element.isNative) {
|
| + if (type.element != null && backend.isNative(type.element)) {
|
| registerBackendImpact(transformed, impacts.nativeTypeCheck);
|
| }
|
| }
|
|
|