1 /* 2 * Copyright (C) 2017 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen; 18 19 import static com.google.testing.compile.CompilationSubject.assertThat; 20 import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE; 21 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE; 22 import static dagger.internal.codegen.Compilers.daggerCompiler; 23 import static dagger.internal.codegen.GeneratedLines.GENERATED_ANNOTATION; 24 25 import com.google.testing.compile.Compilation; 26 import com.google.testing.compile.CompilationSubject; 27 import com.google.testing.compile.JavaFileObjects; 28 import java.util.Collection; 29 import javax.tools.JavaFileObject; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.junit.runners.Parameterized; 33 import org.junit.runners.Parameterized.Parameters; 34 35 @RunWith(Parameterized.class) 36 public class DelegateBindingExpressionTest { 37 @Parameters(name = "{0}") parameters()38 public static Collection<Object[]> parameters() { 39 return CompilerMode.TEST_PARAMETERS; 40 } 41 42 private final CompilerMode compilerMode; 43 DelegateBindingExpressionTest(CompilerMode compilerMode)44 public DelegateBindingExpressionTest(CompilerMode compilerMode) { 45 this.compilerMode = compilerMode; 46 } 47 48 private static final JavaFileObject REGULAR_SCOPED = 49 JavaFileObjects.forSourceLines( 50 "test.RegularScoped", 51 "package test;", 52 "", 53 "import javax.inject.Scope;", 54 "import javax.inject.Inject;", 55 "", 56 "@RegularScoped.CustomScope", 57 "class RegularScoped {", 58 " @Inject RegularScoped() {}", 59 "", 60 " @Scope @interface CustomScope {}", 61 "}"); 62 63 private static final JavaFileObject REUSABLE_SCOPED = 64 JavaFileObjects.forSourceLines( 65 "test.ReusableScoped", 66 "package test;", 67 "", 68 "import dagger.Reusable;", 69 "import javax.inject.Inject;", 70 "", 71 "@Reusable", 72 "class ReusableScoped {", 73 " @Inject ReusableScoped() {}", 74 "}"); 75 76 private static final JavaFileObject UNSCOPED = 77 JavaFileObjects.forSourceLines( 78 "test.Unscoped", 79 "package test;", 80 "", 81 "import javax.inject.Inject;", 82 "", 83 "class Unscoped {", 84 " @Inject Unscoped() {}", 85 "}"); 86 87 private static final JavaFileObject COMPONENT = 88 JavaFileObjects.forSourceLines( 89 "test.TestComponent", 90 "package test;", 91 "", 92 "import dagger.Component;", 93 "", 94 "@Component(modules = TestModule.class)", 95 "@RegularScoped.CustomScope", 96 "interface TestComponent {", 97 " @Qualifier(RegularScoped.class)", 98 " Object regular();", 99 "", 100 " @Qualifier(ReusableScoped.class)", 101 " Object reusable();", 102 "", 103 " @Qualifier(Unscoped.class)", 104 " Object unscoped();", 105 "}"); 106 107 private static final JavaFileObject QUALIFIER = 108 JavaFileObjects.forSourceLines( 109 "test.Qualifier", 110 "package test;", 111 "", 112 "@javax.inject.Qualifier", 113 "@interface Qualifier {", 114 " Class<?> value();", 115 "}"); 116 117 @Test toDoubleCheck()118 public void toDoubleCheck() { 119 JavaFileObject module = 120 JavaFileObjects.forSourceLines( 121 "test.TestModule", 122 "package test;", 123 "", 124 "import dagger.Binds;", 125 "import dagger.Module;", 126 "", 127 "@Module", 128 "interface TestModule {", 129 " @Binds @RegularScoped.CustomScope @Qualifier(RegularScoped.class)", 130 " Object regular(RegularScoped delegate);", 131 "", 132 " @Binds @RegularScoped.CustomScope @Qualifier(ReusableScoped.class)", 133 " Object reusable(ReusableScoped delegate);", 134 "", 135 " @Binds @RegularScoped.CustomScope @Qualifier(Unscoped.class)", 136 " Object unscoped(Unscoped delegate);", 137 "}"); 138 139 assertThatCompilationWithModule(module) 140 .generatedSourceFile("test.DaggerTestComponent") 141 .containsElementsIn( 142 compilerMode 143 .javaFileBuilder("test.DaggerTestComponent") 144 .addLines( 145 "package test;", 146 "", 147 GENERATED_ANNOTATION, 148 "final class DaggerTestComponent implements TestComponent {") 149 .addLinesIn( 150 FAST_INIT_MODE, 151 " private volatile Object regularScoped = new MemoizedSentinel();", 152 " private volatile ReusableScoped reusableScoped;", 153 "", 154 " private RegularScoped getRegularScoped() {", 155 " Object local = regularScoped;", 156 " if (local instanceof MemoizedSentinel) {", 157 " synchronized (local) {", 158 " local = regularScoped;", 159 " if (local instanceof MemoizedSentinel) {", 160 " local = new RegularScoped();", 161 " regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);", 162 " }", 163 " }", 164 " }", 165 " return (RegularScoped) local;", 166 " }", 167 "", 168 " private ReusableScoped getReusableScoped() {", 169 " Object local = reusableScoped;", 170 " if (local == null) {", 171 " local = new ReusableScoped();", 172 " reusableScoped = (ReusableScoped) local;", 173 " }", 174 " return (ReusableScoped) local;", 175 " }", 176 "") 177 .addLinesIn( 178 DEFAULT_MODE, 179 " @SuppressWarnings(\"unchecked\")", 180 " private void initialize() {", 181 " this.regularScopedProvider = ", 182 " DoubleCheck.provider(RegularScoped_Factory.create());", 183 " this.reusableScopedProvider = ", 184 " SingleCheck.provider(ReusableScoped_Factory.create());", 185 " this.reusableProvider = DoubleCheck.provider(", 186 " (Provider) reusableScopedProvider);", 187 " this.unscopedProvider = DoubleCheck.provider(", 188 " (Provider) Unscoped_Factory.create());", 189 " }") 190 .addLines( // 191 "}") 192 .build()); 193 } 194 195 @Test toSingleCheck()196 public void toSingleCheck() { 197 JavaFileObject module = 198 JavaFileObjects.forSourceLines( 199 "test.TestModule", 200 "package test;", 201 "", 202 "import dagger.Binds;", 203 "import dagger.Module;", 204 "import dagger.Reusable;", 205 "", 206 "@Module", 207 "interface TestModule {", 208 " @Binds @Reusable @Qualifier(RegularScoped.class)", 209 " Object regular(RegularScoped delegate);", 210 "", 211 " @Binds @Reusable @Qualifier(ReusableScoped.class)", 212 " Object reusable(ReusableScoped delegate);", 213 "", 214 " @Binds @Reusable @Qualifier(Unscoped.class)", 215 " Object unscoped(Unscoped delegate);", 216 "}"); 217 218 assertThatCompilationWithModule(module) 219 .generatedSourceFile("test.DaggerTestComponent") 220 .containsElementsIn( 221 compilerMode 222 .javaFileBuilder("test.DaggerTestComponent") 223 .addLines( 224 "package test;", 225 "", 226 GENERATED_ANNOTATION, 227 "final class DaggerTestComponent implements TestComponent {") 228 .addLinesIn( 229 FAST_INIT_MODE, 230 " private volatile Object regularScoped = new MemoizedSentinel();", 231 " private volatile ReusableScoped reusableScoped;", 232 "", 233 " private RegularScoped getRegularScoped() {", 234 " Object local = regularScoped;", 235 " if (local instanceof MemoizedSentinel) {", 236 " synchronized (local) {", 237 " local = regularScoped;", 238 " if (local instanceof MemoizedSentinel) {", 239 " local = new RegularScoped();", 240 " regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);", 241 " }", 242 " }", 243 " }", 244 " return (RegularScoped) local;", 245 " }", 246 "", 247 " private ReusableScoped getReusableScoped() {", 248 " Object local = reusableScoped;", 249 " if (local == null) {", 250 " local = new ReusableScoped();", 251 " reusableScoped = (ReusableScoped) local;", 252 " }", 253 " return (ReusableScoped) local;", 254 " }", 255 "") 256 .addLinesIn( 257 DEFAULT_MODE, 258 " @SuppressWarnings(\"unchecked\")", 259 " private void initialize() {", 260 " this.regularScopedProvider = ", 261 " DoubleCheck.provider(RegularScoped_Factory.create());", 262 " this.reusableScopedProvider = ", 263 " SingleCheck.provider(ReusableScoped_Factory.create());", 264 " this.unscopedProvider = SingleCheck.provider(", 265 " (Provider) Unscoped_Factory.create());", 266 " }") 267 .addLines( // 268 "}") 269 .build()); 270 } 271 272 @Test toUnscoped()273 public void toUnscoped() { 274 JavaFileObject module = 275 JavaFileObjects.forSourceLines( 276 "test.TestModule", 277 "package test;", 278 "", 279 "import dagger.Binds;", 280 "import dagger.Module;", 281 "", 282 "@Module", 283 "interface TestModule {", 284 " @Binds @Qualifier(RegularScoped.class)", 285 " Object regular(RegularScoped delegate);", 286 "", 287 " @Binds @Qualifier(ReusableScoped.class)", 288 " Object reusable(ReusableScoped delegate);", 289 "", 290 " @Binds @Qualifier(Unscoped.class)", 291 " Object unscoped(Unscoped delegate);", 292 "}"); 293 294 assertThatCompilationWithModule(module) 295 .generatedSourceFile("test.DaggerTestComponent") 296 .containsElementsIn( 297 compilerMode 298 .javaFileBuilder("test.DaggerTestComponent") 299 .addLines( 300 "package test;", 301 "", 302 GENERATED_ANNOTATION, 303 "final class DaggerTestComponent implements TestComponent {") 304 .addLinesIn( 305 FAST_INIT_MODE, 306 " private volatile Object regularScoped = new MemoizedSentinel();", 307 " private volatile ReusableScoped reusableScoped;", 308 "", 309 " private RegularScoped getRegularScoped() {", 310 " Object local = regularScoped;", 311 " if (local instanceof MemoizedSentinel) {", 312 " synchronized (local) {", 313 " local = regularScoped;", 314 " if (local instanceof MemoizedSentinel) {", 315 " local = new RegularScoped();", 316 " regularScoped = DoubleCheck.reentrantCheck(regularScoped, local);", 317 " }", 318 " }", 319 " }", 320 " return (RegularScoped) local;", 321 " }", 322 "", 323 " private ReusableScoped getReusableScoped() {", 324 " Object local = reusableScoped;", 325 " if (local == null) {", 326 " local = new ReusableScoped();", 327 " reusableScoped = (ReusableScoped) local;", 328 " }", 329 " return (ReusableScoped) local;", 330 " }", 331 "") 332 .addLinesIn( 333 DEFAULT_MODE, 334 " @SuppressWarnings(\"unchecked\")", 335 " private void initialize() {", 336 " this.regularScopedProvider = ", 337 " DoubleCheck.provider(RegularScoped_Factory.create());", 338 " this.reusableScopedProvider = ", 339 " SingleCheck.provider(ReusableScoped_Factory.create());", 340 " }") 341 .addLines( // 342 "}") 343 .build()); 344 } 345 346 @Test castNeeded_rawTypes_Provider_get()347 public void castNeeded_rawTypes_Provider_get() { 348 JavaFileObject accessibleSupertype = 349 JavaFileObjects.forSourceLines( 350 "other.Supertype", 351 "package other;", 352 "", 353 // accessible from the component, but the subtype is not 354 "public interface Supertype {}"); 355 JavaFileObject inaccessibleSubtype = 356 JavaFileObjects.forSourceLines( 357 "other.Subtype", 358 "package other;", 359 "", 360 "import javax.inject.Inject;", 361 "import javax.inject.Singleton;", 362 "", 363 "@Singleton", 364 "class Subtype implements Supertype {", 365 " @Inject Subtype() {}", 366 "}"); 367 JavaFileObject module = 368 JavaFileObjects.forSourceLines( 369 "other.SupertypeModule", 370 "package other;", 371 "", 372 "import dagger.Binds;", 373 "import dagger.Module;", 374 "", 375 "@Module", 376 "public interface SupertypeModule {", 377 " @Binds Supertype to(Subtype subtype);", 378 "}"); 379 JavaFileObject component = 380 JavaFileObjects.forSourceLines( 381 "test.TestComponent", 382 "package test;", 383 "", 384 "import dagger.Component;", 385 "import javax.inject.Singleton;", 386 "", 387 "@Singleton", 388 "@Component(modules = other.SupertypeModule.class)", 389 "interface TestComponent {", 390 " other.Supertype supertype();", 391 "}"); 392 Compilation compilation = 393 daggerCompiler() 394 .withOptions(compilerMode.javacopts()) 395 .compile(accessibleSupertype, inaccessibleSubtype, module, component); 396 assertThat(compilation).succeeded(); 397 assertThat(compilation) 398 .generatedSourceFile("test.DaggerTestComponent") 399 .containsElementsIn( 400 compilerMode 401 .javaFileBuilder("test.DaggerTestComponent") 402 .addLines( 403 "package test;", 404 "", 405 GENERATED_ANNOTATION, 406 "final class DaggerTestComponent implements TestComponent {") 407 .addLinesIn( 408 DEFAULT_MODE, 409 " @SuppressWarnings(\"rawtypes\")", 410 " private Provider subtypeProvider;", 411 "", 412 " @SuppressWarnings(\"unchecked\")", 413 " private void initialize() {", 414 " this.subtypeProvider = DoubleCheck.provider(Subtype_Factory.create());", 415 " }", 416 "", 417 " @Override", 418 " public Supertype supertype() {", 419 " return (Supertype) subtypeProvider.get();", 420 " }") 421 .addLinesIn( 422 FAST_INIT_MODE, 423 " private volatile Object subtype = new MemoizedSentinel();", 424 "", 425 " private Object getSubtype() {", 426 " Object local = subtype;", 427 " if (local instanceof MemoizedSentinel) {", 428 " synchronized (local) {", 429 " local = subtype;", 430 " if (local instanceof MemoizedSentinel) {", 431 " local = Subtype_Factory.newInstance();", 432 " subtype = DoubleCheck.reentrantCheck(subtype, local);", 433 " }", 434 " }", 435 " }", 436 " return (Object) local;", 437 " }", 438 "", 439 " @Override", 440 " public Supertype supertype() {", 441 " return (Supertype) getSubtype();", 442 " }") 443 .build()); 444 } 445 446 @Test noCast_rawTypes_Provider_get_toInaccessibleType()447 public void noCast_rawTypes_Provider_get_toInaccessibleType() { 448 JavaFileObject supertype = 449 JavaFileObjects.forSourceLines( 450 "other.Supertype", 451 "package other;", 452 "", 453 "interface Supertype {}"); 454 JavaFileObject subtype = 455 JavaFileObjects.forSourceLines( 456 "other.Subtype", 457 "package other;", 458 "", 459 "import javax.inject.Inject;", 460 "import javax.inject.Singleton;", 461 "", 462 "@Singleton", 463 "class Subtype implements Supertype {", 464 " @Inject Subtype() {}", 465 "}"); 466 JavaFileObject usesSupertype = 467 JavaFileObjects.forSourceLines( 468 "other.UsesSupertype", 469 "package other;", 470 "", 471 "import javax.inject.Inject;", 472 "", 473 "public class UsesSupertype {", 474 " @Inject UsesSupertype(Supertype supertype) {}", 475 "}"); 476 JavaFileObject module = 477 JavaFileObjects.forSourceLines( 478 "other.SupertypeModule", 479 "package other;", 480 "", 481 "import dagger.Binds;", 482 "import dagger.Module;", 483 "", 484 "@Module", 485 "public interface SupertypeModule {", 486 " @Binds Supertype to(Subtype subtype);", 487 "}"); 488 JavaFileObject component = 489 JavaFileObjects.forSourceLines( 490 "test.TestComponent", 491 "package test;", 492 "", 493 "import dagger.Component;", 494 "import javax.inject.Singleton;", 495 "", 496 "@Singleton", 497 "@Component(modules = other.SupertypeModule.class)", 498 "interface TestComponent {", 499 " other.UsesSupertype usesSupertype();", 500 "}"); 501 Compilation compilation = 502 daggerCompiler() 503 .withOptions(compilerMode.javacopts()) 504 .compile(supertype, subtype, usesSupertype, module, component); 505 assertThat(compilation).succeeded(); 506 assertThat(compilation) 507 .generatedSourceFile("test.DaggerTestComponent") 508 .containsElementsIn( 509 compilerMode 510 .javaFileBuilder("test.DaggerTestComponent") 511 .addLines( 512 "package test;", 513 "", 514 GENERATED_ANNOTATION, 515 "final class DaggerTestComponent implements TestComponent {") 516 .addLinesIn( 517 DEFAULT_MODE, 518 " @SuppressWarnings(\"rawtypes\")", 519 " private Provider subtypeProvider;", 520 "", 521 " @Override", 522 " public UsesSupertype usesSupertype() {", 523 // can't cast the provider.get() to a type that's not accessible 524 " return UsesSupertype_Factory.newInstance(subtypeProvider.get());", 525 " }", 526 "}") 527 .addLinesIn( 528 FAST_INIT_MODE, 529 " private volatile Object subtype = new MemoizedSentinel();", 530 "", 531 " private Object getSubtype() {", 532 " Object local = subtype;", 533 " if (local instanceof MemoizedSentinel) {", 534 " synchronized (local) {", 535 " local = subtype;", 536 " if (local instanceof MemoizedSentinel) {", 537 " local = Subtype_Factory.newInstance();", 538 " subtype = DoubleCheck.reentrantCheck(subtype, local);", 539 " }", 540 " }", 541 " }", 542 " return (Object) local;", 543 " }", 544 "", 545 " @Override", 546 " public UsesSupertype usesSupertype() {", 547 " return UsesSupertype_Factory.newInstance(getSubtype());", 548 " }") 549 .build()); 550 } 551 552 @Test castedToRawType()553 public void castedToRawType() { 554 JavaFileObject module = 555 JavaFileObjects.forSourceLines( 556 "test.TestModule", 557 "package test;", 558 "", 559 "import dagger.Binds;", 560 "import dagger.Module;", 561 "import dagger.Provides;", 562 "import javax.inject.Named;", 563 "", 564 "@Module", 565 "interface TestModule {", 566 " @Provides", 567 " static String provideString() { return new String(); }", 568 "", 569 " @Binds", 570 " CharSequence charSequence(String string);", 571 "", 572 " @Binds", 573 " @Named(\"named\")", 574 " String namedString(String string);", 575 "}"); 576 JavaFileObject component = 577 JavaFileObjects.forSourceLines( 578 "test.TestComponent", 579 "package test;", 580 "", 581 "import dagger.Component;", 582 "import javax.inject.Named;", 583 "import javax.inject.Provider;", 584 "", 585 "@Component(modules = TestModule.class)", 586 "interface TestComponent {", 587 " Provider<CharSequence> charSequence();", 588 "", 589 " @Named(\"named\") Provider<String> namedString();", 590 "}"); 591 592 Compilation compilation = 593 daggerCompiler() 594 .withOptions(compilerMode.javacopts()) 595 .compile(module, component); 596 assertThat(compilation).succeeded(); 597 assertThat(compilation) 598 .generatedSourceFile("test.DaggerTestComponent") 599 .containsElementsIn( 600 compilerMode 601 .javaFileBuilder("test.DaggerTestComponent") 602 .addLines( 603 "package test;", 604 "", 605 GENERATED_ANNOTATION, 606 "final class DaggerTestComponent implements TestComponent {") 607 .addLinesIn( 608 DEFAULT_MODE, 609 " @Override", 610 " public Provider<CharSequence> charSequence() {", 611 " return (Provider) TestModule_ProvideStringFactory.create();", 612 " }", 613 "", 614 " @Override", 615 " public Provider<String> namedString() {", 616 " return TestModule_ProvideStringFactory.create();", 617 " }", 618 "}") 619 .addLinesIn( 620 FAST_INIT_MODE, 621 " private volatile Provider<String> provideStringProvider;", 622 "", 623 " private Provider<String> getStringProvider() {", 624 " Object local = provideStringProvider;", 625 " if (local == null) {", 626 " local = new SwitchingProvider<>(0);", 627 " provideStringProvider = (Provider<String>) local;", 628 " }", 629 " return (Provider<String>) local;", 630 " }", 631 "", 632 " @Override", 633 " public Provider<CharSequence> charSequence() {", 634 " return (Provider) getStringProvider();", 635 " }", 636 "", 637 " @Override", 638 " public Provider<String> namedString() {", 639 " return getStringProvider();", 640 " }", 641 "", 642 " private final class SwitchingProvider<T> implements Provider<T> {", 643 " @SuppressWarnings(\"unchecked\")", 644 " @Override", 645 " public T get() {", 646 " switch (id) {", 647 " case 0:", 648 " return (T) TestModule_ProvideStringFactory.provideString();", 649 " default:", 650 " throw new AssertionError(id);", 651 " }", 652 " }", 653 " }") 654 .build()); 655 } 656 657 @Test doubleBinds()658 public void doubleBinds() { 659 JavaFileObject module = 660 JavaFileObjects.forSourceLines( 661 "test.TestModule", 662 "package test;", 663 "", 664 "import dagger.Binds;", 665 "import dagger.Module;", 666 "import dagger.Provides;", 667 "", 668 "@Module", 669 "interface TestModule {", 670 " @Provides", 671 " static String provideString() { return new String(); }", 672 "", 673 " @Binds", 674 " CharSequence charSequence(String string);", 675 "", 676 " @Binds", 677 " Object object(CharSequence charSequence);", 678 "}"); 679 JavaFileObject component = 680 JavaFileObjects.forSourceLines( 681 "test.TestComponent", 682 "package test;", 683 "", 684 "import dagger.Component;", 685 "import javax.inject.Named;", 686 "import javax.inject.Provider;", 687 "", 688 "@Component(modules = TestModule.class)", 689 "interface TestComponent {", 690 " Provider<CharSequence> charSequence();", 691 " Provider<Object> object();", 692 "}"); 693 694 Compilation compilation = 695 daggerCompiler() 696 .withOptions(compilerMode.javacopts()) 697 .compile(module, component); 698 assertThat(compilation).succeeded(); 699 assertThat(compilation) 700 .generatedSourceFile("test.DaggerTestComponent") 701 .containsElementsIn( 702 compilerMode 703 .javaFileBuilder("test.DaggerTestComponent") 704 .addLines( 705 "package test;", 706 "", 707 GENERATED_ANNOTATION, 708 "final class DaggerTestComponent implements TestComponent {") 709 .addLinesIn( 710 DEFAULT_MODE, 711 " @Override", 712 " public Provider<CharSequence> charSequence() {", 713 " return (Provider) TestModule_ProvideStringFactory.create();", 714 " }", 715 " @Override", 716 " public Provider<Object> object() {", 717 " return (Provider) TestModule_ProvideStringFactory.create();", 718 " }", 719 "}") 720 .addLinesIn( 721 FAST_INIT_MODE, 722 " private volatile Provider<String> provideStringProvider;", 723 "", 724 " private Provider<String> getStringProvider() {", 725 " Object local = provideStringProvider;", 726 " if (local == null) {", 727 " local = new SwitchingProvider<>(0);", 728 " provideStringProvider = (Provider<String>) local;", 729 " }", 730 " return (Provider<String>) local;", 731 " }", 732 "", 733 " @Override", 734 " public Provider<CharSequence> charSequence() {", 735 " return (Provider) getStringProvider();", 736 " }", 737 "", 738 " @Override", 739 " public Provider<Object> object() {", 740 " return (Provider) getStringProvider();", 741 " }", 742 "", 743 " private final class SwitchingProvider<T> implements Provider<T> {", 744 " @SuppressWarnings(\"unchecked\")", 745 " @Override", 746 " public T get() {", 747 " switch (id) {", 748 " case 0:", 749 " return (T) TestModule_ProvideStringFactory.provideString();", 750 " default:", 751 " throw new AssertionError(id);", 752 " }", 753 " }", 754 " }") 755 .build()); 756 } 757 758 @Test inlineFactoryOfInacessibleType()759 public void inlineFactoryOfInacessibleType() { 760 JavaFileObject supertype = 761 JavaFileObjects.forSourceLines( 762 "other.Supertype", "package other;", "", "public interface Supertype {}"); 763 JavaFileObject injectableSubtype = 764 JavaFileObjects.forSourceLines( 765 "other.Subtype", 766 "package other;", 767 "", 768 "import javax.inject.Inject;", 769 "", 770 "final class Subtype implements Supertype {", 771 // important: this doesn't have any dependencies and therefore the factory will be able 772 // to be referenced with an inline Subtype_Factory.create() 773 " @Inject Subtype() {}", 774 "}"); 775 JavaFileObject module = 776 JavaFileObjects.forSourceLines( 777 "other.TestModule", 778 "package other;", 779 "", 780 "import dagger.Binds;", 781 "import dagger.Module;", 782 "", 783 "@Module", 784 "public interface TestModule {", 785 " @Binds Supertype to(Subtype subtype);", 786 "}"); 787 JavaFileObject component = 788 JavaFileObjects.forSourceLines( 789 "test.RequestsSubtypeAsProvider", 790 "package test;", 791 "", 792 "import dagger.Component;", 793 "import javax.inject.Provider;", 794 "", 795 "@Component(modules = other.TestModule.class)", 796 "interface RequestsSubtypeAsProvider {", 797 " Provider<other.Supertype> supertypeProvider();", 798 "}"); 799 800 Compilation compilation = 801 daggerCompiler() 802 .withOptions(compilerMode.javacopts()) 803 .compile(supertype, injectableSubtype, module, component); 804 assertThat(compilation).succeeded(); 805 assertThat(compilation) 806 .generatedSourceFile("test.DaggerRequestsSubtypeAsProvider") 807 .containsElementsIn( 808 compilerMode 809 .javaFileBuilder("test.DaggerRequestsSubtypeAsProvider") 810 .addLines( 811 "package test;", 812 "", 813 GENERATED_ANNOTATION, 814 "final class DaggerRequestsSubtypeAsProvider", 815 " implements RequestsSubtypeAsProvider {") 816 .addLinesIn( 817 DEFAULT_MODE, 818 " @Override", 819 " public Provider<Supertype> supertypeProvider() {", 820 " return (Provider) Subtype_Factory.create();", 821 " }", 822 "}") 823 .addLinesIn( 824 FAST_INIT_MODE, 825 " private volatile Provider subtypeProvider;", 826 "", 827 " private Provider getSubtypeProvider() {", 828 " Object local = subtypeProvider;", 829 " if (local == null) {", 830 " local = new SwitchingProvider<>(0);", 831 " subtypeProvider = (Provider) local;", 832 " }", 833 " return (Provider) local;", 834 " }", 835 "", 836 " @Override", 837 " public Provider<Supertype> supertypeProvider() {", 838 " return getSubtypeProvider();", 839 " }", 840 "", 841 " private final class SwitchingProvider<T> implements Provider<T> {", 842 " @SuppressWarnings(\"unchecked\")", 843 " @Override", 844 " public T get() {", 845 " switch (id) {", 846 " case 0: return (T) Subtype_Factory.newInstance();", 847 " default: throw new AssertionError(id);", 848 " }", 849 " }", 850 " }") 851 .build()); 852 } 853 854 @Test providerWhenBindsScopeGreaterThanDependencyScope()855 public void providerWhenBindsScopeGreaterThanDependencyScope() { 856 JavaFileObject module = 857 JavaFileObjects.forSourceLines( 858 "test.TestModule", 859 "package test;", 860 "", 861 "import dagger.Binds;", 862 "import dagger.Module;", 863 "import dagger.Provides;", 864 "import dagger.Reusable;", 865 "import javax.inject.Singleton;", 866 "", 867 "@Module", 868 "public abstract class TestModule {", 869 " @Reusable", 870 " @Provides", 871 " static String provideString() {", 872 " return \"\";", 873 " }", 874 "", 875 " @Binds", 876 " @Singleton", 877 " abstract Object bindString(String str);", 878 "}"); 879 JavaFileObject component = 880 JavaFileObjects.forSourceLines( 881 "test.TestComponent", 882 "package test;", 883 "", 884 "import dagger.Component;", 885 "import javax.inject.Singleton;", 886 "import javax.inject.Provider;", 887 "", 888 "@Singleton", 889 "@Component(modules = TestModule.class)", 890 "interface TestComponent {", 891 " Provider<Object> getObject();", 892 "}"); 893 894 Compilation compilation = daggerCompiler() 895 .withOptions(compilerMode.javacopts()) 896 .compile(module, component); 897 assertThat(compilation).succeeded(); 898 assertThat(compilation) 899 .generatedSourceFile("test.DaggerTestComponent") 900 .containsElementsIn( 901 compilerMode 902 .javaFileBuilder("test.DaggerTestComponent") 903 .addLines( 904 "package test;", 905 "", 906 GENERATED_ANNOTATION, 907 "final class DaggerTestComponent implements TestComponent {") 908 .addLinesIn( 909 DEFAULT_MODE, 910 " private Provider<String> provideStringProvider;", 911 " private Provider<Object> bindStringProvider;", 912 "", 913 " @SuppressWarnings(\"unchecked\")", 914 " private void initialize() {", 915 " this.provideStringProvider =", 916 " SingleCheck.provider(TestModule_ProvideStringFactory.create());", 917 " this.bindStringProvider =", 918 " DoubleCheck.provider((Provider) provideStringProvider);", 919 " }", 920 "", 921 " @Override", 922 " public Provider<Object> getObject() {", 923 " return bindStringProvider;", 924 " }", 925 "}") 926 .addLinesIn( 927 FAST_INIT_MODE, 928 " private volatile String string;", 929 " private volatile Object object = new MemoizedSentinel();", 930 " private volatile Provider<Object> bindStringProvider;", 931 "", 932 " private String getString() {", 933 " Object local = string;", 934 " if (local == null) {", 935 " local = TestModule_ProvideStringFactory.provideString();", 936 " string = (String) local;", 937 " }", 938 " return (String) local;", 939 " }", 940 "", 941 " private Object getObject2() {", 942 " Object local = object;", 943 " if (local instanceof MemoizedSentinel) {", 944 " synchronized (local) {", 945 " local = object;", 946 " if (local instanceof MemoizedSentinel) {", 947 " local = getString();", 948 " object = DoubleCheck.reentrantCheck(object, local);", 949 " }", 950 " }", 951 " }", 952 " return (Object) local;", 953 " }", 954 "", 955 " @Override", 956 " public Provider<Object> getObject() {", 957 " Object local = bindStringProvider;", 958 " if (local == null) {", 959 " local = new SwitchingProvider<>(0);", 960 " bindStringProvider = (Provider<Object>) local;", 961 " }", 962 " return (Provider<Object>) local;", 963 " }", 964 "", 965 " private final class SwitchingProvider<T> implements Provider<T> {", 966 " @SuppressWarnings(\"unchecked\")", 967 " @Override", 968 " public T get() {", 969 " switch (id) {", 970 " case 0: return (T) DaggerTestComponent.this.getObject2();", 971 " default: throw new AssertionError(id);", 972 " }", 973 " }", 974 " }") 975 .build()); 976 } 977 assertThatCompilationWithModule(JavaFileObject module)978 private CompilationSubject assertThatCompilationWithModule(JavaFileObject module) { 979 Compilation compilation = 980 daggerCompiler() 981 .withOptions(compilerMode.javacopts()) 982 .compile( 983 module, 984 COMPONENT, 985 QUALIFIER, 986 REGULAR_SCOPED, 987 REUSABLE_SCOPED, 988 UNSCOPED); 989 assertThat(compilation).succeeded(); 990 return assertThat(compilation); 991 } 992 } 993