Chromium Code Reviews| 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 |