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 557e9698c9920a6297f09a41a6aab771c43e52f6..5d055ccd356883267dd2f0a6a60d668862a1cda7 100644 |
--- a/pkg/compiler/lib/src/native/resolver.dart |
+++ b/pkg/compiler/lib/src/native/resolver.dart |
@@ -2,10 +2,13 @@ |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
-import 'package:front_end/src/fasta/scanner.dart' show StringToken, Token; |
+import 'package:front_end/src/fasta/scanner.dart' |
+ show BeginGroupToken, StringToken, Token; |
+import 'package:front_end/src/fasta/scanner.dart' as Tokens show EOF_TOKEN; |
import '../common.dart'; |
import '../common/backend_api.dart'; |
+import '../common/resolution.dart'; |
import '../compiler.dart' show Compiler; |
import '../constants/values.dart'; |
import '../elements/elements.dart' |
@@ -17,8 +20,10 @@ import '../elements/elements.dart' |
MemberElement, |
MetadataAnnotation, |
MethodElement; |
+import '../elements/entities.dart'; |
import '../elements/modelx.dart' show FunctionElementX, MetadataAnnotationX; |
import '../elements/resolution_types.dart' show ResolutionDartType; |
+import '../js_backend/backend_helpers.dart'; |
import '../js_backend/js_backend.dart'; |
import '../js_backend/native_data.dart'; |
import '../patch_parser.dart'; |
@@ -378,3 +383,188 @@ class JsInteropAnnotationHandler implements EagerAnnotationHandler<bool> { |
bool get defaultResult => false; |
} |
+ |
+/// Interface for computing all native classes in a set of libraries. |
+abstract class NativeClassResolver { |
+ Iterable<ClassEntity> computeNativeClasses(Iterable<LibraryEntity> libraries); |
+} |
+ |
+class NativeClassResolverImpl implements NativeClassResolver { |
+ final DiagnosticReporter _reporter; |
+ final Resolution _resolution; |
+ final BackendHelpers _helpers; |
+ final NativeBasicData _nativeBasicData; |
+ |
+ Map<String, ClassElement> _tagOwner = new Map<String, ClassElement>(); |
+ |
+ NativeClassResolverImpl( |
+ this._resolution, this._reporter, this._helpers, this._nativeBasicData); |
+ |
+ Iterable<ClassElement> computeNativeClasses( |
+ Iterable<LibraryElement> libraries) { |
+ Set<ClassElement> nativeClasses = new Set<ClassElement>(); |
+ libraries.forEach((l) => _processNativeClassesInLibrary(l, nativeClasses)); |
+ if (_helpers.isolateHelperLibrary != null) { |
+ _processNativeClassesInLibrary( |
+ _helpers.isolateHelperLibrary, nativeClasses); |
+ } |
+ _processSubclassesOfNativeClasses(libraries, nativeClasses); |
+ return nativeClasses; |
+ } |
+ |
+ void _processNativeClassesInLibrary( |
+ LibraryElement library, Set<ClassElement> nativeClasses) { |
+ // Use implementation to ensure the inclusion of injected members. |
+ library.implementation.forEachLocalMember((Element element) { |
+ if (element.isClass) { |
+ ClassElement cls = element; |
+ if (_nativeBasicData.isNativeClass(cls)) { |
+ _processNativeClass(element, nativeClasses); |
+ } |
+ } |
+ }); |
+ } |
+ |
+ void _processNativeClass( |
+ ClassElement classElement, Set<ClassElement> nativeClasses) { |
+ nativeClasses.add(classElement); |
+ // Resolve class to ensure the class has valid inheritance info. |
+ classElement.ensureResolved(_resolution); |
+ // Js Interop interfaces do not have tags. |
+ if (_nativeBasicData.isJsInteropClass(classElement)) return; |
+ // Since we map from dispatch tags to classes, a dispatch tag must be used |
+ // on only one native class. |
+ for (String tag in _nativeBasicData.getNativeTagsOfClass(classElement)) { |
+ ClassElement owner = _tagOwner[tag]; |
+ if (owner != null) { |
+ if (owner != classElement) { |
+ _reporter.internalError( |
+ classElement, "Tag '$tag' already in use by '${owner.name}'"); |
+ } |
+ } else { |
+ _tagOwner[tag] = classElement; |
+ } |
+ } |
+ } |
+ |
+ void _processSubclassesOfNativeClasses( |
+ Iterable<LibraryElement> libraries, Set<ClassElement> nativeClasses) { |
+ Set<ClassElement> nativeClassesAndSubclasses = new Set<ClassElement>(); |
+ // Collect potential subclasses, e.g. |
+ // |
+ // class B extends foo.A {} |
+ // |
+ // String "A" has a potential subclass B. |
+ |
+ var potentialExtends = new Map<String, Set<ClassElement>>(); |
+ |
+ libraries.forEach((library) { |
+ library.implementation.forEachLocalMember((element) { |
+ if (element.isClass) { |
+ String extendsName = _findExtendsNameOfClass(element); |
+ if (extendsName != null) { |
+ Set<ClassElement> potentialSubclasses = potentialExtends |
+ .putIfAbsent(extendsName, () => new Set<ClassElement>()); |
+ potentialSubclasses.add(element); |
+ } |
+ } |
+ }); |
+ }); |
+ |
+ // Resolve all the native classes and any classes that might extend them in |
+ // [potentialExtends], and then check that the properly resolved class is in |
+ // fact a subclass of a native class. |
+ |
+ ClassElement nativeSuperclassOf(ClassElement classElement) { |
+ if (_nativeBasicData.isNativeClass(classElement)) return classElement; |
+ if (classElement.superclass == null) return null; |
+ return nativeSuperclassOf(classElement.superclass); |
+ } |
+ |
+ void walkPotentialSubclasses(ClassElement element) { |
+ if (nativeClassesAndSubclasses.contains(element)) return; |
+ element.ensureResolved(_resolution); |
+ ClassElement nativeSuperclass = nativeSuperclassOf(element); |
+ if (nativeSuperclass != null) { |
+ nativeClassesAndSubclasses.add(element); |
+ Set<ClassElement> potentialSubclasses = potentialExtends[element.name]; |
+ if (potentialSubclasses != null) { |
+ potentialSubclasses.forEach(walkPotentialSubclasses); |
+ } |
+ } |
+ } |
+ |
+ nativeClasses.forEach(walkPotentialSubclasses); |
+ nativeClasses.addAll(nativeClassesAndSubclasses); |
+ } |
+ |
+ /** |
+ * Returns the source string of the class named in the extends clause, or |
+ * `null` if there is no extends clause. |
+ */ |
+ String _findExtendsNameOfClass(ClassElement classElement) { |
+ if (classElement.isResolved) { |
+ ClassElement superClass = classElement.superclass; |
+ while (superClass != null) { |
+ if (!superClass.isUnnamedMixinApplication) { |
+ return superClass.name; |
+ } |
+ superClass = superClass.superclass; |
+ } |
+ return null; |
+ } |
+ |
+ // "class B extends A ... {}" --> "A" |
+ // "class B extends foo.A ... {}" --> "A" |
+ // "class B<T> extends foo.A<T,T> with M1, M2 ... {}" --> "A" |
+ |
+ // We want to avoid calling classElement.parseNode on every class. Doing so |
+ // will slightly increase parse time and size and cause compiler errors and |
+ // warnings to me emitted in more unused code. |
+ |
+ // An alternative to this code is to extend the API of ClassElement to |
+ // expose the name of the extended element. |
+ |
+ // Pattern match the above cases in the token stream. |
+ // [abstract] class X extends [id.]* id |
+ |
+ Token skipTypeParameters(Token token) { |
+ BeginGroupToken beginGroupToken = token; |
+ Token endToken = beginGroupToken.endGroup; |
+ return endToken.next; |
+ //for (;;) { |
+ // token = token.next; |
+ // if (token.stringValue == '>') return token.next; |
+ // if (token.stringValue == '<') return skipTypeParameters(token); |
+ //} |
+ } |
+ |
+ String scanForExtendsName(Token token) { |
+ if (token.stringValue == 'abstract') token = token.next; |
+ if (token.stringValue != 'class') return null; |
+ token = token.next; |
+ if (!token.isIdentifier()) return null; |
+ token = token.next; |
+ // class F<X extends B<X>> extends ... |
+ if (token.stringValue == '<') { |
+ token = skipTypeParameters(token); |
+ } |
+ if (token.stringValue != 'extends') return null; |
+ token = token.next; |
+ Token id = token; |
+ while (token.kind != Tokens.EOF_TOKEN) { |
+ token = token.next; |
+ if (token.stringValue != '.') break; |
+ token = token.next; |
+ if (!token.isIdentifier()) return null; |
+ id = token; |
+ } |
+ // Should be at '{', 'with', 'implements', '<' or 'native'. |
+ return id.lexeme; |
+ } |
+ |
+ return _reporter.withCurrentElement(classElement, () { |
+ return scanForExtendsName(classElement.position); |
+ }); |
+ } |
+} |