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