Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(135)

Unified Diff: pkg/compiler/lib/src/js_backend/backend.dart

Issue 1408043002: Move native and js interop properties from the element model to the JS backend (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Updated cf. comments. Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);
}
}

Powered by Google App Engine
This is Rietveld 408576698