| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 part of native; | 5 part of native; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * This could be an abstract class but we use it as a stub for the dart_backend. | 8 * This could be an abstract class but we use it as a stub for the dart_backend. |
| 9 */ | 9 */ |
| 10 class NativeEnqueuer { | 10 class NativeEnqueuer { |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 ClassElement _annotationJsNameClass; | 90 ClassElement _annotationJsNameClass; |
| 91 | 91 |
| 92 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. | 92 /// Subclasses of [NativeEnqueuerBase] are constructed by the backend. |
| 93 NativeEnqueuerBase(this.world, Compiler compiler, this.enableLiveTypeAnalysis) | 93 NativeEnqueuerBase(this.world, Compiler compiler, this.enableLiveTypeAnalysis) |
| 94 : this.compiler = compiler, | 94 : this.compiler = compiler, |
| 95 processedLibraries = compiler.cacheStrategy.newSet(); | 95 processedLibraries = compiler.cacheStrategy.newSet(); |
| 96 | 96 |
| 97 JavaScriptBackend get backend => compiler.backend; | 97 JavaScriptBackend get backend => compiler.backend; |
| 98 Resolution get resolution => compiler.resolution; | 98 Resolution get resolution => compiler.resolution; |
| 99 | 99 |
| 100 DiagnosticReporter get reporter => compiler.reporter; |
| 101 |
| 100 void processNativeClasses(Iterable<LibraryElement> libraries) { | 102 void processNativeClasses(Iterable<LibraryElement> libraries) { |
| 101 if (compiler.hasIncrementalSupport) { | 103 if (compiler.hasIncrementalSupport) { |
| 102 // Since [Set.add] returns bool if an element was added, this restricts | 104 // Since [Set.add] returns bool if an element was added, this restricts |
| 103 // [libraries] to ones that haven't already been processed. This saves | 105 // [libraries] to ones that haven't already been processed. This saves |
| 104 // time during incremental compiles. | 106 // time during incremental compiles. |
| 105 libraries = libraries.where(processedLibraries.add); | 107 libraries = libraries.where(processedLibraries.add); |
| 106 } | 108 } |
| 107 libraries.forEach(processNativeClassesInLibrary); | 109 libraries.forEach(processNativeClassesInLibrary); |
| 108 if (backend.isolateHelperLibrary != null) { | 110 if (backend.isolateHelperLibrary != null) { |
| 109 processNativeClassesInLibrary(backend.isolateHelperLibrary); | 111 processNativeClassesInLibrary(backend.isolateHelperLibrary); |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 236 token = token.next; | 238 token = token.next; |
| 237 if (token.stringValue != '.') break; | 239 if (token.stringValue != '.') break; |
| 238 token = token.next; | 240 token = token.next; |
| 239 if (!token.isIdentifier()) return null; | 241 if (!token.isIdentifier()) return null; |
| 240 id = token; | 242 id = token; |
| 241 } | 243 } |
| 242 // Should be at '{', 'with', 'implements', '<' or 'native'. | 244 // Should be at '{', 'with', 'implements', '<' or 'native'. |
| 243 return id.value; | 245 return id.value; |
| 244 } | 246 } |
| 245 | 247 |
| 246 return compiler.withCurrentElement(classElement, () { | 248 return reporter.withCurrentElement(classElement, () { |
| 247 return scanForExtendsName(classElement.position); | 249 return scanForExtendsName(classElement.position); |
| 248 }); | 250 }); |
| 249 } | 251 } |
| 250 | 252 |
| 251 ClassElement get annotationCreatesClass { | 253 ClassElement get annotationCreatesClass { |
| 252 findAnnotationClasses(); | 254 findAnnotationClasses(); |
| 253 return _annotationCreatesClass; | 255 return _annotationCreatesClass; |
| 254 } | 256 } |
| 255 | 257 |
| 256 ClassElement get annotationReturnsClass { | 258 ClassElement get annotationReturnsClass { |
| 257 findAnnotationClasses(); | 259 findAnnotationClasses(); |
| 258 return _annotationReturnsClass; | 260 return _annotationReturnsClass; |
| 259 } | 261 } |
| 260 | 262 |
| 261 ClassElement get annotationJsNameClass { | 263 ClassElement get annotationJsNameClass { |
| 262 findAnnotationClasses(); | 264 findAnnotationClasses(); |
| 263 return _annotationJsNameClass; | 265 return _annotationJsNameClass; |
| 264 } | 266 } |
| 265 | 267 |
| 266 void findAnnotationClasses() { | 268 void findAnnotationClasses() { |
| 267 if (_annotationCreatesClass != null) return; | 269 if (_annotationCreatesClass != null) return; |
| 268 ClassElement find(name) { | 270 ClassElement find(name) { |
| 269 Element e = backend.findHelper(name); | 271 Element e = backend.findHelper(name); |
| 270 if (e == null || e is! ClassElement) { | 272 if (e == null || e is! ClassElement) { |
| 271 compiler.internalError(NO_LOCATION_SPANNABLE, | 273 reporter.internalError(NO_LOCATION_SPANNABLE, |
| 272 "Could not find implementation class '${name}'."); | 274 "Could not find implementation class '${name}'."); |
| 273 } | 275 } |
| 274 return e; | 276 return e; |
| 275 } | 277 } |
| 276 _annotationCreatesClass = find('Creates'); | 278 _annotationCreatesClass = find('Creates'); |
| 277 _annotationReturnsClass = find('Returns'); | 279 _annotationReturnsClass = find('Returns'); |
| 278 _annotationJsNameClass = find('JSName'); | 280 _annotationJsNameClass = find('JSName'); |
| 279 } | 281 } |
| 280 | 282 |
| 281 /// Returns the JSName annotation string or `null` if no JSName annotation is | 283 /// Returns the JSName annotation string or `null` if no JSName annotation is |
| 282 /// present. | 284 /// present. |
| 283 String findJsNameFromAnnotation(Element element) { | 285 String findJsNameFromAnnotation(Element element) { |
| 284 String name = null; | 286 String name = null; |
| 285 ClassElement annotationClass = annotationJsNameClass; | 287 ClassElement annotationClass = annotationJsNameClass; |
| 286 for (MetadataAnnotation annotation in element.implementation.metadata) { | 288 for (MetadataAnnotation annotation in element.implementation.metadata) { |
| 287 annotation.ensureResolved(resolution); | 289 annotation.ensureResolved(resolution); |
| 288 ConstantValue value = | 290 ConstantValue value = |
| 289 compiler.constants.getConstantValue(annotation.constant); | 291 compiler.constants.getConstantValue(annotation.constant); |
| 290 if (!value.isConstructedObject) continue; | 292 if (!value.isConstructedObject) continue; |
| 291 ConstructedConstantValue constructedObject = value; | 293 ConstructedConstantValue constructedObject = value; |
| 292 if (constructedObject.type.element != annotationClass) continue; | 294 if (constructedObject.type.element != annotationClass) continue; |
| 293 | 295 |
| 294 Iterable<ConstantValue> fields = constructedObject.fields.values; | 296 Iterable<ConstantValue> fields = constructedObject.fields.values; |
| 295 // TODO(sra): Better validation of the constant. | 297 // TODO(sra): Better validation of the constant. |
| 296 if (fields.length != 1 || fields.single is! StringConstantValue) { | 298 if (fields.length != 1 || fields.single is! StringConstantValue) { |
| 297 compiler.internalError(annotation, | 299 reporter.internalError(annotation, |
| 298 'Annotations needs one string: ${annotation.node}'); | 300 'Annotations needs one string: ${annotation.node}'); |
| 299 } | 301 } |
| 300 StringConstantValue specStringConstant = fields.single; | 302 StringConstantValue specStringConstant = fields.single; |
| 301 String specString = specStringConstant.toDartString().slowToString(); | 303 String specString = specStringConstant.toDartString().slowToString(); |
| 302 if (name == null) { | 304 if (name == null) { |
| 303 name = specString; | 305 name = specString; |
| 304 } else { | 306 } else { |
| 305 compiler.internalError(annotation, | 307 reporter.internalError(annotation, |
| 306 'Too many JSName annotations: ${annotation.node}'); | 308 'Too many JSName annotations: ${annotation.node}'); |
| 307 } | 309 } |
| 308 } | 310 } |
| 309 return name; | 311 return name; |
| 310 } | 312 } |
| 311 | 313 |
| 312 enqueueClass(ClassElement classElement, cause) { | 314 enqueueClass(ClassElement classElement, cause) { |
| 313 assert(unusedClasses.contains(classElement)); | 315 assert(unusedClasses.contains(classElement)); |
| 314 unusedClasses.remove(classElement); | 316 unusedClasses.remove(classElement); |
| 315 pendingClasses.add(classElement); | 317 pendingClasses.add(classElement); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 338 classElement.ensureResolved(resolution); | 340 classElement.ensureResolved(resolution); |
| 339 compiler.backend.registerInstantiatedType( | 341 compiler.backend.registerInstantiatedType( |
| 340 classElement.rawType, world, compiler.globalDependencies); | 342 classElement.rawType, world, compiler.globalDependencies); |
| 341 | 343 |
| 342 if (firstTime) { | 344 if (firstTime) { |
| 343 queue.add(onFirstNativeClass); | 345 queue.add(onFirstNativeClass); |
| 344 } | 346 } |
| 345 } | 347 } |
| 346 | 348 |
| 347 registerElement(Element element) { | 349 registerElement(Element element) { |
| 348 compiler.withCurrentElement(element, () { | 350 reporter.withCurrentElement(element, () { |
| 349 if (element.isFunction || element.isGetter || element.isSetter) { | 351 if (element.isFunction || element.isGetter || element.isSetter) { |
| 350 handleMethodAnnotations(element); | 352 handleMethodAnnotations(element); |
| 351 if (element.isNative) { | 353 if (element.isNative) { |
| 352 registerMethodUsed(element); | 354 registerMethodUsed(element); |
| 353 } | 355 } |
| 354 } else if (element.isField) { | 356 } else if (element.isField) { |
| 355 handleFieldAnnotations(element); | 357 handleFieldAnnotations(element); |
| 356 if (element.isNative) { | 358 if (element.isNative) { |
| 357 registerFieldLoad(element); | 359 registerFieldLoad(element); |
| 358 registerFieldStore(element); | 360 registerFieldStore(element); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 398 /// 2. If [element] has a @JSName annotation that is not an identifier, | 400 /// 2. If [element] has a @JSName annotation that is not an identifier, |
| 399 /// use the declared @JSName as the expression | 401 /// use the declared @JSName as the expression |
| 400 /// 3. If [element] does not have a @JSName annotation, qualify the name of | 402 /// 3. If [element] does not have a @JSName annotation, qualify the name of |
| 401 /// the method with the @Native name of the enclosing class. | 403 /// the method with the @Native name of the enclosing class. |
| 402 void setNativeNameForStaticMethod(ElementX element) { | 404 void setNativeNameForStaticMethod(ElementX element) { |
| 403 String name = findJsNameFromAnnotation(element); | 405 String name = findJsNameFromAnnotation(element); |
| 404 if (name == null) name = element.name; | 406 if (name == null) name = element.name; |
| 405 if (isIdentifier(name)) { | 407 if (isIdentifier(name)) { |
| 406 List<String> nativeNames = nativeTagsOfClassRaw(element.enclosingClass); | 408 List<String> nativeNames = nativeTagsOfClassRaw(element.enclosingClass); |
| 407 if (nativeNames.length != 1) { | 409 if (nativeNames.length != 1) { |
| 408 compiler.internalError(element, | 410 reporter.internalError(element, |
| 409 'Unable to determine a native name for the enclosing class, ' | 411 'Unable to determine a native name for the enclosing class, ' |
| 410 'options: $nativeNames'); | 412 'options: $nativeNames'); |
| 411 } | 413 } |
| 412 element.setNative('${nativeNames[0]}.$name'); | 414 element.setNative('${nativeNames[0]}.$name'); |
| 413 } else { | 415 } else { |
| 414 element.setNative(name); | 416 element.setNative(name); |
| 415 } | 417 } |
| 416 } | 418 } |
| 417 | 419 |
| 418 bool isIdentifier(String s) => _identifier.hasMatch(s); | 420 bool isIdentifier(String s) => _identifier.hasMatch(s); |
| 419 | 421 |
| 420 bool isNativeMethod(FunctionElementX element) { | 422 bool isNativeMethod(FunctionElementX element) { |
| 421 if (!element.library.canUseNative) return false; | 423 if (!element.library.canUseNative) return false; |
| 422 // Native method? | 424 // Native method? |
| 423 return compiler.withCurrentElement(element, () { | 425 return reporter.withCurrentElement(element, () { |
| 424 Node node = element.parseNode(resolution.parsing); | 426 Node node = element.parseNode(resolution.parsing); |
| 425 if (node is! FunctionExpression) return false; | 427 if (node is! FunctionExpression) return false; |
| 426 FunctionExpression functionExpression = node; | 428 FunctionExpression functionExpression = node; |
| 427 node = functionExpression.body; | 429 node = functionExpression.body; |
| 428 Token token = node.getBeginToken(); | 430 Token token = node.getBeginToken(); |
| 429 if (identical(token.stringValue, 'native')) return true; | 431 if (identical(token.stringValue, 'native')) return true; |
| 430 return false; | 432 return false; |
| 431 }); | 433 }); |
| 432 } | 434 } |
| 433 | 435 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 488 assert(type is DartType); | 490 assert(type is DartType); |
| 489 enqueueUnusedClassesMatching( | 491 enqueueUnusedClassesMatching( |
| 490 (nativeClass) => compiler.types.isSubtype(nativeClass.thisType, type), | 492 (nativeClass) => compiler.types.isSubtype(nativeClass.thisType, type), |
| 491 cause, | 493 cause, |
| 492 'subtypeof($type)'); | 494 'subtypeof($type)'); |
| 493 } | 495 } |
| 494 | 496 |
| 495 // Give an info so that library developers can compile with -v to find why | 497 // Give an info so that library developers can compile with -v to find why |
| 496 // all the native classes are included. | 498 // all the native classes are included. |
| 497 if (unusedClasses.isEmpty && !allUsedBefore) { | 499 if (unusedClasses.isEmpty && !allUsedBefore) { |
| 498 compiler.log('All native types marked as used due to $cause.'); | 500 reporter.log('All native types marked as used due to $cause.'); |
| 499 } | 501 } |
| 500 } | 502 } |
| 501 | 503 |
| 502 enqueueUnusedClassesMatching(bool predicate(classElement), | 504 enqueueUnusedClassesMatching(bool predicate(classElement), |
| 503 cause, | 505 cause, |
| 504 [String reason]) { | 506 [String reason]) { |
| 505 Iterable matches = unusedClasses.where(predicate); | 507 Iterable matches = unusedClasses.where(predicate); |
| 506 matches.toList().forEach((c) => enqueueClass(c, cause)); | 508 matches.toList().forEach((c) => enqueueClass(c, cause)); |
| 507 } | 509 } |
| 508 | 510 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 541 | 543 |
| 542 void processNativeClass(ClassElement classElement) { | 544 void processNativeClass(ClassElement classElement) { |
| 543 super.processNativeClass(classElement); | 545 super.processNativeClass(classElement); |
| 544 | 546 |
| 545 // Since we map from dispatch tags to classes, a dispatch tag must be used | 547 // Since we map from dispatch tags to classes, a dispatch tag must be used |
| 546 // on only one native class. | 548 // on only one native class. |
| 547 for (String tag in nativeTagsOfClass(classElement)) { | 549 for (String tag in nativeTagsOfClass(classElement)) { |
| 548 ClassElement owner = tagOwner[tag]; | 550 ClassElement owner = tagOwner[tag]; |
| 549 if (owner != null) { | 551 if (owner != null) { |
| 550 if (owner != classElement) { | 552 if (owner != classElement) { |
| 551 compiler.internalError( | 553 reporter.internalError( |
| 552 classElement, "Tag '$tag' already in use by '${owner.name}'"); | 554 classElement, "Tag '$tag' already in use by '${owner.name}'"); |
| 553 } | 555 } |
| 554 } else { | 556 } else { |
| 555 tagOwner[tag] = classElement; | 557 tagOwner[tag] = classElement; |
| 556 } | 558 } |
| 557 } | 559 } |
| 558 } | 560 } |
| 559 | 561 |
| 560 void logSummary(log(message)) { | 562 void logSummary(log(message)) { |
| 561 log('Resolved ${registeredClasses.length} native elements used, ' | 563 log('Resolved ${registeredClasses.length} native elements used, ' |
| 562 '${unusedClasses.length} native elements dead.'); | 564 '${unusedClasses.length} native elements dead.'); |
| 563 } | 565 } |
| 564 | 566 |
| 565 /** | 567 /** |
| 566 * Handles JS-calls, which can be an instantiation point for types. | 568 * Handles JS-calls, which can be an instantiation point for types. |
| 567 * | 569 * |
| 568 * For example, the following code instantiates and returns native classes | 570 * For example, the following code instantiates and returns native classes |
| 569 * that are `_DOMWindowImpl` or a subtype. | 571 * that are `_DOMWindowImpl` or a subtype. |
| 570 * | 572 * |
| 571 * JS('_DOMWindowImpl', 'window') | 573 * JS('_DOMWindowImpl', 'window') |
| 572 * | 574 * |
| 573 */ | 575 */ |
| 574 void registerJsCall(Send node, ForeignResolver resolver) { | 576 void registerJsCall(Send node, ForeignResolver resolver) { |
| 575 NativeBehavior behavior = NativeBehavior.ofJsCall(node, compiler, resolver); | 577 NativeBehavior behavior = NativeBehavior.ofJsCall( |
| 578 node, reporter, compiler.parsing, compiler.coreTypes, resolver); |
| 576 registerNativeBehavior(behavior, node); | 579 registerNativeBehavior(behavior, node); |
| 577 nativeBehaviors[node] = behavior; | 580 nativeBehaviors[node] = behavior; |
| 578 } | 581 } |
| 579 | 582 |
| 580 | 583 |
| 581 /** | 584 /** |
| 582 * Handles JS-embedded global calls, which can be an instantiation point for | 585 * Handles JS-embedded global calls, which can be an instantiation point for |
| 583 * types. | 586 * types. |
| 584 * | 587 * |
| 585 * For example, the following code instantiates and returns a String class | 588 * For example, the following code instantiates and returns a String class |
| 586 * | 589 * |
| 587 * JS_EMBEDDED_GLOBAL('String', 'foo') | 590 * JS_EMBEDDED_GLOBAL('String', 'foo') |
| 588 * | 591 * |
| 589 */ | 592 */ |
| 590 void registerJsEmbeddedGlobalCall(Send node, ForeignResolver resolver) { | 593 void registerJsEmbeddedGlobalCall(Send node, ForeignResolver resolver) { |
| 591 NativeBehavior behavior = | 594 NativeBehavior behavior = NativeBehavior.ofJsEmbeddedGlobalCall( |
| 592 NativeBehavior.ofJsEmbeddedGlobalCall(node, compiler, resolver); | 595 node, reporter, compiler.parsing, compiler.coreTypes, resolver); |
| 593 registerNativeBehavior(behavior, node); | 596 registerNativeBehavior(behavior, node); |
| 594 nativeBehaviors[node] = behavior; | 597 nativeBehaviors[node] = behavior; |
| 595 } | 598 } |
| 596 | 599 |
| 597 | 600 |
| 598 /** | 601 /** |
| 599 * Handles JS-compiler builtin calls, which can be an instantiation point for | 602 * Handles JS-compiler builtin calls, which can be an instantiation point for |
| 600 * types. | 603 * types. |
| 601 * | 604 * |
| 602 * For example, the following code instantiates and returns a String class | 605 * For example, the following code instantiates and returns a String class |
| 603 * | 606 * |
| 604 * JS_BUILTIN('String', 'int2string', 0) | 607 * JS_BUILTIN('String', 'int2string', 0) |
| 605 * | 608 * |
| 606 */ | 609 */ |
| 607 void registerJsBuiltinCall(Send node, ForeignResolver resolver) { | 610 void registerJsBuiltinCall(Send node, ForeignResolver resolver) { |
| 608 NativeBehavior behavior = | 611 NativeBehavior behavior = NativeBehavior.ofJsBuiltinCall( |
| 609 NativeBehavior.ofJsBuiltinCall(node, compiler, resolver); | 612 node, reporter, compiler.parsing, compiler.coreTypes, resolver); |
| 610 registerNativeBehavior(behavior, node); | 613 registerNativeBehavior(behavior, node); |
| 611 nativeBehaviors[node] = behavior; | 614 nativeBehaviors[node] = behavior; |
| 612 } | 615 } |
| 613 } | 616 } |
| 614 | 617 |
| 615 | 618 |
| 616 class NativeCodegenEnqueuer extends NativeEnqueuerBase { | 619 class NativeCodegenEnqueuer extends NativeEnqueuerBase { |
| 617 | 620 |
| 618 final CodeEmitterTask emitter; | 621 final CodeEmitterTask emitter; |
| 619 | 622 |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 671 superclass, | 674 superclass, |
| 672 () => <ClassElement>[]); | 675 () => <ClassElement>[]); |
| 673 directSubtypes.add(cls); | 676 directSubtypes.add(cls); |
| 674 } | 677 } |
| 675 | 678 |
| 676 void logSummary(log(message)) { | 679 void logSummary(log(message)) { |
| 677 log('Compiled ${registeredClasses.length} native classes, ' | 680 log('Compiled ${registeredClasses.length} native classes, ' |
| 678 '${unusedClasses.length} native classes omitted.'); | 681 '${unusedClasses.length} native classes omitted.'); |
| 679 } | 682 } |
| 680 } | 683 } |
| OLD | NEW |