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