OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library native; | 5 library native; |
6 | 6 |
7 import 'dart:uri'; | 7 import 'dart:uri'; |
8 import 'dart2jslib.dart' hide SourceString; | 8 import 'dart2jslib.dart' hide SourceString; |
9 import 'elements/elements.dart'; | 9 import 'elements/elements.dart'; |
10 import 'js_backend/js_backend.dart'; | 10 import 'js_backend/js_backend.dart'; |
11 import 'scanner/scannerlib.dart'; | 11 import 'scanner/scannerlib.dart'; |
12 import 'ssa/ssa.dart'; | 12 import 'ssa/ssa.dart'; |
13 import 'tree/tree.dart'; | 13 import 'tree/tree.dart'; |
14 import 'util/util.dart'; | 14 import 'util/util.dart'; |
15 | 15 |
16 void processNativeClasses(Enqueuer world, | 16 |
17 CodeEmitterTask emitter, | 17 // 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.
| |
18 Collection<LibraryElement> libraries) { | 18 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
| |
19 for (LibraryElement library in libraries) { | 19 final String name; |
20 processNativeClassesInLibrary(world, emitter, library); | 20 const SpecialType._(this.name); |
21 } | 21 |
22 } | 22 // Object, but no subtypes: |
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
| |
23 | 23 static const JsObject = const SpecialType._('=Object'); |
24 void addSubtypes(ClassElement cls, | 24 |
25 NativeEmitter emitter) { | 25 // The specific implementation of List that is JavaScript Array: |
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
| |
26 for (DartType type in cls.allSupertypes) { | 26 static const JsArray = const SpecialType._('=List'); |
27 List<Element> subtypes = emitter.subtypes.putIfAbsent( | 27 } |
28 type.element, | 28 |
29 | |
30 // 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.
| |
31 class NativeEnqueuer { | |
32 void processNativeClasses(Collection<LibraryElement> libraries) {} | |
33 | |
34 void registerElement(Element element) {} | |
35 | |
36 // Method is a member of a native class. | |
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
| |
37 void registerMethod(Element method) {} | |
38 | |
39 // Field is a member of a native class. | |
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
| |
40 void registerFieldLoad(Element field) {} | |
41 void registerFieldStore(Element field) {} | |
42 | |
43 // 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.
| |
44 // | |
45 // JS('_DOMWindowImpl', 'window'); | |
46 // | |
47 void registerJsCall(Send node, resolver) {} | |
48 | |
49 // Emits a summary log line. | |
ahe
2012/11/14 13:51:30
///
sra1
2012/11/15 00:09:10
Done.
| |
50 void logSummary(log(message)) {} | |
51 } | |
52 | |
53 | |
54 class NativeEnqueuerBase implements NativeEnqueuer { | |
55 | |
56 final Set<ClassElement> nativeClasses = new Set<ClassElement>(); | |
57 | |
58 // 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.
| |
59 final Set<ClassElement> registeredClasses = new Set<ClassElement>(); | |
60 final Set<ClassElement> unusedClasses = new Set<ClassElement>(); | |
61 final Set<ClassElement> pendingClasses = new Set<ClassElement>(); | |
62 | |
63 // Once a type constraint (SpecialType, DartType) has been matched, there is | |
64 // 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.
| |
65 final Set matchedTypeConstraints = new Set(); | |
66 | |
67 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.
| |
68 bool flushing = false; | |
69 | |
70 | |
71 final Enqueuer world; | |
72 final Compiler compiler; | |
73 final bool enableLiveTypeAnalysis; | |
74 | |
75 Element annotationCreatesClass; | |
76 Element annotationReturnsClass; | |
77 | |
78 // 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.
| |
79 NativeEnqueuerBase(this.world, this.compiler, this.enableLiveTypeAnalysis); | |
80 | |
81 void processNativeClasses(Collection<LibraryElement> libraries) { | |
82 Element find(name) { | |
83 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.
| |
84 if (e == null) { | |
85 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.
| |
86 } | |
87 return e; | |
88 } | |
89 annotationCreatesClass = find(const SourceString('Creates')); | |
90 annotationReturnsClass = find(const SourceString('Returns')); | |
91 | |
92 libraries.forEach(processNativeClassesInLibrary); | |
93 | |
94 if (!enableLiveTypeAnalysis) { | |
95 nativeClasses.forEach((c) => enqueueClass(c, 'forced')); | |
96 flushQueue(); | |
97 } | |
98 } | |
99 | |
100 void processNativeClassesInLibrary(LibraryElement library) { | |
101 // Use implementation to ensure the inclusion of injected members. | |
102 library.implementation.forEachLocalMember((Element element) { | |
103 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.
| |
104 ClassElement classElement = element; | |
105 if (classElement.isNative()) { | |
106 nativeClasses.add(classElement); | |
107 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.
| |
108 | |
109 // Resolve class to ensure the class has valid inheritance info. | |
110 classElement.ensureResolved(compiler); | |
111 } | |
112 } | |
113 }); | |
114 } | |
115 | |
116 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.
| |
117 assert(unusedClasses.contains(classElement)); | |
118 // compiler.log( | |
ngeoffray
2012/11/14 21:17:39
Remove debugging code
sra1
2012/11/15 00:09:10
Done.
| |
119 // 'Adding native class ${classElement.name.slowToString()},' | |
120 // ' why: $cause'); | |
121 unusedClasses.remove(classElement); | |
122 pendingClasses.add(classElement); | |
123 queue.add(() { processClass(classElement, cause); }); | |
124 } | |
125 | |
126 void flushQueue() { | |
127 if (flushing) return; | |
128 flushing = true; | |
129 while (!queue.isEmpty) { | |
130 (queue.removeFirst())(); | |
131 } | |
132 flushing = false; | |
133 } | |
134 | |
135 processClass(ClassElement classElement, cause) { | |
136 assert(!registeredClasses.contains(classElement)); | |
137 | |
138 bool firstTime = registeredClasses.isEmpty; | |
139 pendingClasses.remove(classElement); | |
140 registeredClasses.add(classElement); | |
141 | |
142 // print('processClass $classElement $cause'); | |
ngeoffray
2012/11/14 21:17:39
Remove debugging code.
sra1
2012/11/15 00:09:10
Done.
| |
143 world.registerInstantiatedClass(classElement); | |
144 | |
145 // Also parse the node to know all its methods because otherwise it will | |
146 // only be parsed if there is a call to one of its constructors. | |
147 classElement.parseNode(compiler); | |
148 | |
149 if (firstTime) { | |
150 queue.add(onFirstNativeClass); | |
151 } | |
152 } | |
153 | |
154 registerElement(Element element) { | |
155 if (element.isFunction()) return registerMethod(element); | |
156 } | |
157 | |
158 registerMethod(Element method) { | |
159 if (isNativeMethod(method)) { | |
160 // print('Native method call $method'); | |
ngeoffray
2012/11/14 21:17:39
Remove debugging code.
sra1
2012/11/15 00:09:10
Done.
| |
161 processNativeBehavior( | |
162 NativeBehavior.ofMethod(method, compiler), | |
163 method); | |
164 flushQueue(); | |
165 } | |
166 } | |
167 | |
168 bool isNativeMethod(Element element) { | |
169 // Native method? | |
170 Node node = element.parseNode(compiler); | |
171 if (node is! FunctionExpression) return false; | |
172 node = node.body; | |
173 Token token = node.getBeginToken(); | |
174 if (token.stringValue == 'native') return true; | |
175 return false; | |
176 } | |
177 | |
178 void registerFieldLoad(Element field) { | |
179 // print('Native load field $field'); | |
ngeoffray
2012/11/14 21:17:39
Remove
sra1
2012/11/15 00:09:10
Done.
| |
180 processNativeBehavior( | |
181 NativeBehavior.ofFieldLoad(field, compiler), | |
182 field); | |
183 flushQueue(); | |
184 } | |
185 | |
186 void registerFieldStore(Element field) { | |
187 // print('Native store field $field'); | |
ngeoffray
2012/11/14 21:17:39
Remove
sra1
2012/11/15 00:09:10
Done.
| |
188 processNativeBehavior( | |
189 NativeBehavior.ofFieldStore(field, compiler), | |
190 field); | |
191 flushQueue(); | |
192 } | |
193 | |
194 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.
| |
195 processNativeBehavior( | |
196 NativeBehavior.ofJsCall(node, compiler, resolver), | |
197 node); | |
198 flushQueue(); | |
199 } | |
200 | |
201 processNativeBehavior(NativeBehavior behavior, cause) { | |
202 bool allUsedBefore = unusedClasses.isEmpty; | |
203 for (var type in behavior.typesInstantiated) { | |
204 if (matchedTypeConstraints.contains(type)) continue; | |
205 matchedTypeConstraints.add(type); | |
206 if (type is SpecialType) { | |
207 // 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.
| |
208 continue; | |
209 } | |
210 assert(type is DartType); | |
211 enqueueUnusedClassesMatching( | |
212 (nativeClass) => compiler.types.isSubtype(nativeClass.type, type), | |
213 cause, | |
214 'subtypeof($type)'); | |
215 } | |
216 | |
217 // Give an info so that library developers can compile with -v to find why | |
218 // all the native classes are included. | |
219 if (unusedClasses.isEmpty && !allUsedBefore) { | |
220 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.
| |
221 // throw 'Eugh $cause'; | |
ngeoffray
2012/11/14 21:17:39
Remove throw
sra1
2012/11/15 00:09:10
Done.
| |
222 compiler.log('All native types marked as used due to $cause.'); | |
223 } else if (cause.position() != null) { | |
224 //compiler.cancel('Eugh!', token: cause.position()); | |
ngeoffray
2012/11/14 21:17:39
Remove cancel
sra1
2012/11/15 00:09:10
Done.
| |
225 compiler.log('All native types marked used due to $cause.'); | |
226 } else { | |
227 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.
| |
228 } | |
229 } | |
230 } | |
231 | |
232 enqueueUnusedClassesMatching(predicate, cause, [reason]) { | |
ngeoffray
2012/11/14 21:17:39
Types please
sra1
2012/11/15 00:09:10
Done.
| |
233 var matches = unusedClasses.filter(predicate); | |
ngeoffray
2012/11/14 21:17:39
ditto
sra1
2012/11/15 00:09:10
Done.
| |
234 // print('${matches.length} matches: $matches\n.. $reason $world'); | |
ngeoffray
2012/11/14 21:17:39
Remove
sra1
2012/11/15 00:09:10
Done.
| |
235 matches.forEach((c) => enqueueClass(c, cause)); | |
236 } | |
237 | |
238 onFirstNativeClass() { | |
239 staticUse(name) => world.registerStaticUse(compiler.findHelper(name)); | |
240 | |
241 staticUse(const SourceString('dynamicFunction')); | |
242 staticUse(const SourceString('dynamicSetMetadata')); | |
243 staticUse(const SourceString('defineProperty')); | |
244 staticUse(const SourceString('toStringForNativeObject')); | |
245 staticUse(const SourceString('hashCodeForNativeObject')); | |
246 | |
247 addNativeExceptions(); | |
248 } | |
249 | |
250 addNativeExceptions() { | |
251 enqueueUnusedClassesMatching((classElement) { | |
252 // TODO(sra): Annotate exception classes in dart:html. | |
253 var name = classElement.name.slowToString(); | |
ngeoffray
2012/11/14 21:17:39
String name
sra1
2012/11/15 00:09:10
Done.
| |
254 if (name.contains('Exception')) return true; | |
255 if (name.contains('Error')) return true; | |
256 return false; | |
257 }, | |
258 'native exception'); | |
259 } | |
260 } | |
261 | |
262 | |
263 class NativeResolutionEnqueuer extends NativeEnqueuerBase { | |
264 | |
265 NativeResolutionEnqueuer(Enqueuer world, Compiler compiler, | |
266 bool enableLiveTypeAnalysis) | |
267 : super(world, compiler, enableLiveTypeAnalysis); | |
268 | |
269 void logSummary(log(message)) { | |
270 log('Resolved ${registeredClasses.length} native elements used, ' | |
271 '${unusedClasses.length} native elements dead.'); | |
272 } | |
273 } | |
274 | |
275 | |
276 class NativeCodegenEnqueuer extends NativeEnqueuerBase { | |
277 | |
278 final CodeEmitterTask emitter; | |
279 | |
280 NativeCodegenEnqueuer(Enqueuer world, Compiler compiler, this.emitter, | |
281 bool enableLiveTypeAnalysis) | |
282 : super(world, compiler, enableLiveTypeAnalysis); | |
283 | |
284 void processNativeClasses(Collection<LibraryElement> libraries) { | |
285 super.processNativeClasses(libraries); | |
286 | |
287 // HACK HACK - add all the resolved classes. | |
288 for (final classElement | |
289 in compiler.enqueuer.resolution.nativeEnqueuer.registeredClasses) { | |
290 if (unusedClasses.contains(classElement)) { | |
291 enqueueClass(classElement, 'was resolved'); | |
292 } | |
293 } | |
294 flushQueue(); | |
295 } | |
296 | |
297 processClass(ClassElement classElement, cause) { | |
298 super.processClass(classElement, cause); | |
299 // Add the information that this class is a subtype of its supertypes. The | |
300 // code emitter and the ssa builder use that information. | |
301 addSubtypes(classElement, emitter.nativeEmitter); | |
302 } | |
303 | |
304 void addSubtypes(ClassElement cls, NativeEmitter emitter) { | |
305 for (DartType type in cls.allSupertypes) { | |
306 List<Element> subtypes = emitter.subtypes.putIfAbsent( | |
307 type.element, | |
308 () => <ClassElement>[]); | |
309 subtypes.add(cls); | |
310 } | |
311 | |
312 List<Element> directSubtypes = emitter.directSubtypes.putIfAbsent( | |
313 cls.superclass, | |
29 () => <ClassElement>[]); | 314 () => <ClassElement>[]); |
30 subtypes.add(cls); | 315 directSubtypes.add(cls); |
31 } | 316 } |
32 | 317 |
33 List<Element> directSubtypes = emitter.directSubtypes.putIfAbsent( | 318 void logSummary(log(message)) { |
34 cls.superclass, | 319 log('Compiled ${registeredClasses.length} native classes, ' |
35 () => <ClassElement>[]); | 320 '${unusedClasses.length} native classes omitted.'); |
36 directSubtypes.add(cls); | 321 } |
37 } | 322 } |
38 | 323 |
39 void processNativeClassesInLibrary(Enqueuer world, | |
40 CodeEmitterTask emitter, | |
41 LibraryElement library) { | |
42 bool hasNativeClass = false; | |
43 final compiler = emitter.compiler; | |
44 // Use implementation to ensure the inclusion of injected members. | |
45 library.implementation.forEachLocalMember((Element element) { | |
46 if (element.kind == ElementKind.CLASS) { | |
47 ClassElement classElement = element; | |
48 if (classElement.isNative()) { | |
49 hasNativeClass = true; | |
50 world.registerInstantiatedClass(classElement); | |
51 // Also parse the node to know all its methods because | |
52 // otherwise it will only be parsed if there is a call to | |
53 // one of its constructor. | |
54 classElement.parseNode(compiler); | |
55 // Resolve to setup the inheritance. | |
56 classElement.ensureResolved(compiler); | |
57 // Add the information that this class is a subtype of | |
58 // its supertypes. The code emitter and the ssa builder use that | |
59 // information. | |
60 addSubtypes(classElement, emitter.nativeEmitter); | |
61 } | |
62 } | |
63 }); | |
64 if (hasNativeClass) { | |
65 world.registerStaticUse(compiler.findHelper( | |
66 const SourceString('dynamicFunction'))); | |
67 world.registerStaticUse(compiler.findHelper( | |
68 const SourceString('dynamicSetMetadata'))); | |
69 world.registerStaticUse(compiler.findHelper( | |
70 const SourceString('defineProperty'))); | |
71 world.registerStaticUse(compiler.findHelper( | |
72 const SourceString('toStringForNativeObject'))); | |
73 world.registerStaticUse(compiler.findHelper( | |
74 const SourceString('hashCodeForNativeObject'))); | |
75 } | |
76 } | |
77 | |
78 void maybeEnableNative(Compiler compiler, | 324 void maybeEnableNative(Compiler compiler, |
79 LibraryElement library, | 325 LibraryElement library, |
80 Uri uri) { | 326 Uri uri) { |
81 String libraryName = uri.toString(); | 327 String libraryName = uri.toString(); |
82 if (library.entryCompilationUnit.script.name.contains( | 328 if (library.entryCompilationUnit.script.name.contains( |
83 'dart/tests/compiler/dart2js_native') | 329 'dart/tests/compiler/dart2js_native') |
84 || libraryName == 'dart:isolate' | 330 || libraryName == 'dart:isolate' |
85 || libraryName == 'dart:html' | 331 || libraryName == 'dart:html' |
86 || libraryName == 'dart:svg') { | 332 || libraryName == 'dart:svg') { |
87 library.canUseNative = true; | 333 library.canUseNative = true; |
88 } | 334 } |
89 } | 335 } |
90 | 336 |
337 class NativeBehavior { | |
338 // Native code can return values of one type and cause native subtypes of | |
339 // another type to be instantiated. By default, we compute both from the | |
340 // declared type. | |
341 // | |
342 // A field might yield any native type that 'is' the field type. | |
343 // | |
344 // 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.
| |
345 // declared return type, and a callback argument may be called with | |
346 // instances of the callback parameter type (e.g. Event). | |
347 // | |
348 // If there is one or more @Creates annotations, the union of the named types | |
349 // replaces the inferred instantiated type, and the return type is ignored for | |
350 // the purpose of inferring instantiated types. | |
351 // | |
352 // @Creates(IDBCursor) // Created asynchronously. | |
353 // @Creates(IDBRequest) // Created synchronously (for return value). | |
354 // IDBRequest request = objectStore.openCursor(); | |
355 // | |
356 // If there is one or more @Returns annotations, the union of the named types | |
357 // replaces the declared return type. | |
358 // | |
359 // @Returns(IDBRequest) | |
360 // IDBRequest request = objectStore.openCursor(); | |
361 // | |
362 | |
363 // These collections contain DartTypes or SpecialTypes. | |
364 final Collection typesReturned = []; | |
365 final Collection typesInstantiated = []; | |
366 | |
367 const NativeBehavior(); | |
368 | |
369 static NativeBehavior ofJsCall(Send jsCall, Compiler compiler, resolver) { | |
370 // The first argument of a JS-call is a string encoding various attributes | |
371 // of the code. | |
372 // | |
373 // 'Type1|Type2'. A union type. | |
374 // '=Object'. A JavaScript Object, no subtype. | |
375 // '=List'. A JavaScript Array, no subtype. | |
376 | |
377 var argNodes = jsCall.arguments; | |
378 if (argNodes.isEmpty) { | |
379 compiler.cancel("JS expression has no type", node: node); | |
380 } | |
381 | |
382 var firstArg = argNodes.head; | |
383 LiteralString specLiteral = firstArg.asLiteralString(); | |
384 if (specLiteral != null) { | |
385 String specString = specLiteral.dartString.slowToString(); | |
386 // Various things that are not in fact types. | |
387 if (specString == 'void') return const NativeBehavior(); | |
388 if (specString == '' || specString == 'var') { | |
389 var behavior = new NativeBehavior(); | |
390 behavior.typesReturned.add(compiler.objectClass.computeType(compiler)); | |
391 return behavior; | |
392 } | |
393 var behavior = new NativeBehavior(); | |
394 for (final typeString in specString.split('|')) { | |
395 var type = _parseType(typeString, compiler, | |
396 (name) => resolver.resolveTypeFromString(name), | |
397 jsCall); | |
398 behavior.typesInstantiated.add(type); | |
399 behavior.typesReturned.add(type); | |
400 } | |
401 return behavior; | |
402 } | |
403 | |
404 // TODO(sra): We could accept a type identifier? e.g. JS(bool, '1<2'). It | |
405 // is not very satisfactory because it does not work for void, dynamic. | |
406 | |
407 compiler.cancel("Unexpected JS first argument", node: firstArg); | |
408 } | |
409 | |
410 static NativeBehavior ofMethod(Element method, Compiler compiler) { | |
411 DartType type = method.computeType(compiler); | |
412 var behavior = new NativeBehavior(); | |
413 behavior.typesReturned.add(type.returnType); | |
414 behavior._capture(type, compiler); | |
415 | |
416 // TODO(sra): Optional arguments are currently missing from the | |
417 // DartType. This should be fixed so the following work-around can be | |
418 // removed. | |
419 method.computeSignature(compiler).forEachOptionalParameter( | |
420 (Element parameter) { | |
421 behavior._escape(parameter.computeType(compiler), compiler); | |
422 }); | |
423 | |
424 behavior._overrideWithAnnotations(method, compiler); | |
425 return behavior; | |
426 } | |
427 | |
428 static NativeBehavior ofFieldLoad(Element field, Compiler compiler) { | |
429 DartType type = field.computeType(compiler); | |
430 var behavior = new NativeBehavior(); | |
431 behavior.typesReturned.add(type); | |
432 behavior._capture(type, compiler); | |
433 behavior._overrideWithAnnotations(field, compiler); | |
434 return behavior; | |
435 } | |
436 | |
437 static NativeBehavior ofFieldStore(Element field, Compiler compiler) { | |
438 DartType type = field.computeType(compiler); | |
439 var behavior = new NativeBehavior(); | |
440 behavior._escape(type, compiler); | |
441 // We don't override the default behaviour - the annotations apply to | |
442 // loading the field. | |
443 return behavior; | |
444 } | |
445 | |
446 void _overrideWithAnnotations(Element element, Compiler compiler) { | |
447 if (element.metadata.isEmpty) return; | |
448 | |
449 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
| |
450 Element e = element.buildScope().lookup(new SourceString(name)); | |
451 if (e == null) return null; | |
452 if (e is! ClassElement) return null; | |
453 e.ensureResolved(compiler); | |
454 return e.computeType(compiler); | |
455 } | |
456 | |
457 var creates = | |
458 _collect(element, compiler, | |
459 compiler.enqueuer.resolution.nativeEnqueuer.annotationCreatesClass, | |
460 lookup); | |
461 var returns = | |
462 _collect(element, compiler, | |
463 compiler.enqueuer.resolution.nativeEnqueuer.annotationReturnsClass, | |
464 lookup); | |
465 | |
466 if (creates != null) { | |
467 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.
| |
468 } | |
469 if (returns != null) { | |
470 typesReturned..clear()..addAll(returns); | |
ngeoffray
2012/11/14 21:17:39
And second :)
sra1
2012/11/15 00:09:10
Done.
| |
471 } | |
472 } | |
473 | |
474 /** | |
475 * Returns a list of type constraints from the annotations of | |
476 * [annotationClass]. | |
477 * Returns [:null:] if no constraints. | |
478 */ | |
479 static _collect(Element element, Compiler compiler, Element annotationClass, | |
480 lookup(str)) { | |
481 var types = null; | |
482 for (Link<MetadataAnnotation> link = element.metadata; | |
483 !link.isEmpty; | |
484 link = link.tail) { | |
485 MetadataAnnotation annotation = link.head.ensureResolved(compiler); | |
486 var value = annotation.value; | |
487 if (value is! ConstructedConstant) continue; | |
488 if (value.type is! InterfaceType) continue; | |
489 if (!identical(value.type.element, annotationClass)) continue; | |
490 | |
491 var fields = value.fields; | |
492 // TODO(sra): Better validation of the constant. | |
493 if (fields.length != 1 || | |
494 fields[0] is! StringConstant) { | |
495 compiler.cancel( | |
496 'Annotations needs one string: ${annotation.parseNode(compiler)}'); | |
497 } | |
498 String specString = fields[0].toDartString().slowToString(); | |
499 for (final typeString in specString.split('|')) { | |
500 var type = _parseType(typeString, compiler, lookup, annotation); | |
501 if (types == null) types = []; | |
502 types.add(type); | |
503 } | |
504 } | |
505 return types; | |
506 } | |
507 | |
508 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.
| |
509 type = type.unalias(compiler); | |
510 if (type is FunctionType) { | |
511 _escape(type.returnType, compiler); | |
512 for (Link<DartType> parameters = type.parameterTypes; | |
513 !parameters.isEmpty; | |
514 parameters = parameters.tail) { | |
515 _capture(parameters.head, compiler); | |
516 } | |
517 } | |
518 } | |
519 | |
520 void _capture(DartType type, Compiler compiler) { | |
ngeoffray
2012/11/14 21:17:39
ditto
sra1
2012/11/15 00:09:10
Done.
| |
521 type = type.unalias(compiler); | |
522 if (type is FunctionType) { | |
523 _capture(type.returnType, compiler); | |
524 for (Link<DartType> parameters = type.parameterTypes; | |
525 !parameters.isEmpty; | |
526 parameters = parameters.tail) { | |
527 _escape(parameters.head, compiler); | |
528 } | |
529 } else { | |
530 typesInstantiated.add(type); | |
531 } | |
532 } | |
533 | |
534 static _parseType(String typeString, Compiler compiler, | |
535 lookup(name), locationNodeOrElement) { | |
536 if (typeString == '=Object') return SpecialType.JsObject; | |
537 if (typeString == '=List') return SpecialType.JsArray; | |
538 if (typeString == 'dynamic') { | |
539 return compiler.dynamicClass.computeType(compiler); | |
540 } | |
541 DartType type = lookup(typeString); | |
542 if (type != null) return type; | |
543 | |
544 int index = typeString.indexOf('<'); | |
545 if (index < 1) { | |
546 compiler.cancel("Type '$typeString' not found", | |
547 node: _errorNode(locationNodeOrElement, compiler)); | |
548 } | |
549 type = lookup(typeString.substring(0, index)); | |
550 if (type != null) { | |
551 // TODO(sra): Parse type parameters. | |
552 return type; | |
553 } | |
554 compiler.cancel("Type '$typeString' not found", | |
555 node: _errorNode(locationNodeOrElement, compiler)); | |
556 } | |
557 | |
558 static _errorNode(locationNodeOrElement, compiler) { | |
559 if (locationNodeOrElement is Node) return locationNodeOrElement; | |
560 return locationNodeOrElement.parseNode(compiler); | |
561 } | |
562 } | |
563 | |
91 void checkAllowedLibrary(ElementListener listener, Token token) { | 564 void checkAllowedLibrary(ElementListener listener, Token token) { |
92 LibraryElement currentLibrary = listener.compilationUnitElement.getLibrary(); | 565 LibraryElement currentLibrary = listener.compilationUnitElement.getLibrary(); |
93 if (!currentLibrary.canUseNative) { | 566 if (!currentLibrary.canUseNative) { |
94 listener.recoverableError("Unexpected token", token: token); | 567 listener.recoverableError("Unexpected token", token: token); |
95 } | 568 } |
96 } | 569 } |
97 | 570 |
98 Token handleNativeBlockToSkip(Listener listener, Token token) { | 571 Token handleNativeBlockToSkip(Listener listener, Token token) { |
99 checkAllowedLibrary(listener, token); | 572 checkAllowedLibrary(listener, token); |
100 token = token.next; | 573 token = token.next; |
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
333 String parameters) { | 806 String parameters) { |
334 buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty"); | 807 buffer.add(" if (Object.getPrototypeOf(this).hasOwnProperty"); |
335 buffer.add("('$methodName')) {\n"); | 808 buffer.add("('$methodName')) {\n"); |
336 buffer.add(" $code"); | 809 buffer.add(" $code"); |
337 buffer.add(" } else {\n"); | 810 buffer.add(" } else {\n"); |
338 buffer.add(" return Object.prototype.$methodName.call(this"); | 811 buffer.add(" return Object.prototype.$methodName.call(this"); |
339 buffer.add(parameters == '' ? '' : ', $parameters'); | 812 buffer.add(parameters == '' ? '' : ', $parameters'); |
340 buffer.add(");\n"); | 813 buffer.add(");\n"); |
341 buffer.add(" }\n"); | 814 buffer.add(" }\n"); |
342 } | 815 } |
OLD | NEW |