Chromium Code Reviews| Index: sdk/lib/_internal/compiler/implementation/native_handler.dart |
| diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart |
| index db579d390c26495a605b23c06d6d02738e870184..c49a3b2806a94d50b4d18cc2a9198717185be0c1 100644 |
| --- a/sdk/lib/_internal/compiler/implementation/native_handler.dart |
| +++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart |
| @@ -13,65 +13,311 @@ import 'ssa/ssa.dart'; |
| import 'tree/tree.dart'; |
| import 'util/util.dart'; |
| -void processNativeClasses(Enqueuer world, |
| - CodeEmitterTask emitter, |
| - Collection<LibraryElement> libraries) { |
| - for (LibraryElement library in libraries) { |
| - processNativeClassesInLibrary(world, emitter, library); |
| - } |
| + |
| +// This class is a temporary work-around until we get a more powerful DartType. |
|
ahe
2012/11/14 13:51:30
Not a proper documentation comment. That is, use
sra1
2012/11/15 00:09:10
Done.
|
| +class SpecialType { |
|
ahe
2012/11/14 13:51:30
Can we use ConcreteType from dart/sdk/lib/_interna
sra1
2012/11/15 00:09:10
Lets keep it separate for now.
It is not clear tha
|
| + final String name; |
| + const SpecialType._(this.name); |
| + |
| + // Object, but no subtypes: |
|
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
|
| + static const JsObject = const SpecialType._('=Object'); |
| + |
| + // The specific implementation of List that is JavaScript Array: |
|
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
|
| + static const JsArray = const SpecialType._('=List'); |
| } |
| -void addSubtypes(ClassElement cls, |
| - NativeEmitter emitter) { |
| - for (DartType type in cls.allSupertypes) { |
| - List<Element> subtypes = emitter.subtypes.putIfAbsent( |
| - type.element, |
| - () => <ClassElement>[]); |
| - subtypes.add(cls); |
| + |
| +// This could be an abstract class but we use it as a stub for the dart_backend. |
|
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
|
| +class NativeEnqueuer { |
| + void processNativeClasses(Collection<LibraryElement> libraries) {} |
| + |
| + void registerElement(Element element) {} |
| + |
| + // Method is a member of a native class. |
|
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
|
| + void registerMethod(Element method) {} |
| + |
| + // Field is a member of a native class. |
|
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
|
| + void registerFieldLoad(Element field) {} |
| + void registerFieldStore(Element field) {} |
| + |
| + // JS-form code can be an instantiation point for types. |
|
ahe
2012/11/14 13:51:30
This comment needs a bit of work to become a prope
sra1
2012/11/15 00:09:10
Done.
|
| + // |
| + // JS('_DOMWindowImpl', 'window'); |
| + // |
| + void registerJsCall(Send node, resolver) {} |
| + |
| + // Emits a summary log line. |
|
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
|
| + void logSummary(log(message)) {} |
| +} |
| + |
| + |
| +class NativeEnqueuerBase implements NativeEnqueuer { |
| + |
| + final Set<ClassElement> nativeClasses = new Set<ClassElement>(); |
| + |
| + // Each class is exactly one of these sets. |
|
ahe
2012/11/14 13:51:30
I think this comment applies to the following fiel
sra1
2012/11/15 00:09:10
Done.
|
| + final Set<ClassElement> registeredClasses = new Set<ClassElement>(); |
| + final Set<ClassElement> unusedClasses = new Set<ClassElement>(); |
| + final Set<ClassElement> pendingClasses = new Set<ClassElement>(); |
| + |
| + // Once a type constraint (SpecialType, DartType) has been matched, there is |
| + // no need to match it again. |
|
ahe
2012/11/14 13:51:30
///
Perhaps also reword the comment. For exampl
sra1
2012/11/15 00:09:10
Done.
|
| + final Set matchedTypeConstraints = new Set(); |
| + |
| + final queue = new Queue(); // Classes in pendingClasse have thunks in queue. |
|
ahe
2012/11/14 13:51:30
Move comment to previous line and use ///.
ngeoffray
2012/11/14 21:17:39
Explain what those thunks are.
sra1
2012/11/15 00:09:10
Done.
sra1
2012/11/15 00:09:10
Done.
|
| + bool flushing = false; |
| + |
| + |
| + final Enqueuer world; |
| + final Compiler compiler; |
| + final bool enableLiveTypeAnalysis; |
| + |
| + Element annotationCreatesClass; |
| + Element annotationReturnsClass; |
| + |
| + // constructed by backend. |
|
ahe
2012/11/14 13:51:30
///
Also, start sentence with an uppercase letter
sra1
2012/11/15 00:09:10
Done.
|
| + NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); |
| + |
| + void processNativeClasses(Collection<LibraryElement> libraries) { |
| + Element find(name) { |
| + Element e = compiler.coreLibrary.find(name); |
|
ahe
2012/11/14 13:51:30
Should be compiler.findHelper
sra1
2012/11/15 00:09:10
Done.
|
| + if (e == null) { |
| + compiler.cancel("Core library missing class '${name}'"); |
|
ahe
2012/11/14 13:51:30
How about: "Could not find implementation class '$
sra1
2012/11/15 00:09:10
Done.
|
| + } |
| + return e; |
| + } |
| + annotationCreatesClass = find(const SourceString('Creates')); |
| + annotationReturnsClass = find(const SourceString('Returns')); |
| + |
| + libraries.forEach(processNativeClassesInLibrary); |
| + |
| + if (!enableLiveTypeAnalysis) { |
| + nativeClasses.forEach((c) => enqueueClass(c, 'forced')); |
| + flushQueue(); |
| + } |
| + } |
| + |
| + void processNativeClassesInLibrary(LibraryElement library) { |
| + // Use implementation to ensure the inclusion of injected members. |
| + library.implementation.forEachLocalMember((Element element) { |
| + if (element.kind == ElementKind.CLASS) { |
|
ngeoffray
2012/11/14 21:17:39
too much indentation.
sra1
2012/11/15 00:09:10
OK.
I think we should change the style, though.
|
| + ClassElement classElement = element; |
| + if (classElement.isNative()) { |
| + nativeClasses.add(classElement); |
| + unusedClasses.add(classElement); |
|
ngeoffray
2012/11/14 21:17:39
Add a comment why you're also adding it to 'unused
sra1
2012/11/15 00:09:10
I added a comment on nativeClasses.
|
| + |
| + // Resolve class to ensure the class has valid inheritance info. |
| + classElement.ensureResolved(compiler); |
| + } |
| + } |
| + }); |
| + } |
| + |
| + enqueueClass(ClassElement classElement, cause) { |
|
ngeoffray
2012/11/14 21:17:39
String cause?
sra1
2012/11/15 00:09:10
It can be a member.
|
| + assert(unusedClasses.contains(classElement)); |
| + // compiler.log( |
|
ngeoffray
2012/11/14 21:17:39
Remove debugging code
sra1
2012/11/15 00:09:10
Done.
|
| + // 'Adding native class ${classElement.name.slowToString()},' |
| + // ' why: $cause'); |
| + unusedClasses.remove(classElement); |
| + pendingClasses.add(classElement); |
| + queue.add(() { processClass(classElement, cause); }); |
| + } |
| + |
| + void flushQueue() { |
| + if (flushing) return; |
| + flushing = true; |
| + while (!queue.isEmpty) { |
| + (queue.removeFirst())(); |
| + } |
| + flushing = false; |
| } |
| - List<Element> directSubtypes = emitter.directSubtypes.putIfAbsent( |
| - cls.superclass, |
| - () => <ClassElement>[]); |
| - directSubtypes.add(cls); |
| + processClass(ClassElement classElement, cause) { |
| + assert(!registeredClasses.contains(classElement)); |
| + |
| + bool firstTime = registeredClasses.isEmpty; |
| + pendingClasses.remove(classElement); |
| + registeredClasses.add(classElement); |
| + |
| + // print('processClass $classElement $cause'); |
|
ngeoffray
2012/11/14 21:17:39
Remove debugging code.
sra1
2012/11/15 00:09:10
Done.
|
| + world.registerInstantiatedClass(classElement); |
| + |
| + // Also parse the node to know all its methods because otherwise it will |
| + // only be parsed if there is a call to one of its constructors. |
| + classElement.parseNode(compiler); |
| + |
| + if (firstTime) { |
| + queue.add(onFirstNativeClass); |
| + } |
| + } |
| + |
| + registerElement(Element element) { |
| + if (element.isFunction()) return registerMethod(element); |
| + } |
| + |
| + registerMethod(Element method) { |
| + if (isNativeMethod(method)) { |
| + // print('Native method call $method'); |
|
ngeoffray
2012/11/14 21:17:39
Remove debugging code.
sra1
2012/11/15 00:09:10
Done.
|
| + processNativeBehavior( |
| + NativeBehavior.ofMethod(method, compiler), |
| + method); |
| + flushQueue(); |
| + } |
| + } |
| + |
| + bool isNativeMethod(Element element) { |
| + // Native method? |
| + Node node = element.parseNode(compiler); |
| + if (node is! FunctionExpression) return false; |
| + node = node.body; |
| + Token token = node.getBeginToken(); |
| + if (token.stringValue == 'native') return true; |
| + return false; |
| + } |
| + |
| + void registerFieldLoad(Element field) { |
| + // print('Native load field $field'); |
|
ngeoffray
2012/11/14 21:17:39
Remove
sra1
2012/11/15 00:09:10
Done.
|
| + processNativeBehavior( |
| + NativeBehavior.ofFieldLoad(field, compiler), |
| + field); |
| + flushQueue(); |
| + } |
| + |
| + void registerFieldStore(Element field) { |
| + // print('Native store field $field'); |
|
ngeoffray
2012/11/14 21:17:39
Remove
sra1
2012/11/15 00:09:10
Done.
|
| + processNativeBehavior( |
| + NativeBehavior.ofFieldStore(field, compiler), |
| + field); |
| + flushQueue(); |
| + } |
| + |
| + void registerJsCall(Send node, resolver) { |
|
ngeoffray
2012/11/14 21:17:39
Add type annotation to resolver.
sra1
2012/11/15 00:09:10
Done.
|
| + processNativeBehavior( |
| + NativeBehavior.ofJsCall(node, compiler, resolver), |
| + node); |
| + flushQueue(); |
| + } |
| + |
| + processNativeBehavior(NativeBehavior behavior, cause) { |
| + bool allUsedBefore = unusedClasses.isEmpty; |
| + for (var type in behavior.typesInstantiated) { |
| + if (matchedTypeConstraints.contains(type)) continue; |
| + matchedTypeConstraints.add(type); |
| + if (type is SpecialType) { |
| + // These two special types are always instantiated. |
|
ngeoffray
2012/11/14 21:17:39
What are these two? Add them in the comment.
sra1
2012/11/15 00:09:10
Done.
|
| + continue; |
| + } |
| + assert(type is DartType); |
| + enqueueUnusedClassesMatching( |
| + (nativeClass) => compiler.types.isSubtype(nativeClass.type, type), |
| + cause, |
| + 'subtypeof($type)'); |
| + } |
| + |
| + // Give an info so that library developers can compile with -v to find why |
| + // all the native classes are included. |
| + if (unusedClasses.isEmpty && !allUsedBefore) { |
| + if (cause is Node) { |
|
ngeoffray
2012/11/14 21:17:39
What is the difference between this if and the nex
sra1
2012/11/15 00:09:10
Done.
|
| + // throw 'Eugh $cause'; |
|
ngeoffray
2012/11/14 21:17:39
Remove throw
sra1
2012/11/15 00:09:10
Done.
|
| + compiler.log('All native types marked as used due to $cause.'); |
| + } else if (cause.position() != null) { |
| + //compiler.cancel('Eugh!', token: cause.position()); |
|
ngeoffray
2012/11/14 21:17:39
Remove cancel
sra1
2012/11/15 00:09:10
Done.
|
| + compiler.log('All native types marked used due to $cause.'); |
| + } else { |
| + compiler.log('All native types marked used due to $type.'); |
|
ngeoffray
2012/11/14 21:17:39
What is type here?
sra1
2012/11/15 00:09:10
Old debugging code.
|
| + } |
| + } |
| + } |
| + |
| + enqueueUnusedClassesMatching(predicate, cause, [reason]) { |
|
ngeoffray
2012/11/14 21:17:39
Types please
sra1
2012/11/15 00:09:10
Done.
|
| + var matches = unusedClasses.filter(predicate); |
|
ngeoffray
2012/11/14 21:17:39
ditto
sra1
2012/11/15 00:09:10
Done.
|
| + // print('${matches.length} matches: $matches\n.. $reason $world'); |
|
ngeoffray
2012/11/14 21:17:39
Remove
sra1
2012/11/15 00:09:10
Done.
|
| + matches.forEach((c) => enqueueClass(c, cause)); |
| + } |
| + |
| + onFirstNativeClass() { |
| + staticUse(name) => world.registerStaticUse(compiler.findHelper(name)); |
| + |
| + staticUse(const SourceString('dynamicFunction')); |
| + staticUse(const SourceString('dynamicSetMetadata')); |
| + staticUse(const SourceString('defineProperty')); |
| + staticUse(const SourceString('toStringForNativeObject')); |
| + staticUse(const SourceString('hashCodeForNativeObject')); |
| + |
| + addNativeExceptions(); |
| + } |
| + |
| + addNativeExceptions() { |
| + enqueueUnusedClassesMatching((classElement) { |
| + // TODO(sra): Annotate exception classes in dart:html. |
| + var name = classElement.name.slowToString(); |
|
ngeoffray
2012/11/14 21:17:39
String name
sra1
2012/11/15 00:09:10
Done.
|
| + if (name.contains('Exception')) return true; |
| + if (name.contains('Error')) return true; |
| + return false; |
| + }, |
| + 'native exception'); |
| + } |
| } |
| -void processNativeClassesInLibrary(Enqueuer world, |
| - CodeEmitterTask emitter, |
| - LibraryElement library) { |
| - bool hasNativeClass = false; |
| - final compiler = emitter.compiler; |
| - // Use implementation to ensure the inclusion of injected members. |
| - library.implementation.forEachLocalMember((Element element) { |
| - if (element.kind == ElementKind.CLASS) { |
| - ClassElement classElement = element; |
| - if (classElement.isNative()) { |
| - hasNativeClass = true; |
| - world.registerInstantiatedClass(classElement); |
| - // Also parse the node to know all its methods because |
| - // otherwise it will only be parsed if there is a call to |
| - // one of its constructor. |
| - classElement.parseNode(compiler); |
| - // Resolve to setup the inheritance. |
| - classElement.ensureResolved(compiler); |
| - // Add the information that this class is a subtype of |
| - // its supertypes. The code emitter and the ssa builder use that |
| - // information. |
| - addSubtypes(classElement, emitter.nativeEmitter); |
| + |
| +class NativeResolutionEnqueuer extends NativeEnqueuerBase { |
| + |
| + NativeResolutionEnqueuer(Enqueuer world, Compiler compiler, |
| + bool enableLiveTypeAnalysis) |
| + : super(world, compiler, enableLiveTypeAnalysis); |
| + |
| + void logSummary(log(message)) { |
| + log('Resolved ${registeredClasses.length} native elements used, ' |
| + '${unusedClasses.length} native elements dead.'); |
| + } |
| +} |
| + |
| + |
| +class NativeCodegenEnqueuer extends NativeEnqueuerBase { |
| + |
| + final CodeEmitterTask emitter; |
| + |
| + NativeCodegenEnqueuer(Enqueuer world, Compiler compiler, this.emitter, |
| + bool enableLiveTypeAnalysis) |
| + : super(world, compiler, enableLiveTypeAnalysis); |
| + |
| + void processNativeClasses(Collection<LibraryElement> libraries) { |
| + super.processNativeClasses(libraries); |
| + |
| + // HACK HACK - add all the resolved classes. |
| + for (final classElement |
| + in compiler.enqueuer.resolution.nativeEnqueuer.registeredClasses) { |
| + if (unusedClasses.contains(classElement)) { |
| + enqueueClass(classElement, 'was resolved'); |
| } |
| } |
| - }); |
| - if (hasNativeClass) { |
| - world.registerStaticUse(compiler.findHelper( |
| - const SourceString('dynamicFunction'))); |
| - world.registerStaticUse(compiler.findHelper( |
| - const SourceString('dynamicSetMetadata'))); |
| - world.registerStaticUse(compiler.findHelper( |
| - const SourceString('defineProperty'))); |
| - world.registerStaticUse(compiler.findHelper( |
| - const SourceString('toStringForNativeObject'))); |
| - world.registerStaticUse(compiler.findHelper( |
| - const SourceString('hashCodeForNativeObject'))); |
| + flushQueue(); |
| + } |
| + |
| + processClass(ClassElement classElement, cause) { |
| + super.processClass(classElement, cause); |
| + // Add the information that this class is a subtype of its supertypes. The |
| + // code emitter and the ssa builder use that information. |
| + addSubtypes(classElement, emitter.nativeEmitter); |
| + } |
| + |
| + void addSubtypes(ClassElement cls, NativeEmitter emitter) { |
| + for (DartType type in cls.allSupertypes) { |
| + List<Element> subtypes = emitter.subtypes.putIfAbsent( |
| + type.element, |
| + () => <ClassElement>[]); |
| + subtypes.add(cls); |
| + } |
| + |
| + List<Element> directSubtypes = emitter.directSubtypes.putIfAbsent( |
| + cls.superclass, |
| + () => <ClassElement>[]); |
| + directSubtypes.add(cls); |
| + } |
| + |
| + void logSummary(log(message)) { |
| + log('Compiled ${registeredClasses.length} native classes, ' |
| + '${unusedClasses.length} native classes omitted.'); |
| } |
| } |
| @@ -88,6 +334,233 @@ void maybeEnableNative(Compiler compiler, |
| } |
| } |
| +class NativeBehavior { |
| + // Native code can return values of one type and cause native subtypes of |
| + // another type to be instantiated. By default, we compute both from the |
| + // declared type. |
| + // |
| + // A field might yield any native type that 'is' the field type. |
| + // |
| + // A method might create and return instances of native subclasses its |
|
ngeoffray
2012/11/14 21:17:39
of its
sra1
2012/11/15 00:09:10
Done.
|
| + // declared return type, and a callback argument may be called with |
| + // instances of the callback parameter type (e.g. Event). |
| + // |
| + // If there is one or more @Creates annotations, the union of the named types |
| + // replaces the inferred instantiated type, and the return type is ignored for |
| + // the purpose of inferring instantiated types. |
| + // |
| + // @Creates(IDBCursor) // Created asynchronously. |
| + // @Creates(IDBRequest) // Created synchronously (for return value). |
| + // IDBRequest request = objectStore.openCursor(); |
| + // |
| + // If there is one or more @Returns annotations, the union of the named types |
| + // replaces the declared return type. |
| + // |
| + // @Returns(IDBRequest) |
| + // IDBRequest request = objectStore.openCursor(); |
| + // |
| + |
| + // These collections contain DartTypes or SpecialTypes. |
| + final Collection typesReturned = []; |
| + final Collection typesInstantiated = []; |
| + |
| + const NativeBehavior(); |
| + |
| + static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) { |
| + // The first argument of a JS-call is a string encoding various attributes |
| + // of the code. |
| + // |
| + // 'Type1|Type2'. A union type. |
| + // '=Object'. A JavaScript Object, no subtype. |
| + // '=List'. A JavaScript Array, no subtype. |
| + |
| + var argNodes = jsCall.arguments; |
| + if (argNodes.isEmpty) { |
| + compiler.cancel("JS expression has no type", node: node); |
| + } |
| + |
| + var firstArg = argNodes.head; |
| + LiteralString specLiteral = firstArg.asLiteralString(); |
| + if (specLiteral != null) { |
| + String specString = specLiteral.dartString.slowToString(); |
| + // Various things that are not in fact types. |
| + if (specString == 'void') return const NativeBehavior(); |
| + if (specString == '' || specString == 'var') { |
| + var behavior = new NativeBehavior(); |
| + behavior.typesReturned.add(compiler.objectClass.computeType(compiler)); |
| + return behavior; |
| + } |
| + var behavior = new NativeBehavior(); |
| + for (final typeString in specString.split('|')) { |
| + var type = _parseType(typeString, compiler, |
| + (name) => resolver.resolveTypeFromString(name), |
| + jsCall); |
| + behavior.typesInstantiated.add(type); |
| + behavior.typesReturned.add(type); |
| + } |
| + return behavior; |
| + } |
| + |
| + // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It |
| + // is not very satisfactory because it does not work for void, dynamic. |
| + |
| + compiler.cancel("Unexpected JS first argument", node: firstArg); |
| + } |
| + |
| + static NativeBehavior ofMethod(Element method, Compiler compiler) { |
| + DartType type = method.computeType(compiler); |
| + var behavior = new NativeBehavior(); |
| + behavior.typesReturned.add(type.returnType); |
| + behavior._capture(type, compiler); |
| + |
| + // TODO(sra): Optional arguments are currently missing from the |
| + // DartType. This should be fixed so the following work-around can be |
| + // removed. |
| + method.computeSignature(compiler).forEachOptionalParameter( |
| + (Element parameter) { |
| + behavior._escape(parameter.computeType(compiler), compiler); |
| + }); |
| + |
| + behavior._overrideWithAnnotations(method, compiler); |
| + return behavior; |
| + } |
| + |
| + static NativeBehavior ofFieldLoad(Element field, Compiler compiler) { |
| + DartType type = field.computeType(compiler); |
| + var behavior = new NativeBehavior(); |
| + behavior.typesReturned.add(type); |
| + behavior._capture(type, compiler); |
| + behavior._overrideWithAnnotations(field, compiler); |
| + return behavior; |
| + } |
| + |
| + static NativeBehavior ofFieldStore(Element field, Compiler compiler) { |
| + DartType type = field.computeType(compiler); |
| + var behavior = new NativeBehavior(); |
| + behavior._escape(type, compiler); |
| + // We don't override the default behaviour - the annotations apply to |
| + // loading the field. |
| + return behavior; |
| + } |
| + |
| + void _overrideWithAnnotations(Element element, Compiler compiler) { |
| + if (element.metadata.isEmpty) return; |
| + |
| + DartType lookup(String name) { |
|
ngeoffray
2012/11/14 21:17:39
You could move this method into _collect.
sra1
2012/11/15 00:09:10
But then I allocate two closures .. I think it is
|
| + Element e = element.buildScope().lookup(new SourceString(name)); |
| + if (e == null) return null; |
| + if (e is! ClassElement) return null; |
| + e.ensureResolved(compiler); |
| + return e.computeType(compiler); |
| + } |
| + |
| + var creates = |
| + _collect(element, compiler, |
| + compiler.enqueuer.resolution.nativeEnqueuer.annotationCreatesClass, |
| + lookup); |
| + var returns = |
| + _collect(element, compiler, |
| + compiler.enqueuer.resolution.nativeEnqueuer.annotationReturnsClass, |
| + lookup); |
| + |
| + if (creates != null) { |
| + typesInstantiated..clear()..addAll(creates); |
|
ngeoffray
2012/11/14 21:17:39
First use of cascaded calls in dart2js I see! Cong
sra1
2012/11/15 00:09:10
Done.
|
| + } |
| + if (returns != null) { |
| + typesReturned..clear()..addAll(returns); |
|
ngeoffray
2012/11/14 21:17:39
And second :)
sra1
2012/11/15 00:09:10
Done.
|
| + } |
| + } |
| + |
| + /** |
| + * Returns a list of type constraints from the annotations of |
| + * [annotationClass]. |
| + * Returns [:null:] if no constraints. |
| + */ |
| + static _collect(Element element, Compiler compiler, Element annotationClass, |
| + lookup(str)) { |
| + var types = null; |
| + for (Link<MetadataAnnotation> link = element.metadata; |
| + !link.isEmpty; |
| + link = link.tail) { |
| + MetadataAnnotation annotation = link.head.ensureResolved(compiler); |
| + var value = annotation.value; |
| + if (value is! ConstructedConstant) continue; |
| + if (value.type is! InterfaceType) continue; |
| + if (!identical(value.type.element, annotationClass)) continue; |
| + |
| + var fields = value.fields; |
| + // TODO(sra): Better validation of the constant. |
| + if (fields.length != 1 || |
| + fields[0] is! StringConstant) { |
| + compiler.cancel( |
| + 'Annotations needs one string: ${annotation.parseNode(compiler)}'); |
| + } |
| + String specString = fields[0].toDartString().slowToString(); |
| + for (final typeString in specString.split('|')) { |
| + var type = _parseType(typeString, compiler, lookup, annotation); |
| + if (types == null) types = []; |
| + types.add(type); |
| + } |
| + } |
| + return types; |
| + } |
| + |
| + void _escape(DartType type, Compiler compiler) { |
|
ngeoffray
2012/11/14 21:17:39
Please add comments on what this function does.
sra1
2012/11/15 00:09:10
Done.
|
| + type = type.unalias(compiler); |
| + if (type is FunctionType) { |
| + _escape(type.returnType, compiler); |
| + for (Link<DartType> parameters = type.parameterTypes; |
| + !parameters.isEmpty; |
| + parameters = parameters.tail) { |
| + _capture(parameters.head, compiler); |
| + } |
| + } |
| + } |
| + |
| + void _capture(DartType type, Compiler compiler) { |
|
ngeoffray
2012/11/14 21:17:39
ditto
sra1
2012/11/15 00:09:10
Done.
|
| + type = type.unalias(compiler); |
| + if (type is FunctionType) { |
| + _capture(type.returnType, compiler); |
| + for (Link<DartType> parameters = type.parameterTypes; |
| + !parameters.isEmpty; |
| + parameters = parameters.tail) { |
| + _escape(parameters.head, compiler); |
| + } |
| + } else { |
| + typesInstantiated.add(type); |
| + } |
| + } |
| + |
| + static _parseType(String typeString, Compiler compiler, |
| + lookup(name), locationNodeOrElement) { |
| + if (typeString == '=Object') return SpecialType.JsObject; |
| + if (typeString == '=List') return SpecialType.JsArray; |
| + if (typeString == 'dynamic') { |
| + return compiler.dynamicClass.computeType(compiler); |
| + } |
| + DartType type = lookup(typeString); |
| + if (type != null) return type; |
| + |
| + int index = typeString.indexOf('<'); |
| + if (index < 1) { |
| + compiler.cancel("Type '$typeString' not found", |
| + node: _errorNode(locationNodeOrElement, compiler)); |
| + } |
| + type = lookup(typeString.substring(0, index)); |
| + if (type != null) { |
| + // TODO(sra): Parse type parameters. |
| + return type; |
| + } |
| + compiler.cancel("Type '$typeString' not found", |
| + node: _errorNode(locationNodeOrElement, compiler)); |
| + } |
| + |
| + static _errorNode(locationNodeOrElement, compiler) { |
| + if (locationNodeOrElement is Node) return locationNodeOrElement; |
| + return locationNodeOrElement.parseNode(compiler); |
| + } |
| +} |
| + |
| void checkAllowedLibrary(ElementListener listener, Token token) { |
| LibraryElement currentLibrary = listener.compilationUnitElement.getLibrary(); |
| if (!currentLibrary.canUseNative) { |