update to 4.4.2

This commit is contained in:
Jörg Prante 2019-12-09 15:32:54 +01:00
parent 70a8da3a4e
commit 79970ae966
221 changed files with 8310 additions and 10659 deletions

View file

@ -1,7 +1,3 @@
sudo: false
language: java language: java
jdk: jdk:
- oraclejdk8 - openjdk11
cache:
directories:
- $HOME/.m2

View file

@ -17,7 +17,7 @@ original [Google Guice Core Library](https://github.com/google/guice):
- code is conforming to JRE `compact1` profile - code is conforming to JRE `compact1` profile
- the extra AOP stuff was removed (aopalliance/cglib), no intercepting any more - the extra AOP stuff was removed (aopalliance/cglib), no intercepting any more
- removed all logging, uses of java.util.logger.* stuff and logger default binding - removed all logging, uses of java.util.logger.* stuff and logger default binding
- removed all java.io.Serializable dependent stuff - removed all java.io.Serializable dependent stuff. Note: everything which relies to be serializable was removed.
- reformatted source by IntelliJ and cleaned up source of @author and @since tags - reformatted source by IntelliJ and cleaned up source of @author and @since tags
- target of compilation is Java 8, no support for Java 6/7 - target of compilation is Java 8, no support for Java 6/7
- introduced Gradle as build system - introduced Gradle as build system

View file

@ -1,50 +1,43 @@
plugins { plugins {
id "org.sonarqube" version "2.2" id "org.sonarqube" version "2.8"
id "io.codearte.nexus-staging" version "0.21.1"
} }
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'maven' apply plugin: 'maven'
apply plugin: 'signing'
apply plugin: 'findbugs'
apply plugin: 'pmd'
apply plugin: 'checkstyle'
apply plugin: "jacoco"
repositories { repositories {
mavenCentral() mavenCentral()
} }
configurations {
wagon
}
dependencies { dependencies {
compile "javax.inject:javax.inject:1" compile "org.xbib:javax-inject:${project.property('javax-inject.version')}"
compile 'org.xbib:jsr-305:1.0.0' compile "org.xbib:guava:${project.property('guava.version')}"
compile "com.google.guava:guava:21.0" testCompile "org.junit.jupiter:junit-jupiter-api:${project.property('junit.version')}"
testCompile "junit:junit:4.12" testCompile "org.junit.jupiter:junit-jupiter-params:${project.property('junit.version')}"
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:2.7" testCompile "org.junit.jupiter:junit-jupiter-engine:${project.property('junit.version')}"
testCompile "org.apache.logging.log4j:log4j-core:2.7" testCompile "org.junit.vintage:junit-vintage-engine:${project.property('junit.version')}"
testCompile "javax.inject:javax.inject-tck:1" testCompile "junit:junit:${project.property('junit4.version')}"
testCompile "com.google.guava:guava-testlib:21.0" // Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester
wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10' testCompile "com.google.guava:guava-testlib:28.1-jre"
} }
sourceCompatibility = JavaVersion.VERSION_1_8 compileJava {
targetCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
compileTestJava {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:all" << "-profile" << "compact1" options.compilerArgs << "-Xlint:all,-fallthrough"
} }
test { test {
exclude '*$*' useJUnitPlatform()
exclude '**/ErrorHandlingTest*'
exclude '**/OSGiContainerTest*'
exclude '**/ScopesTest*'
exclude '**/TypeConversionTest*'
testLogging { testLogging {
showStandardStreams = false showStandardStreams = false
exceptionFormat = 'full' exceptionFormat = 'full'
@ -55,18 +48,145 @@ task sourcesJar(type: Jar, dependsOn: classes) {
classifier 'sources' classifier 'sources'
from sourceSets.main.allSource from sourceSets.main.allSource
} }
task javadocJar(type: Jar, dependsOn: javadoc) { task javadocJar(type: Jar, dependsOn: javadoc) {
classifier 'javadoc' classifier 'javadoc'
} }
artifacts { artifacts {
archives sourcesJar, javadocJar archives sourcesJar, javadocJar
} }
if (project.hasProperty('signing.keyId')) {
signing { ext {
sign configurations.archives user = 'xbib'
projectName = 'guice'
projectDescription = 'Guice for Java'
scmUrl = 'https://github.com/xbib/guice'
scmConnection = 'scm:git:git://github.com/xbib/guice.git'
scmDeveloperConnection = 'scm:git:git://github.com/xbib/guice.git'
}
task sonatypeUpload(type: Upload) {
configuration = configurations.archives
uploadDescriptor = true
repositories {
if (project.hasProperty('ossrhUsername')) {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: uri(ossrhReleaseUrl)) {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
snapshotRepository(url: uri(ossrhSnapshotUrl)) {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
pom.project {
groupId project.group
artifactId project.name
version project.version
name project.name
description projectDescription
packaging 'jar'
inceptionYear '2016'
url scmUrl
organization {
name 'xbib'
url 'http://xbib.org'
}
developers {
developer {
id user
name 'Jörg Prante'
email 'joergprante@gmail.com'
url 'https://github.com/jprante'
}
}
scm {
url scmUrl
connection scmConnection
developerConnection scmDeveloperConnection
}
licenses {
license {
name 'The Apache License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
}
}
}
} }
} }
apply from: "${rootProject.projectDir}/gradle/ext.gradle"
apply from: "${rootProject.projectDir}/gradle/publish.gradle" nexusStaging {
apply from: "${rootProject.projectDir}/gradle/sonarqube.gradle" packageGroup = "org.xbib"
}
/*
spotbugs {
effort = "max"
reportLevel = "low"
}
tasks.withType(com.github.spotbugs.SpotBugsTask) {
ignoreFailures = true
reports {
xml.enabled = false
html.enabled = true
}
}
tasks.withType(Pmd) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
tasks.withType(Checkstyle) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
pmd {
toolVersion = '6.11.0'
ruleSets = ['category/java/bestpractices.xml']
}
checkstyle {
configFile = rootProject.file('config/checkstyle/checkstyle.xml')
ignoreFailures = true
showViolations = false
}
sonarqube {
properties {
property "sonar.projectName", "${project.group} ${project.name}"
property "sonar.sourceEncoding", "UTF-8"
property "sonar.tests", "src/test/java"
property "sonar.scm.provider", "git"
}
}
*/
afterEvaluate {
compileJava {
inputs.property("moduleName", 'org.xbib.guice')
doFirst {
options.compilerArgs = ['--module-path', classpath.asPath]
classpath = files()
}
}
javadoc {
inputs.property("moduleName", 'org.xbib.guice')
doFirst {
options.addStringOption('Xdoclint:none', '-quiet')
options.addStringOption('-module-path', classpath.asPath)
classpath = files()
}
}
}

View file

@ -1,3 +1,11 @@
group = org.xbib group = org.xbib
name = guice name = guice
version = 4.0.4 version = 4.4.2
javax-inject.version = 1
guava.version = 28.1
# test
junit.version = 5.5.2
junit4.version = 4.12
log4j.version = 2.12.1

View file

@ -1,8 +0,0 @@
ext {
user = 'xbib'
projectName = 'guice'
projectDescription = 'Guice for Java'
scmUrl = 'https://github.com/xbib/guice'
scmConnection = 'scm:git:git://github.com/xbib/guice.git'
scmDeveloperConnection = 'scm:git:git://github.com/xbib/guice.git'
}

View file

@ -1,66 +0,0 @@
task xbibUpload(type: Upload) {
configuration = configurations.archives
uploadDescriptor = true
repositories {
if (project.hasProperty('xbibUsername')) {
mavenDeployer {
configuration = configurations.wagon
repository(url: uri('scpexe://xbib.org/repository')) {
authentication(userName: xbibUsername, privateKey: xbibPrivateKey)
}
}
}
}
}
task sonatypeUpload(type: Upload) {
configuration = configurations.archives
uploadDescriptor = true
repositories {
if (project.hasProperty('ossrhUsername')) {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: uri(ossrhReleaseUrl)) {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
snapshotRepository(url: uri(ossrhSnapshotUrl)) {
authentication(userName: ossrhUsername, password: ossrhPassword)
}
pom.project {
groupId project.group
artifactId project.name
version project.version
name project.name
description projectDescription
packaging 'jar'
inceptionYear '2016'
url scmUrl
organization {
name 'xbib'
url 'http://xbib.org'
}
developers {
developer {
id user
name 'Jörg Prante'
email 'joergprante@gmail.com'
url 'https://github.com/jprante'
}
}
scm {
url scmUrl
connection scmConnection
developerConnection scmDeveloperConnection
}
licenses {
license {
name 'The Apache License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
}
}
}
}
}

View file

@ -1,41 +0,0 @@
tasks.withType(FindBugs) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = false
}
}
tasks.withType(Pmd) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
tasks.withType(Checkstyle) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
jacocoTestReport {
reports {
xml.enabled true
csv.enabled false
xml.destination "${buildDir}/reports/jacoco-xml"
html.destination "${buildDir}/reports/jacoco-html"
}
}
sonarqube {
properties {
property "sonar.projectName", "${project.group} ${project.name}"
property "sonar.sourceEncoding", "UTF-8"
property "sonar.tests", "src/test/java"
property "sonar.scm.provider", "git"
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.junit.reportsPath", "build/test-results/test/"
}
}

Binary file not shown.

View file

@ -1,6 +1,6 @@
#Fri Feb 03 12:38:43 CET 2017 #Wed Nov 20 21:27:17 CET 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip zipStoreBase=GRADLE_USER_HOME

28
gradlew vendored
View file

@ -1,5 +1,21 @@
#!/usr/bin/env sh #!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
############################################################################## ##############################################################################
## ##
## Gradle start up script for UN*X ## Gradle start up script for UN*X
@ -28,16 +44,16 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="" DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD="maximum"
warn ( ) { warn () {
echo "$*" echo "$*"
} }
die ( ) { die () {
echo echo
echo "$*" echo "$*"
echo echo
@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi fi
# For Cygwin, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if $cygwin ; then if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"` APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"` JAVACMD=`cygpath --unix "$JAVACMD"`
@ -155,7 +171,7 @@ if $cygwin ; then
fi fi
# Escape application args # Escape application args
save ( ) { save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " " echo " "
} }

18
gradlew.bat vendored
View file

@ -1,3 +1,19 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS= set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe @rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome if defined JAVA_HOME goto findJavaFromJavaHome

View file

@ -36,6 +36,7 @@ public abstract class AbstractModule implements Module {
Binder binder; Binder binder;
@Override
public final synchronized void configure(Binder builder) { public final synchronized void configure(Binder builder) {
checkState(this.binder == null, "Re-entry is not allowed."); checkState(this.binder == null, "Re-entry is not allowed.");
@ -63,8 +64,7 @@ public abstract class AbstractModule implements Module {
/** /**
* @see Binder#bindScope(Class, Scope) * @see Binder#bindScope(Class, Scope)
*/ */
protected void bindScope(Class<? extends Annotation> scopeAnnotation, protected void bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
Scope scope) {
binder().bindScope(scopeAnnotation, scope); binder().bindScope(scopeAnnotation, scope);
} }

View file

@ -367,7 +367,7 @@ public interface Binder {
* their clients. * their clients.
* @return a binder that shares its configuration with this binder. * @return a binder that shares its configuration with this binder.
*/ */
Binder skipSources(Class... classesToSkip); Binder skipSources(Class<?>... classesToSkip);
/** /**
* Creates a new private child environment for bindings and other configuration. The returned * Creates a new private child environment for bindings and other configuration. The returned

View file

@ -1,7 +1,7 @@
package com.google.inject; package com.google.inject;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.Errors; import com.google.inject.internal.Messages;
import com.google.inject.spi.Message; import com.google.inject.spi.Message;
import java.util.Collection; import java.util.Collection;
@ -24,7 +24,7 @@ public final class ConfigurationException extends RuntimeException {
*/ */
public ConfigurationException(Iterable<Message> messages) { public ConfigurationException(Iterable<Message> messages) {
this.messages = ImmutableSet.copyOf(messages); this.messages = ImmutableSet.copyOf(messages);
initCause(Errors.getOnlyCause(this.messages)); initCause(Messages.getOnlyCause(this.messages));
} }
/** /**
@ -59,6 +59,6 @@ public final class ConfigurationException extends RuntimeException {
@Override @Override
public String getMessage() { public String getMessage() {
return Errors.format("Guice configuration errors", messages); return Messages.formatMessages("Guice configuration errors", messages);
} }
} }

View file

@ -1,7 +1,7 @@
package com.google.inject; package com.google.inject;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.Errors; import com.google.inject.internal.Messages;
import com.google.inject.spi.Message; import com.google.inject.spi.Message;
import java.util.Collection; import java.util.Collection;
@ -12,9 +12,9 @@ import static com.google.common.base.Preconditions.checkArgument;
* Thrown when errors occur while creating a {@link Injector}. Includes a list of encountered * Thrown when errors occur while creating a {@link Injector}. Includes a list of encountered
* errors. Clients should catch this exception, log it, and stop execution. * errors. Clients should catch this exception, log it, and stop execution.
*/ */
@SuppressWarnings("serial")
public class CreationException extends RuntimeException { public class CreationException extends RuntimeException {
private static final long serialVersionUID = 0;
private final ImmutableSet<Message> messages; private final ImmutableSet<Message> messages;
/** /**
@ -23,7 +23,7 @@ public class CreationException extends RuntimeException {
public CreationException(Collection<Message> messages) { public CreationException(Collection<Message> messages) {
this.messages = ImmutableSet.copyOf(messages); this.messages = ImmutableSet.copyOf(messages);
checkArgument(!this.messages.isEmpty()); checkArgument(!this.messages.isEmpty());
initCause(Errors.getOnlyCause(this.messages)); initCause(Messages.getOnlyCause(this.messages));
} }
/** /**
@ -35,6 +35,6 @@ public class CreationException extends RuntimeException {
@Override @Override
public String getMessage() { public String getMessage() {
return Errors.format("Unable to create injector, see the following errors", messages); return Messages.formatMessages("Unable to create injector, see the following errors", messages);
} }
} }

View file

@ -76,11 +76,7 @@ public final class Guice {
* @throws CreationException if one or more errors occur during injector * @throws CreationException if one or more errors occur during injector
* construction * construction
*/ */
public static Injector createInjector(Stage stage, public static Injector createInjector(Stage stage, Iterable<? extends Module> modules) {
Iterable<? extends Module> modules) { return new InternalInjectorCreator().stage(stage).addModules(modules).build();
return new InternalInjectorCreator()
.stage(stage)
.addModules(modules)
.build();
} }
} }

View file

@ -1,5 +1,7 @@
package com.google.inject; package com.google.inject;
import com.google.inject.spi.Element;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.TypeConverterBinding; import com.google.inject.spi.TypeConverterBinding;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
@ -225,4 +227,37 @@ public interface Injector {
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions. * <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
*/ */
Set<TypeConverterBinding> getTypeConverterBindings(); Set<TypeConverterBinding> getTypeConverterBindings();
/**
* Returns the elements that make up this injector. Note that not all kinds of elements are
* returned.
*
* <p>The returned list does not include elements inherited from a {@link #getParent() parent
* injector}, should one exist.
*
* <p>The returned list is immutable; it contains only the elements that were present when {@link
* #getElements} was invoked. Subsequent calls may return a list with additional elements.
*
* <p>The returned list does not include data inherited from a {@link #getParent() parent
* injector}, should one exist.
*
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
*/
List<Element> getElements();
/**
* Returns the injection points created for calls to {@link #getMembersInjector} (either directly
* or indirectly, e.g. through {@link #injectMembers}.
*
* <p>This excludes any injection points from elements (which are accessible from each element via
* the SPI), unless {@link #getMembersInjector} or {@link #injectMembers} were also called for the
* same key.
*
* <p>The returned multimap does not include data inherited from a {@link #getParent() parent
* injector}, should one exist.
*
* <p>This method is part of the Guice SPI and is intended for use by tools and extensions.
*/
Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints();
} }

View file

@ -39,8 +39,13 @@ public class Key<T> {
private final AnnotationStrategy annotationStrategy; private final AnnotationStrategy annotationStrategy;
private final TypeLiteral<T> typeLiteral; private final TypeLiteral<T> typeLiteral;
private final int hashCode; private final int hashCode;
private final Supplier<String> toStringSupplier;
// This field is updated using the 'Data-Race-Ful' lazy intialization pattern
// See http://jeremymanson.blogspot.com/2008/12/benign-data-races-in-java.html for a detailed
// explanation.
private String toString;
/** /**
* Constructs a new key. Derives the type from this class's type parameter. * Constructs a new key. Derives the type from this class's type parameter.
@ -60,7 +65,6 @@ public class Key<T> {
this.typeLiteral = MoreTypes.canonicalizeForKey( this.typeLiteral = MoreTypes.canonicalizeForKey(
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass())); (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
this.hashCode = computeHashCode(); this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
} }
/** /**
@ -82,7 +86,6 @@ public class Key<T> {
this.typeLiteral = MoreTypes.canonicalizeForKey( this.typeLiteral = MoreTypes.canonicalizeForKey(
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass())); (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
this.hashCode = computeHashCode(); this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
} }
/** /**
@ -102,7 +105,6 @@ public class Key<T> {
this.typeLiteral = MoreTypes.canonicalizeForKey( this.typeLiteral = MoreTypes.canonicalizeForKey(
(TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass())); (TypeLiteral<T>) TypeLiteral.fromSuperclassTypeParameter(getClass()));
this.hashCode = computeHashCode(); this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
} }
/** /**
@ -113,7 +115,6 @@ public class Key<T> {
this.annotationStrategy = annotationStrategy; this.annotationStrategy = annotationStrategy;
this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type)); this.typeLiteral = MoreTypes.canonicalizeForKey((TypeLiteral<T>) TypeLiteral.get(type));
this.hashCode = computeHashCode(); this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
} }
/** /**
@ -123,14 +124,12 @@ public class Key<T> {
this.annotationStrategy = annotationStrategy; this.annotationStrategy = annotationStrategy;
this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral); this.typeLiteral = MoreTypes.canonicalizeForKey(typeLiteral);
this.hashCode = computeHashCode(); this.hashCode = computeHashCode();
this.toStringSupplier = createToStringSupplier();
} }
/** /**
* Gets a key for an injection type and an annotation strategy. * Gets a key for an injection type and an annotation strategy.
*/ */
static <T> Key<T> get(Class<T> type, static <T> Key<T> get(Class<T> type, AnnotationStrategy annotationStrategy) {
AnnotationStrategy annotationStrategy) {
return new Key<T>(type, annotationStrategy); return new Key<T>(type, annotationStrategy);
} }
@ -144,8 +143,7 @@ public class Key<T> {
/** /**
* Gets a key for an injection type and an annotation type. * Gets a key for an injection type and an annotation type.
*/ */
public static <T> Key<T> get(Class<T> type, public static <T> Key<T> get(Class<T> type, Class<? extends Annotation> annotationType) {
Class<? extends Annotation> annotationType) {
return new Key<T>(type, strategyFor(annotationType)); return new Key<T>(type, strategyFor(annotationType));
} }
@ -166,8 +164,7 @@ public class Key<T> {
/** /**
* Gets a key for an injection type and an annotation type. * Gets a key for an injection type and an annotation type.
*/ */
public static Key<?> get(Type type, public static Key<?> get(Type type, Class<? extends Annotation> annotationType) {
Class<? extends Annotation> annotationType) {
return new Key<Object>(type, strategyFor(annotationType)); return new Key<Object>(type, strategyFor(annotationType));
} }
@ -188,16 +185,14 @@ public class Key<T> {
/** /**
* Gets a key for an injection type and an annotation type. * Gets a key for an injection type and an annotation type.
*/ */
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, public static <T> Key<T> get(TypeLiteral<T> typeLiteral, Class<? extends Annotation> annotationType) {
Class<? extends Annotation> annotationType) {
return new Key<T>(typeLiteral, strategyFor(annotationType)); return new Key<T>(typeLiteral, strategyFor(annotationType));
} }
/** /**
* Gets a key for an injection type and an annotation. * Gets a key for an injection type and an annotation.
*/ */
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, public static <T> Key<T> get(TypeLiteral<T> typeLiteral, Annotation annotation) {
Annotation annotation) {
return new Key<T>(typeLiteral, strategyFor(annotation)); return new Key<T>(typeLiteral, strategyFor(annotation));
} }
@ -253,20 +248,6 @@ public class Key<T> {
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode(); return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
} }
/**
* @return a {@link Supplier} which memoizes the value for lazy initialization.
*/
private Supplier<String> createToStringSupplier() {
// The performance hit on access is acceptable since the intended use is for non-performance-
// critical applications.
return Suppliers.memoize(new Supplier<String>() {
@Override
public String get() {
return "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
}
});
}
/** /**
* Gets the key type. * Gets the key type.
*/ */
@ -333,7 +314,14 @@ public class Key<T> {
@Override @Override
public final String toString() { public final String toString() {
return toStringSupplier.get(); // Note: to not introduce dangerous data races the field should only be read once in this
// method.
String local = toString;
if (local == null) {
local = "Key[type=" + typeLiteral + ", annotation=" + annotationStrategy + "]";
toString = local;
}
return local;
} }
/** /**
@ -375,21 +363,25 @@ public class Key<T> {
return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes()); return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
} }
static enum NullAnnotationStrategy implements AnnotationStrategy { enum NullAnnotationStrategy implements AnnotationStrategy {
INSTANCE; INSTANCE;
@Override
public boolean hasAttributes() { public boolean hasAttributes() {
return false; return false;
} }
@Override
public AnnotationStrategy withoutAttributes() { public AnnotationStrategy withoutAttributes() {
throw new UnsupportedOperationException("Key already has no attributes."); throw new UnsupportedOperationException("Key already has no attributes.");
} }
@Override
public Annotation getAnnotation() { public Annotation getAnnotation() {
return null; return null;
} }
@Override
public Class<? extends Annotation> getAnnotationType() { public Class<? extends Annotation> getAnnotationType() {
return null; return null;
} }
@ -401,6 +393,7 @@ public class Key<T> {
} }
interface AnnotationStrategy { interface AnnotationStrategy {
Annotation getAnnotation(); Annotation getAnnotation();
Class<? extends Annotation> getAnnotationType(); Class<? extends Annotation> getAnnotationType();
@ -419,18 +412,22 @@ public class Key<T> {
this.annotation = checkNotNull(annotation, "annotation"); this.annotation = checkNotNull(annotation, "annotation");
} }
@Override
public boolean hasAttributes() { public boolean hasAttributes() {
return true; return true;
} }
@Override
public AnnotationStrategy withoutAttributes() { public AnnotationStrategy withoutAttributes() {
return new AnnotationTypeStrategy(getAnnotationType(), annotation); return new AnnotationTypeStrategy(getAnnotationType(), annotation);
} }
@Override
public Annotation getAnnotation() { public Annotation getAnnotation() {
return annotation; return annotation;
} }
@Override
public Class<? extends Annotation> getAnnotationType() { public Class<? extends Annotation> getAnnotationType() {
return annotation.annotationType(); return annotation.annotationType();
} }
@ -469,18 +466,22 @@ public class Key<T> {
this.annotation = annotation; this.annotation = annotation;
} }
@Override
public boolean hasAttributes() { public boolean hasAttributes() {
return false; return false;
} }
@Override
public AnnotationStrategy withoutAttributes() { public AnnotationStrategy withoutAttributes() {
throw new UnsupportedOperationException("Key already has no attributes."); throw new UnsupportedOperationException("Key already has no attributes.");
} }
@Override
public Annotation getAnnotation() { public Annotation getAnnotation() {
return annotation; return annotation;
} }
@Override
public Class<? extends Annotation> getAnnotationType() { public Class<? extends Annotation> getAnnotationType() {
return annotationType; return annotationType;
} }

View file

@ -0,0 +1,22 @@
package com.google.inject;
/**
* Thrown from {@link Provider#get} when an attempt is made to access a scoped object while the
* scope in question is not currently active.
*
*/
@SuppressWarnings("serial")
public final class OutOfScopeException extends RuntimeException {
public OutOfScopeException(String message) {
super(message);
}
public OutOfScopeException(String message, Throwable cause) {
super(message, cause);
}
public OutOfScopeException(Throwable cause) {
super(cause);
}
}

View file

@ -31,5 +31,5 @@ public interface PrivateBinder extends Binder {
PrivateBinder withSource(Object source); PrivateBinder withSource(Object source);
PrivateBinder skipSources(Class... classesToSkip); PrivateBinder skipSources(Class<?>... classesToSkip);
} }

View file

@ -2,6 +2,7 @@ package com.google.inject;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import javax.inject.Provider;
import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;

View file

@ -32,5 +32,6 @@ public interface Provider<T> extends javax.inject.Provider<T> {
* @throws ProvisionException if an instance cannot be provided. Such exceptions include messages * @throws ProvisionException if an instance cannot be provided. Such exceptions include messages
* and throwables to describe why provision failed. * and throwables to describe why provision failed.
*/ */
@Override
T get(); T get();
} }

View file

@ -1,7 +1,7 @@
package com.google.inject; package com.google.inject;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.Errors; import com.google.inject.internal.Messages;
import com.google.inject.spi.Message; import com.google.inject.spi.Message;
import java.util.Collection; import java.util.Collection;
@ -23,7 +23,7 @@ public final class ProvisionException extends RuntimeException {
public ProvisionException(Iterable<Message> messages) { public ProvisionException(Iterable<Message> messages) {
this.messages = ImmutableSet.copyOf(messages); this.messages = ImmutableSet.copyOf(messages);
checkArgument(!this.messages.isEmpty()); checkArgument(!this.messages.isEmpty());
initCause(Errors.getOnlyCause(this.messages)); initCause(Messages.getOnlyCause(this.messages));
} }
public ProvisionException(String message, Throwable cause) { public ProvisionException(String message, Throwable cause) {
@ -44,6 +44,6 @@ public final class ProvisionException extends RuntimeException {
@Override @Override
public String getMessage() { public String getMessage() {
return Errors.format("Unable to provision, see the following errors", messages); return Messages.formatMessages("Unable to provision, see the following errors", messages);
} }
} }

View file

@ -28,6 +28,7 @@ public class Scopes {
* this to "no scope" in your binding. * this to "no scope" in your binding.
*/ */
public static final Scope NO_SCOPE = new Scope() { public static final Scope NO_SCOPE = new Scope() {
@Override
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) { public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
return unscoped; return unscoped;
} }
@ -37,21 +38,26 @@ public class Scopes {
return "Scopes.NO_SCOPE"; return "Scopes.NO_SCOPE";
} }
}; };
private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR private static final BindingScopingVisitor<Boolean> IS_SINGLETON_VISITOR
= new BindingScopingVisitor<Boolean>() { = new BindingScopingVisitor<>() {
@Override
public Boolean visitNoScoping() { public Boolean visitNoScoping() {
return false; return false;
} }
@Override
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) { public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
return scopeAnnotation == Singleton.class return scopeAnnotation == Singleton.class
|| scopeAnnotation == javax.inject.Singleton.class; || scopeAnnotation == javax.inject.Singleton.class;
} }
@Override
public Boolean visitScope(Scope scope) { public Boolean visitScope(Scope scope) {
return scope == Scopes.SINGLETON; return scope == Scopes.SINGLETON;
} }
@Override
public Boolean visitEagerSingleton() { public Boolean visitEagerSingleton() {
return true; return true;
} }
@ -107,18 +113,22 @@ public class Scopes {
final Class<? extends Annotation> scopeAnnotation) { final Class<? extends Annotation> scopeAnnotation) {
do { do {
boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() { boolean matches = binding.acceptScopingVisitor(new BindingScopingVisitor<Boolean>() {
@Override
public Boolean visitNoScoping() { public Boolean visitNoScoping() {
return false; return false;
} }
@Override
public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) { public Boolean visitScopeAnnotation(Class<? extends Annotation> visitedAnnotation) {
return visitedAnnotation == scopeAnnotation; return visitedAnnotation == scopeAnnotation;
} }
@Override
public Boolean visitScope(Scope visitedScope) { public Boolean visitScope(Scope visitedScope) {
return visitedScope == scope; return visitedScope == scope;
} }
@Override
public Boolean visitEagerSingleton() { public Boolean visitEagerSingleton() {
return false; return false;
} }

View file

@ -170,7 +170,7 @@ public class TypeLiteral<T> {
// this implementation is made a little more complicated in an attempt to avoid object-creation // this implementation is made a little more complicated in an attempt to avoid object-creation
while (true) { while (true) {
if (toResolve instanceof TypeVariable) { if (toResolve instanceof TypeVariable) {
TypeVariable original = (TypeVariable) toResolve; TypeVariable<?> original = (TypeVariable) toResolve;
toResolve = MoreTypes.resolveTypeVariable(type, rawType, original); toResolve = MoreTypes.resolveTypeVariable(type, rawType, original);
if (toResolve == original) { if (toResolve == original) {
return toResolve; return toResolve;

View file

@ -13,6 +13,7 @@ import com.google.inject.Provider;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.internal.Errors; import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException; import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.Messages;
import com.google.inject.spi.Dependency; import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies; import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.Message; import com.google.inject.spi.Message;
@ -256,7 +257,7 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
} }
private static ConfigurationException newConfigurationException(String format, Object... args) { private static ConfigurationException newConfigurationException(String format, Object... args) {
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args)))); return new ConfigurationException(ImmutableSet.of(new Message(Messages.format(format, args))));
} }
@Inject @Inject
@ -355,9 +356,9 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
}; };
@SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T> @SuppressWarnings("unchecked") // we imprecisely treat the class literal of T as a Class<T>
Class<F> factoryRawType = (Class<F>) (Class<?>) factoryType.getRawType(); Class<F> factoryRawType = (Class<F>) factoryType.getRawType();
return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(), return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(),
new Class[]{factoryRawType}, invocationHandler)); new Class<?>[]{factoryRawType}, invocationHandler));
} }
@Override @Override

View file

@ -96,7 +96,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
@Override @Override
public String toString() { public String toString() {
return "@" + Assisted.class.getName() + "(value=)"; return "@" + Assisted.class.getName() + "(value=\"\")";
} }
}; };
/** /**
@ -555,7 +555,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
+ "Stop injecting @Assisted Provider<T> (instead use @Assisted T) " + "Stop injecting @Assisted Provider<T> (instead use @Assisted T) "
+ "or Injector to speed things up. (It will be a ~6500% speed bump!) " + "or Injector to speed things up. (It will be a ~6500% speed bump!) "
+ "The exact offending deps are: {2}", + "The exact offending deps are: {2}",
new Object[]{factoryType, implementation, badDeps}); new Object[] { factoryType, implementation, badDeps } );
return false; return false;
} }
return true; return true;

View file

@ -51,20 +51,17 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
/** /**
* See the EDSL examples at {@link com.google.inject.Binder}. * See the EDSL examples at {@link com.google.inject.Binder}.
*/ */
ScopedBindingBuilder toProvider( ScopedBindingBuilder toProvider(Class<? extends javax.inject.Provider<? extends T>> providerType);
Class<? extends javax.inject.Provider<? extends T>> providerType);
/** /**
* See the EDSL examples at {@link com.google.inject.Binder}. * See the EDSL examples at {@link com.google.inject.Binder}.
*/ */
ScopedBindingBuilder toProvider( ScopedBindingBuilder toProvider(TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType);
/** /**
* See the EDSL examples at {@link com.google.inject.Binder}. * See the EDSL examples at {@link com.google.inject.Binder}.
*/ */
ScopedBindingBuilder toProvider( ScopedBindingBuilder toProvider(Key<? extends javax.inject.Provider<? extends T>> providerKey);
Key<? extends javax.inject.Provider<? extends T>> providerKey);
/** /**
* See the EDSL examples at {@link com.google.inject.Binder}. * See the EDSL examples at {@link com.google.inject.Binder}.
@ -74,6 +71,5 @@ public interface LinkedBindingBuilder<T> extends ScopedBindingBuilder {
/** /**
* See the EDSL examples at {@link com.google.inject.Binder}. * See the EDSL examples at {@link com.google.inject.Binder}.
*/ */
<S extends T> ScopedBindingBuilder toConstructor( <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor, TypeLiteral<? extends S> type);
Constructor<S> constructor, TypeLiteral<? extends S> type);
} }

View file

@ -18,14 +18,14 @@ import static com.google.common.base.Preconditions.checkNotNull;
public abstract class AbstractBindingBuilder<T> { public abstract class AbstractBindingBuilder<T> {
public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once."; public static final String IMPLEMENTATION_ALREADY_SET = "Implementation is set more than once.";
public static final String SINGLE_INSTANCE_AND_SCOPE public static final String SINGLE_INSTANCE_AND_SCOPE =
= "Setting the scope is not permitted when binding to a single instance."; "Setting the scope is not permitted when binding to a single instance.";
public static final String SCOPE_ALREADY_SET = "Scope is set more than once."; public static final String SCOPE_ALREADY_SET = "Scope is set more than once.";
public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. " public static final String BINDING_TO_NULL = "Binding to null instances is not allowed. " +
+ "Use toProvider(Providers.of(null)) if this is your intended behaviour."; "Use toProvider(Providers.of(null)) if this is your intended behaviour.";
public static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once."; public static final String CONSTANT_VALUE_ALREADY_SET = "Constant value is set more than once.";
public static final String ANNOTATION_ALREADY_SPECIFIED public static final String ANNOTATION_ALREADY_SPECIFIED =
= "More than one annotation is specified for this binding."; "More than one annotation is specified for this binding.";
protected static final Key<?> NULL_KEY = Key.get(Void.class); protected static final Key<?> NULL_KEY = Key.get(Void.class);
protected final Binder binder; protected final Binder binder;
@ -37,7 +37,7 @@ public abstract class AbstractBindingBuilder<T> {
this.binder = binder; this.binder = binder;
this.elements = elements; this.elements = elements;
this.position = elements.size(); this.position = elements.size();
this.binding = new UntargettedBindingImpl<T>(source, key, Scoping.UNSCOPED); this.binding = new UntargettedBindingImpl<>(source, key, Scoping.UNSCOPED);
elements.add(position, this.binding); elements.add(position, this.binding);
} }
@ -57,8 +57,7 @@ public abstract class AbstractBindingBuilder<T> {
protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) { protected BindingImpl<T> annotatedWithInternal(Class<? extends Annotation> annotationType) {
checkNotNull(annotationType, "annotationType"); checkNotNull(annotationType, "annotationType");
checkNotAnnotated(); checkNotAnnotated();
return setBinding(binding.withKey( return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
Key.get(this.binding.getKey().getTypeLiteral(), annotationType)));
} }
/** /**
@ -67,8 +66,7 @@ public abstract class AbstractBindingBuilder<T> {
protected BindingImpl<T> annotatedWithInternal(Annotation annotation) { protected BindingImpl<T> annotatedWithInternal(Annotation annotation) {
checkNotNull(annotation, "annotation"); checkNotNull(annotation, "annotation");
checkNotAnnotated(); checkNotAnnotated();
return setBinding(binding.withKey( return setBinding(binding.withKey(Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
Key.get(this.binding.getKey().getTypeLiteral(), annotation)));
} }
public void in(final Class<? extends Annotation> scopeAnnotation) { public void in(final Class<? extends Annotation> scopeAnnotation) {

View file

@ -24,7 +24,7 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
// It's unfortunate that we have to maintain a blacklist of specific // It's unfortunate that we have to maintain a blacklist of specific
// classes, but we can't easily block the whole package because of // classes, but we can't easily block the whole package because of
// all our unit tests. // all our unit tests.
private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.<Class<?>>of( private static final Set<Class<?>> FORBIDDEN_TYPES = ImmutableSet.of(
AbstractModule.class, AbstractModule.class,
Binder.class, Binder.class,
Binding.class, Binding.class,
@ -93,7 +93,7 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
*/ */
private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) { private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) {
if (original instanceof ExposedBindingImpl) { if (original instanceof ExposedBindingImpl) {
ExposedBindingImpl exposed = (ExposedBindingImpl) original; ExposedBindingImpl<?> exposed = (ExposedBindingImpl) original;
InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector(); InjectorImpl exposedFrom = (InjectorImpl) exposed.getPrivateElements().getInjector();
return (exposedFrom == binding.getInjector()); return (exposedFrom == binding.getInjector());
} else { } else {
@ -131,16 +131,28 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
scoping = Scoping.makeInjectable(scoping, injector, errors); scoping = Scoping.makeInjectable(scoping, injector, errors);
} }
protected void scheduleInitialization(final BindingImpl<?> binding) { /**
bindingData.addUninitializedBinding(new Runnable() { * Schedule initialization of this binding to occur immediately after all bindings have been
public void run() { * initialially processed.
try { */
binding.getInjector().initializeBinding(binding, errors.withSource(source)); protected void scheduleInitialization(BindingImpl<?> binding) {
} catch (ErrorsException e) { bindingData.addUninitializedBinding(() -> initializeBinding(binding));
errors.merge(e.getErrors()); }
}
} /**
}); * Schedule initialization for this binding to occur after all other static initialization of
* bindings.
*/
protected void scheduleDelayedInitialization(BindingImpl<?> binding) {
bindingData.addDelayedUninitializedBinding(() -> initializeBinding(binding));
}
private void initializeBinding(BindingImpl<?> binding) {
try {
binding.getInjector().initializeBinding(binding, errors.withSource(source));
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
} }
} }
} }

View file

@ -1,5 +1,7 @@
package com.google.inject.internal; package com.google.inject.internal;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Joiner.MapJoiner; import com.google.common.base.Joiner.MapJoiner;
@ -43,14 +45,17 @@ public class Annotations {
return s.substring(1, s.length() - 1); // cut off brackets return s.substring(1, s.length() - 1); // cut off brackets
} }
}; };
private static final LoadingCache<Class<? extends Annotation>, Annotation> cache = private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
CacheBuilder.newBuilder().weakKeys().build( CacheBuilder.newBuilder()
new CacheLoader<Class<? extends Annotation>, Annotation>() { .weakKeys()
.build(new CacheLoader<>() {
@Override @Override
public Annotation load(Class<? extends Annotation> input) { public Annotation load(Class<? extends Annotation> input) {
return generateAnnotationImpl(input); return generateAnnotationImpl(input);
} }
}); });
private static final AnnotationChecker scopeChecker = new AnnotationChecker( private static final AnnotationChecker scopeChecker = new AnnotationChecker(
Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class)); Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
private static final AnnotationChecker bindingAnnotationChecker = new AnnotationChecker( private static final AnnotationChecker bindingAnnotationChecker = new AnnotationChecker(
@ -90,21 +95,19 @@ public class Annotations {
return annotationType.cast(Proxy.newProxyInstance( return annotationType.cast(Proxy.newProxyInstance(
annotationType.getClassLoader(), annotationType.getClassLoader(),
new Class<?>[]{annotationType}, new Class<?>[]{annotationType},
new InvocationHandler() { (proxy, method, args) -> {
@Override String name = method.getName();
public Object invoke(Object proxy, Method method, Object[] args) throws Exception { switch (name) {
String name = method.getName(); case "annotationType":
if (name.equals("annotationType")) {
return annotationType; return annotationType;
} else if (name.equals("toString")) { case "toString":
return annotationToString(annotationType, members); return annotationToString(annotationType, members);
} else if (name.equals("hashCode")) { case "hashCode":
return annotationHashCode(annotationType, members); return annotationHashCode(annotationType, members);
} else if (name.equals("equals")) { case "equals":
return annotationEquals(annotationType, members, args[0]); return annotationEquals(annotationType, members, args[0]);
} else { default:
return members.get(name); return members.get(name);
}
} }
})); }));
} }
@ -207,6 +210,30 @@ public class Annotations {
return false; return false;
} }
private static final boolean QUOTE_MEMBER_VALUES = determineWhetherToQuote();
public static String memberValueString(String value) {
return QUOTE_MEMBER_VALUES ? "\"" + value + "\"" : value;
}
@Retention(RUNTIME)
private @interface TestAnnotation {
String value();
}
@TestAnnotation("determineWhetherToQuote")
private static boolean determineWhetherToQuote() {
try {
String annotation = Annotations.class
.getDeclaredMethod("determineWhetherToQuote")
.getAnnotation(TestAnnotation.class)
.toString();
return annotation.contains("\"determineWhetherToQuote\"");
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
}
public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) { public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
return scopeChecker.hasAnnotations(annotationType); return scopeChecker.hasAnnotations(annotationType);
} }
@ -243,8 +270,7 @@ public class Annotations {
/** /**
* Returns the binding annotation on {@code member}, or null if there isn't one. * Returns the binding annotation on {@code member}, or null if there isn't one.
*/ */
public static Annotation findBindingAnnotation( public static Annotation findBindingAnnotation(Errors errors, Member member, Annotation[] annotations) {
Errors errors, Member member, Annotation[] annotations) {
Annotation found = null; Annotation found = null;
for (Annotation annotation : annotations) { for (Annotation annotation : annotations) {
@ -293,6 +319,24 @@ public class Annotations {
} }
} }
/**
* Returns the name the binding should use. This is based on the annotation. If the annotation has
* an instance and is not a marker annotation, we ask the annotation for its toString. If it was a
* marker annotation or just an annotation type, we use the annotation's name. Otherwise, the name
* is the empty string.
*/
public static String nameOf(Key<?> key) {
Annotation annotation = key.getAnnotation();
Class<? extends Annotation> annotationType = key.getAnnotationType();
if (annotation != null && !isMarker(annotationType)) {
return key.getAnnotation().toString();
} else if (key.getAnnotationType() != null) {
return "@" + key.getAnnotationType().getName();
} else {
return "";
}
}
/** /**
* Checks for the presence of annotations. Caches results because Android doesn't. * Checks for the presence of annotations. Caches results because Android doesn't.
*/ */
@ -303,7 +347,8 @@ public class Annotations {
* Returns true if the given class has one of the desired annotations. * Returns true if the given class has one of the desired annotations.
*/ */
private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations = private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations =
new CacheLoader<Class<? extends Annotation>, Boolean>() { new CacheLoader<>() {
@Override
public Boolean load(Class<? extends Annotation> annotationType) { public Boolean load(Class<? extends Annotation> annotationType) {
for (Annotation annotation : annotationType.getAnnotations()) { for (Annotation annotation : annotationType.getAnnotations()) {
if (annotationTypes.contains(annotation.annotationType())) { if (annotationTypes.contains(annotation.annotationType())) {
@ -314,8 +359,8 @@ public class Annotations {
} }
}; };
final LoadingCache<Class<? extends Annotation>, Boolean> cache = CacheBuilder.newBuilder().weakKeys() final LoadingCache<Class<? extends Annotation>, Boolean> cache =
.build(hasAnnotations); CacheBuilder.newBuilder().weakKeys().build(hasAnnotations);
/** /**
* Constructs a new checker that looks for annotations of the given types. * Constructs a new checker that looks for annotations of the given types.

View file

@ -29,24 +29,29 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
super(binder, elements, source, key); super(binder, elements, source, key);
} }
@Override
public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) { public BindingBuilder<T> annotatedWith(Class<? extends Annotation> annotationType) {
annotatedWithInternal(annotationType); annotatedWithInternal(annotationType);
return this; return this;
} }
@Override
public BindingBuilder<T> annotatedWith(Annotation annotation) { public BindingBuilder<T> annotatedWith(Annotation annotation) {
annotatedWithInternal(annotation); annotatedWithInternal(annotation);
return this; return this;
} }
@Override
public BindingBuilder<T> to(Class<? extends T> implementation) { public BindingBuilder<T> to(Class<? extends T> implementation) {
return to(Key.get(implementation)); return to(Key.get(implementation));
} }
@Override
public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) { public BindingBuilder<T> to(TypeLiteral<? extends T> implementation) {
return to(Key.get(implementation)); return to(Key.get(implementation));
} }
@Override
public BindingBuilder<T> to(Key<? extends T> linkedKey) { public BindingBuilder<T> to(Key<? extends T> linkedKey) {
checkNotNull(linkedKey, "linkedKey"); checkNotNull(linkedKey, "linkedKey");
checkNotTargetted(); checkNotTargetted();
@ -56,6 +61,7 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
return this; return this;
} }
@Override
public void toInstance(T instance) { public void toInstance(T instance) {
checkNotTargetted(); checkNotTargetted();
@ -79,10 +85,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override
public BindingBuilder<T> toProvider(Provider<? extends T> provider) { public BindingBuilder<T> toProvider(Provider<? extends T> provider) {
return toProvider((javax.inject.Provider<T>) provider); return toProvider((javax.inject.Provider<T>) provider);
} }
@Override
public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) { public BindingBuilder<T> toProvider(javax.inject.Provider<? extends T> provider) {
checkNotNull(provider, "provider"); checkNotNull(provider, "provider");
checkNotTargetted(); checkNotTargetted();
@ -102,16 +110,19 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
return this; return this;
} }
@Override
public BindingBuilder<T> toProvider( public BindingBuilder<T> toProvider(
Class<? extends javax.inject.Provider<? extends T>> providerType) { Class<? extends javax.inject.Provider<? extends T>> providerType) {
return toProvider(Key.get(providerType)); return toProvider(Key.get(providerType));
} }
@Override
public BindingBuilder<T> toProvider( public BindingBuilder<T> toProvider(
TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) { TypeLiteral<? extends javax.inject.Provider<? extends T>> providerType) {
return toProvider(Key.get(providerType)); return toProvider(Key.get(providerType));
} }
@Override
public BindingBuilder<T> toProvider( public BindingBuilder<T> toProvider(
Key<? extends javax.inject.Provider<? extends T>> providerKey) { Key<? extends javax.inject.Provider<? extends T>> providerKey) {
checkNotNull(providerKey, "providerKey"); checkNotNull(providerKey, "providerKey");
@ -123,10 +134,12 @@ public class BindingBuilder<T> extends AbstractBindingBuilder<T>
return this; return this;
} }
@Override
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) { public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor) {
return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass())); return toConstructor(constructor, TypeLiteral.get(constructor.getDeclaringClass()));
} }
@Override
public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor, public <S extends T> ScopedBindingBuilder toConstructor(Constructor<S> constructor,
TypeLiteral<? extends S> type) { TypeLiteral<? extends S> type) {
checkNotNull(constructor, "constructor"); checkNotNull(constructor, "constructor");

View file

@ -34,14 +34,17 @@ public abstract class BindingImpl<T> implements Binding<T> {
this.scoping = scoping; this.scoping = scoping;
} }
@Override
public Key<T> getKey() { public Key<T> getKey() {
return key; return key;
} }
@Override
public Object getSource() { public Object getSource() {
return source; return source;
} }
@Override
public Provider<T> getProvider() { public Provider<T> getProvider() {
if (provider == null) { if (provider == null) {
if (injector == null) { if (injector == null) {
@ -69,10 +72,12 @@ public abstract class BindingImpl<T> implements Binding<T> {
return this instanceof InstanceBinding; return this instanceof InstanceBinding;
} }
@Override
public <V> V acceptVisitor(ElementVisitor<V> visitor) { public <V> V acceptVisitor(ElementVisitor<V> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) { public <V> V acceptScopingVisitor(BindingScopingVisitor<V> visitor) {
return scoping.acceptVisitor(visitor); return scoping.acceptVisitor(visitor);
} }

View file

@ -49,7 +49,7 @@ final class BindingProcessor extends AbstractBindingProcessor {
return true; return true;
} }
return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>) command) { return command.acceptTargetVisitor(new Processor<>((BindingImpl<T>) command) {
@Override @Override
public Boolean visit(ConstructorBinding<? extends T> binding) { public Boolean visit(ConstructorBinding<? extends T> binding) {
prepareBinding(); prepareBinding();
@ -72,13 +72,13 @@ final class BindingProcessor extends AbstractBindingProcessor {
T instance = binding.getInstance(); T instance = binding.getInstance();
@SuppressWarnings("unchecked") // safe to cast to binding<T> because @SuppressWarnings("unchecked") // safe to cast to binding<T> because
// the processor was constructed w/ it // the processor was constructed w/ it
Initializable<T> ref = initializer.requestInjection( Initializable<T> ref = initializer.requestInjection(
injector, instance, (Binding<T>) binding, source, injectionPoints); injector, instance, (Binding<T>) binding, source, injectionPoints);
ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref); ConstantFactory<? extends T> factory = new ConstantFactory<>(ref);
InternalFactory<? extends T> scopedFactory InternalFactory<? extends T> scopedFactory =
= Scoping.scope(key, injector, factory, source, scoping); Scoping.scope(key, injector, factory, source, scoping);
putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints, putBinding(new InstanceBindingImpl<>(injector, key, source,
instance)); scopedFactory, injectionPoints, instance));
return true; return true;
} }
@ -86,18 +86,22 @@ final class BindingProcessor extends AbstractBindingProcessor {
public Boolean visit(ProviderInstanceBinding<? extends T> binding) { public Boolean visit(ProviderInstanceBinding<? extends T> binding) {
prepareBinding(); prepareBinding();
javax.inject.Provider<? extends T> provider = binding.getUserSuppliedProvider(); javax.inject.Provider<? extends T> provider = binding.getUserSuppliedProvider();
if (provider instanceof InternalProviderInstanceBindingImpl.Factory) {
@SuppressWarnings("unchecked")
InternalProviderInstanceBindingImpl.Factory<T> asProviderMethod =
(InternalProviderInstanceBindingImpl.Factory<T>) provider;
return visitInternalProviderInstanceBindingFactory(asProviderMethod);
}
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints(); Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
Initializable<? extends javax.inject.Provider<? extends T>> initializable = Initializable<? extends javax.inject.Provider<? extends T>> initializable =
initializer.<javax.inject.Provider<? extends T>>requestInjection( initializer.requestInjection(injector, provider, null, source, injectionPoints);
injector, provider, null, source, injectionPoints);
// always visited with Binding<T> // always visited with Binding<T>
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<T>( InternalFactory<T> factory = new InternalFactoryToInitializableAdapter<>(
initializable, source, initializable, source,
injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding)); injector.provisionListenerStore.get((ProviderInstanceBinding<T>) binding));
InternalFactory<? extends T> scopedFactory InternalFactory<? extends T> scopedFactory = Scoping.scope(key, injector, factory, source, scoping);
= Scoping.scope(key, injector, factory, source, scoping); putBinding(new ProviderInstanceBindingImpl<>(injector, key, source, scopedFactory, scoping,
putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scoping,
provider, injectionPoints)); provider, injectionPoints));
return true; return true;
} }
@ -108,13 +112,13 @@ final class BindingProcessor extends AbstractBindingProcessor {
Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey(); Key<? extends javax.inject.Provider<? extends T>> providerKey = binding.getProviderKey();
// always visited with Binding<T> // always visited with Binding<T>
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<T>( BoundProviderFactory<T> boundProviderFactory = new BoundProviderFactory<>(
injector, providerKey, source, injector, providerKey, source,
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding)); injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
bindingData.addCreationListener(boundProviderFactory); bindingData.addCreationListener(boundProviderFactory);
InternalFactory<? extends T> scopedFactory = Scoping.scope( InternalFactory<? extends T> scopedFactory = Scoping.scope(
key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping); key, injector, boundProviderFactory, source, scoping);
putBinding(new LinkedProviderBindingImpl<T>( putBinding(new LinkedProviderBindingImpl<>(
injector, key, source, scopedFactory, scoping, providerKey)); injector, key, source, scopedFactory, scoping, providerKey));
return true; return true;
} }
@ -126,13 +130,36 @@ final class BindingProcessor extends AbstractBindingProcessor {
if (key.equals(linkedKey)) { if (key.equals(linkedKey)) {
errors.recursiveBinding(); errors.recursiveBinding();
} }
FactoryProxy<T> factory = new FactoryProxy<>(injector, key, linkedKey, source);
FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source);
bindingData.addCreationListener(factory); bindingData.addCreationListener(factory);
InternalFactory<? extends T> scopedFactory InternalFactory<? extends T> scopedFactory =
= Scoping.scope(key, injector, factory, source, scoping); Scoping.scope(key, injector, factory, source, scoping);
putBinding( putBinding(new LinkedBindingImpl<>(injector, key, source, scopedFactory, scoping, linkedKey));
new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey)); return true;
}
/** Handle ProviderMethods specially. */
private Boolean visitInternalProviderInstanceBindingFactory(
InternalProviderInstanceBindingImpl.Factory<T> provider) {
InternalProviderInstanceBindingImpl<T> binding =
new InternalProviderInstanceBindingImpl<>(
injector,
key,
source,
provider,
Scoping.scope(key, injector, provider, source, scoping),
scoping);
switch (binding.getInitializationTiming()) {
case DELAYED:
scheduleDelayedInitialization(binding);
break;
case EAGER:
scheduleInitialization(binding);
break;
default:
throw new AssertionError();
}
putBinding(binding);
return true; return true;
} }
@ -172,9 +199,9 @@ final class BindingProcessor extends AbstractBindingProcessor {
} }
private <T> void bindExposed(PrivateElements privateElements, Key<T> key) { private <T> void bindExposed(PrivateElements privateElements, Key<T> key) {
ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<T>(key, privateElements); ExposedKeyFactory<T> exposedKeyFactory = new ExposedKeyFactory<>(key, privateElements);
bindingData.addCreationListener(exposedKeyFactory); bindingData.addCreationListener(exposedKeyFactory);
putBinding(new ExposedBindingImpl<T>( putBinding(new ExposedBindingImpl<>(
injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements)); injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements));
} }
} }

View file

@ -6,16 +6,17 @@ import com.google.inject.spi.Dependency;
import javax.inject.Provider; import javax.inject.Provider;
import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* Delegates to a custom factory which is also bound in the injector. * Delegates to a custom factory which is also bound in the injector.
*/ */
final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implements CreationListener { final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implements CreationListener {
final Key<? extends javax.inject.Provider<? extends T>> providerKey; private final Key<? extends javax.inject.Provider<? extends T>> providerKey;
private final ProvisionListenerStackCallback<T> provisionCallback; private final ProvisionListenerStackCallback<T> provisionCallback;
private final InjectorImpl injector; private final InjectorImpl injector;
private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory; private InternalFactory<? extends javax.inject.Provider<? extends T>> providerFactory;
BoundProviderFactory( BoundProviderFactory(
@ -24,11 +25,12 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
Object source, Object source,
ProvisionListenerStackCallback<T> provisionCallback) { ProvisionListenerStackCallback<T> provisionCallback) {
super(source); super(source);
this.provisionCallback = checkNotNull(provisionCallback, "provisionCallback"); this.provisionCallback = provisionCallback;
this.injector = injector; this.injector = injector;
this.providerKey = providerKey; this.providerKey = providerKey;
} }
@Override
public void notify(Errors errors) { public void notify(Errors errors) {
try { try {
providerFactory = injector.getInternalFactory(providerKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT); providerFactory = injector.getInternalFactory(providerKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
@ -37,25 +39,27 @@ final class BoundProviderFactory<T> extends ProviderInternalFactory<T> implement
} }
} }
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) @Override
throws ErrorsException { public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
context.pushState(providerKey, source); context.pushState(providerKey, source);
try { try {
errors = errors.withSource(providerKey); javax.inject.Provider<? extends T> provider = providerFactory.get(context, dependency, true);
javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true); return circularGet(provider, context, dependency, provisionCallback);
return circularGet(provider, errors, context, dependency, provisionCallback); } catch (InternalProvisionException ipe) {
throw ipe.addSource(providerKey);
} finally { } finally {
context.popState(); context.popState();
} }
} }
@Override @Override
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency, protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
ConstructionContext<T> constructionContext) throws ErrorsException { ConstructionContext<T> constructionContext) throws InternalProvisionException {
try { try {
return super.provision(provider, errors, dependency, constructionContext); return super.provision(provider, dependency, constructionContext);
} catch (RuntimeException userException) { } catch (RuntimeException userException) {
throw errors.errorInProvider(userException).toException(); throw InternalProvisionException.errorInProvider(userException);
} }
} }

View file

@ -6,7 +6,6 @@ import com.google.inject.Key;
import com.google.inject.binder.AnnotatedConstantBindingBuilder; import com.google.inject.binder.AnnotatedConstantBindingBuilder;
import com.google.inject.binder.ConstantBindingBuilder; import com.google.inject.binder.ConstantBindingBuilder;
import com.google.inject.spi.Element; import com.google.inject.spi.Element;
import com.google.inject.spi.InjectionPoint;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.List; import java.util.List;
@ -24,56 +23,69 @@ public final class ConstantBindingBuilderImpl<T>
super(binder, elements, source, (Key<T>) NULL_KEY); super(binder, elements, source, (Key<T>) NULL_KEY);
} }
@Override
public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) { public ConstantBindingBuilder annotatedWith(Class<? extends Annotation> annotationType) {
annotatedWithInternal(annotationType); annotatedWithInternal(annotationType);
return this; return this;
} }
@Override
public ConstantBindingBuilder annotatedWith(Annotation annotation) { public ConstantBindingBuilder annotatedWith(Annotation annotation) {
annotatedWithInternal(annotation); annotatedWithInternal(annotation);
return this; return this;
} }
@Override
public void to(final String value) { public void to(final String value) {
toConstant(String.class, value); toConstant(String.class, value);
} }
@Override
public void to(final int value) { public void to(final int value) {
toConstant(Integer.class, value); toConstant(Integer.class, value);
} }
@Override
public void to(final long value) { public void to(final long value) {
toConstant(Long.class, value); toConstant(Long.class, value);
} }
@Override
public void to(final boolean value) { public void to(final boolean value) {
toConstant(Boolean.class, value); toConstant(Boolean.class, value);
} }
@Override
public void to(final double value) { public void to(final double value) {
toConstant(Double.class, value); toConstant(Double.class, value);
} }
@Override
public void to(final float value) { public void to(final float value) {
toConstant(Float.class, value); toConstant(Float.class, value);
} }
@Override
public void to(final short value) { public void to(final short value) {
toConstant(Short.class, value); toConstant(Short.class, value);
} }
@Override
public void to(final char value) { public void to(final char value) {
toConstant(Character.class, value); toConstant(Character.class, value);
} }
@Override
public void to(final byte value) { public void to(final byte value) {
toConstant(Byte.class, value); toConstant(Byte.class, value);
} }
@Override
public void to(final Class<?> value) { public void to(final Class<?> value) {
toConstant(Class.class, value); toConstant(Class.class, value);
} }
@Override
public <E extends Enum<E>> void to(final E value) { public <E extends Enum<E>> void to(final E value) {
toConstant(value.getDeclaringClass(), value); toConstant(value.getDeclaringClass(), value);
} }
@ -105,7 +117,7 @@ public final class ConstantBindingBuilderImpl<T>
} }
setBinding(new InstanceBindingImpl<T>( setBinding(new InstanceBindingImpl<T>(
base.getSource(), key, base.getScoping(), ImmutableSet.<InjectionPoint>of(), instanceAsT)); base.getSource(), key, base.getScoping(), ImmutableSet.of(), instanceAsT));
} }
@Override @Override

View file

@ -7,15 +7,17 @@ final class ConstantFactory<T> implements InternalFactory<T> {
private final Initializable<T> initializable; private final Initializable<T> initializable;
public ConstantFactory(Initializable<T> initializable) { ConstantFactory(Initializable<T> initializable) {
this.initializable = initializable; this.initializable = initializable;
} }
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked) @Override
throws ErrorsException { public T get(InternalContext context, Dependency<?> dependency, boolean linked)
return initializable.get(errors); throws InternalProvisionException {
return initializable.get();
} }
@Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(ConstantFactory.class) return MoreObjects.toStringHelper(ConstantFactory.class)
.add("value", initializable) .add("value", initializable)

View file

@ -41,26 +41,26 @@ final class ConstructionContext<T> {
invocationHandlers = null; invocationHandlers = null;
} }
public Object createProxy(Errors errors, InjectorOptions injectorOptions, public Object createProxy(InjectorOptions injectorOptions,
Class<?> expectedType) throws ErrorsException { Class<?> expectedType) throws InternalProvisionException {
if (injectorOptions.disableCircularProxies) { if (injectorOptions.disableCircularProxies) {
throw errors.circularProxiesDisabled(expectedType).toException(); throw InternalProvisionException.circularDependenciesDisabled(expectedType);
} }
if (!expectedType.isInterface()) { if (!expectedType.isInterface()) {
throw errors.cannotSatisfyCircularDependency(expectedType).toException(); throw InternalProvisionException.cannotProxyClass(expectedType);
} }
if (invocationHandlers == null) { if (invocationHandlers == null) {
invocationHandlers = new ArrayList<DelegatingInvocationHandler<T>>(); invocationHandlers = new ArrayList<>();
} }
DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<T>(); DelegatingInvocationHandler<T> invocationHandler = new DelegatingInvocationHandler<>();
invocationHandlers.add(invocationHandler); invocationHandlers.add(invocationHandler);
ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ? ClassLoader classLoader = expectedType.getClass().getClassLoader() != null ?
expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader(); expectedType.getClass().getClassLoader() : ClassLoader.getSystemClassLoader();
return expectedType.cast(Proxy.newProxyInstance(classLoader, return expectedType.cast(Proxy.newProxyInstance(classLoader,
new Class[]{expectedType, CircularDependencyProxy.class}, invocationHandler)); new Class<?>[]{expectedType, CircularDependencyProxy.class}, invocationHandler));
} }
public void setProxyDelegates(T delegate) { public void setProxyDelegates(T delegate) {

View file

@ -26,10 +26,15 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
implements ConstructorBinding<T>, DelayedInitialize { implements ConstructorBinding<T>, DelayedInitialize {
private final Factory<T> factory; private final Factory<T> factory;
private final InjectionPoint constructorInjectionPoint; private final InjectionPoint constructorInjectionPoint;
private ConstructorBindingImpl(InjectorImpl injector, Key<T> key, Object source, private ConstructorBindingImpl(InjectorImpl injector,
InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory, Key<T> key,
Object source,
InternalFactory<? extends T> scopedFactory,
Scoping scoping,
Factory<T> factory,
InjectionPoint constructorInjectionPoint) { InjectionPoint constructorInjectionPoint) {
super(injector, key, source, scopedFactory, scoping); super(injector, key, source, scopedFactory, scoping);
this.factory = factory; this.factory = factory;
@ -53,7 +58,10 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
* only succeed if retrieved from a linked binding * only succeed if retrieved from a linked binding
*/ */
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key, static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors, InjectionPoint constructorInjector,
Object source,
Scoping scoping,
Errors errors,
boolean failIfNotLinked, boolean failIfNotExplicit) boolean failIfNotLinked, boolean failIfNotExplicit)
throws ErrorsException { throws ErrorsException {
int numErrors = errors.size(); int numErrors = errors.size();
@ -65,7 +73,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
// We can't inject abstract classes. // We can't inject abstract classes.
if (Modifier.isAbstract(rawType.getModifiers())) { if (Modifier.isAbstract(rawType.getModifiers())) {
errors.missingImplementation(key); errors.missingImplementationWithHint(key, injector);
} }
// Error: Inner class. // Error: Inner class.
@ -110,7 +118,7 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
/** /**
* Returns true if the inject annotation is on the constructor. * Returns true if the inject annotation is on the constructor.
*/ */
private static boolean hasAtInject(Constructor cxtor) { private static boolean hasAtInject(Constructor<?> cxtor) {
return cxtor.isAnnotationPresent(Inject.class) return cxtor.isAnnotationPresent(Inject.class)
|| cxtor.isAnnotationPresent(javax.inject.Inject.class); || cxtor.isAnnotationPresent(javax.inject.Inject.class);
} }
@ -162,21 +170,25 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
return Dependency.forInjectionPoints(builder.build()); return Dependency.forInjectionPoints(builder.build());
} }
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
checkState(factory.constructorInjector != null, "not initialized"); checkState(factory.constructorInjector != null, "not initialized");
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public InjectionPoint getConstructor() { public InjectionPoint getConstructor() {
checkState(factory.constructorInjector != null, "Binding is not ready"); checkState(factory.constructorInjector != null, "Binding is not ready");
return factory.constructorInjector.getConstructionProxy().getInjectionPoint(); return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
} }
@Override
public Set<InjectionPoint> getInjectableMembers() { public Set<InjectionPoint> getInjectableMembers() {
checkState(factory.constructorInjector != null, "Binding is not ready"); checkState(factory.constructorInjector != null, "Binding is not ready");
return factory.constructorInjector.getInjectableMembers(); return factory.constructorInjector.getInjectableMembers();
} }
@Override
public Set<Dependency<?>> getDependencies() { public Set<Dependency<?>> getDependencies() {
return Dependency.forInjectionPoints(new ImmutableSet.Builder<InjectionPoint>() return Dependency.forInjectionPoints(new ImmutableSet.Builder<InjectionPoint>()
.add(getConstructor()) .add(getConstructor())
@ -241,18 +253,19 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException { throws InternalProvisionException {
checkState(constructorInjector != null, "Constructor not ready"); ConstructorInjector<T> localInjector = constructorInjector;
if (localInjector == null) {
if (failIfNotLinked && !linked) { throw new IllegalStateException("Constructor not ready");
throw errors.jitDisabled(key).toException(); }
if (!linked && failIfNotLinked) {
throw InternalProvisionException.jitDisabled(key);
} }
// This may not actually be safe because it could return a super type of T (if that's all the // This may not actually be safe because it could return a super type of T (if that's all the
// client needs), but it should be OK in practice thanks to the wonders of erasure. // client needs), but it should be OK in practice thanks to the wonders of erasure.
return (T) constructorInjector.construct(errors, context, return (T) localInjector.construct(context, dependency, provisionCallback);
dependency.getKey().getTypeLiteral().getRawType(), provisionCallback);
} }
} }
} }

View file

@ -2,6 +2,7 @@ package com.google.inject.internal;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback; import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint; import com.google.inject.spi.InjectionPoint;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -40,37 +41,39 @@ final class ConstructorInjector<T> {
* Construct an instance. Returns {@code Object} instead of {@code T} because * Construct an instance. Returns {@code Object} instead of {@code T} because
* it may return a proxy. * it may return a proxy.
*/ */
Object construct(final Errors errors, final InternalContext context, Object construct(final InternalContext context,
Class<?> expectedType, Dependency<?> dependency,
ProvisionListenerStackCallback<T> provisionCallback) ProvisionListenerStackCallback<T> provisionCallback)
throws ErrorsException { throws InternalProvisionException {
final ConstructionContext<T> constructionContext = context.getConstructionContext(this); final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
// We have a circular reference between constructors. Return a proxy. // We have a circular reference between constructors. Return a proxy.
if (constructionContext.isConstructing()) { if (constructionContext.isConstructing()) {
// TODO (crazybob): if we can't proxy this object, can we proxy the other object? // TODO (crazybob): if we can't proxy this object, can we proxy the other object?
return constructionContext.createProxy( return constructionContext.createProxy(
errors, context.getInjectorOptions(), expectedType); context.getInjectorOptions(), dependency.getKey().getTypeLiteral().getRawType());
} }
// If we're re-entering this factory while injecting fields or methods, // If we're re-entering this factory while injecting fields or methods,
// return the same instance. This prevents infinite loops. // return the same instance. This prevents infinite loops.
T t = constructionContext.getCurrentReference(); T t = constructionContext.getCurrentReference();
if (t != null) { if (t != null) {
return t; if (context.getInjectorOptions().disableCircularProxies) {
throw InternalProvisionException.circularDependenciesDisabled(
dependency.getKey().getTypeLiteral().getRawType());
} else {
return t;
}
} }
constructionContext.startConstruction(); constructionContext.startConstruction();
try { try {
// Optimization: Don't go through the callback stack if we have no listeners. // Optimization: Don't go through the callback stack if we have no listeners.
if (!provisionCallback.hasListeners()) { if (provisionCallback == null) {
return provision(errors, context, constructionContext); return provision(context, constructionContext);
} else { } else {
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() { return provisionCallback.provision(context, () ->
public T call() throws ErrorsException { provision(context, constructionContext));
return provision(errors, context, constructionContext);
}
});
} }
} finally { } finally {
constructionContext.finishConstruction(); constructionContext.finishConstruction();
@ -80,12 +83,12 @@ final class ConstructorInjector<T> {
/** /**
* Provisions a new T. * Provisions a new T.
*/ */
private T provision(Errors errors, InternalContext context, private T provision(InternalContext context, ConstructionContext<T> constructionContext)
ConstructionContext<T> constructionContext) throws ErrorsException { throws InternalProvisionException {
try { try {
T t; T t;
try { try {
Object[] parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors); Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
t = constructionProxy.newInstance(parameters); t = constructionProxy.newInstance(parameters);
constructionContext.setProxyDelegates(t); constructionContext.setProxyDelegates(t);
} finally { } finally {
@ -94,17 +97,17 @@ final class ConstructorInjector<T> {
// Store reference. If an injector re-enters this factory, they'll get the same reference. // Store reference. If an injector re-enters this factory, they'll get the same reference.
constructionContext.setCurrentReference(t); constructionContext.setCurrentReference(t);
MembersInjectorImpl<T> localMembersInjector = membersInjector;
membersInjector.injectMembers(t, errors, context, false); localMembersInjector.injectMembers(t, context, false);
membersInjector.notifyListeners(t, errors); localMembersInjector.notifyListeners(t);
return t; return t;
} catch (InvocationTargetException userException) { } catch (InvocationTargetException userException) {
Throwable cause = userException.getCause() != null Throwable cause = userException.getCause() != null
? userException.getCause() ? userException.getCause()
: userException; : userException;
throw errors.withSource(constructionProxy.getInjectionPoint()) throw InternalProvisionException.errorInjectingConstructor(cause)
.errorInjectingConstructor(cause).toException(); .addSource(constructionProxy.getInjectionPoint());
} finally { } finally {
constructionContext.removeCurrentReference(); constructionContext.removeCurrentReference();
} }

View file

@ -8,8 +8,8 @@ import com.google.inject.spi.InjectionPoint;
final class ConstructorInjectorStore { final class ConstructorInjectorStore {
private final InjectorImpl injector; private final InjectorImpl injector;
private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache private final FailableCache<InjectionPoint, ConstructorInjector<?>> cache =
= new FailableCache<InjectionPoint, ConstructorInjector<?>>() { new FailableCache<>() {
@Override @Override
protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors) protected ConstructorInjector<?> create(InjectionPoint constructorInjector, Errors errors)
throws ErrorsException { throws ErrorsException {
@ -46,12 +46,12 @@ final class ConstructorInjectorStore {
throws ErrorsException { throws ErrorsException {
int numErrorsBefore = errors.size(); int numErrorsBefore = errors.size();
SingleParameterInjector<?>[] constructorParameterInjectors SingleParameterInjector<?>[] constructorParameterInjectors =
= injector.getParametersInjectors(injectionPoint.getDependencies(), errors); injector.getParametersInjectors(injectionPoint.getDependencies(), errors);
@SuppressWarnings("unchecked") // the injector type agrees with the injection point type @SuppressWarnings("unchecked") // the injector type agrees with the injection point type
MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>) injector.membersInjectorStore MembersInjectorImpl<T> membersInjector = (MembersInjectorImpl<T>)
.get(injectionPoint.getDeclaringType(), errors); injector.membersInjectorStore.get(injectionPoint.getDeclaringType(), errors);
ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint); ConstructionProxyFactory<T> factory = new DefaultConstructionProxyFactory<T>(injectionPoint);

View file

@ -1,5 +0,0 @@
package com.google.inject.internal;
interface ContextualCallable<T> {
T call(InternalContext context) throws ErrorsException;
}

View file

@ -1,7 +1,6 @@
package com.google.inject.internal; package com.google.inject.internal;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.ListMultimap; import com.google.common.collect.ListMultimap;
@ -12,7 +11,6 @@ import com.google.common.collect.Multimaps;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -43,7 +41,7 @@ interface CycleDetectingLock<ID> {
* In case no cycle is detected performance is O(threads creating singletons), * In case no cycle is detected performance is O(threads creating singletons),
* in case cycle is detected performance is O(singleton locks). * in case cycle is detected performance is O(singleton locks).
*/ */
ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle(); ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle();
/** /**
* Unlocks previously locked lock. * Unlocks previously locked lock.
@ -82,7 +80,7 @@ interface CycleDetectingLock<ID> {
* *
* Guarded by {@code this}. * Guarded by {@code this}.
*/ */
private final Multimap<Long, ReentrantCycleDetectingLock> locksOwnedByThread = private static final Multimap<Thread, ReentrantCycleDetectingLock<?>> locksOwnedByThread =
LinkedHashMultimap.create(); LinkedHashMultimap.create();
/** /**
* Specifies lock that thread is currently waiting on to own it. * Specifies lock that thread is currently waiting on to own it.
@ -100,22 +98,22 @@ interface CycleDetectingLock<ID> {
* *
* Guarded by {@code this}. * Guarded by {@code this}.
*/ */
private Map<Long, ReentrantCycleDetectingLock> lockThreadIsWaitingOn = Maps.newHashMap(); private static Map<Thread, ReentrantCycleDetectingLock<?>> lockThreadIsWaitingOn = Maps.newHashMap();
/** /**
* Creates new lock within this factory context. We can guarantee that locks created by * Creates new lock within this factory context. We can guarantee that locks created by
* the same factory would not deadlock. * the same factory would not deadlock.
* *
* @param newLockId lock id that would be used to report lock cycles if detected * @param userLockId lock id that would be used to report lock cycles if detected
*/ */
CycleDetectingLock<ID> create(ID newLockId) { CycleDetectingLock<ID> create(ID userLockId) {
return new ReentrantCycleDetectingLock(newLockId, new ReentrantLock()); return new ReentrantCycleDetectingLock<>(this, userLockId, new ReentrantLock());
} }
/** /**
* The implementation for {@link CycleDetectingLock}. * The implementation for {@link CycleDetectingLock}.
*/ */
class ReentrantCycleDetectingLock implements CycleDetectingLock<ID> { static class ReentrantCycleDetectingLock<ID> implements CycleDetectingLock<ID> {
/** /**
* Underlying lock used for actual waiting when no potential deadlocks are detected. * Underlying lock used for actual waiting when no potential deadlocks are detected.
@ -125,50 +123,56 @@ interface CycleDetectingLock<ID> {
* User id for this lock. * User id for this lock.
*/ */
private final ID userLockId; private final ID userLockId;
/** Factory that was used to create this lock. */
private final CycleDetectingLockFactory<ID> lockFactory;
/** /**
* Thread id for the thread that owned this lock. Nullable. * Thread id for the thread that owned this lock. Nullable.
* Guarded by {@code CycleDetectingLockFactory.this}. * Guarded by {@code CycleDetectingLockFactory.this}.
*/ */
private Long lockOwnerThreadId = null; private Thread lockOwnerThread = null;
/** /**
* Number of times that thread owned this lock. * Number of times that thread owned this lock.
* Guarded by {@code CycleDetectingLockFactory.this}. * Guarded by {@code CycleDetectingLockFactory.this}.
*/ */
private int lockReentranceCount = 0; private int lockReentranceCount = 0;
ReentrantCycleDetectingLock(ID userLockId, Lock lockImplementation) { ReentrantCycleDetectingLock(
CycleDetectingLockFactory<ID> lockFactory, ID userLockId, Lock lockImplementation) {
this.lockFactory = lockFactory;
this.userLockId = Preconditions.checkNotNull(userLockId, "userLockId"); this.userLockId = Preconditions.checkNotNull(userLockId, "userLockId");
this.lockImplementation = Preconditions.checkNotNull( this.lockImplementation = Preconditions.checkNotNull(
lockImplementation, "lockImplementation"); lockImplementation, "lockImplementation");
} }
@Override @Override
public ListMultimap<Long, ID> lockOrDetectPotentialLocksCycle() { public ListMultimap<Thread, ID> lockOrDetectPotentialLocksCycle() {
final long currentThreadId = Thread.currentThread().getId(); final Thread currentThread = Thread.currentThread();
synchronized (CycleDetectingLockFactory.this) { synchronized (CycleDetectingLockFactory.class) {
checkState(); checkState();
ListMultimap<Long, ID> locksInCycle = detectPotentialLocksCycle(); // Add this lock to the waiting map to ensure it is included in any reported lock cycle.
lockThreadIsWaitingOn.put(currentThread, this);
ListMultimap<Thread, ID> locksInCycle = detectPotentialLocksCycle();
if (!locksInCycle.isEmpty()) { if (!locksInCycle.isEmpty()) {
// We aren't actually going to wait for this lock, so remove it from the map.
lockThreadIsWaitingOn.remove(currentThread);
// potential deadlock is found, we don't try to take this lock // potential deadlock is found, we don't try to take this lock
return locksInCycle; return locksInCycle;
} }
lockThreadIsWaitingOn.put(currentThreadId, this);
} }
// this may be blocking, but we don't expect it to cause a deadlock // this may be blocking, but we don't expect it to cause a deadlock
lockImplementation.lock(); lockImplementation.lock();
synchronized (CycleDetectingLockFactory.this) { synchronized (CycleDetectingLockFactory.class) {
// current thread is no longer waiting on this lock // current thread is no longer waiting on this lock
lockThreadIsWaitingOn.remove(currentThreadId); lockThreadIsWaitingOn.remove(currentThread);
checkState(); checkState();
// mark it as owned by us // mark it as owned by us
lockOwnerThreadId = currentThreadId; lockOwnerThread = currentThread;
lockReentranceCount++; lockReentranceCount++;
// add this lock to the list of locks owned by a current thread // add this lock to the list of locks owned by a current thread
locksOwnedByThread.put(currentThreadId, this); locksOwnedByThread.put(currentThread, this);
} }
// no deadlock is found, locking successful // no deadlock is found, locking successful
return ImmutableListMultimap.of(); return ImmutableListMultimap.of();
@ -176,12 +180,12 @@ interface CycleDetectingLock<ID> {
@Override @Override
public void unlock() { public void unlock() {
final long currentThreadId = Thread.currentThread().getId(); final Thread currentThread = Thread.currentThread();
synchronized (CycleDetectingLockFactory.this) { synchronized (CycleDetectingLockFactory.class) {
checkState(); checkState();
Preconditions.checkState(lockOwnerThreadId != null, Preconditions.checkState(lockOwnerThread != null,
"Thread is trying to unlock a lock that is not locked"); "Thread is trying to unlock a lock that is not locked");
Preconditions.checkState(lockOwnerThreadId == currentThreadId, Preconditions.checkState(lockOwnerThread == currentThread,
"Thread is trying to unlock a lock owned by another thread"); "Thread is trying to unlock a lock owned by another thread");
// releasing underlying lock // releasing underlying lock
@ -191,12 +195,12 @@ interface CycleDetectingLock<ID> {
lockReentranceCount--; lockReentranceCount--;
if (lockReentranceCount == 0) { if (lockReentranceCount == 0) {
// we no longer own this lock // we no longer own this lock
lockOwnerThreadId = null; lockOwnerThread = null;
Preconditions.checkState(locksOwnedByThread.remove(currentThreadId, this), Preconditions.checkState(locksOwnedByThread.remove(currentThread, this),
"Internal error: Can not find this lock in locks owned by a current thread"); "Internal error: Can not find this lock in locks owned by a current thread");
if (locksOwnedByThread.get(currentThreadId).isEmpty()) { if (locksOwnedByThread.get(currentThread).isEmpty()) {
// clearing memory // clearing memory
locksOwnedByThread.removeAll(currentThreadId); locksOwnedByThread.removeAll(currentThread);
} }
} }
} }
@ -206,14 +210,14 @@ interface CycleDetectingLock<ID> {
* Check consistency of an internal state. * Check consistency of an internal state.
*/ */
void checkState() throws IllegalStateException { void checkState() throws IllegalStateException {
final long currentThreadId = Thread.currentThread().getId(); final Thread currentThread = Thread.currentThread();
Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThreadId), Preconditions.checkState(!lockThreadIsWaitingOn.containsKey(currentThread),
"Internal error: Thread should not be in a waiting thread on a lock now"); "Internal error: Thread should not be in a waiting thread on a lock now");
if (lockOwnerThreadId != null) { if (lockOwnerThread != null) {
// check state of a locked lock // check state of a locked lock
Preconditions.checkState(lockReentranceCount >= 0, Preconditions.checkState(lockReentranceCount >= 0,
"Internal error: Lock ownership and reentrance count internal states do not match"); "Internal error: Lock ownership and reentrance count internal states do not match");
Preconditions.checkState(locksOwnedByThread.get(lockOwnerThreadId).contains(this), Preconditions.checkState(locksOwnedByThread.get(lockOwnerThread).contains(this),
"Internal error: Set of locks owned by a current thread and lock " "Internal error: Set of locks owned by a current thread and lock "
+ "ownership status do not match"); + "ownership status do not match");
} else { } else {
@ -233,74 +237,80 @@ interface CycleDetectingLock<ID> {
* *
* @see CycleDetectingLock#lockOrDetectPotentialLocksCycle() * @see CycleDetectingLock#lockOrDetectPotentialLocksCycle()
*/ */
private ListMultimap<Long, ID> detectPotentialLocksCycle() { private ListMultimap<Thread, ID> detectPotentialLocksCycle() {
final long currentThreadId = Thread.currentThread().getId(); final Thread currentThread = Thread.currentThread();
if (lockOwnerThreadId == null || lockOwnerThreadId == currentThreadId) { if (lockOwnerThread == null || lockOwnerThread == currentThread) {
// if nobody owns this lock, lock cycle is impossible // if nobody owns this lock, lock cycle is impossible
// if a current thread owns this lock, we let Guice to handle it // if a current thread owns this lock, we let Guice to handle it
return ImmutableListMultimap.of(); return ImmutableListMultimap.of();
} }
ListMultimap<Long, ID> potentialLocksCycle = Multimaps.newListMultimap( ListMultimap<Thread, ID> potentialLocksCycle = Multimaps.newListMultimap(
new LinkedHashMap<Long, Collection<ID>>(), new LinkedHashMap<>(), Lists::newArrayList);
new Supplier<List<ID>>() {
@Override
public List<ID> get() {
return Lists.newArrayList();
}
});
// lock that is a part of a potential locks cycle, starts with current lock // lock that is a part of a potential locks cycle, starts with current lock
ReentrantCycleDetectingLock lockOwnerWaitingOn = this; ReentrantCycleDetectingLock<?> lockOwnerWaitingOn = this;
// try to find a dependency path between lock's owner thread and a current thread // try to find a dependency path between lock's owner thread and a current thread
while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThreadId != null) { while (lockOwnerWaitingOn != null && lockOwnerWaitingOn.lockOwnerThread != null) {
Long threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThreadId; Thread threadOwnerThreadWaits = lockOwnerWaitingOn.lockOwnerThread;
// in case locks cycle exists lock we're waiting for is part of it // in case locks cycle exists lock we're waiting for is part of it
potentialLocksCycle.putAll(threadOwnerThreadWaits, lockOwnerWaitingOn =
getAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn)); addAllLockIdsAfter(threadOwnerThreadWaits, lockOwnerWaitingOn, potentialLocksCycle);
if (threadOwnerThreadWaits == currentThread) {
if (threadOwnerThreadWaits == currentThreadId) {
// owner thread depends on current thread, cycle detected // owner thread depends on current thread, cycle detected
return potentialLocksCycle; return potentialLocksCycle;
} }
// going for the next thread we wait on indirectly
lockOwnerWaitingOn = lockThreadIsWaitingOn.get(threadOwnerThreadWaits);
} }
// no dependency path from an owner thread to a current thread // no dependency path from an owner thread to a current thread
return ImmutableListMultimap.of(); return ImmutableListMultimap.of();
} }
/** /**
* Return locks owned by a thread after a lock specified, inclusive. * Adds all locks held by the given thread that are after the given lock and then returns the
* lock the thread is currently waiting on, if any
*/ */
private List<ID> getAllLockIdsAfter(long threadId, ReentrantCycleDetectingLock lock) { private ReentrantCycleDetectingLock<?> addAllLockIdsAfter(
List<ID> ids = Lists.newArrayList(); Thread thread,
ReentrantCycleDetectingLock<?> lock,
ListMultimap<Thread, ID> potentialLocksCycle) {
boolean found = false; boolean found = false;
Collection<ReentrantCycleDetectingLock> ownedLocks = locksOwnedByThread.get(threadId); Collection<ReentrantCycleDetectingLock<?>> ownedLocks = locksOwnedByThread.get(thread);
Preconditions.checkNotNull(ownedLocks, Preconditions.checkNotNull(ownedLocks,
"Internal error: No locks were found taken by a thread"); "Internal error: No locks were found taken by a thread");
for (ReentrantCycleDetectingLock ownedLock : ownedLocks) { for (ReentrantCycleDetectingLock<?> ownedLock : ownedLocks) {
if (ownedLock == lock) { if (ownedLock == lock) {
found = true; found = true;
} }
if (found) { if (found && ownedLock.lockFactory == this.lockFactory) {
ids.add(ownedLock.userLockId); // All locks are stored in a shared map therefore there is no way to
// enforce type safety. We know that our cast is valid as we check for a lock's
// factory. If the lock was generated by the
// same factory it has to have same type as the current lock.
@SuppressWarnings("unchecked")
ID userLockId = (ID) ownedLock.userLockId;
potentialLocksCycle.put(thread, userLockId);
} }
} }
Preconditions.checkState(found, "Internal error: We can not find locks that " Preconditions.checkState(found,
+ "created a cycle that we detected"); "Internal error: We can not find locks that created a cycle that we detected");
return ids; ReentrantCycleDetectingLock<?> unownedLock = lockThreadIsWaitingOn.get(thread);
// If this thread is waiting for a lock add it to the cycle and return it
if (unownedLock != null && unownedLock.lockFactory == this.lockFactory) {
@SuppressWarnings("unchecked")
ID typed = (ID) unownedLock.userLockId;
potentialLocksCycle.put(thread, typed);
}
return unownedLock;
} }
@Override @Override
public String toString() { public String toString() {
// copy is made to prevent a data race // copy is made to prevent a data race
// no synchronization is used, potentially stale data, should be good enough // no synchronization is used, potentially stale data, should be good enough
Long localLockOwnerThreadId = this.lockOwnerThreadId; Thread thread = this.lockOwnerThread;
if (localLockOwnerThreadId != null) { if (thread != null) {
return String.format("CycleDetectingLock[%s][locked by %s]", return String.format("%s[%s][locked by %s]", super.toString(), userLockId, thread);
userLockId, localLockOwnerThreadId);
} else { } else {
return String.format("CycleDetectingLock[%s][unlocked]", userLockId); return String.format("%s[%s][unlocked]", super.toString(), userLockId);
} }
} }
} }

View file

@ -0,0 +1,81 @@
package com.google.inject.internal;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Utility class for retrieving declared fields or methods in a particular order, because the JVM
* doesn't guarantee ordering for listing declared methods. We don't externally guarantee an
* ordering, but having a consistent ordering allows deterministic behavior and simpler tests.
*/
public final class DeclaredMembers {
private DeclaredMembers() {}
public static Field[] getDeclaredFields(Class<?> type) {
Field[] fields = type.getDeclaredFields();
Arrays.sort(fields, FIELD_ORDERING);
return fields;
}
public static Method[] getDeclaredMethods(Class<?> type) {
Method[] methods = type.getDeclaredMethods();
Arrays.sort(methods, METHOD_ORDERING);
return methods;
}
/**
* An ordering suitable for comparing two classes if they are loaded by the same classloader
*
* <p>Within a single classloader there can only be one class with a given name, so we just
* compare the names.
*/
private static final Ordering<Class<?>> CLASS_ORDERING =
new Ordering<>() {
@Override
public int compare(Class<?> o1, Class<?> o2) {
return o1.getName().compareTo(o2.getName());
}
};
/**
* An ordering suitable for comparing two fields if they are owned by the same class.
*
* <p>Within a single class it is sufficent to compare the non-generic field signature which
* consists of the field name and type.
*/
private static final Ordering<Field> FIELD_ORDERING =
new Ordering<>() {
@Override
public int compare(Field left, Field right) {
return ComparisonChain.start()
.compare(left.getName(), right.getName())
.compare(left.getType(), right.getType(), CLASS_ORDERING)
.result();
}
};
/**
* An ordering suitable for comparing two methods if they are owned by the same class.
*
* <p>Within a single class it is sufficient to compare the non-generic method signature which
* consists of the name, return type and parameter types.
*/
private static final Ordering<Method> METHOD_ORDERING =
new Ordering<>() {
@Override
public int compare(Method left, Method right) {
return ComparisonChain.start()
.compare(left.getName(), right.getName())
.compare(left.getReturnType(), right.getReturnType(), CLASS_ORDERING)
.compare(
Arrays.asList(left.getParameterTypes()),
Arrays.asList(right.getParameterTypes()),
CLASS_ORDERING.lexicographical())
.result();
}
};
}

View file

@ -24,13 +24,8 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
@SuppressWarnings("unchecked") // the injection point is for a constructor of T @SuppressWarnings("unchecked") // the injection point is for a constructor of T
final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember(); final Constructor<T> constructor = (Constructor<T>) injectionPoint.getMember();
// Use FastConstructor if the constructor is public. if (!Modifier.isPublic(constructor.getDeclaringClass().getModifiers()) ||
if (Modifier.isPublic(constructor.getModifiers())) { !Modifier.isPublic(constructor.getModifiers())) {
Class<T> classToConstruct = constructor.getDeclaringClass();
if (!Modifier.isPublic(classToConstruct.getModifiers())) {
constructor.setAccessible(true);
}
} else {
constructor.setAccessible(true); constructor.setAccessible(true);
} }
@ -38,17 +33,17 @@ final class DefaultConstructionProxyFactory<T> implements ConstructionProxyFacto
public T newInstance(Object... arguments) throws InvocationTargetException { public T newInstance(Object... arguments) throws InvocationTargetException {
try { try {
return constructor.newInstance(arguments); return constructor.newInstance(arguments);
} catch (InstantiationException e) { } catch (InstantiationException | IllegalAccessException e) {
throw new AssertionError(e); // shouldn't happen, we know this is a concrete type throw new AssertionError(e); // shouldn't happen, we know this is a concrete type
} catch (IllegalAccessException e) {
throw new AssertionError(e); // a security manager is blocking us, we're hosed
} }
} }
@Override
public InjectionPoint getInjectionPoint() { public InjectionPoint getInjectionPoint() {
return injectionPoint; return injectionPoint;
} }
@Override
public Constructor<T> getConstructor() { public Constructor<T> getConstructor() {
return constructor; return constructor;
} }

View file

@ -32,13 +32,13 @@ final class DeferredLookups implements Lookups {
} }
public <T> Provider<T> getProvider(Key<T> key) { public <T> Provider<T> getProvider(Key<T> key) {
ProviderLookup<T> lookup = new ProviderLookup<T>(key, key); ProviderLookup<T> lookup = new ProviderLookup<>(key, key);
lookups.add(lookup); lookups.add(lookup);
return lookup.getProvider(); return lookup.getProvider();
} }
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) { public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> type) {
MembersInjectorLookup<T> lookup = new MembersInjectorLookup<T>(type, type); MembersInjectorLookup<T> lookup = new MembersInjectorLookup<>(type, type);
lookups.add(lookup); lookups.add(lookup);
return lookup.getMembersInjector(); return lookup.getMembersInjector();
} }

View file

@ -1,4 +1,4 @@
package com.google.inject.multibindings; package com.google.inject.internal;
import com.google.inject.BindingAnnotation; import com.google.inject.BindingAnnotation;
@ -25,7 +25,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
enum Type { enum Type {
MAPBINDER, MAPBINDER,
MULTIBINDER, MULTIBINDER;
OPTIONALBINDER;
} }
} }

View file

@ -43,6 +43,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
: ImmutableSet.copyOf(injectionListeners); : ImmutableSet.copyOf(injectionListeners);
} }
@Override
public void register(MembersInjector<? super T> membersInjector) { public void register(MembersInjector<? super T> membersInjector) {
checkState(valid, "Encounters may not be used after hear() returns."); checkState(valid, "Encounters may not be used after hear() returns.");
@ -53,6 +54,7 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
membersInjectors.add(membersInjector); membersInjectors.add(membersInjector);
} }
@Override
public void register(InjectionListener<? super T> injectionListener) { public void register(InjectionListener<? super T> injectionListener) {
checkState(valid, "Encounters may not be used after hear() returns."); checkState(valid, "Encounters may not be used after hear() returns.");
@ -63,35 +65,42 @@ final class EncounterImpl<T> implements TypeEncounter<T> {
injectionListeners.add(injectionListener); injectionListeners.add(injectionListener);
} }
@Override
public void addError(String message, Object... arguments) { public void addError(String message, Object... arguments) {
checkState(valid, "Encounters may not be used after hear() returns."); checkState(valid, "Encounters may not be used after hear() returns.");
errors.addMessage(message, arguments); errors.addMessage(message, arguments);
} }
@Override
public void addError(Throwable t) { public void addError(Throwable t) {
checkState(valid, "Encounters may not be used after hear() returns."); checkState(valid, "Encounters may not be used after hear() returns.");
errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage()); errors.errorInUserCode(t, "An exception was caught and reported. Message: %s", t.getMessage());
} }
@Override
public void addError(Message message) { public void addError(Message message) {
checkState(valid, "Encounters may not be used after hear() returns."); checkState(valid, "Encounters may not be used after hear() returns.");
errors.addMessage(message); errors.addMessage(message);
} }
@Override
public <T> Provider<T> getProvider(Key<T> key) { public <T> Provider<T> getProvider(Key<T> key) {
checkState(valid, "Encounters may not be used after hear() returns."); checkState(valid, "Encounters may not be used after hear() returns.");
return lookups.getProvider(key); return lookups.getProvider(key);
} }
@Override
public <T> Provider<T> getProvider(Class<T> type) { public <T> Provider<T> getProvider(Class<T> type) {
return getProvider(Key.get(type)); return getProvider(Key.get(type));
} }
@Override
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) { public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
checkState(valid, "Encounters may not be used after hear() returns."); checkState(valid, "Encounters may not be used after hear() returns.");
return lookups.getMembersInjector(typeLiteral); return lookups.getMembersInjector(typeLiteral);
} }
@Override
public <T> MembersInjector<T> getMembersInjector(Class<T> type) { public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
return getMembersInjector(TypeLiteral.get(type)); return getMembersInjector(TypeLiteral.get(type));
} }

View file

@ -4,41 +4,33 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import com.google.common.primitives.Primitives;
import com.google.inject.Binding;
import com.google.inject.ConfigurationException; import com.google.inject.ConfigurationException;
import com.google.inject.CreationException; import com.google.inject.CreationException;
import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Provider;
import com.google.inject.Provides;
import com.google.inject.ProvisionException; import com.google.inject.ProvisionException;
import com.google.inject.Scope; import com.google.inject.Scope;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes;
import com.google.inject.internal.util.SourceProvider; import com.google.inject.internal.util.SourceProvider;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message; import com.google.inject.spi.Message;
import com.google.inject.spi.ScopeBinding; import com.google.inject.spi.ScopeBinding;
import com.google.inject.spi.TypeConverterBinding; import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.spi.TypeListenerBinding; import com.google.inject.spi.TypeListenerBinding;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Formatter; import java.util.Formatter;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* A collection of error messages. If this type is passed as a method parameter, the method is * A collection of error messages. If this type is passed as a method parameter, the method is
@ -56,43 +48,40 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public final class Errors { public final class Errors {
private static final Set<Dependency<?>> warnedDependencies = /** When a binding is not found, show at most this many bindings with the same type */
Collections.newSetFromMap(new ConcurrentHashMap<Dependency<?>, Boolean>()); private static final int MAX_MATCHING_TYPES_REPORTED = 3;
private static final String CONSTRUCTOR_RULES =
"Classes must have either one (and only one) constructor " /** When a binding is not found, show at most this many bindings that have some similarities */
+ "annotated with @Inject or a zero-argument constructor that is not private."; private static final int MAX_RELATED_TYPES_REPORTED = 3;
private static final Collection<Converter<?>> converters = ImmutableList.of(
new Converter<Class>(Class.class) { static <T> T checkNotNull(T reference, String name) {
@Override if (reference != null) {
public String toString(Class c) { return reference;
return c.getName(); }
} NullPointerException npe = new NullPointerException(name);
}, throw new ConfigurationException(ImmutableSet.of(new Message(npe.toString(), npe)));
new Converter<Member>(Member.class) { }
@Override
public String toString(Member member) { static void checkConfiguration(boolean condition, String format, Object... args) {
return Classes.toString(member); if (condition) {
} return;
}, }
new Converter<Key>(Key.class) { throw new ConfigurationException(ImmutableSet.of(new Message(Messages.format(format, args))));
@Override }
public String toString(Key key) {
if (key.getAnnotationType() != null) { private static final ImmutableSet<Class<?>> COMMON_AMBIGUOUS_TYPES =
return key.getTypeLiteral() + " annotated with " ImmutableSet.<Class<?>>builder()
+ (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType()); .add(Object.class)
} else { .add(String.class)
return key.getTypeLiteral().toString(); .addAll(Primitives.allWrapperTypes())
} .build();
}
}); /** The root errors object. Used to access the list of error messages. */
/**
* The root errors object. Used to access the list of error messages.
*/
private final Errors root; private final Errors root;
/**
* The parent errors object. Used to obtain the chain of source objects. /** The parent errors object. Used to obtain the chain of source objects. */
*/
private final Errors parent; private final Errors parent;
/** /**
* The leaf source for errors added here. * The leaf source for errors added here.
*/ */
@ -120,246 +109,101 @@ public final class Errors {
this.source = source; this.source = source;
} }
public static Collection<Message> getMessagesFromThrowable(Throwable throwable) { /** Returns an instance that uses {@code source} as a reference point for newly added errors. */
if (throwable instanceof ProvisionException) {
return ((ProvisionException) throwable).getErrorMessages();
} else if (throwable instanceof ConfigurationException) {
return ((ConfigurationException) throwable).getErrorMessages();
} else if (throwable instanceof CreationException) {
return ((CreationException) throwable).getErrorMessages();
} else {
return ImmutableSet.of();
}
}
public static String format(String messageFormat, Object... arguments) {
for (int i = 0; i < arguments.length; i++) {
arguments[i] = Errors.convert(arguments[i]);
}
return String.format(messageFormat, arguments);
}
/**
* Returns the formatted message for an exception with the specified messages.
*/
public static String format(String heading, Collection<Message> errorMessages) {
Formatter fmt = new Formatter().format(heading).format(":%n%n");
int index = 1;
boolean displayCauses = getOnlyCause(errorMessages) == null;
for (Message errorMessage : errorMessages) {
fmt.format("%s) %s%n", index++, errorMessage.getMessage());
List<Object> dependencies = errorMessage.getSources();
for (int i = dependencies.size() - 1; i >= 0; i--) {
Object source = dependencies.get(i);
formatSource(fmt, source);
}
Throwable cause = errorMessage.getCause();
if (displayCauses && cause != null) {
StringWriter writer = new StringWriter();
cause.printStackTrace(new PrintWriter(writer));
fmt.format("Caused by: %s", writer.getBuffer());
}
fmt.format("%n");
}
if (errorMessages.size() == 1) {
fmt.format("1 error");
} else {
fmt.format("%s errors", errorMessages.size());
}
return fmt.toString();
}
/**
* Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
* zero or multiple messages with causes, null is returned.
*/
public static Throwable getOnlyCause(Collection<Message> messages) {
Throwable onlyCause = null;
for (Message message : messages) {
Throwable messageCause = message.getCause();
if (messageCause == null) {
continue;
}
if (onlyCause != null) {
return null;
}
onlyCause = messageCause;
}
return onlyCause;
}
public static Object convert(Object o) {
ElementSource source = null;
if (o instanceof ElementSource) {
source = (ElementSource) o;
o = source.getDeclaringSource();
}
return convert(o, source);
}
public static Object convert(Object o, ElementSource source) {
for (Converter<?> converter : converters) {
if (converter.appliesTo(o)) {
return appendModules(converter.convert(o), source);
}
}
return appendModules(o, source);
}
private static Object appendModules(Object source, ElementSource elementSource) {
String modules = moduleSourceString(elementSource);
if (modules.length() == 0) {
return source;
} else {
return source + modules;
}
}
private static String moduleSourceString(ElementSource elementSource) {
// if we only have one module (or don't know what they are), then don't bother
// reporting it, because the source already is going to report exactly that module.
if (elementSource == null) {
return "";
}
List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
// Insert any original element sources w/ module info into the path.
while (elementSource.getOriginalElementSource() != null) {
elementSource = elementSource.getOriginalElementSource();
modules.addAll(0, elementSource.getModuleClassNames());
}
if (modules.size() <= 1) {
return "";
}
// Ideally we'd do:
// return Joiner.on(" -> ")
// .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
// .append(")").toString();
// ... but for some reason we can't find Lists.reverse, so do it the boring way.
StringBuilder builder = new StringBuilder(" (via modules: ");
for (int i = modules.size() - 1; i >= 0; i--) {
builder.append(modules.get(i));
if (i != 0) {
builder.append(" -> ");
}
}
builder.append(")");
return builder.toString();
}
public static void formatSource(Formatter formatter, Object source) {
ElementSource elementSource = null;
if (source instanceof ElementSource) {
elementSource = (ElementSource) source;
source = elementSource.getDeclaringSource();
}
formatSource(formatter, source, elementSource);
}
public static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
String modules = moduleSourceString(elementSource);
if (source instanceof Dependency) {
Dependency<?> dependency = (Dependency<?>) source;
InjectionPoint injectionPoint = dependency.getInjectionPoint();
if (injectionPoint != null) {
formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
} else {
formatSource(formatter, dependency.getKey(), elementSource);
}
} else if (source instanceof InjectionPoint) {
formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);
} else if (source instanceof Class) {
formatter.format(" at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);
} else if (source instanceof Member) {
formatter.format(" at %s%s%n", StackTraceElements.forMember((Member) source), modules);
} else if (source instanceof TypeLiteral) {
formatter.format(" while locating %s%s%n", source, modules);
} else if (source instanceof Key) {
Key<?> key = (Key<?>) source;
formatter.format(" while locating %s%n", convert(key, elementSource));
} else if (source instanceof Thread) {
formatter.format(" in thread %s%n", source);
} else {
formatter.format(" at %s%s%n", source, modules);
}
}
public static void formatInjectionPoint(Formatter formatter, Dependency<?> dependency,
InjectionPoint injectionPoint, ElementSource elementSource) {
Member member = injectionPoint.getMember();
Class<? extends Member> memberType = Classes.memberType(member);
if (memberType == Field.class) {
dependency = injectionPoint.getDependencies().get(0);
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
formatter.format(" for field at %s%n", StackTraceElements.forMember(member));
} else if (dependency != null) {
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
formatter.format(" for parameter %s at %s%n",
dependency.getParameterIndex(), StackTraceElements.forMember(member));
} else {
formatSource(formatter, injectionPoint.getMember());
}
}
/**
* Returns an instance that uses {@code source} as a reference point for newly added errors.
*/
public Errors withSource(Object source) { public Errors withSource(Object source) {
return source == this.source || source == SourceProvider.UNKNOWN_SOURCE return source == this.source || source == SourceProvider.UNKNOWN_SOURCE
? this ? this
: new Errors(this, source); : new Errors(this, source);
} }
/** public Errors missingImplementation(Key<?> key) {
* We use a fairly generic error message here. The motivation is to share the
* same message for both bind time errors:
* <pre><code>Guice.createInjector(new AbstractModule() {
* public void configure() {
* bind(Runnable.class);
* }
* }</code></pre>
* ...and at provide-time errors:
* <pre><code>Guice.createInjector().getInstance(Runnable.class);</code></pre>
* Otherwise we need to know who's calling when resolving a just-in-time
* binding, which makes things unnecessarily complex.
*/
public Errors missingImplementation(Key key) {
return addMessage("No implementation for %s was bound.", key); return addMessage("No implementation for %s was bound.", key);
} }
public Errors jitDisabled(Key key) { /** Within guice's core, allow for better missing binding messages */
<T> Errors missingImplementationWithHint(Key<T> key, Injector injector) {
StringBuilder sb = new StringBuilder();
sb.append(Messages.format("No implementation for %s was bound.", key));
// Keys which have similar strings as the desired key
List<String> possibleMatches = new ArrayList<>();
// Check for other keys that may have the same type,
// but not the same annotation
TypeLiteral<T> type = key.getTypeLiteral();
List<Binding<T>> sameTypes = injector.findBindingsByType(type);
if (!sameTypes.isEmpty()) {
sb.append(Messages.format("%n Did you mean?"));
int howMany = Math.min(sameTypes.size(), MAX_MATCHING_TYPES_REPORTED);
for (int i = 0; i < howMany; ++i) {
// TODO: Look into a better way to prioritize suggestions. For example, possbily
// use levenshtein distance of the given annotation vs actual annotation.
sb.append(Messages.format("%n * %s", sameTypes.get(i).getKey()));
}
int remaining = sameTypes.size() - MAX_MATCHING_TYPES_REPORTED;
if (remaining > 0) {
String plural = (remaining == 1) ? "" : "s";
sb.append(Messages.format("%n %d more binding%s with other annotations.", remaining, plural));
}
} else {
// For now, do a simple substring search for possibilities. This can help spot
// issues when there are generics being used (such as a wrapper class) and the
// user has forgotten they need to bind based on the wrapper, not the underlying
// class. In the future, consider doing a strict in-depth type search.
// TODO: Look into a better way to prioritize suggestions. For example, possbily
// use levenshtein distance of the type literal strings.
String want = type.toString();
Map<Key<?>, Binding<?>> bindingMap = injector.getAllBindings();
for (Key<?> bindingKey : bindingMap.keySet()) {
String have = bindingKey.getTypeLiteral().toString();
if (have.contains(want) || want.contains(have)) {
Formatter fmt = new Formatter();
Messages.formatSource(fmt, bindingMap.get(bindingKey).getSource());
String match = String.format("%s bound%s", Messages.convert(bindingKey), fmt.toString());
possibleMatches.add(match);
// TODO: Consider a check that if there are more than some number of results,
// don't suggest any.
if (possibleMatches.size() > MAX_RELATED_TYPES_REPORTED) {
// Early exit if we have found more than we need.
break;
}
}
}
if ((possibleMatches.size() > 0) && (possibleMatches.size() <= MAX_RELATED_TYPES_REPORTED)) {
sb.append(Messages.format("%n Did you mean?"));
for (String possibleMatch : possibleMatches) {
sb.append(Messages.format("%n %s", possibleMatch));
}
}
}
// If where are no possibilities to suggest, then handle the case of missing
// annotations on simple types. This is usually a bad idea.
if (sameTypes.isEmpty()
&& possibleMatches.isEmpty()
&& key.getAnnotationType() == null
&& COMMON_AMBIGUOUS_TYPES.contains(key.getTypeLiteral().getRawType())) {
// We don't recommend using such simple types without annotations.
sb.append(Messages.format("%nThe key seems very generic, did you forget an annotation?"));
}
return addMessage(sb.toString());
}
public Errors jitDisabled(Key<?> key) {
return addMessage("Explicit bindings are required and %s is not explicitly bound.", key); return addMessage("Explicit bindings are required and %s is not explicitly bound.", key);
} }
public Errors jitDisabledInParent(Key<?> key) { public Errors jitDisabledInParent(Key<?> key) {
return addMessage( return addMessage("Explicit bindings are required and %s would be bound in a parent injector.%n"
"Explicit bindings are required and %s would be bound in a parent injector.%n"
+ "Please add an explicit binding for it, either in the child or the parent.", + "Please add an explicit binding for it, either in the child or the parent.",
key); key);
} }
public Errors atInjectRequired(Class clazz) { public Errors atInjectRequired(Class<?> clazz) {
return addMessage( return addMessage("Explicit @Inject annotations are required on constructors,"
"Explicit @Inject annotations are required on constructors,"
+ " but %s has no constructors annotated with @Inject.", + " but %s has no constructors annotated with @Inject.",
clazz); clazz);
} }
@ -368,7 +212,7 @@ public final class Errors {
TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) { TypeLiteral<?> type, TypeConverterBinding typeConverterBinding) {
return addMessage("Received null converting '%s' (bound at %s) to %s%n" return addMessage("Received null converting '%s' (bound at %s) to %s%n"
+ " using %s.", + " using %s.",
stringValue, convert(source), type, typeConverterBinding); stringValue, Messages.convert(source), type, typeConverterBinding);
} }
public Errors conversionTypeError(String stringValue, Object source, TypeLiteral<?> type, public Errors conversionTypeError(String stringValue, Object source, TypeLiteral<?> type,
@ -376,7 +220,7 @@ public final class Errors {
return addMessage("Type mismatch converting '%s' (bound at %s) to %s%n" return addMessage("Type mismatch converting '%s' (bound at %s) to %s%n"
+ " using %s.%n" + " using %s.%n"
+ " Converter returned %s.", + " Converter returned %s.",
stringValue, convert(source), type, typeConverterBinding, converted); stringValue, Messages.convert(source), type, typeConverterBinding, converted);
} }
public Errors conversionError(String stringValue, Object source, public Errors conversionError(String stringValue, Object source,
@ -384,7 +228,7 @@ public final class Errors {
return errorInUserCode(cause, "Error converting '%s' (bound at %s) to %s%n" return errorInUserCode(cause, "Error converting '%s' (bound at %s) to %s%n"
+ " using %s.%n" + " using %s.%n"
+ " Reason: %s", + " Reason: %s",
stringValue, convert(source), type, typeConverterBinding, cause); stringValue, Messages.convert(source), type, typeConverterBinding, cause);
} }
public Errors ambiguousTypeConversion(String stringValue, Object source, TypeLiteral<?> type, public Errors ambiguousTypeConversion(String stringValue, Object source, TypeLiteral<?> type,
@ -393,18 +237,13 @@ public final class Errors {
+ " %s and%n" + " %s and%n"
+ " %s.%n" + " %s.%n"
+ " Please adjust your type converter configuration to avoid overlapping matches.", + " Please adjust your type converter configuration to avoid overlapping matches.",
stringValue, convert(source), type, a, b); stringValue, Messages.convert(source), type, a, b);
} }
public Errors bindingToProvider() { public Errors bindingToProvider() {
return addMessage("Binding to Provider is not allowed."); return addMessage("Binding to Provider is not allowed.");
} }
public Errors subtypeNotProvided(Class<? extends Provider<?>> providerType,
Class<?> type) {
return addMessage("%s doesn't provide instances of %s.", providerType, type);
}
public Errors notASubtype(Class<?> implementationType, Class<?> type) { public Errors notASubtype(Class<?> implementationType, Class<?> type) {
return addMessage("%s doesn't extend %s.", implementationType, type); return addMessage("%s doesn't extend %s.", implementationType, type);
} }
@ -418,14 +257,14 @@ public final class Errors {
} }
public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) { public Errors missingRuntimeRetention(Class<? extends Annotation> annotation) {
return addMessage(format("Please annotate %s with @Retention(RUNTIME).", annotation)); return addMessage(Messages.format("Please annotate %s with @Retention(RUNTIME).", annotation));
} }
public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) { public Errors missingScopeAnnotation(Class<? extends Annotation> annotation) {
return addMessage(format("Please annotate %s with @ScopeAnnotation.", annotation)); return addMessage(Messages.format("Please annotate %s with @ScopeAnnotation.", annotation));
} }
public Errors optionalConstructor(Constructor constructor) { public Errors optionalConstructor(Constructor<?> constructor) {
return addMessage("%s is annotated @Inject(optional=true), " return addMessage("%s is annotated @Inject(optional=true), "
+ "but constructors cannot be optional.", constructor); + "but constructors cannot be optional.", constructor);
} }
@ -441,7 +280,7 @@ public final class Errors {
public Errors scopeAnnotationOnAbstractType( public Errors scopeAnnotationOnAbstractType(
Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) { Class<? extends Annotation> scopeAnnotation, Class<?> type, Object source) {
return addMessage("%s is annotated with %s, but scope annotations are not supported " return addMessage("%s is annotated with %s, but scope annotations are not supported "
+ "for abstract types.%n Bound at %s.", type, scopeAnnotation, convert(source)); + "for abstract types.%n Bound at %s.", type, scopeAnnotation, Messages.convert(source));
} }
public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) { public Errors misplacedBindingAnnotation(Member member, Annotation bindingAnnotation) {
@ -449,14 +288,27 @@ public final class Errors {
+ "to its parameters instead.", member, bindingAnnotation); + "to its parameters instead.", member, bindingAnnotation);
} }
public Errors missingConstructor(Class<?> implementation) { private static final String CONSTRUCTOR_RULES =
return addMessage("Could not find a suitable constructor in %s. " + CONSTRUCTOR_RULES, "Injectable classes must have either one (and only one) constructor annotated with @Inject"
implementation); + " or a zero-argument constructor that is not private.";
public Errors missingConstructor(TypeLiteral<?> type) {
// Don't bother including the type in the message twice, unless the type is generic (i.e. the
// type has generics that the raw class loses)
String typeString = type.toString();
String rawTypeString = MoreTypes.getRawType(type.getType()).getName();
return addMessage(
"No implementation for %s (with no qualifier annotation) was bound, and could not find an"
+ " injectable constructor%s. %s",
typeString,
typeString.equals(rawTypeString) ? "" : " in " + rawTypeString,
CONSTRUCTOR_RULES);
} }
public Errors tooManyConstructors(Class<?> implementation) { public Errors tooManyConstructors(Class<?> implementation) {
return addMessage("%s has more than one constructor annotated with @Inject. " return addMessage(
+ CONSTRUCTOR_RULES, implementation); "%s has more than one constructor annotated with @Inject. %s",
implementation, CONSTRUCTOR_RULES);
} }
public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) { public Errors constructorNotDefinedByType(Constructor<?> constructor, TypeLiteral<?> type) {
@ -518,7 +370,7 @@ public final class Errors {
} }
public Errors bindingAlreadySet(Key<?> key, Object source) { public Errors bindingAlreadySet(Key<?> key, Object source) {
return addMessage("A binding to %s was already configured at %s.", key, convert(source)); return addMessage("A binding to %s was already configured at %s.", key, Messages.convert(source));
} }
public Errors jitBindingAlreadySet(Key<?> key) { public Errors jitBindingAlreadySet(Key<?> key) {
@ -534,24 +386,19 @@ public final class Errors {
allSources.format("%n bound at %s", source); allSources.format("%n bound at %s", source);
} }
} }
Errors errors = addMessage( return addMessage(
"Unable to create binding for %s." "Unable to create binding for %s."
+ " It was already configured on one or more child injectors or private modules" + " It was already configured on one or more child injectors or private modules"
+ "%s%n" + "%s%n"
+ " If it was in a PrivateModule, did you forget to expose the binding?", + " If it was in a PrivateModule, did you forget to expose the binding?",
key, allSources.out()); key, allSources.out());
return errors;
} }
public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) { public Errors errorCheckingDuplicateBinding(Key<?> key, Object source, Throwable t) {
return addMessage( return addMessage(
"A binding to %s was already configured at %s and an error was thrown " "A binding to %s was already configured at %s and an error was thrown "
+ "while checking duplicate bindings. Error: %s", + "while checking duplicate bindings. Error: %s",
key, convert(source), t); key, Messages.convert(source), t);
}
public Errors errorInjectingMethod(Throwable cause) {
return errorInUserCode(cause, "Error injecting method, %s", cause);
} }
public Errors errorNotifyingTypeListener(TypeListenerBinding listener, public Errors errorNotifyingTypeListener(TypeListenerBinding listener,
@ -559,28 +406,7 @@ public final class Errors {
return errorInUserCode(cause, return errorInUserCode(cause,
"Error notifying TypeListener %s (bound at %s) of %s.%n" "Error notifying TypeListener %s (bound at %s) of %s.%n"
+ " Reason: %s", + " Reason: %s",
listener.getListener(), convert(listener.getSource()), type, cause); listener.getListener(), Messages.convert(listener.getSource()), type, cause);
}
public Errors errorInjectingConstructor(Throwable cause) {
return errorInUserCode(cause, "Error injecting constructor, %s", cause);
}
public Errors errorInProvider(RuntimeException runtimeException) {
Throwable unwrapped = unwrap(runtimeException);
return errorInUserCode(unwrapped, "Error in custom provider, %s", unwrapped);
}
public Errors errorInUserInjector(
MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
return errorInUserCode(cause, "Error injecting %s using %s.%n"
+ " Reason: %s", type, listener, cause);
}
public Errors errorNotifyingInjectionListener(
InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
return errorInUserCode(cause, "Error notifying InjectionListener %s of %s.%n"
+ " Reason: %s", listener, type, cause);
} }
public Errors exposedButNotBound(Key<?> key) { public Errors exposedButNotBound(Key<?> key) {
@ -595,6 +421,18 @@ public final class Errors {
return errorInUserCode(cause, "Unable to method intercept: %s", clazz); return errorInUserCode(cause, "Unable to method intercept: %s", clazz);
} }
public static Collection<Message> getMessagesFromThrowable(Throwable throwable) {
if (throwable instanceof ProvisionException) {
return ((ProvisionException) throwable).getErrorMessages();
} else if (throwable instanceof ConfigurationException) {
return ((ConfigurationException) throwable).getErrorMessages();
} else if (throwable instanceof CreationException) {
return ((CreationException) throwable).getErrorMessages();
} else {
return ImmutableSet.of();
}
}
public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) { public Errors errorInUserCode(Throwable cause, String messageFormat, Object... arguments) {
Collection<Message> messages = getMessagesFromThrowable(cause); Collection<Message> messages = getMessagesFromThrowable(cause);
@ -605,13 +443,6 @@ public final class Errors {
} }
} }
private Throwable unwrap(RuntimeException runtimeException) {
if (runtimeException instanceof Exceptions.UnhandledCheckedUserException) {
return runtimeException.getCause();
} else {
return runtimeException;
}
}
public Errors cannotInjectRawProvider() { public Errors cannotInjectRawProvider() {
return addMessage("Cannot inject a Provider that has no type parameter"); return addMessage("Cannot inject a Provider that has no type parameter");
@ -629,23 +460,10 @@ public final class Errors {
return addMessage("Cannot inject a TypeLiteral that has no type parameter"); return addMessage("Cannot inject a TypeLiteral that has no type parameter");
} }
public Errors cannotSatisfyCircularDependency(Class<?> expectedType) {
return addMessage(
"Tried proxying %s to support a circular dependency, but it is not an interface.",
expectedType);
}
public Errors circularProxiesDisabled(Class<?> expectedType) {
return addMessage(
"Tried proxying %s to support a circular dependency, but circular proxies are disabled.",
expectedType);
}
public void throwCreationExceptionIfErrorsExist() { public void throwCreationExceptionIfErrorsExist() {
if (!hasErrors()) { if (!hasErrors()) {
return; return;
} }
throw new CreationException(getMessages()); throw new CreationException(getMessages());
} }
@ -653,28 +471,13 @@ public final class Errors {
if (!hasErrors()) { if (!hasErrors()) {
return; return;
} }
throw new ConfigurationException(getMessages()); throw new ConfigurationException(getMessages());
} }
public void throwProvisionExceptionIfErrorsExist() {
if (!hasErrors()) {
return;
}
throw new ProvisionException(getMessages());
}
private Message merge(Message message) {
List<Object> sources = Lists.newArrayList();
sources.addAll(getSources());
sources.addAll(message.getSources());
return new Message(sources, message.getMessage(), message.getCause());
}
public Errors merge(Collection<Message> messages) { public Errors merge(Collection<Message> messages) {
List<Object> sources = getSources();
for (Message message : messages) { for (Message message : messages) {
addMessage(merge(message)); addMessage(Messages.mergeSources(sources, message));
} }
return this; return this;
} }
@ -683,11 +486,15 @@ public final class Errors {
if (moreErrors.root == root || moreErrors.root.errors == null) { if (moreErrors.root == root || moreErrors.root.errors == null) {
return this; return this;
} }
merge(moreErrors.root.errors); merge(moreErrors.root.errors);
return this; return this;
} }
public Errors merge(InternalProvisionException ipe) {
merge(ipe.getErrors());
return this;
}
public List<Object> getSources() { public List<Object> getSources() {
List<Object> sources = Lists.newArrayList(); List<Object> sources = Lists.newArrayList();
for (Errors e = this; e != null; e = e.parent) { for (Errors e = this; e != null; e = e.parent) {
@ -719,8 +526,7 @@ public final class Errors {
} }
private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) { private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) {
String message = format(messageFormat, arguments); addMessage(Messages.create(cause, getSources(), messageFormat, arguments));
addMessage(new Message(getSources(), message, cause));
return this; return this;
} }
@ -736,7 +542,6 @@ public final class Errors {
if (root.errors == null) { if (root.errors == null) {
return ImmutableList.of(); return ImmutableList.of();
} }
return new Ordering<Message>() { return new Ordering<Message>() {
@Override @Override
public int compare(Message a, Message b) { public int compare(Message a, Message b) {
@ -745,73 +550,7 @@ public final class Errors {
}.sortedCopy(root.errors); }.sortedCopy(root.errors);
} }
/**
* Returns {@code value} if it is non-null allowed to be null. Otherwise a message is added and
* an {@code ErrorsException} is thrown.
*/
public <T> T checkForNull(T value, Object source, Dependency<?> dependency)
throws ErrorsException {
if (value != null || dependency.isNullable()) {
return value;
}
// Hack to allow null parameters to @Provides methods, for backwards compatibility.
if (dependency.getInjectionPoint().getMember() instanceof Method) {
Method annotated = (Method) dependency.getInjectionPoint().getMember();
if (annotated.isAnnotationPresent(Provides.class)) {
switch (InternalFlags.getNullableProvidesOption()) {
case ERROR:
break; // break out & let the below exception happen
case IGNORE:
return value; // user doesn't care about injecting nulls to non-@Nullables.
case WARN:
// Warn only once, otherwise we spam logs too much.
if (!warnedDependencies.add(dependency)) {
return value;
}
/*logger.log(Level.WARNING,
"Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
+ " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
+ " error.",
new Object[]{
dependency.getParameterIndex(),
convert(dependency.getInjectionPoint().getMember()),
convert(dependency.getKey())});*/
return null; // log & exit.
}
}
}
int parameterIndex = dependency.getParameterIndex();
String parameterName = (parameterIndex != -1)
? "parameter " + parameterIndex + " of "
: "";
addMessage("null returned by binding at %s%n but %s%s is not @Nullable",
source, parameterName, dependency.getInjectionPoint().getMember());
throw toException();
}
public int size() { public int size() {
return root.errors == null ? 0 : root.errors.size(); return root.errors == null ? 0 : root.errors.size();
} }
private static abstract class Converter<T> {
final Class<T> type;
Converter(Class<T> type) {
this.type = type;
}
boolean appliesTo(Object o) {
return o != null && type.isAssignableFrom(o.getClass());
}
String convert(Object o) {
return toString(type.cast(o));
}
abstract String toString(T t);
}
} }

View file

@ -8,6 +8,9 @@ package com.google.inject.internal;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class ErrorsException extends Exception { public class ErrorsException extends Exception {
// NOTE: this is used by Gin which is abandoned. So changing this API will prevent Gin users from
// upgrading Guice version.
private final Errors errors; private final Errors errors;
public ErrorsException(Errors errors) { public ErrorsException(Errors errors) {

View file

@ -1,46 +0,0 @@
package com.google.inject.internal;
/**
* Rethrows user-code exceptions in wrapped exceptions so that Errors can target the correct
* exception.
*/
class Exceptions {
/**
* Rethrows the exception (or it's cause, if it has one) directly if possible.
* If it was a checked exception, this wraps the exception in a stack trace
* with no frames, so that the exception is shown immediately with no frames
* above it.
*/
public static RuntimeException rethrowCause(Throwable throwable) {
Throwable cause = throwable;
if (cause.getCause() != null) {
cause = cause.getCause();
}
return rethrow(cause);
}
/**
* Rethrows the exception.
*/
public static RuntimeException rethrow(Throwable throwable) {
if (throwable instanceof RuntimeException) {
throw (RuntimeException) throwable;
} else if (throwable instanceof Error) {
throw (Error) throwable;
} else {
throw new UnhandledCheckedUserException(throwable);
}
}
/**
* A marker exception class that we look for in order to unwrap the exception
* into the user exception, to provide a cleaner stack trace.
*/
@SuppressWarnings("serial")
static class UnhandledCheckedUserException extends RuntimeException {
public UnhandledCheckedUserException(Throwable cause) {
super(cause);
}
}
}

View file

@ -22,14 +22,17 @@ public final class ExposedBindingImpl<T> extends BindingImpl<T> implements Expos
this.privateElements = privateElements; this.privateElements = privateElements;
} }
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public Set<Dependency<?>> getDependencies() { public Set<Dependency<?>> getDependencies() {
return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class))); return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
} }
@Override
public PrivateElements getPrivateElements() { public PrivateElements getPrivateElements() {
return privateElements; return privateElements;
} }

View file

@ -18,6 +18,7 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
this.privateElements = privateElements; this.privateElements = privateElements;
} }
@Override
public void notify(Errors errors) { public void notify(Errors errors) {
InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector(); InjectorImpl privateInjector = (InjectorImpl) privateElements.getInjector();
BindingImpl<T> explicitBinding = privateInjector.state.getExplicitBinding(key); BindingImpl<T> explicitBinding = privateInjector.state.getExplicitBinding(key);
@ -33,8 +34,8 @@ final class ExposedKeyFactory<T> implements InternalFactory<T>, CreationListener
this.delegate = explicitBinding; this.delegate = explicitBinding;
} }
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) public T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException { throws InternalProvisionException {
return delegate.getInternalFactory().get(errors, context, dependency, linked); return delegate.getInternalFactory().get(context, dependency, linked);
} }
} }

View file

@ -27,12 +27,14 @@ public class ExposureBuilder<T> implements AnnotatedElementBuilder {
} }
} }
@Override
public void annotatedWith(Class<? extends Annotation> annotationType) { public void annotatedWith(Class<? extends Annotation> annotationType) {
Preconditions.checkNotNull(annotationType, "annotationType"); Preconditions.checkNotNull(annotationType, "annotationType");
checkNotAnnotated(); checkNotAnnotated();
key = Key.get(key.getTypeLiteral(), annotationType); key = Key.get(key.getTypeLiteral(), annotationType);
} }
@Override
public void annotatedWith(Annotation annotation) { public void annotatedWith(Annotation annotation) {
Preconditions.checkNotNull(annotation, "annotation"); Preconditions.checkNotNull(annotation, "annotation");
checkNotAnnotated(); checkNotAnnotated();

View file

@ -25,6 +25,7 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
this.source = source; this.source = source;
} }
@Override
public void notify(final Errors errors) { public void notify(final Errors errors) {
try { try {
targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT); targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source), JitLimitation.NEW_OR_EXISTING_JIT);
@ -33,11 +34,15 @@ final class FactoryProxy<T> implements InternalFactory<T>, CreationListener {
} }
} }
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) @Override
throws ErrorsException { public T get(InternalContext context, Dependency<?> dependency, boolean linked)
context.pushState(targetKey, source); throws InternalProvisionException {
Key<? extends T> localTargetKey = targetKey;
context.pushState(localTargetKey, source);
try { try {
return targetFactory.get(errors.withSource(targetKey), context, dependency, true); return targetFactory.get(context, dependency, true);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localTargetKey);
} finally { } finally {
context.popState(); context.popState();
} }

View file

@ -3,6 +3,9 @@ package com.google.inject.internal;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.Map;
/** /**
* Lazily creates (and caches) values for keys. If creating the value fails (with errors), an * Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
@ -10,8 +13,8 @@ import com.google.common.cache.LoadingCache;
*/ */
public abstract class FailableCache<K, V> { public abstract class FailableCache<K, V> {
private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder().build( private final LoadingCache<K, Object> delegate = CacheBuilder.newBuilder()
new CacheLoader<K, Object>() { .build(new CacheLoader<>() {
public Object load(K key) { public Object load(K key) {
Errors errors = new Errors(); Errors errors = new Errors();
V result = null; V result = null;
@ -33,7 +36,7 @@ public abstract class FailableCache<K, V> {
throw errors.toException(); throw errors.toException();
} else { } else {
@SuppressWarnings("unchecked") // create returned a non-error result, so this is safe @SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
V result = (V) resultOrError; V result = (V) resultOrError;
return result; return result;
} }
} }
@ -41,4 +44,14 @@ public abstract class FailableCache<K, V> {
boolean remove(K key) { boolean remove(K key) {
return delegate.asMap().remove(key) != null; return delegate.asMap().remove(key) != null;
} }
Map<K, V> asMap() {
return Maps.transformValues(Maps.filterValues(ImmutableMap.copyOf(delegate.asMap()),
resultOrError -> !(resultOrError instanceof Errors)),
resultOrError -> {
@SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
V result = (V) resultOrError;
return result;
});
}
} }

View file

@ -1,4 +1,4 @@
package com.google.inject.multibindings; package com.google.inject.internal;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.inject.Binding; import com.google.inject.Binding;
@ -23,17 +23,22 @@ import java.lang.annotation.Annotation;
/** /**
* Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding * Visits bindings to return a {@code IndexedBinding} that can be used to emulate the binding
* deduplication that Guice internally performs. * deduplication that Guice internally performs.
*
* <p>Note: simply using equals/hashCode on the BindingImpls doesn't work because they all have
* unique annotations. This works around that by reimplementing equality semantics that ignores
* {@link Element#uniqueId()}. A better solution might be to introduce the idea of an 'anonymous'
* binding to guice, that might support this usecase directly.
*/ */
class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding> public class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding>
implements BindingScopingVisitor<Object> { implements BindingScopingVisitor<Object> {
private static final Object EAGER_SINGLETON = new Object(); private static final Object EAGER_SINGLETON = new Object();
final Injector injector; private final Injector injector;
Indexer(Injector injector) { public Indexer(Injector injector) {
this.injector = injector; this.injector = injector;
} }
boolean isIndexable(Binding<?> binding) { public boolean isIndexable(Binding<?> binding) {
return binding.getKey().getAnnotation() instanceof Element; return binding.getKey().getAnnotation() instanceof Element;
} }
@ -126,7 +131,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
PROVIDED_BY, PROVIDED_BY,
} }
static class IndexedBinding { public static class IndexedBinding {
final String annotationName; final String annotationName;
final Element.Type annotationType; final Element.Type annotationType;
final TypeLiteral<?> typeLiteral; final TypeLiteral<?> typeLiteral;
@ -134,7 +139,7 @@ class Indexer extends DefaultBindingTargetVisitor<Object, Indexer.IndexedBinding
final BindingType type; final BindingType type;
final Object extraEquality; final Object extraEquality;
IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) { public IndexedBinding(Binding<?> binding, BindingType type, Object scope, Object extraEquality) {
this.scope = scope; this.scope = scope;
this.type = type; this.type = type;
this.extraEquality = extraEquality; this.extraEquality = extraEquality;

View file

@ -3,17 +3,23 @@ package com.google.inject.internal;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Binding; import com.google.inject.Binding;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.Scope; import com.google.inject.Scope;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionRequest;
import com.google.inject.spi.MembersInjectorLookup;
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding; import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
import com.google.inject.spi.ProviderLookup;
import com.google.inject.spi.ProvisionListenerBinding; import com.google.inject.spi.ProvisionListenerBinding;
import com.google.inject.spi.ScopeBinding; import com.google.inject.spi.ScopeBinding;
import com.google.inject.spi.StaticInjectionRequest;
import com.google.inject.spi.TypeConverterBinding; import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.spi.TypeListenerBinding; import com.google.inject.spi.TypeListenerBinding;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -27,9 +33,13 @@ final class InheritingState implements State {
// Must be a linked hashmap in order to preserve order of bindings in Modules. // Must be a linked hashmap in order to preserve order of bindings in Modules.
private final Map<Key<?>, Binding<?>> explicitBindingsMutable = Maps.newLinkedHashMap(); private final Map<Key<?>, Binding<?>> explicitBindingsMutable = Maps.newLinkedHashMap();
private final Map<Key<?>, Binding<?>> explicitBindings private final Map<Key<?>, Binding<?>> explicitBindings =
= Collections.unmodifiableMap(explicitBindingsMutable); Collections.unmodifiableMap(explicitBindingsMutable);
private final Map<Class<? extends Annotation>, ScopeBinding> scopes = Maps.newHashMap(); private final Map<Class<? extends Annotation>, ScopeBinding> scopes = Maps.newHashMap();
private final Set<ProviderLookup<?>> providerLookups = Sets.newLinkedHashSet();
private final Set<StaticInjectionRequest> staticInjectionRequests = Sets.newLinkedHashSet();
private final Set<MembersInjectorLookup<?>> membersInjectorLookups = Sets.newLinkedHashSet();
private final Set<InjectionRequest<?>> injectionRequests = Sets.newLinkedHashSet();
private final List<TypeConverterBinding> converters = Lists.newArrayList(); private final List<TypeConverterBinding> converters = Lists.newArrayList();
private final List<TypeListenerBinding> typeListenerBindings = Lists.newArrayList(); private final List<TypeListenerBinding> typeListenerBindings = Lists.newArrayList();
private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList(); private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList();
@ -43,41 +53,95 @@ final class InheritingState implements State {
this.blacklistedKeys = new WeakKeySet(lock); this.blacklistedKeys = new WeakKeySet(lock);
} }
@Override
public State parent() { public State parent() {
return parent; return parent;
} }
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types @SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
@Override
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) { public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
Binding<?> binding = explicitBindings.get(key); Binding<?> binding = explicitBindings.get(key);
return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key); return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key);
} }
@Override
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() { public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
return explicitBindings; return explicitBindings;
} }
@Override
public void putBinding(Key<?> key, BindingImpl<?> binding) { public void putBinding(Key<?> key, BindingImpl<?> binding) {
explicitBindingsMutable.put(key, binding); explicitBindingsMutable.put(key, binding);
} }
@Override
public void putProviderLookup(ProviderLookup<?> lookup) {
providerLookups.add(lookup);
}
@Override
public Set<ProviderLookup<?>> getProviderLookupsThisLevel() {
return providerLookups;
}
@Override
public void putStaticInjectionRequest(StaticInjectionRequest staticInjectionRequest) {
staticInjectionRequests.add(staticInjectionRequest);
}
@Override
public Set<StaticInjectionRequest> getStaticInjectionRequestsThisLevel() {
return staticInjectionRequests;
}
@Override
public void putInjectionRequest(InjectionRequest<?> injectionRequest) {
injectionRequests.add(injectionRequest);
}
@Override
public Set<InjectionRequest<?>> getInjectionRequestsThisLevel() {
return injectionRequests;
}
@Override
public void putMembersInjectorLookup(MembersInjectorLookup<?> membersInjectorLookup) {
membersInjectorLookups.add(membersInjectorLookup);
}
@Override
public Set<MembersInjectorLookup<?>> getMembersInjectorLookupsThisLevel() {
return membersInjectorLookups;
}
@Override
public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) { public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
ScopeBinding scopeBinding = scopes.get(annotationType); ScopeBinding scopeBinding = scopes.get(annotationType);
return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType); return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType);
} }
@Override
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) { public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
scopes.put(annotationType, scope); scopes.put(annotationType, scope);
} }
@Override
public Collection<ScopeBinding> getScopeBindingsThisLevel() {
return scopes.values();
}
@Override
public Iterable<TypeConverterBinding> getConvertersThisLevel() { public Iterable<TypeConverterBinding> getConvertersThisLevel() {
return converters; return converters;
} }
@Override
public void addConverter(TypeConverterBinding typeConverterBinding) { public void addConverter(TypeConverterBinding typeConverterBinding) {
converters.add(typeConverterBinding); converters.add(typeConverterBinding);
} }
@Override
public TypeConverterBinding getConverter( public TypeConverterBinding getConverter(
String stringValue, TypeLiteral<?> type, Errors errors, Object source) { String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
TypeConverterBinding matchingConverter = null; TypeConverterBinding matchingConverter = null;
@ -94,10 +158,12 @@ final class InheritingState implements State {
return matchingConverter; return matchingConverter;
} }
@Override
public void addTypeListener(TypeListenerBinding listenerBinding) { public void addTypeListener(TypeListenerBinding listenerBinding) {
typeListenerBindings.add(listenerBinding); typeListenerBindings.add(listenerBinding);
} }
@Override
public List<TypeListenerBinding> getTypeListenerBindings() { public List<TypeListenerBinding> getTypeListenerBindings() {
List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings(); List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings();
List<TypeListenerBinding> result = List<TypeListenerBinding> result =
@ -107,10 +173,17 @@ final class InheritingState implements State {
return result; return result;
} }
@Override
public List<TypeListenerBinding> getTypeListenerBindingsThisLevel() {
return typeListenerBindings;
}
@Override
public void addProvisionListener(ProvisionListenerBinding listenerBinding) { public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
provisionListenerBindings.add(listenerBinding); provisionListenerBindings.add(listenerBinding);
} }
@Override
public List<ProvisionListenerBinding> getProvisionListenerBindings() { public List<ProvisionListenerBinding> getProvisionListenerBindings() {
List<ProvisionListenerBinding> parentBindings = parent.getProvisionListenerBindings(); List<ProvisionListenerBinding> parentBindings = parent.getProvisionListenerBindings();
List<ProvisionListenerBinding> result = List<ProvisionListenerBinding> result =
@ -120,10 +193,17 @@ final class InheritingState implements State {
return result; return result;
} }
@Override
public List<ProvisionListenerBinding> getProvisionListenerBindingsThisLevel() {
return provisionListenerBindings;
}
@Override
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) { public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
scannerBindings.add(scanner); scannerBindings.add(scanner);
} }
@Override
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() { public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
List<ModuleAnnotatedMethodScannerBinding> parentBindings = parent.getScannerBindings(); List<ModuleAnnotatedMethodScannerBinding> parentBindings = parent.getScannerBindings();
List<ModuleAnnotatedMethodScannerBinding> result = List<ModuleAnnotatedMethodScannerBinding> result =
@ -133,23 +213,33 @@ final class InheritingState implements State {
return result; return result;
} }
@Override
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindingsThisLevel() {
return scannerBindings;
}
@Override
public void blacklist(Key<?> key, State state, Object source) { public void blacklist(Key<?> key, State state, Object source) {
parent.blacklist(key, state, source); parent.blacklist(key, state, source);
blacklistedKeys.add(key, state, source); blacklistedKeys.add(key, state, source);
} }
@Override
public boolean isBlacklisted(Key<?> key) { public boolean isBlacklisted(Key<?> key) {
return blacklistedKeys.contains(key); return blacklistedKeys.contains(key);
} }
@Override
public Set<Object> getSourcesForBlacklistedKey(Key<?> key) { public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
return blacklistedKeys.getSources(key); return blacklistedKeys.getSources(key);
} }
@Override
public Object lock() { public Object lock() {
return lock; return lock;
} }
@Override
public Map<Class<? extends Annotation>, Scope> getScopes() { public Map<Class<? extends Annotation>, Scope> getScopes() {
ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder(); ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder();
for (Map.Entry<Class<? extends Annotation>, ScopeBinding> entry : scopes.entrySet()) { for (Map.Entry<Class<? extends Annotation>, ScopeBinding> entry : scopes.entrySet()) {

View file

@ -8,5 +8,5 @@ interface Initializable<T> {
/** /**
* Ensures the reference is initialized, then returns it. * Ensures the reference is initialized, then returns it.
*/ */
T get(Errors errors) throws ErrorsException; T get() throws InternalProvisionException;
} }

View file

@ -6,8 +6,8 @@ final class Initializables {
* Returns an initializable for an instance that requires no initialization. * Returns an initializable for an instance that requires no initialization.
*/ */
static <T> Initializable<T> of(final T instance) { static <T> Initializable<T> of(final T instance) {
return new Initializable<T>() { return new Initializable<>() {
public T get(Errors errors) throws ErrorsException { public T get() {
return instance; return instance;
} }

View file

@ -3,15 +3,16 @@ package com.google.inject.internal;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.inject.Binding; import com.google.inject.Binding;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.Stage; import com.google.inject.Stage;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.spi.InjectionPoint; import com.google.inject.spi.InjectionPoint;
import java.util.Map; import java.util.IdentityHashMap;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CountDownLatch;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -23,27 +24,30 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/ */
final class Initializer { final class Initializer {
/** /** Is set to true once {@link #validateOustandingInjections} is called. */
* the only thread that we'll use to inject members. private volatile boolean validationStarted = false;
*/
private final Thread creatingThread = Thread.currentThread();
/** /**
* zero means everything is injected. * Allows us to detect circular dependencies. It's only used during injectable reference
* initialization. After initialization direct access through volatile field is used.
*/ */
private final CountDownLatch ready = new CountDownLatch(1); private final CycleDetectingLock.CycleDetectingLockFactory<Class<?>> cycleDetectingLockFactory =
new CycleDetectingLock.CycleDetectingLockFactory<>();
/** /**
* Maps from instances that need injection to the MembersInjector that will inject them. * Instances that need injection during injector creation to a source that registered them. New
* references added before {@link #validateOustandingInjections}. Cleared up in {@link
* #injectAll}.
*/ */
private final Map<Object, MembersInjectorImpl<?>> pendingMembersInjectors = private final List<InjectableReference<?>> pendingInjections = Lists.newArrayList();
/**
* Map that guarantees that no instance would get two references. New references added before
* {@link #validateOustandingInjections}. Cleared up in {@link #validateOustandingInjections}.
*/
private final IdentityHashMap<Object, InjectableReference<?>> initializablesCache =
Maps.newIdentityHashMap(); Maps.newIdentityHashMap();
/**
* Maps instances that need injection to a source that registered them
*/
private final Map<Object, InjectableReference<?>> pendingInjection = Maps.newIdentityHashMap();
/** /**
* Registers an instance for member injection when that step is performed. * Registers an instance for member injection when that step is performed.
* *
@ -55,21 +59,35 @@ final class Initializer {
<T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding, <T> Initializable<T> requestInjection(InjectorImpl injector, T instance, Binding<T> binding,
Object source, Set<InjectionPoint> injectionPoints) { Object source, Set<InjectionPoint> injectionPoints) {
checkNotNull(source); checkNotNull(source);
Preconditions.checkState(
!validationStarted, "Member injection could not be requested after validation is started");
ProvisionListenerStackCallback<T> provisionCallback = ProvisionListenerStackCallback<T> provisionCallback =
binding == null ? null : injector.provisionListenerStore.get(binding); binding == null ? null : injector.provisionListenerStore.get(binding);
// short circuit if the object has no injections or listeners. // short circuit if the object has no injections or listeners.
if (instance == null || (injectionPoints.isEmpty() if (instance == null || (injectionPoints.isEmpty()
&& !injector.membersInjectorStore.hasTypeListeners() && !injector.membersInjectorStore.hasTypeListeners()
&& (provisionCallback == null || !provisionCallback.hasListeners()))) { && (provisionCallback == null))) {
return Initializables.of(instance); return Initializables.of(instance);
} }
InjectableReference<T> initializable = new InjectableReference<T>( if (initializablesCache.containsKey(instance)) {
injector, instance, binding == null ? null : binding.getKey(), provisionCallback, source); @SuppressWarnings("unchecked") // Map from T to InjectableReference<T>
pendingInjection.put(instance, initializable); Initializable<T> cached = (Initializable<T>) initializablesCache.get(instance);
return initializable; return cached;
}
InjectableReference<T> injectableReference =
new InjectableReference<T>(
injector,
instance,
binding == null ? null : binding.getKey(),
provisionCallback,
source,
cycleDetectingLockFactory.create(instance.getClass()));
initializablesCache.put(instance, injectableReference);
pendingInjections.add(injectableReference);
return injectableReference;
} }
/** /**
@ -77,9 +95,11 @@ final class Initializer {
* on the injected instances. * on the injected instances.
*/ */
void validateOustandingInjections(Errors errors) { void validateOustandingInjections(Errors errors) {
for (InjectableReference<?> reference : pendingInjection.values()) { validationStarted = true;
initializablesCache.clear();
for (InjectableReference<?> reference : pendingInjections) {
try { try {
pendingMembersInjectors.put(reference.instance, reference.validate(errors)); reference.validate(errors);
} catch (ErrorsException e) { } catch (ErrorsException e) {
errors.merge(e.getErrors()); errors.merge(e.getErrors());
} }
@ -92,85 +112,122 @@ final class Initializer {
* instances are codependent (directly or transitively), ordering of injection is arbitrary. * instances are codependent (directly or transitively), ordering of injection is arbitrary.
*/ */
void injectAll(final Errors errors) { void injectAll(final Errors errors) {
// loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy Preconditions.checkState(validationStarted, "Validation should be done before injection");
// is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work for (InjectableReference<?> reference : pendingInjections) {
for (InjectableReference<?> reference : Lists.newArrayList(pendingInjection.values())) {
try { try {
reference.get(errors); reference.get();
} catch (ErrorsException e) { } catch (InternalProvisionException ipe) {
errors.merge(e.getErrors()); errors.merge(ipe);
} }
} }
pendingInjections.clear();
if (!pendingInjection.isEmpty()) {
throw new AssertionError("Failed to satisfy " + pendingInjection);
}
ready.countDown();
} }
private class InjectableReference<T> implements Initializable<T> { private enum InjectableReferenceState {
NEW,
VALIDATED,
INJECTING,
READY
}
private static class InjectableReference<T> implements Initializable<T> {
private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
private volatile MembersInjectorImpl<T> membersInjector = null;
private final InjectorImpl injector; private final InjectorImpl injector;
private final T instance; private final T instance;
private final Object source; private final Object source;
private final Key<T> key; private final Key<T> key;
private final ProvisionListenerStackCallback<T> provisionCallback; private final ProvisionListenerStackCallback<T> provisionCallback;
private final CycleDetectingLock<?> lock;
public InjectableReference(InjectorImpl injector, T instance, Key<T> key, public InjectableReference(InjectorImpl injector, T instance, Key<T> key,
ProvisionListenerStackCallback<T> provisionCallback, Object source) { ProvisionListenerStackCallback<T> provisionCallback,
Object source,
CycleDetectingLock<?> lock) {
this.injector = injector; this.injector = injector;
this.key = key; // possibly null! this.key = key; // possibly null!
this.provisionCallback = provisionCallback; // possibly null! this.provisionCallback = provisionCallback; // possibly null!
this.instance = checkNotNull(instance, "instance"); this.instance = checkNotNull(instance, "instance");
this.source = checkNotNull(source, "source"); this.source = checkNotNull(source, "source");
this.lock = checkNotNull(lock, "lock");
} }
public MembersInjectorImpl<T> validate(Errors errors) throws ErrorsException { public void validate(Errors errors) throws ErrorsException {
@SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T> @SuppressWarnings("unchecked") // the type of 'T' is a TypeLiteral<T>
TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass()); TypeLiteral<T> type = TypeLiteral.get((Class<T>) instance.getClass());
return injector.membersInjectorStore.get(type, errors.withSource(source)); membersInjector = injector.membersInjectorStore.get(type, errors.withSource(source));
Preconditions.checkNotNull(
membersInjector,
"No membersInjector available for instance: %s, from key: %s",
instance,
key);
state = InjectableReferenceState.VALIDATED;
} }
/** /**
* Reentrant. If {@code instance} was registered for injection at injector-creation time, this * Reentrant. If {@code instance} was registered for injection at injector-creation time, this
* method will ensure that all its members have been injected before returning. * method will ensure that all its members have been injected before returning.
*/ */
public T get(Errors errors) throws ErrorsException { @Override
if (ready.getCount() == 0) { public T get() throws InternalProvisionException {
// skipping acquiring lock if initialization is already finished
if (state == InjectableReferenceState.READY) {
return instance; return instance;
} }
// acquire lock for current binding to initialize an instance
Multimap<?, ?> lockCycle = lock.lockOrDetectPotentialLocksCycle();
if (!lockCycle.isEmpty()) {
// Potential deadlock detected and creation lock is not taken.
// According to injectAll()'s contract return non-initialized instance.
// just wait for everything to be injected by another thread // This condition should not be possible under the current Guice implementation.
if (Thread.currentThread() != creatingThread) { // This clause exists for defensive programming purposes.
try {
ready.await(); // Reasoning:
return instance; // get() is called either directly from injectAll(), holds no locks and can not create
} catch (InterruptedException e) { // a cycle, or it is called through a singleton scope, which resolves deadlocks by itself.
// Give up, since we don't know if our injection is ready // Before calling get() object has to be requested for injection.
throw new RuntimeException(e); // Initializer.requestInjection() is called either for constant object bindings, which wrap
} // creation into a Singleton scope, or from Binder.requestInjection(), which
// has to use Singleton scope to reuse the same InjectableReference to potentially
// create a lock cycle.
return instance;
} }
try {
// toInject needs injection, do it right away. we only do this once, even if it fails // lock acquired, current thread owns this instance initialization
if (pendingInjection.remove(instance) != null) { switch (state) {
// safe because we only insert a members injector for the appropriate instance case READY:
@SuppressWarnings("unchecked") return instance;
MembersInjectorImpl<T> membersInjector = // When instance depends on itself in the same thread potential dead lock
(MembersInjectorImpl<T>) pendingMembersInjectors.remove(instance); // is not detected. We have to prevent a stack overflow and we use
Preconditions.checkState(membersInjector != null, // an "injecting" stage to short-circuit a call.
"No membersInjector available for instance: %s, from key: %s", instance, key); case INJECTING:
return instance;
case VALIDATED:
state = InjectableReferenceState.INJECTING;
break;
case NEW:
throw new IllegalStateException("InjectableReference is not validated yet");
default:
throw new IllegalStateException("Unknown state: " + state);
}
// if in Stage.TOOL, we only want to inject & notify toolable injection points. // if in Stage.TOOL, we only want to inject & notify toolable injection points.
// (otherwise we'll inject all of them) // (otherwise we'll inject all of them)
membersInjector.injectAndNotify(instance, try {
errors.withSource(source), membersInjector.injectAndNotify(
key, instance, key, provisionCallback, source, injector.options.stage == Stage.TOOL);
provisionCallback, } catch (InternalProvisionException ipe) {
source, throw ipe.addSource(source);
injector.options.stage == Stage.TOOL); }
// mark instance as ready to skip a lock on subsequent calls
state = InjectableReferenceState.READY;
return instance;
} finally {
// always release our creation lock, even on failures
lock.unlock();
} }
return instance;
} }
@Override @Override

View file

@ -98,21 +98,19 @@ final class InjectionRequestProcessor extends AbstractProcessor {
} }
void injectMembers() { void injectMembers() {
try { try (InternalContext context = injector.enterContext()) {
injector.callInContext(new ContextualCallable<Void>() { boolean isStageTool = injector.options.stage == Stage.TOOL;
public Void call(InternalContext context) { for (SingleMemberInjector memberInjector : memberInjectors) {
for (SingleMemberInjector memberInjector : memberInjectors) { // Run injections if we're not in tool stage (ie, PRODUCTION or DEV),
// Run injections if we're not in tool stage (ie, PRODUCTION or DEV), // or if we are in tool stage and the injection point is toolable.
// or if we are in tool stage and the injection point is toolable. if (!isStageTool || memberInjector.getInjectionPoint().isToolable()) {
if (injector.options.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) { try {
memberInjector.inject(errors, context, null); memberInjector.inject(context, null);
} } catch (InternalProvisionException e) {
errors.merge(e);
} }
return null;
} }
}); }
} catch (ErrorsException e) {
throw new AssertionError();
} }
} }
} }

View file

@ -1,12 +1,16 @@
package com.google.inject.internal; package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists; import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.inject.Binder; import com.google.inject.Binder;
import com.google.inject.Binding; import com.google.inject.Binding;
@ -18,7 +22,6 @@ import com.google.inject.MembersInjector;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.ProvidedBy; import com.google.inject.ProvidedBy;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Scope; import com.google.inject.Scope;
import com.google.inject.Stage; import com.google.inject.Stage;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
@ -26,8 +29,10 @@ import com.google.inject.internal.util.SourceProvider;
import com.google.inject.spi.BindingTargetVisitor; import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConvertedConstantBinding; import com.google.inject.spi.ConvertedConstantBinding;
import com.google.inject.spi.Dependency; import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.HasDependencies; import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InjectionPoint; import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.InstanceBinding;
import com.google.inject.spi.ProviderBinding; import com.google.inject.spi.ProviderBinding;
import com.google.inject.spi.TypeConverterBinding; import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.util.Providers; import com.google.inject.util.Providers;
@ -42,45 +47,46 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentMap;
/** /**
* Default {@link Injector} implementation. * Default {@link Injector} implementation.
* *
*/ */
final class InjectorImpl implements Injector, Lookups { final class InjectorImpl implements Injector, Lookups {
public static final TypeLiteral<String> STRING_TYPE = TypeLiteral.get(String.class); public static final TypeLiteral<String> STRING_TYPE = TypeLiteral.get(String.class);
/**
* Synchronization: map value is modified only for the current thread,
* it's ok to read map values of other threads. It can change between your
* calls.
*
* @see #getGlobalInternalContext
*/
private static final ConcurrentMap<Thread, InternalContext> globalInternalContext =
Maps.newConcurrentMap();
final State state; final State state;
final InjectorImpl parent; final InjectorImpl parent;
final BindingsMultimap bindingsMultimap = new BindingsMultimap();
final ListMultimap<TypeLiteral<?>, Binding<?>> bindingsMultimap = ArrayListMultimap.create();
final InjectorOptions options; final InjectorOptions options;
/** /**
* Just-in-time binding cache. Guarded by state.lock() * Just-in-time binding cache. Guarded by state.lock()
*/ */
final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap(); final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
/** /**
* Cache of Keys that we were unable to create JIT bindings for, so we don't * Cache of Keys that we were unable to create JIT bindings for, so we don't
* keep trying. Also guarded by state.lock(). * keep trying. Also guarded by state.lock().
*/ */
final Set<Key<?>> failedJitBindings = Sets.newHashSet(); final Set<Key<?>> failedJitBindings = Sets.newHashSet();
Lookups lookups = new DeferredLookups(this);
/** The set of types passed to {@link #getMembersInjector} and {@link #injectMembers}. */
final Set<TypeLiteral<?>> userRequestedMembersInjectorTypes = Sets.newConcurrentHashSet();
/** /**
* Cached constructor injectors for each type * Cached constructor injectors for each type
*/ */
final ConstructorInjectorStore constructors = new ConstructorInjectorStore(this); final ConstructorInjectorStore constructors = new ConstructorInjectorStore(this);
/**
* @see #getGlobalInternalContext
*/
private final ThreadLocal<Object[]> localContext; private final ThreadLocal<Object[]> localContext;
Lookups lookups = new DeferredLookups(this);
/** /**
* Cached field and method injectors for each type. * Cached field and method injectors for each type.
*/ */
@ -98,56 +104,32 @@ final class InjectorImpl implements Injector, Lookups {
if (parent != null) { if (parent != null) {
localContext = parent.localContext; localContext = parent.localContext;
} else { } else {
localContext = new ThreadLocal<Object[]>(); // No ThreadLocal.initialValue(), as that would cause classloader leaks. See
// https://github.com/google/guice/issues/288#issuecomment-48216933,
// https://github.com/google/guice/issues/288#issuecomment-48216944
localContext = new ThreadLocal<>();
} }
} }
/**
* Returns true if the key type is Provider (but not a subclass of Provider). /** Only to be called by the {@link SingletonScope} provider. */
*/ InternalContext getLocalContext() {
private static boolean isProvider(Key<?> key) { return (InternalContext) localContext.get()[0];
return key.getTypeLiteral().getRawType().equals(Provider.class);
} }
private static boolean isTypeLiteral(Key<?> key) { InternalContext enterContext() {
return key.getTypeLiteral().getRawType().equals(TypeLiteral.class); Object[] reference = localContext.get();
} if (reference == null) {
reference = new Object[1];
private static <T> Key<T> getProvidedKey(Key<Provider<T>> key, Errors errors) throws ErrorsException { localContext.set(reference);
Type providerType = key.getTypeLiteral().getType();
// If the Provider has no type parameter (raw Provider)...
if (!(providerType instanceof ParameterizedType)) {
throw errors.cannotInjectRawProvider().toException();
} }
InternalContext ctx = (InternalContext) reference[0];
Type entryType = ((ParameterizedType) providerType).getActualTypeArguments()[0]; if (ctx == null) {
reference[0] = ctx = new InternalContext(options, reference);
@SuppressWarnings("unchecked") // safe because T came from Key<Provider<T>> } else {
Key<T> providedKey = (Key<T>) key.ofType(entryType); ctx.enter();
return providedKey; }
} return ctx;
/**
* Returns true if the key type is MembersInjector (but not a subclass of MembersInjector).
*/
private static boolean isMembersInjector(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(MembersInjector.class)
&& key.getAnnotationType() == null;
}
/**
* Provides access to the internal context for the current injector of all threads.
* One does not need to use this from Guice source code as context could be passed on the stack.
* It is required for custom scopes which are called from Guice and sometimes do require
* access to current internal context, but it is not passed in. Contrary to {@link #localContext}
* it is not used to store injector-specific state, but to provide easy access to the current
* state.
*
* @return unmodifiable map
*/
static Map<Thread, InternalContext> getGlobalInternalContext() {
return Collections.unmodifiableMap(globalInternalContext);
} }
/** /**
@ -155,23 +137,23 @@ final class InjectorImpl implements Injector, Lookups {
*/ */
void index() { void index() {
for (Binding<?> binding : state.getExplicitBindingsThisLevel().values()) { for (Binding<?> binding : state.getExplicitBindingsThisLevel().values()) {
index(binding); bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
} }
} }
<T> void index(Binding<T> binding) { @Override
bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
}
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) { public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
return bindingsMultimap.getAll(type); @SuppressWarnings("unchecked") // safe because we only put matching entries into the map
List<Binding<T>> list = (List<Binding<T>>) (List) bindingsMultimap.get(checkNotNull(type, "type"));
return Collections.unmodifiableList(list);
} }
/** /**
* Returns the binding for {@code key} * Returns the binding for {@code key}
*/ */
@Override
public <T> BindingImpl<T> getBinding(Key<T> key) { public <T> BindingImpl<T> getBinding(Key<T> key) {
Errors errors = new Errors(key); Errors errors = new Errors(checkNotNull(key, "key"));
try { try {
BindingImpl<T> result = getBindingOrThrow(key, errors, JitLimitation.EXISTING_JIT); BindingImpl<T> result = getBindingOrThrow(key, errors, JitLimitation.EXISTING_JIT);
errors.throwConfigurationExceptionIfErrorsExist(); errors.throwConfigurationExceptionIfErrorsExist();
@ -181,6 +163,7 @@ final class InjectorImpl implements Injector, Lookups {
} }
} }
@Override
public <T> BindingImpl<T> getExistingBinding(Key<T> key) { public <T> BindingImpl<T> getExistingBinding(Key<T> key) {
// Check explicit bindings, i.e. bindings created by modules. // Check explicit bindings, i.e. bindings created by modules.
BindingImpl<T> explicitBinding = state.getExplicitBinding(key); BindingImpl<T> explicitBinding = state.getExplicitBinding(key);
@ -235,21 +218,22 @@ final class InjectorImpl implements Injector, Lookups {
return getJustInTimeBinding(key, errors, jitType); return getJustInTimeBinding(key, errors, jitType);
} }
@Override
public <T> Binding<T> getBinding(Class<T> type) { public <T> Binding<T> getBinding(Class<T> type) {
return getBinding(Key.get(type)); return getBinding(Key.get(checkNotNull(type, "type")));
} }
@Override
public Injector getParent() { public Injector getParent() {
return parent; return parent;
} }
@Override
public Injector createChildInjector(Iterable<? extends Module> modules) { public Injector createChildInjector(Iterable<? extends Module> modules) {
return new InternalInjectorCreator() return new InternalInjectorCreator().parentInjector(this).addModules(modules).build();
.parentInjector(this)
.addModules(modules)
.build();
} }
@Override
public Injector createChildInjector(Module... modules) { public Injector createChildInjector(Module... modules) {
return createChildInjector(ImmutableList.copyOf(modules)); return createChildInjector(ImmutableList.copyOf(modules));
} }
@ -267,7 +251,7 @@ final class InjectorImpl implements Injector, Lookups {
// first try to find a JIT binding that we've already created // first try to find a JIT binding that we've already created
for (InjectorImpl injector = this; injector != null; injector = injector.parent) { for (InjectorImpl injector = this; injector != null; injector = injector.parent) {
@SuppressWarnings("unchecked") // we only store bindings that match their key @SuppressWarnings("unchecked") // we only store bindings that match their key
BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key); BindingImpl<T> binding = (BindingImpl<T>) injector.jitBindings.get(key);
if (binding != null) { if (binding != null) {
// If we found a JIT binding and we don't allow them, // If we found a JIT binding and we don't allow them,
@ -304,6 +288,40 @@ final class InjectorImpl implements Injector, Lookups {
} // end synchronized(state.lock()) } // end synchronized(state.lock())
} }
/**
* Returns true if the key type is Provider (but not a subclass of Provider).
*/
private static boolean isProvider(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(Provider.class);
}
private static boolean isTypeLiteral(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(TypeLiteral.class);
}
private static <T> Key<T> getProvidedKey(Key<Provider<T>> key, Errors errors)
throws ErrorsException {
Type providerType = key.getTypeLiteral().getType();
// If the Provider has no type parameter (raw Provider)...
if (!(providerType instanceof ParameterizedType)) {
throw errors.cannotInjectRawProvider().toException();
}
Type entryType = ((ParameterizedType) providerType).getActualTypeArguments()[0];
@SuppressWarnings("unchecked") // safe because T came from Key<Provider<T>>
Key<T> providedKey = (Key<T>) key.ofType(entryType);
return providedKey;
}
/** Returns true if the key type is MembersInjector (but not a subclass of MembersInjector). */
private static boolean isMembersInjector(Key<?> key) {
return key.getTypeLiteral().getRawType().equals(MembersInjector.class)
&& key.getAnnotationType() == null;
}
private <T> BindingImpl<MembersInjector<T>> createMembersInjectorBinding( private <T> BindingImpl<MembersInjector<T>> createMembersInjectorBinding(
Key<MembersInjector<T>> key, Errors errors) throws ErrorsException { Key<MembersInjector<T>> key, Errors errors) throws ErrorsException {
Type membersInjectorType = key.getTypeLiteral().getType(); Type membersInjectorType = key.getTypeLiteral().getType();
@ -312,16 +330,15 @@ final class InjectorImpl implements Injector, Lookups {
} }
@SuppressWarnings("unchecked") // safe because T came from Key<MembersInjector<T>> @SuppressWarnings("unchecked") // safe because T came from Key<MembersInjector<T>>
TypeLiteral<T> instanceType = (TypeLiteral<T>) TypeLiteral.get( TypeLiteral<T> instanceType = (TypeLiteral<T>) TypeLiteral.get(
((ParameterizedType) membersInjectorType).getActualTypeArguments()[0]); ((ParameterizedType) membersInjectorType).getActualTypeArguments()[0]);
MembersInjector<T> membersInjector = membersInjectorStore.get(instanceType, errors); MembersInjector<T> membersInjector = membersInjectorStore.get(instanceType, errors);
InternalFactory<MembersInjector<T>> factory = new ConstantFactory<MembersInjector<T>>( InternalFactory<MembersInjector<T>> factory =
Initializables.of(membersInjector)); new ConstantFactory<>(Initializables.of(membersInjector));
return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
return new InstanceBindingImpl<MembersInjector<T>>(this, key, SourceProvider.UNKNOWN_SOURCE, factory, ImmutableSet.of(), membersInjector);
factory, ImmutableSet.<InjectionPoint>of(), membersInjector);
} }
/** /**
@ -350,7 +367,11 @@ final class InjectorImpl implements Injector, Lookups {
return null; return null;
} }
String stringValue = stringBinding.getProvider().get(); // We can't call getProvider().get() because this InstanceBinding may not have been inintialized
// yet (because we may have been called during InternalInjectorCreator.initializeStatically and
// instance binding validation hasn't happened yet.)
@SuppressWarnings("unchecked")
String stringValue = ((InstanceBinding<String>) stringBinding).getInstance();
Object source = stringBinding.getSource(); Object source = stringBinding.getSource();
// Find a matching type converter. // Find a matching type converter.
@ -365,7 +386,7 @@ final class InjectorImpl implements Injector, Lookups {
// Try to convert the string. A failed conversion results in an error. // Try to convert the string. A failed conversion results in an error.
try { try {
@SuppressWarnings("unchecked") // This cast is safe because we double check below. @SuppressWarnings("unchecked") // This cast is safe because we double check below.
T converted = (T) typeConverterBinding.getTypeConverter().convert(stringValue, type); T converted = (T) typeConverterBinding.getTypeConverter().convert(stringValue, type);
if (converted == null) { if (converted == null) {
throw errors.converterReturnedNull(stringValue, source, type, typeConverterBinding) throw errors.converterReturnedNull(stringValue, source, type, typeConverterBinding)
@ -379,8 +400,6 @@ final class InjectorImpl implements Injector, Lookups {
return new ConvertedConstantBindingImpl<T>(this, key, converted, stringBinding, return new ConvertedConstantBindingImpl<T>(this, key, converted, stringBinding,
typeConverterBinding); typeConverterBinding);
} catch (ErrorsException e) {
throw e;
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw errors.conversionError(stringValue, source, type, typeConverterBinding, e) throw errors.conversionError(stringValue, source, type, typeConverterBinding, e)
.toException(); .toException();
@ -411,7 +430,7 @@ final class InjectorImpl implements Injector, Lookups {
// so that cached exceptions while constructing it get stored. // so that cached exceptions while constructing it get stored.
// See TypeListenerTest#testTypeListenerThrows // See TypeListenerTest#testTypeListenerThrows
removeFailedJitBinding(binding, null); removeFailedJitBinding(binding, null);
cleanup(binding, new HashSet<Key>()); cleanup(binding, new HashSet<>());
} }
} }
} }
@ -423,18 +442,18 @@ final class InjectorImpl implements Injector, Lookups {
* optimistically added to allow circular dependency support, so dependencies may pass where they * optimistically added to allow circular dependency support, so dependencies may pass where they
* should have failed. * should have failed.
*/ */
private boolean cleanup(BindingImpl<?> binding, Set<Key> encountered) { private boolean cleanup(BindingImpl<?> binding, Set<Key<?>> encountered) {
boolean bindingFailed = false; boolean bindingFailed = false;
Set<Dependency<?>> deps = getInternalDependencies(binding); Set<Dependency<?>> deps = getInternalDependencies(binding);
for (Dependency dep : deps) { for (Dependency<?> dep : deps) {
Key<?> depKey = dep.getKey(); Key<?> depKey = dep.getKey();
InjectionPoint ip = dep.getInjectionPoint(); InjectionPoint ip = dep.getInjectionPoint();
if (encountered.add(depKey)) { // only check if we haven't looked at this key yet if (encountered.add(depKey)) { // only check if we haven't looked at this key yet
BindingImpl depBinding = jitBindings.get(depKey); BindingImpl<?> depBinding = jitBindings.get(depKey);
if (depBinding != null) { // if the binding still exists, validate if (depBinding != null) { // if the binding still exists, validate
boolean failed = cleanup(depBinding, encountered); // if children fail, we fail boolean failed = cleanup(depBinding, encountered); // if children fail, we fail
if (depBinding instanceof ConstructorBindingImpl) { if (depBinding instanceof ConstructorBindingImpl) {
ConstructorBindingImpl ctorBinding = (ConstructorBindingImpl) depBinding; ConstructorBindingImpl<?> ctorBinding = (ConstructorBindingImpl) depBinding;
ip = ctorBinding.getInternalConstructor(); ip = ctorBinding.getInternalConstructor();
if (!ctorBinding.isInitialized()) { if (!ctorBinding.isInitialized()) {
failed = true; failed = true;
@ -493,13 +512,13 @@ final class InjectorImpl implements Injector, Lookups {
// Don't try to inject arrays or enums annotated with @ImplementedBy. // Don't try to inject arrays or enums annotated with @ImplementedBy.
if (rawType.isArray() || (rawType.isEnum() && implementedBy != null)) { if (rawType.isArray() || (rawType.isEnum() && implementedBy != null)) {
throw errors.missingImplementation(key).toException(); throw errors.missingImplementationWithHint(key, this).toException();
} }
// Handle TypeLiteral<T> by binding the inner type // Handle TypeLiteral<T> by binding the inner type
if (rawType == TypeLiteral.class) { if (rawType == TypeLiteral.class) {
@SuppressWarnings("unchecked") // we have to fudge the inner type as Object @SuppressWarnings("unchecked") // we have to fudge the inner type as Object
BindingImpl<T> binding = (BindingImpl<T>) createTypeLiteralBinding( BindingImpl<T> binding = (BindingImpl<T>) createTypeLiteralBinding(
(Key<TypeLiteral<Object>>) key, errors); (Key<TypeLiteral<Object>>) key, errors);
return binding; return binding;
} }
@ -551,11 +570,10 @@ final class InjectorImpl implements Injector, Lookups {
} }
@SuppressWarnings("unchecked") // by definition, innerType == T, so this is safe @SuppressWarnings("unchecked") // by definition, innerType == T, so this is safe
TypeLiteral<T> value = (TypeLiteral<T>) TypeLiteral.get(innerType); TypeLiteral<T> value = (TypeLiteral<T>) TypeLiteral.get(innerType);
InternalFactory<TypeLiteral<T>> factory = new ConstantFactory<TypeLiteral<T>>( InternalFactory<TypeLiteral<T>> factory = new ConstantFactory<>(Initializables.of(value));
Initializables.of(value)); return new InstanceBindingImpl<>(this, key, SourceProvider.UNKNOWN_SOURCE,
return new InstanceBindingImpl<TypeLiteral<T>>(this, key, SourceProvider.UNKNOWN_SOURCE, factory, ImmutableSet.of(), value);
factory, ImmutableSet.<InjectionPoint>of(), value);
} }
/** /**
@ -564,7 +582,7 @@ final class InjectorImpl implements Injector, Lookups {
<T> BindingImpl<T> createProvidedByBinding(Key<T> key, Scoping scoping, <T> BindingImpl<T> createProvidedByBinding(Key<T> key, Scoping scoping,
ProvidedBy providedBy, Errors errors) throws ErrorsException { ProvidedBy providedBy, Errors errors) throws ErrorsException {
Class<?> rawType = key.getTypeLiteral().getRawType(); Class<?> rawType = key.getTypeLiteral().getRawType();
Class<? extends Provider<?>> providerType = providedBy.value(); Class<? extends javax.inject.Provider<?>> providerType = providedBy.value();
// Make sure it's not the same type. TODO: Can we check for deeper loops? // Make sure it's not the same type. TODO: Can we check for deeper loops?
if (providerType == rawType) { if (providerType == rawType) {
@ -576,12 +594,11 @@ final class InjectorImpl implements Injector, Lookups {
Key<? extends Provider<T>> providerKey = (Key<? extends Provider<T>>) Key.get(providerType); Key<? extends Provider<T>> providerKey = (Key<? extends Provider<T>>) Key.get(providerType);
ProvidedByInternalFactory<T> internalFactory = ProvidedByInternalFactory<T> internalFactory =
new ProvidedByInternalFactory<T>(rawType, providerType, providerKey); new ProvidedByInternalFactory<T>(rawType, providerType, providerKey);
Object source = rawType;
BindingImpl<T> binding = LinkedProviderBindingImpl.createWithInitializer( BindingImpl<T> binding = LinkedProviderBindingImpl.createWithInitializer(
this, this,
key, key,
source, rawType,
Scoping.<T>scope(key, this, internalFactory, source, scoping), Scoping.<T>scope(key, this, internalFactory, rawType, scoping),
scoping, scoping,
providerKey, providerKey,
internalFactory); internalFactory);
@ -613,27 +630,14 @@ final class InjectorImpl implements Injector, Lookups {
// Look up the target binding. // Look up the target binding.
final Key<? extends T> targetKey = Key.get(subclass); final Key<? extends T> targetKey = Key.get(subclass);
final BindingImpl<? extends T> targetBinding = getBindingOrThrow(targetKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
InternalFactory<T> internalFactory = new InternalFactory<T>() { FactoryProxy<T> factory = new FactoryProxy<>(this, key, targetKey, rawType);
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) factory.notify(errors); // causes the factory to initialize itself internally
throws ErrorsException {
context.pushState(targetKey, targetBinding.getSource());
try {
return targetBinding.getInternalFactory().get(
errors.withSource(targetKey), context, dependency, true);
} finally {
context.popState();
}
}
};
Object source = rawType;
return new LinkedBindingImpl<T>( return new LinkedBindingImpl<T>(
this, this,
key, key,
source, rawType,
Scoping.<T>scope(key, this, internalFactory, source, scoping), Scoping.<T>scope(key, this, factory, rawType, scoping),
scoping, scoping,
targetKey); targetKey);
} }
@ -725,9 +729,7 @@ final class InjectorImpl implements Injector, Lookups {
return convertedBinding; return convertedBinding;
} }
if (!isTypeLiteral(key) if (!isTypeLiteral(key) && jitDisabled && jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
&& jitDisabled
&& jitType != JitLimitation.NEW_OR_EXISTING_JIT) {
throw errors.jitDisabled(key).toException(); throw errors.jitDisabled(key).toException();
} }
@ -757,10 +759,12 @@ final class InjectorImpl implements Injector, Lookups {
return getBindingOrThrow(key, errors, jitType).getInternalFactory(); return getBindingOrThrow(key, errors, jitType).getInternalFactory();
} }
@Override
public Map<Key<?>, Binding<?>> getBindings() { public Map<Key<?>, Binding<?>> getBindings() {
return state.getExplicitBindingsThisLevel(); return state.getExplicitBindingsThisLevel();
} }
@Override
public Map<Key<?>, Binding<?>> getAllBindings() { public Map<Key<?>, Binding<?>> getAllBindings() {
synchronized (state.lock()) { synchronized (state.lock()) {
return new ImmutableMap.Builder<Key<?>, Binding<?>>() return new ImmutableMap.Builder<Key<?>, Binding<?>>()
@ -770,14 +774,47 @@ final class InjectorImpl implements Injector, Lookups {
} }
} }
@Override
public Map<Class<? extends Annotation>, Scope> getScopeBindings() { public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
return ImmutableMap.copyOf(state.getScopes()); return ImmutableMap.copyOf(state.getScopes());
} }
@Override
public Set<TypeConverterBinding> getTypeConverterBindings() { public Set<TypeConverterBinding> getTypeConverterBindings() {
return ImmutableSet.copyOf(state.getConvertersThisLevel()); return ImmutableSet.copyOf(state.getConvertersThisLevel());
} }
@Override
public List<Element> getElements() {
ImmutableList.Builder<Element> elements = ImmutableList.builder();
elements.addAll(getAllBindings().values());
elements.addAll(state.getProviderLookupsThisLevel());
elements.addAll(state.getConvertersThisLevel());
elements.addAll(state.getScopeBindingsThisLevel());
elements.addAll(state.getTypeListenerBindingsThisLevel());
elements.addAll(state.getProvisionListenerBindingsThisLevel());
elements.addAll(state.getScannerBindingsThisLevel());
elements.addAll(state.getStaticInjectionRequestsThisLevel());
elements.addAll(state.getMembersInjectorLookupsThisLevel());
elements.addAll(state.getInjectionRequestsThisLevel());
return elements.build();
}
@SuppressWarnings("unchecked")
@Override
public Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints() {
// Note, this is a safe cast per the ListMultimap javadocs.
// We could use Multimaps.asMap to avoid the cast, but unfortunately it's a @Beta method.
return (Map<TypeLiteral<?>, List<InjectionPoint>>)
(Map<TypeLiteral<?>, ?>)
ImmutableListMultimap.copyOf(
Multimaps.filterKeys(
membersInjectorStore.getAllInjectionPoints(),
userRequestedMembersInjectorTypes::contains))
.asMap();
}
/** /**
* Returns parameter injectors, or {@code null} if there are no parameters. * Returns parameter injectors, or {@code null} if there are no parameters.
*/ */
@ -808,13 +845,18 @@ final class InjectorImpl implements Injector, Lookups {
return new SingleParameterInjector<T>(dependency, binding); return new SingleParameterInjector<T>(dependency, binding);
} }
@SuppressWarnings("unchecked") // the members injector type is consistent with instance's type @SuppressWarnings({"unchecked","rawtypes"}) // the members injector type is consistent with instance's type
@Override
public void injectMembers(Object instance) { public void injectMembers(Object instance) {
MembersInjector membersInjector = getMembersInjector(instance.getClass()); MembersInjector membersInjector = getMembersInjector(instance.getClass());
membersInjector.injectMembers(instance); membersInjector.injectMembers(instance);
} }
@Override
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) { public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
checkNotNull(typeLiteral, "typeLiteral");
userRequestedMembersInjectorTypes.add(typeLiteral);
Errors errors = new Errors(typeLiteral); Errors errors = new Errors(typeLiteral);
try { try {
return membersInjectorStore.get(typeLiteral, errors); return membersInjectorStore.get(typeLiteral, errors);
@ -823,47 +865,45 @@ final class InjectorImpl implements Injector, Lookups {
} }
} }
@Override
public <T> MembersInjector<T> getMembersInjector(Class<T> type) { public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
return getMembersInjector(TypeLiteral.get(type)); return getMembersInjector(TypeLiteral.get(type));
} }
@Override
public <T> Provider<T> getProvider(Class<T> type) { public <T> Provider<T> getProvider(Class<T> type) {
return getProvider(Key.get(type)); return getProvider(Key.get(checkNotNull(type, "type")));
} }
<T> Provider<T> getProviderOrThrow(final Dependency<T> dependency, Errors errors) throws ErrorsException { <T> Provider<T> getProviderOrThrow(final Dependency<T> dependency, Errors errors) throws ErrorsException {
final Key<T> key = dependency.getKey(); final Key<T> key = dependency.getKey();
final BindingImpl<? extends T> binding = getBindingOrThrow(key, errors, JitLimitation.NO_JIT); final BindingImpl<? extends T> binding = getBindingOrThrow(key, errors, JitLimitation.NO_JIT);
final InternalFactory<? extends T> internalFactory = binding.getInternalFactory();
return new Provider<T>() { final Object source = binding.getSource();
return new Provider<>() {
public T get() { public T get() {
final Errors errors = new Errors(dependency); InternalContext currentContext = enterContext();
Dependency<?> previous = currentContext.pushDependency(dependency, source);
try { try {
T t = callInContext(new ContextualCallable<T>() { return internalFactory.get(currentContext, dependency, false);
public T call(InternalContext context) throws ErrorsException { } catch (InternalProvisionException e) {
Dependency previous = context.pushDependency(dependency, binding.getSource()); throw e.addSource(dependency).toProvisionException();
try { } finally {
return binding.getInternalFactory().get(errors, context, dependency, false); currentContext.popStateAndSetDependency(previous);
} finally { currentContext.close();
context.popStateAndSetDependency(previous);
}
}
});
errors.throwIfNewErrors(0);
return t;
} catch (ErrorsException e) {
throw new ProvisionException(errors.merge(e.getErrors()).getMessages());
} }
} }
@Override @Override
public String toString() { public String toString() {
return binding.getInternalFactory().toString(); return internalFactory.toString();
} }
}; };
} }
@Override
public <T> Provider<T> getProvider(final Key<T> key) { public <T> Provider<T> getProvider(final Key<T> key) {
checkNotNull(key, "key");
Errors errors = new Errors(key); Errors errors = new Errors(key);
try { try {
Provider<T> result = getProviderOrThrow(Dependency.get(key), errors); Provider<T> result = getProviderOrThrow(Dependency.get(key), errors);
@ -874,50 +914,16 @@ final class InjectorImpl implements Injector, Lookups {
} }
} }
@Override
public <T> T getInstance(Key<T> key) { public <T> T getInstance(Key<T> key) {
return getProvider(key).get(); return getProvider(key).get();
} }
@Override
public <T> T getInstance(Class<T> type) { public <T> T getInstance(Class<T> type) {
return getProvider(type).get(); return getProvider(type).get();
} }
/**
* Looks up thread local context. Creates (and removes) a new context if necessary.
*/
<T> T callInContext(ContextualCallable<T> callable) throws ErrorsException {
Object[] reference = localContext.get();
if (reference == null) {
reference = new Object[1];
localContext.set(reference);
}
Thread currentThread = Thread.currentThread();
if (reference[0] == null) {
reference[0] = new InternalContext(options);
globalInternalContext.put(currentThread, (InternalContext) reference[0]);
try {
return callable.call((InternalContext) reference[0]);
} finally {
// Only clear contexts if this call created them.
reference[0] = null;
globalInternalContext.remove(currentThread);
}
} else {
Object previousGlobalInternalContext = globalInternalContext.get(currentThread);
globalInternalContext.put(currentThread, (InternalContext) reference[0]);
try {
// Someone else will clean up this local context.
return callable.call((InternalContext) reference[0]);
} finally {
if (previousGlobalInternalContext != null) {
globalInternalContext.put(currentThread, (InternalContext) previousGlobalInternalContext);
} else {
globalInternalContext.remove(currentThread);
}
}
}
}
@Override @Override
public String toString() { public String toString() {
return MoreObjects.toStringHelper(Injector.class) return MoreObjects.toStringHelper(Injector.class)
@ -994,21 +1000,20 @@ final class InjectorImpl implements Injector, Lookups {
static <T> InternalFactory<Provider<T>> createInternalFactory(Binding<T> providedBinding) { static <T> InternalFactory<Provider<T>> createInternalFactory(Binding<T> providedBinding) {
final Provider<T> provider = providedBinding.getProvider(); final Provider<T> provider = providedBinding.getProvider();
return new InternalFactory<Provider<T>>() { return (context, dependency, linked) -> provider;
public Provider<T> get(Errors errors, InternalContext context, Dependency dependency, boolean linked) {
return provider;
}
};
} }
@Override
public Key<? extends T> getProvidedKey() { public Key<? extends T> getProvidedKey() {
return providedBinding.getKey(); return providedBinding.getKey();
} }
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Provider<T>, V> visitor) { public <V> V acceptTargetVisitor(BindingTargetVisitor<? super Provider<T>, V> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public void applyTo(Binder binder) { public void applyTo(Binder binder) {
throw new UnsupportedOperationException("This element represents a synthetic binding."); throw new UnsupportedOperationException("This element represents a synthetic binding.");
} }
@ -1021,6 +1026,7 @@ final class InjectorImpl implements Injector, Lookups {
.toString(); .toString();
} }
@Override
public Set<Dependency<?>> getDependencies() { public Set<Dependency<?>> getDependencies() {
return ImmutableSet.<Dependency<?>>of(Dependency.get(getProvidedKey())); return ImmutableSet.<Dependency<?>>of(Dependency.get(getProvidedKey()));
} }
@ -1050,9 +1056,11 @@ final class InjectorImpl implements Injector, Lookups {
final Binding<String> originalBinding; final Binding<String> originalBinding;
final TypeConverterBinding typeConverterBinding; final TypeConverterBinding typeConverterBinding;
ConvertedConstantBindingImpl( ConvertedConstantBindingImpl(InjectorImpl injector,
InjectorImpl injector, Key<T> key, T value, Binding<String> originalBinding, Key<T> key,
TypeConverterBinding typeConverterBinding) { T value,
Binding<String> originalBinding,
TypeConverterBinding typeConverterBinding) {
super(injector, key, originalBinding.getSource(), super(injector, key, originalBinding.getSource(),
new ConstantFactory<T>(Initializables.of(value)), Scoping.UNSCOPED); new ConstantFactory<T>(Initializables.of(value)), Scoping.UNSCOPED);
this.value = value; this.value = value;
@ -1066,26 +1074,32 @@ final class InjectorImpl implements Injector, Lookups {
return provider; return provider;
} }
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public T getValue() { public T getValue() {
return value; return value;
} }
@Override
public TypeConverterBinding getTypeConverterBinding() { public TypeConverterBinding getTypeConverterBinding() {
return typeConverterBinding; return typeConverterBinding;
} }
@Override
public Key<String> getSourceKey() { public Key<String> getSourceKey() {
return originalBinding.getKey(); return originalBinding.getKey();
} }
@Override
public Set<Dependency<?>> getDependencies() { public Set<Dependency<?>> getDependencies() {
return ImmutableSet.<Dependency<?>>of(Dependency.get(getSourceKey())); return ImmutableSet.<Dependency<?>>of(Dependency.get(getSourceKey()));
} }
@Override
public void applyTo(Binder binder) { public void applyTo(Binder binder) {
throw new UnsupportedOperationException("This element represents a synthetic binding."); throw new UnsupportedOperationException("This element represents a synthetic binding.");
} }
@ -1116,27 +1130,4 @@ final class InjectorImpl implements Injector, Lookups {
return Objects.hashCode(getKey(), getScoping(), value); return Objects.hashCode(getKey(), getScoping(), value);
} }
} }
private static class BindingsMultimap {
final Map<TypeLiteral<?>, List<Binding<?>>> multimap = Maps.newHashMap();
<T> void put(TypeLiteral<T> type, Binding<T> binding) {
List<Binding<?>> bindingsForType = multimap.get(type);
if (bindingsForType == null) {
bindingsForType = Lists.newArrayList();
multimap.put(type, bindingsForType);
}
bindingsForType.add(binding);
}
@SuppressWarnings("unchecked")
// safe because we only put matching entries into the map
<T> List<Binding<T>> getAll(TypeLiteral<T> type) {
List<Binding<?>> bindings = multimap.get(type);
return bindings != null
? Collections.<Binding<T>>unmodifiableList((List) multimap.get(type))
: ImmutableList.<Binding<T>>of();
}
}
} }

View file

@ -15,7 +15,6 @@ import com.google.inject.internal.util.Stopwatch;
import com.google.inject.spi.Dependency; import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element; import com.google.inject.spi.Element;
import com.google.inject.spi.Elements; import com.google.inject.spi.Elements;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding; import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
import com.google.inject.spi.PrivateElements; import com.google.inject.spi.PrivateElements;
import com.google.inject.spi.ProvisionListenerBinding; import com.google.inject.spi.ProvisionListenerBinding;
@ -35,11 +34,20 @@ final class InjectorShell {
private final List<Element> elements; private final List<Element> elements;
private final InjectorImpl injector; private final InjectorImpl injector;
private InjectorShell(Builder builder, List<Element> elements, InjectorImpl injector) { private InjectorShell(List<Element> elements, InjectorImpl injector) {
this.elements = elements; this.elements = elements;
this.injector = injector; this.injector = injector;
} }
InjectorImpl getInjector() {
return injector;
}
List<Element> getElements() {
return elements;
}
/** /**
* The Injector is a special case because we allow both parent and child injectors to both have * The Injector is a special case because we allow both parent and child injectors to both have
* a binding for that key. * a binding for that key.
@ -48,44 +56,23 @@ final class InjectorShell {
Key<Injector> key = Key.get(Injector.class); Key<Injector> key = Key.get(Injector.class);
InjectorFactory injectorFactory = new InjectorFactory(injector); InjectorFactory injectorFactory = new InjectorFactory(injector);
injector.state.putBinding(key, injector.state.putBinding(key,
new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE, new ProviderInstanceBindingImpl<>(injector, key, SourceProvider.UNKNOWN_SOURCE,
injectorFactory, Scoping.UNSCOPED, injectorFactory, injectorFactory, Scoping.UNSCOPED, injectorFactory,
ImmutableSet.<InjectionPoint>of())); ImmutableSet.of()));
} }
/**
* The Logger is a special case because it knows the injection point of the injected member. It's
* the only binding that does this.
*/
/*private static void bindLogger(InjectorImpl injector) {
Key<Logger> key = Key.get(Logger.class);
LoggerFactory loggerFactory = new LoggerFactory();
injector.state.putBinding(key,
new ProviderInstanceBindingImpl<Logger>(injector, key,
SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scoping.UNSCOPED,
loggerFactory, ImmutableSet.<InjectionPoint>of()));
}*/
private static void bindStage(InjectorImpl injector, Stage stage) { private static void bindStage(InjectorImpl injector, Stage stage) {
Key<Stage> key = Key.get(Stage.class); Key<Stage> key = Key.get(Stage.class);
InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<Stage>( InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<>(
injector, injector,
key, key,
SourceProvider.UNKNOWN_SOURCE, SourceProvider.UNKNOWN_SOURCE,
new ConstantFactory<Stage>(Initializables.of(stage)), new ConstantFactory<>(Initializables.of(stage)),
ImmutableSet.<InjectionPoint>of(), ImmutableSet.of(),
stage); stage);
injector.state.putBinding(key, stageBinding); injector.state.putBinding(key, stageBinding);
} }
InjectorImpl getInjector() {
return injector;
}
List<Element> getElements() {
return elements;
}
static class Builder { static class Builder {
private final List<Element> elements = Lists.newArrayList(); private final List<Element> elements = Lists.newArrayList();
private final List<Module> modules = Lists.newArrayList(); private final List<Module> modules = Lists.newArrayList();
@ -213,7 +200,7 @@ final class InjectorShell {
stopwatch.resetAndLog("Module annotated method scanners creation"); stopwatch.resetAndLog("Module annotated method scanners creation");
List<InjectorShell> injectorShells = Lists.newArrayList(); List<InjectorShell> injectorShells = Lists.newArrayList();
injectorShells.add(new InjectorShell(this, elements, injector)); injectorShells.add(new InjectorShell(elements, injector));
// recursively build child shells // recursively build child shells
PrivateElementProcessor processor = new PrivateElementProcessor(errors); PrivateElementProcessor processor = new PrivateElementProcessor(errors);
@ -241,11 +228,12 @@ final class InjectorShell {
this.injector = injector; this.injector = injector;
} }
public Injector get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) @Override
throws ErrorsException { public Injector get(InternalContext context, Dependency<?> dependency, boolean linked) {
return injector; return injector;
} }
@Override
public Injector get() { public Injector get() {
return injector; return injector;
} }
@ -255,24 +243,8 @@ final class InjectorShell {
} }
} }
/*private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
public Logger get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) {
InjectionPoint injectionPoint = dependency.getInjectionPoint();
return injectionPoint == null
? Logger.getAnonymousLogger()
: Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
}
public Logger get() {
return Logger.getAnonymousLogger();
}
public String toString() {
return "Provider<Logger>";
}
}*/
private static class RootModule implements Module { private static class RootModule implements Module {
@Override
public void configure(Binder binder) { public void configure(Binder binder) {
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE); binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
binder.bindScope(Singleton.class, SINGLETON); binder.bindScope(Singleton.class, SINGLETON);
@ -287,6 +259,7 @@ final class InjectorShell {
this.state = state; this.state = state;
} }
@Override
public void configure(Binder binder) { public void configure(Binder binder) {
for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) { for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) {
binding.applyTo(binder); binding.applyTo(binder);

View file

@ -5,20 +5,17 @@ import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder; import com.google.inject.Binder;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.spi.BindingTargetVisitor; import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency; import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies; import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.InjectionPoint; import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.InstanceBinding; import com.google.inject.spi.InstanceBinding;
import com.google.inject.util.Providers;
import java.util.Set; import java.util.Set;
final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> { final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBinding<T> {
final T instance; final T instance;
final Provider<T> provider;
final ImmutableSet<InjectionPoint> injectionPoints; final ImmutableSet<InjectionPoint> injectionPoints;
public InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source, public InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
@ -27,7 +24,6 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
super(injector, key, source, internalFactory, Scoping.EAGER_SINGLETON); super(injector, key, source, internalFactory, Scoping.EAGER_SINGLETON);
this.injectionPoints = ImmutableSet.copyOf(injectionPoints); this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
this.instance = instance; this.instance = instance;
this.provider = Providers.of(instance);
} }
public InstanceBindingImpl(Object source, Key<T> key, Scoping scoping, public InstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
@ -35,40 +31,41 @@ final class InstanceBindingImpl<T> extends BindingImpl<T> implements InstanceBin
super(source, key, scoping); super(source, key, scoping);
this.injectionPoints = ImmutableSet.copyOf(injectionPoints); this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
this.instance = instance; this.instance = instance;
this.provider = Providers.of(instance);
} }
@Override @Override
public Provider<T> getProvider() {
return this.provider;
}
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public T getInstance() { public T getInstance() {
return instance; return instance;
} }
@Override
public Set<InjectionPoint> getInjectionPoints() { public Set<InjectionPoint> getInjectionPoints() {
return injectionPoints; return injectionPoints;
} }
@Override
public Set<Dependency<?>> getDependencies() { public Set<Dependency<?>> getDependencies() {
return instance instanceof HasDependencies return instance instanceof HasDependencies
? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies()) ? ImmutableSet.copyOf(((HasDependencies) instance).getDependencies())
: Dependency.forInjectionPoints(injectionPoints); : Dependency.forInjectionPoints(injectionPoints);
} }
@Override
public BindingImpl<T> withScoping(Scoping scoping) { public BindingImpl<T> withScoping(Scoping scoping) {
return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance); return new InstanceBindingImpl<T>(getSource(), getKey(), scoping, injectionPoints, instance);
} }
@Override
public BindingImpl<T> withKey(Key<T> key) { public BindingImpl<T> withKey(Key<T> key) {
return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance); return new InstanceBindingImpl<T>(getSource(), key, getScoping(), injectionPoints, instance);
} }
@Override
public void applyTo(Binder binder) { public void applyTo(Binder binder) {
// instance bindings aren't scoped // instance bindings aren't scoped
binder.withSource(getSource()).bind(getKey()).toInstance(instance); binder.withSource(getSource()).bind(getKey()).toInstance(instance);

View file

@ -1,37 +1,76 @@
package com.google.inject.internal; package com.google.inject.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.internal.InjectorImpl.InjectorOptions; import com.google.inject.internal.InjectorImpl.InjectorOptions;
import com.google.inject.spi.Dependency; import com.google.inject.spi.Dependency;
import com.google.inject.spi.DependencyAndSource;
import java.util.Arrays; import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Internal context. Used to coordinate injections and support circular * Internal context. Used to coordinate injections and support circular
* dependencies. * dependencies.
*/ */
final class InternalContext { final class InternalContext implements AutoCloseable {
private final InjectorOptions options; private final InjectorOptions options;
/**
* Keeps track of the hierarchy of types needed during injection. private final Map<Object, ConstructionContext<?>> constructionContexts =
*/ new IdentityHashMap<>();
private final DependencyStack state = new DependencyStack();
private Map<Object, ConstructionContext<?>> constructionContexts = Maps.newHashMap();
/** /**
* Keeps track of the type that is currently being requested for injection. * Keeps track of the type that is currently being requested for injection.
*/ */
private Dependency<?> dependency; private Dependency<?> dependency;
InternalContext(InjectorOptions options) { /**
* Keeps track of the hierarchy of types needed during injection.
*
* <p>This is a pairwise combination of dependencies and sources, with dependencies or keys on
* even indices, and sources on odd indices. This structure is to avoid the memory overhead of
* DependencyAndSource objects, which can add to several tens of megabytes in large applications.
*/
private Object[] dependencyStack = new Object[16];
private int dependencyStackSize = 0;
/**
* The number of times {@link #enter()} has been called + 1 for initial construction.
*/
private int enterCount;
/**
* A single element array to clear when the {@link #enterCount} hits {@code 0}.
*
* <p>This is the value stored in the {@code InjectorImpl.localContext} thread local.
*/
private final Object[] toClear;
InternalContext(InjectorOptions options, Object[] toClear) {
this.options = options; this.options = options;
this.toClear = toClear;
this.enterCount = 1;
} }
/** Should only be called by InjectorImpl.enterContext(). */
void enter() {
enterCount++;
}
/** Should be called any any method that received an instance via InjectorImpl.enterContext(). */
@Override
public void close() {
int newCount = --enterCount;
if (newCount < 0) {
throw new IllegalStateException("Called close() too many times");
}
if (newCount == 0) {
toClear[0] = null;
}
}
public InjectorOptions getInjectorOptions() { public InjectorOptions getInjectorOptions() {
return options; return options;
} }
@ -57,7 +96,7 @@ final class InternalContext {
public Dependency<?> pushDependency(Dependency<?> dependency, Object source) { public Dependency<?> pushDependency(Dependency<?> dependency, Object source) {
Dependency<?> previous = this.dependency; Dependency<?> previous = this.dependency;
this.dependency = dependency; this.dependency = dependency;
state.add(dependency, source); doPushState(dependency, source);
return previous; return previous;
} }
@ -65,7 +104,7 @@ final class InternalContext {
* Pops the current state & sets the new dependency. * Pops the current state & sets the new dependency.
*/ */
public void popStateAndSetDependency(Dependency<?> newDependency) { public void popStateAndSetDependency(Dependency<?> newDependency) {
state.pop(); popState();
this.dependency = newDependency; this.dependency = newDependency;
} }
@ -73,64 +112,29 @@ final class InternalContext {
* Adds to the state without setting the dependency. * Adds to the state without setting the dependency.
*/ */
public void pushState(Key<?> key, Object source) { public void pushState(Key<?> key, Object source) {
state.add(key, source); doPushState(key, source);
}
private void doPushState(Object dependencyOrKey, Object source) {
int localSize = dependencyStackSize;
Object[] localStack = dependencyStack;
if (localStack.length < localSize + 2) {
localStack = dependencyStack =
java.util.Arrays.copyOf(localStack, (localStack.length * 3) / 2 + 2);
}
localStack[localSize++] = dependencyOrKey;
localStack[localSize++] = source;
dependencyStackSize = localSize;
} }
/** /**
* Pops from the state without setting a dependency. * Pops from the state without setting a dependency.
*/ */
public void popState() { public void popState() {
state.pop(); // N.B. we don't null out the array entries. It isn't necessary since all the objects in the
} // array (Key, Dependency, or Binding source objects) are all tied to the lifetime of the
// injector, which is greater than the lifetime of this object. So removing them from the array
/** // doesn't matter.
* Returns the current dependency chain (all the state). dependencyStackSize -= 2;
*/
public List<DependencyAndSource> getDependencyChain() {
ImmutableList.Builder<DependencyAndSource> builder = ImmutableList.builder();
for (int i = 0; i < state.size(); i += 2) {
Object evenEntry = state.get(i);
Dependency<?> dependency;
if (evenEntry instanceof Key) {
dependency = Dependency.get((Key<?>) evenEntry);
} else {
dependency = (Dependency<?>) evenEntry;
}
builder.add(new DependencyAndSource(dependency, state.get(i + 1)));
}
return builder.build();
}
/**
* Keeps track of the hierarchy of types needed during injection.
*
* <p>This is a pairwise combination of dependencies and sources, with dependencies or keys on
* even indices, and sources on odd indices. This structure is to avoid the memory overhead of
* DependencyAndSource objects, which can add to several tens of megabytes in large applications.
*/
private static final class DependencyStack {
private Object[] elements = new Object[16];
private int size = 0;
public void add(Object dependencyOrKey, Object source) {
if (elements.length < size + 2) {
elements = Arrays.copyOf(elements, (elements.length * 3) / 2 + 2);
}
elements[size++] = dependencyOrKey;
elements[size++] = source;
}
public void pop() {
elements[--size] = null;
elements[--size] = null;
}
public Object get(int i) {
return elements[i];
}
public int size() {
return size;
}
} }
} }

View file

@ -12,9 +12,9 @@ interface InternalFactory<T> {
* *
* @param context of this injection * @param context of this injection
* @param linked true if getting as a result of a linked binding * @param linked true if getting as a result of a linked binding
* @return instance to be injected * @return instance that was created
* @throws com.google.inject.internal.ErrorsException if a value cannot be provided * @throws InternalProvisionException if a value cannot be provided
*/ */
T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) T get(InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException; throws InternalProvisionException;
} }

View file

@ -14,7 +14,7 @@ final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFac
private final ProvisionListenerStackCallback<T> provisionCallback; private final ProvisionListenerStackCallback<T> provisionCallback;
private final Initializable<? extends javax.inject.Provider<? extends T>> initializable; private final Initializable<? extends javax.inject.Provider<? extends T>> initializable;
public InternalFactoryToInitializableAdapter( InternalFactoryToInitializableAdapter(
Initializable<? extends javax.inject.Provider<? extends T>> initializable, Initializable<? extends javax.inject.Provider<? extends T>> initializable,
Object source, ProvisionListenerStackCallback<T> provisionCallback) { Object source, ProvisionListenerStackCallback<T> provisionCallback) {
super(source); super(source);
@ -22,19 +22,20 @@ final class InternalFactoryToInitializableAdapter<T> extends ProviderInternalFac
this.initializable = checkNotNull(initializable, "provider"); this.initializable = checkNotNull(initializable, "provider");
} }
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) @Override
throws ErrorsException { public T get(InternalContext context, Dependency<?> dependency, boolean linked)
return circularGet(initializable.get(errors), errors, context, dependency, throws InternalProvisionException {
provisionCallback); return circularGet(initializable.get(), context, dependency, provisionCallback);
} }
@Override @Override
protected T provision(javax.inject.Provider<? extends T> provider, Errors errors, protected T provision(javax.inject.Provider<? extends T> provider,
Dependency<?> dependency, ConstructionContext<T> constructionContext) throws ErrorsException { Dependency<?> dependency,
ConstructionContext<T> constructionContext) throws InternalProvisionException {
try { try {
return super.provision(provider, errors, dependency, constructionContext); return super.provision(provider, dependency, constructionContext);
} catch (RuntimeException userException) { } catch (RuntimeException userException) {
throw errors.withSource(source).errorInProvider(userException).toException(); throw InternalProvisionException.errorInProvider(userException).addSource(source);
} }
} }

View file

@ -10,18 +10,22 @@ final class InternalFactoryToProviderAdapter<T> implements InternalFactory<T> {
private final Provider<? extends T> provider; private final Provider<? extends T> provider;
private final Object source; private final Object source;
public InternalFactoryToProviderAdapter(Provider<? extends T> provider, Object source) { InternalFactoryToProviderAdapter(Provider<? extends T> provider, Object source) {
this.provider = checkNotNull(provider, "provider"); this.provider = checkNotNull(provider, "provider");
this.source = checkNotNull(source, "source"); this.source = checkNotNull(source, "source");
} }
public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) @Override
throws ErrorsException { public T get(InternalContext context, Dependency<?> dependency, boolean linked)
// TODO(sameb): Does this need to push state into the context? throws InternalProvisionException {
try { try {
return errors.checkForNull(provider.get(), source, dependency); T t = provider.get();
if (t == null && !dependency.isNullable()) {
InternalProvisionException.onNullInjectedIntoNonNullableDependency(source, dependency);
}
return t;
} catch (RuntimeException userException) { } catch (RuntimeException userException) {
throw errors.withSource(source).errorInProvider(userException).toException(); throw InternalProvisionException.errorInProvider(userException).addSource(source);
} }
} }

View file

@ -66,19 +66,13 @@ public class InternalFlags {
private static <T extends Enum<T>> T getSystemOption(final String name, T defaultValue, private static <T extends Enum<T>> T getSystemOption(final String name, T defaultValue,
T secureValue) { T secureValue) {
Class<T> enumType = defaultValue.getDeclaringClass(); Class<T> enumType = defaultValue.getDeclaringClass();
String value = null;
try { try {
value = AccessController.doPrivileged(new PrivilegedAction<String>() { String value = AccessController.doPrivileged((PrivilegedAction<String>) ()
public String run() { -> System.getProperty(name));
return System.getProperty(name);
}
});
return (value != null && value.length() > 0) ? Enum.valueOf(enumType, value) : defaultValue; return (value != null && value.length() > 0) ? Enum.valueOf(enumType, value) : defaultValue;
} catch (SecurityException e) { } catch (SecurityException e) {
return secureValue; return secureValue;
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
//logger.warning(value + " is not a valid flag value for " + name + ". "
// + " Values must be one of " + Arrays.asList(enumType.getEnumConstants()));
return defaultValue; return defaultValue;
} }
} }

View file

@ -1,7 +1,5 @@
package com.google.inject.internal; package com.google.inject.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.inject.Binding; import com.google.inject.Binding;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
@ -13,9 +11,12 @@ import com.google.inject.Stage;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Stopwatch; import com.google.inject.internal.util.Stopwatch;
import com.google.inject.spi.Dependency; import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.TypeConverterBinding; import com.google.inject.spi.TypeConverterBinding;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -64,7 +65,7 @@ public final class InternalInjectorCreator {
* Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's * Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's
* stage to the stage of {@code parent}. * stage to the stage of {@code parent}.
*/ */
public InternalInjectorCreator parentInjector(InjectorImpl parent) { InternalInjectorCreator parentInjector(InjectorImpl parent) {
shellBuilder.parent(parent); shellBuilder.parent(parent);
return this; return this;
} }
@ -102,36 +103,32 @@ public final class InternalInjectorCreator {
private void initializeStatically() { private void initializeStatically() {
bindingData.initializeBindings(); bindingData.initializeBindings();
stopwatch.resetAndLog("Binding initialization"); stopwatch.resetAndLog("Binding initialization");
for (InjectorShell shell : shells) { for (InjectorShell shell : shells) {
shell.getInjector().index(); shell.getInjector().index();
} }
stopwatch.resetAndLog("Binding indexing"); stopwatch.resetAndLog("Binding indexing");
injectionRequestProcessor.process(shells); injectionRequestProcessor.process(shells);
stopwatch.resetAndLog("Collecting injection requests"); stopwatch.resetAndLog("Collecting injection requests");
bindingData.runCreationListeners(errors); bindingData.runCreationListeners(errors);
stopwatch.resetAndLog("Binding validation"); stopwatch.resetAndLog("Binding validation");
injectionRequestProcessor.validate(); injectionRequestProcessor.validate();
stopwatch.resetAndLog("Static validation"); stopwatch.resetAndLog("Static validation");
initializer.validateOustandingInjections(errors); initializer.validateOustandingInjections(errors);
stopwatch.resetAndLog("Instance member validation"); stopwatch.resetAndLog("Instance member validation");
new LookupProcessor(errors).process(shells); new LookupProcessor(errors).process(shells);
for (InjectorShell shell : shells) { for (InjectorShell shell : shells) {
((DeferredLookups) shell.getInjector().lookups).initialize(errors); ((DeferredLookups) shell.getInjector().lookups).initialize(errors);
} }
stopwatch.resetAndLog("Provider verification"); stopwatch.resetAndLog("Provider verification");
// This needs to come late since some user bindings rely on requireBinding calls to create
// jit bindings during the LookupProcessor.
bindingData.initializeDelayedBindings();
stopwatch.resetAndLog("Delayed Binding initialization");
for (InjectorShell shell : shells) { for (InjectorShell shell : shells) {
if (!shell.getElements().isEmpty()) { if (!shell.getElements().isEmpty()) {
throw new AssertionError("Failed to execute " + shell.getElements()); throw new AssertionError("Failed to execute " + shell.getElements());
} }
} }
errors.throwCreationExceptionIfErrorsExist(); errors.throwCreationExceptionIfErrorsExist();
} }
@ -170,31 +167,25 @@ public final class InternalInjectorCreator {
*/ */
void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) { void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) {
@SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe @SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe
Iterable<BindingImpl<?>> candidateBindings = ImmutableList.copyOf(Iterables.concat( Collection<BindingImpl<?>> bindingsAtThisLevel =
(Collection) injector.state.getExplicitBindingsThisLevel().values(), (Collection) injector.state.getExplicitBindingsThisLevel().values();
injector.jitBindings.values())); List<BindingImpl<?>> candidateBindings = new ArrayList<>(bindingsAtThisLevel);
for (final BindingImpl<?> binding : candidateBindings) { synchronized (injector.state.lock()) {
if (isEagerSingleton(injector, binding, stage)) { // jit bindings must be accessed while holding the lock.
try { candidateBindings.addAll(injector.jitBindings.values());
injector.callInContext(new ContextualCallable<Void>() { }
Dependency<?> dependency = Dependency.get(binding.getKey()); try (InternalContext context = injector.enterContext()) {
for (BindingImpl<?> binding : candidateBindings) {
public Void call(InternalContext context) { if (isEagerSingleton(injector, binding, stage)) {
Dependency previous = context.pushDependency(dependency, binding.getSource()); Dependency<?> dependency = Dependency.get(binding.getKey());
Errors errorsForBinding = errors.withSource(dependency); Dependency<?> previous = context.pushDependency(dependency, binding.getSource());
try { try {
binding.getInternalFactory().get(errorsForBinding, context, dependency, false); binding.getInternalFactory().get(context, dependency, false);
} catch (ErrorsException e) { } catch (InternalProvisionException e) {
errorsForBinding.merge(e.getErrors()); errors.withSource(dependency).merge(e);
} finally { } finally {
context.popStateAndSetDependency(previous); context.popStateAndSetDependency(previous);
} }
return null;
}
});
} catch (ErrorsException e) {
throw new AssertionError();
} }
} }
} }
@ -207,10 +198,11 @@ public final class InternalInjectorCreator {
// handle a corner case where a child injector links to a binding in a parent injector, and // handle a corner case where a child injector links to a binding in a parent injector, and
// that binding is singleton. We won't catch this otherwise because we only iterate the child's // that binding is singleton. We won't catch this otherwise because we only iterate the child's
// bindings. // bindings. This only applies if the linked binding is not itself scoped.
if (binding instanceof LinkedBindingImpl) { if (binding instanceof LinkedBindingImpl) {
Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey(); Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey();
return isEagerSingleton(injector, injector.getBinding(linkedBinding), stage); return binding.getScoping().isNoScope() &&
isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
} }
return false; return false;
@ -226,80 +218,108 @@ public final class InternalInjectorCreator {
this.delegateInjector = delegateInjector; this.delegateInjector = delegateInjector;
} }
@Override
public void injectMembers(Object o) { public void injectMembers(Object o) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Injector.injectMembers(Object) is not supported in Stage.TOOL"); "Injector.injectMembers(Object) is not supported in Stage.TOOL");
} }
@Override
public Map<Key<?>, Binding<?>> getBindings() { public Map<Key<?>, Binding<?>> getBindings() {
return this.delegateInjector.getBindings(); return this.delegateInjector.getBindings();
} }
@Override
public Map<Key<?>, Binding<?>> getAllBindings() { public Map<Key<?>, Binding<?>> getAllBindings() {
return this.delegateInjector.getAllBindings(); return this.delegateInjector.getAllBindings();
} }
@Override
public <T> Binding<T> getBinding(Key<T> key) { public <T> Binding<T> getBinding(Key<T> key) {
return this.delegateInjector.getBinding(key); return this.delegateInjector.getBinding(key);
} }
@Override
public <T> Binding<T> getBinding(Class<T> type) { public <T> Binding<T> getBinding(Class<T> type) {
return this.delegateInjector.getBinding(type); return this.delegateInjector.getBinding(type);
} }
@Override
public <T> Binding<T> getExistingBinding(Key<T> key) { public <T> Binding<T> getExistingBinding(Key<T> key) {
return this.delegateInjector.getExistingBinding(key); return this.delegateInjector.getExistingBinding(key);
} }
@Override
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) { public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
return this.delegateInjector.findBindingsByType(type); return this.delegateInjector.findBindingsByType(type);
} }
@Override
public Injector getParent() { public Injector getParent() {
return delegateInjector.getParent(); return delegateInjector.getParent();
} }
@Override
public Injector createChildInjector(Iterable<? extends Module> modules) { public Injector createChildInjector(Iterable<? extends Module> modules) {
return delegateInjector.createChildInjector(modules); return delegateInjector.createChildInjector(modules);
} }
@Override
public Injector createChildInjector(Module... modules) { public Injector createChildInjector(Module... modules) {
return delegateInjector.createChildInjector(modules); return delegateInjector.createChildInjector(modules);
} }
@Override
public Map<Class<? extends Annotation>, Scope> getScopeBindings() { public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
return delegateInjector.getScopeBindings(); return delegateInjector.getScopeBindings();
} }
@Override
public Set<TypeConverterBinding> getTypeConverterBindings() { public Set<TypeConverterBinding> getTypeConverterBindings() {
return delegateInjector.getTypeConverterBindings(); return delegateInjector.getTypeConverterBindings();
} }
@Override
public List<Element> getElements() {
return delegateInjector.getElements();
}
@Override
public Map<TypeLiteral<?>, List<InjectionPoint>> getAllMembersInjectorInjectionPoints() {
return delegateInjector.getAllMembersInjectorInjectionPoints();
}
@Override
public <T> Provider<T> getProvider(Key<T> key) { public <T> Provider<T> getProvider(Key<T> key) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Injector.getProvider(Key<T>) is not supported in Stage.TOOL"); "Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
} }
@Override
public <T> Provider<T> getProvider(Class<T> type) { public <T> Provider<T> getProvider(Class<T> type) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Injector.getProvider(Class<T>) is not supported in Stage.TOOL"); "Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
} }
@Override
public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) { public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL"); "Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
} }
@Override
public <T> MembersInjector<T> getMembersInjector(Class<T> type) { public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL"); "Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
} }
@Override
public <T> T getInstance(Key<T> key) { public <T> T getInstance(Key<T> key) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Injector.getInstance(Key<T>) is not supported in Stage.TOOL"); "Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
} }
@Override
public <T> T getInstance(Class<T> type) { public <T> T getInstance(Class<T> type) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Injector.getInstance(Class<T>) is not supported in Stage.TOOL"); "Injector.getInstance(Class<T>) is not supported in Stage.TOOL");

View file

@ -0,0 +1,179 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.ProviderWithExtensionVisitor;
/**
* A {@link ProviderInstanceBindingImpl} for implementing 'native' guice extensions.
*
* <p>Beyond the normal binding contract that is mostly handled by our baseclass, this also
* implements {@link DelayedInitialize} in order to initialize factory state.
*/
final class InternalProviderInstanceBindingImpl<T> extends ProviderInstanceBindingImpl<T>
implements DelayedInitialize {
enum InitializationTiming {
/** This factory can be initialized eagerly. This should be the case for most things. */
EAGER,
/**
* Initialization of this factory should be delayed until after all other static initialization
* completes. This will be useful for factories that need to call {@link
* InjectorImpl#getExistingBinding(Key)} to not create jit bindings, but also want to be able to
* conditionally consume jit bindings created by other other bindings.
*/
DELAYED
}
private final Factory<T> originalFactory;
InternalProviderInstanceBindingImpl(
InjectorImpl injector,
Key<T> key,
Object source,
Factory<T> originalFactory,
InternalFactory<? extends T> scopedFactory,
Scoping scoping) {
super(
injector,
key,
source,
scopedFactory,
scoping,
originalFactory,
ImmutableSet.of());
this.originalFactory = originalFactory;
}
InitializationTiming getInitializationTiming() {
return originalFactory.initializationTiming;
}
@Override
public void initialize(final InjectorImpl injector, final Errors errors) throws ErrorsException {
originalFactory.source = getSource();
originalFactory.provisionCallback = injector.provisionListenerStore.get(this);
// For these kinds of providers, the 'user supplied provider' is really 'guice supplied'
// So make our user supplied provider just delegate to the guice supplied one.
originalFactory.delegateProvider = getProvider();
originalFactory.initialize(injector, errors);
}
/**
* A base factory implementation. Any Factories that delegate to other bindings should use the
* {@code CyclicFactory} subclass, but trivial factories can use this one.
*/
abstract static class Factory<T> implements InternalFactory<T>, Provider<T>, HasDependencies {
private final InitializationTiming initializationTiming;
private Object source;
private Provider<T> delegateProvider;
ProvisionListenerStackCallback<T> provisionCallback;
Factory(InitializationTiming initializationTiming) {
this.initializationTiming = initializationTiming;
}
/**
* The binding source.
*
* <p>May be useful for augmenting runtime error messages.
*
* <p>Note: this will return {#code null} until {@link #initialize(InjectorImpl, Errors)} has
* already been called.
*/
final Object getSource() {
return source;
}
/**
* A callback that allows for implementations to fetch dependencies on other bindings.
*
* <p>Will be called exactly once, prior to any call to {@link #doProvision}.
*/
abstract void initialize(InjectorImpl injector, Errors errors) throws ErrorsException;
@Override
public final T get() {
Provider<T> local = delegateProvider;
if (local == null) {
throw new IllegalStateException(
"This Provider cannot be used until the Injector has been created.");
}
return local.get();
}
@Override
public T get(final InternalContext context, final Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
if (provisionCallback == null) {
return doProvision(context, dependency);
} else {
return provisionCallback.provision(context, () -> doProvision(context, dependency));
}
}
/**
* Creates an object to be injected.
*
* @throws com.google.inject.internal.InternalProvisionException if a value cannot be provided
* @return instance to be injected
*/
protected abstract T doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException;
}
/**
* An base factory implementation that can be extended to provide a specialized implementation of
* a {@link ProviderWithExtensionVisitor} and also implements {@link InternalFactory}
*/
abstract static class CyclicFactory<T> extends Factory<T> {
CyclicFactory(InitializationTiming initializationTiming) {
super(initializationTiming);
}
@Override
public final T get(
final InternalContext context, final Dependency<?> dependency, boolean linked)
throws InternalProvisionException {
final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
// We have a circular reference between bindings. Return a proxy.
if (constructionContext.isConstructing()) {
Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
@SuppressWarnings("unchecked")
T proxyType =
(T) constructionContext.createProxy(context.getInjectorOptions(), expectedType);
return proxyType;
}
// Optimization: Don't go through the callback stack if no one's listening.
constructionContext.startConstruction();
try {
if (provisionCallback == null) {
return provision(dependency, context, constructionContext);
} else {
return provisionCallback.provision(context, () -> provision(dependency, context, constructionContext));
}
} finally {
constructionContext.removeCurrentReference();
constructionContext.finishConstruction();
}
}
private T provision(
Dependency<?> dependency,
InternalContext context,
ConstructionContext<T> constructionContext)
throws InternalProvisionException {
try {
T t = doProvision(context, dependency);
constructionContext.setProxyDelegates(t);
return t;
} catch (InternalProvisionException ipe) {
throw ipe.addSource(getSource());
} catch (Throwable t) {
throw InternalProvisionException.errorInProvider(t).addSource(getSource());
}
}
}
}

View file

@ -0,0 +1,216 @@
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Provides;
import com.google.inject.ProvisionException;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.SourceProvider;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.Message;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A checked exception for provisioning errors.
*
* <p>This is the internal dual of {@link ProvisionException}, similar to the relationship between
* {@link com.google.inject.ConfigurationException} and {@link ErrorsException}. This is useful for
* several reasons:
*
* <ul>
* <li>Since it is a checked exception, we get some assistance from the java compiler in ensuring
* that we correctly handle it everywhere. ProvisionException is unchecked.
* <li>Since this is an internal package, we can add useful construction and mutation APIs that
* would be undesirable in a public supported API.
* </ul>
*
* <p>This exception will be thrown when errors are encountered during provisioning, ErrorsException
* will continue to be used for errors that are encountered during provisioning and both make use of
* the {@link Message} as the core model.
*
* <p>NOTE: this object stores a list of messages but in the most common case the cardinality will
* be 1. The only time that multiple errors might be reported via this mechanism is when {@link
* #errorInUserCode} is called with an exception that holds multiple errors (like
* ProvisionException).
*/
@SuppressWarnings("serial")
public final class InternalProvisionException extends Exception {
private static final Logger logger = Logger.getLogger(Guice.class.getName());
private static final Set<Dependency<?>> warnedDependencies =
Collections.newSetFromMap(new ConcurrentHashMap<>());
public static InternalProvisionException circularDependenciesDisabled(Class<?> expectedType) {
return create(
"Found a circular dependency involving %s, and circular dependencies are disabled.",
expectedType);
}
public static InternalProvisionException cannotProxyClass(Class<?> expectedType) {
return create(
"Tried proxying %s to support a circular dependency, but it is not an interface.",
expectedType);
}
public static InternalProvisionException create(String format, Object... arguments) {
return new InternalProvisionException(Messages.create(format, arguments));
}
public static InternalProvisionException errorInUserCode(
Throwable cause, String messageFormat, Object... arguments) {
Collection<Message> messages = Errors.getMessagesFromThrowable(cause);
if (!messages.isEmpty()) {
// TODO(lukes): it seems like we are dropping some valuable context here..
// consider eliminating this special case
return new InternalProvisionException(messages);
} else {
return new InternalProvisionException(Messages.create(cause, messageFormat, arguments));
}
}
public static InternalProvisionException subtypeNotProvided(
Class<? extends javax.inject.Provider<?>> providerType, Class<?> type) {
return create("%s doesn't provide instances of %s.", providerType, type);
}
public static InternalProvisionException errorInProvider(Throwable cause) {
return errorInUserCode(cause, "Error in custom provider, %s", cause);
}
public static InternalProvisionException errorInjectingMethod(Throwable cause) {
return errorInUserCode(cause, "Error injecting method, %s", cause);
}
public static InternalProvisionException errorInjectingConstructor(Throwable cause) {
return errorInUserCode(cause, "Error injecting constructor, %s", cause);
}
public static InternalProvisionException errorInUserInjector(
MembersInjector<?> listener, TypeLiteral<?> type, RuntimeException cause) {
return errorInUserCode(
cause, "Error injecting %s using %s.%n Reason: %s", type, listener, cause);
}
public static InternalProvisionException jitDisabled(Key<?> key) {
return create("Explicit bindings are required and %s is not explicitly bound.", key);
}
public static InternalProvisionException errorNotifyingInjectionListener(
InjectionListener<?> listener, TypeLiteral<?> type, RuntimeException cause) {
return errorInUserCode(
cause, "Error notifying InjectionListener %s of %s.%n Reason: %s", listener, type, cause);
}
/**
* Returns {@code value} if it is non-null or allowed to be null. Otherwise a message is added and
* an {@code InternalProvisionException} is thrown.
*/
static void onNullInjectedIntoNonNullableDependency(Object source, Dependency<?> dependency)
throws InternalProvisionException {
// Hack to allow null parameters to @Provides methods, for backwards compatibility.
if (dependency.getInjectionPoint().getMember() instanceof Method) {
Method annotated = (Method) dependency.getInjectionPoint().getMember();
if (annotated.isAnnotationPresent(Provides.class)) {
switch (InternalFlags.getNullableProvidesOption()) {
case ERROR:
break; // break out & let the below exception happen
case IGNORE:
return; // user doesn't care about injecting nulls to non-@Nullables.
case WARN:
// Warn only once, otherwise we spam logs too much.
if (warnedDependencies.add(dependency)) {
logger.log(
Level.WARNING,
"Guice injected null into {0} (a {1}), please mark it @Nullable."
+ " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
+ " error.",
new Object[] {
Messages.formatParameter(dependency), Messages.convert(dependency.getKey())
});
}
return;
}
}
}
Object formattedDependency =
(dependency.getParameterIndex() != -1)
? Messages.formatParameter(dependency)
: StackTraceElements.forMember(dependency.getInjectionPoint().getMember());
throw InternalProvisionException.create(
"null returned by binding at %s%n but %s is not @Nullable", source, formattedDependency)
.addSource(source);
}
private final List<Object> sourcesToPrepend = new ArrayList<>();
private final ImmutableList<Message> errors;
private InternalProvisionException(Message error) {
this(ImmutableList.of(error));
}
private InternalProvisionException(Iterable<Message> errors) {
this.errors = ImmutableList.copyOf(errors);
checkArgument(!this.errors.isEmpty(), "Can't create a provision exception with no errors");
}
/**
* Prepends the given {@code source} to the stack of binding sources for the errors reported in
* this exception.
*
* <p>See {@link Errors#withSource(Object)}
*
* <p>It is expected that this method is called as the exception propagates up the stack.
*
* @param source
* @return {@code this}
*/
InternalProvisionException addSource(Object source) {
if (source == SourceProvider.UNKNOWN_SOURCE) {
return this;
}
int sz = sourcesToPrepend.size();
if (sz > 0 && sourcesToPrepend.get(sz - 1) == source) {
// This is for when there are two identical sources added in a row. This behavior is copied
// from Errors.withSource where it can happen when an constructor/provider method throws an
// exception
return this;
}
sourcesToPrepend.add(source);
return this;
}
ImmutableList<Message> getErrors() {
ImmutableList.Builder<Message> builder = ImmutableList.builder();
// reverse them since sources are added as the exception propagates (so the first source is the
// last one added)
List<Object> newSources = Lists.reverse(sourcesToPrepend);
for (Message error : errors) {
builder.add(Messages.mergeSources(newSources, error));
}
return builder.build();
}
/** Returns this exception convered to a ProvisionException. */
public ProvisionException toProvisionException() {
return new ProvisionException(getErrors());
}
}

View file

@ -15,8 +15,9 @@ import java.util.Set;
final class LinkedProviderBindingImpl<T> final class LinkedProviderBindingImpl<T>
extends BindingImpl<T> implements ProviderKeyBinding<T>, HasDependencies, DelayedInitialize { extends BindingImpl<T> implements ProviderKeyBinding<T>, HasDependencies, DelayedInitialize {
final Key<? extends javax.inject.Provider<? extends T>> providerKey; private final Key<? extends javax.inject.Provider<? extends T>> providerKey;
final DelayedInitialize delayedInitializer;
private final DelayedInitialize delayedInitializer;
private LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source, private LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> internalFactory, Scoping scoping, InternalFactory<? extends T> internalFactory, Scoping scoping,
@ -27,7 +28,7 @@ final class LinkedProviderBindingImpl<T>
this.delayedInitializer = delayedInitializer; this.delayedInitializer = delayedInitializer;
} }
public LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source, LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> internalFactory, Scoping scoping, InternalFactory<? extends T> internalFactory, Scoping scoping,
Key<? extends javax.inject.Provider<? extends T>> providerKey) { Key<? extends javax.inject.Provider<? extends T>> providerKey) {
this(injector, key, source, internalFactory, scoping, providerKey, null); this(injector, key, source, internalFactory, scoping, providerKey, null);
@ -44,36 +45,43 @@ final class LinkedProviderBindingImpl<T>
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping, Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
Key<? extends javax.inject.Provider<? extends T>> providerKey, Key<? extends javax.inject.Provider<? extends T>> providerKey,
DelayedInitialize delayedInitializer) { DelayedInitialize delayedInitializer) {
return new LinkedProviderBindingImpl<T>(injector, key, source, internalFactory, scoping, return new LinkedProviderBindingImpl<>(injector, key, source, internalFactory, scoping,
providerKey, delayedInitializer); providerKey, delayedInitializer);
} }
@Override
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) { public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
@Override
public Key<? extends javax.inject.Provider<? extends T>> getProviderKey() { public Key<? extends javax.inject.Provider<? extends T>> getProviderKey() {
return providerKey; return providerKey;
} }
@Override
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException { public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
if (delayedInitializer != null) { if (delayedInitializer != null) {
delayedInitializer.initialize(injector, errors); delayedInitializer.initialize(injector, errors);
} }
} }
@Override
public Set<Dependency<?>> getDependencies() { public Set<Dependency<?>> getDependencies() {
return ImmutableSet.<Dependency<?>>of(Dependency.get(providerKey)); return ImmutableSet.of(Dependency.get(providerKey));
} }
@Override
public BindingImpl<T> withScoping(Scoping scoping) { public BindingImpl<T> withScoping(Scoping scoping) {
return new LinkedProviderBindingImpl<T>(getSource(), getKey(), scoping, providerKey); return new LinkedProviderBindingImpl<>(getSource(), getKey(), scoping, providerKey);
} }
@Override
public BindingImpl<T> withKey(Key<T> key) { public BindingImpl<T> withKey(Key<T> key) {
return new LinkedProviderBindingImpl<T>(getSource(), key, getScoping(), providerKey); return new LinkedProviderBindingImpl<>(getSource(), key, getScoping(), providerKey);
} }
@Override
public void applyTo(Binder binder) { public void applyTo(Binder binder) {
getScoping().applyTo(binder.withSource(getSource()) getScoping().applyTo(binder.withSource(getSource())
.bind(getKey()).toProvider(getProviderKey())); .bind(getKey()).toProvider(getProviderKey()));

View file

@ -13,68 +13,68 @@ import com.google.inject.spi.InjectionPoint;
* Injects members of instances of a given type. * Injects members of instances of a given type.
*/ */
final class MembersInjectorImpl<T> implements MembersInjector<T> { final class MembersInjectorImpl<T> implements MembersInjector<T> {
private final TypeLiteral<T> typeLiteral;
private final InjectorImpl injector;
private final ImmutableList<SingleMemberInjector> memberInjectors;
private final ImmutableSet<MembersInjector<? super T>> userMembersInjectors;
private final ImmutableSet<InjectionListener<? super T>> injectionListeners;
MembersInjectorImpl(InjectorImpl injector, TypeLiteral<T> typeLiteral, private final TypeLiteral<T> typeLiteral;
EncounterImpl<T> encounter, ImmutableList<SingleMemberInjector> memberInjectors) {
private final InjectorImpl injector;
private final ImmutableList<SingleMemberInjector> memberInjectors;
private final ImmutableList<MembersInjector<? super T>> userMembersInjectors;
private final ImmutableList<InjectionListener<? super T>> injectionListeners;
MembersInjectorImpl(InjectorImpl injector,
TypeLiteral<T> typeLiteral,
EncounterImpl<T> encounter,
ImmutableList<SingleMemberInjector> memberInjectors) {
this.injector = injector; this.injector = injector;
this.typeLiteral = typeLiteral; this.typeLiteral = typeLiteral;
this.memberInjectors = memberInjectors; this.memberInjectors = memberInjectors.isEmpty() ? null : memberInjectors;
this.userMembersInjectors = encounter.getMembersInjectors(); this.userMembersInjectors =
this.injectionListeners = encounter.getInjectionListeners(); encounter.getMembersInjectors().isEmpty() ? null : encounter.getMembersInjectors().asList();
this.injectionListeners =
encounter.getInjectionListeners().isEmpty()
? null
: encounter.getInjectionListeners().asList();
} }
public ImmutableList<SingleMemberInjector> getMemberInjectors() { public ImmutableList<SingleMemberInjector> getMemberInjectors() {
return memberInjectors; return memberInjectors == null ? ImmutableList.of() : memberInjectors;
} }
public void injectMembers(T instance) { public void injectMembers(T instance) {
Errors errors = new Errors(typeLiteral); TypeLiteral<T> localTypeLiteral = typeLiteral;
try { try {
injectAndNotify(instance, errors, null, null, typeLiteral, false); injectAndNotify(instance, null, null, localTypeLiteral, false);
} catch (ErrorsException e) { } catch (InternalProvisionException ipe) {
errors.merge(e.getErrors()); throw ipe.addSource(localTypeLiteral).toProvisionException();
} }
errors.throwProvisionExceptionIfErrorsExist();
} }
void injectAndNotify(final T instance, void injectAndNotify(final T instance,
final Errors errors,
final Key<T> key, // possibly null! final Key<T> key, // possibly null!
final ProvisionListenerStackCallback<T> provisionCallback, // possibly null! final ProvisionListenerStackCallback<T> provisionCallback, // possibly null!
final Object source, final Object source,
final boolean toolableOnly) throws ErrorsException { final boolean toolableOnly) throws InternalProvisionException {
if (instance == null) { if (instance == null) {
return; return;
} }
final InternalContext context = injector.enterContext();
injector.callInContext(new ContextualCallable<Void>() { context.pushState(key, source);
@Override try {
public Void call(final InternalContext context) throws ErrorsException { if (provisionCallback != null && provisionCallback.hasListeners()) {
context.pushState(key, source); provisionCallback.provision(context, () -> {
try { injectMembers(instance, context, toolableOnly);
if (provisionCallback != null && provisionCallback.hasListeners()) { return instance;
provisionCallback.provision(errors, context, new ProvisionCallback<T>() {
@Override
public T call() {
injectMembers(instance, errors, context, toolableOnly);
return instance;
}
}); });
} else { } else {
injectMembers(instance, errors, context, toolableOnly); injectMembers(instance, context, toolableOnly);
}
} finally {
context.popState();
}
return null;
} }
}); } finally {
context.popState();
context.close();
}
// TODO: We *could* notify listeners too here, // TODO: We *could* notify listeners too here,
// but it's not clear if we want to. There's no way to know // but it's not clear if we want to. There's no way to know
@ -85,38 +85,54 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
// the above callInContext could return 'true' if it injected // the above callInContext could return 'true' if it injected
// anything.) // anything.)
if (!toolableOnly) { if (!toolableOnly) {
notifyListeners(instance, errors); notifyListeners(instance);
} }
} }
void notifyListeners(T instance, Errors errors) throws ErrorsException { void notifyListeners(T instance) throws InternalProvisionException {
int numErrorsBefore = errors.size(); ImmutableList<InjectionListener<? super T>> localInjectionListeners = injectionListeners;
for (InjectionListener<? super T> injectionListener : injectionListeners) { if (localInjectionListeners == null) {
// no listeners
return;
}
// optimization: use manual for/each to save allocating an iterator here
for (int i = 0; i < localInjectionListeners.size(); i++) {
InjectionListener<? super T> injectionListener = localInjectionListeners.get(i);
try { try {
injectionListener.afterInjection(instance); injectionListener.afterInjection(instance);
} catch (RuntimeException e) { } catch (RuntimeException e) {
errors.errorNotifyingInjectionListener(injectionListener, typeLiteral, e); throw InternalProvisionException.errorNotifyingInjectionListener(
injectionListener, typeLiteral, e);
} }
} }
errors.throwIfNewErrors(numErrorsBefore);
} }
void injectMembers(T t, Errors errors, InternalContext context, boolean toolableOnly) { void injectMembers(T t, InternalContext context, boolean toolableOnly)
// optimization: use manual for/each to save allocating an iterator here throws InternalProvisionException {
for (int i = 0, size = memberInjectors.size(); i < size; i++) { ImmutableList<SingleMemberInjector> localMembersInjectors = memberInjectors;
SingleMemberInjector injector = memberInjectors.get(i); if (localMembersInjectors != null) {
if (!toolableOnly || injector.getInjectionPoint().isToolable()) { // optimization: use manual for/each to save allocating an iterator here
injector.inject(errors, context, t); for (int i = 0, size = localMembersInjectors.size(); i < size; i++) {
SingleMemberInjector injector = localMembersInjectors.get(i);
if (!toolableOnly || injector.getInjectionPoint().isToolable()) {
injector.inject(context, t);
}
} }
} }
// TODO: There's no way to know if a user's MembersInjector wants toolable injections. // TODO: There's no way to know if a user's MembersInjector wants toolable injections.
if (!toolableOnly) { if (!toolableOnly) {
for (MembersInjector<? super T> userMembersInjector : userMembersInjectors) { ImmutableList<MembersInjector<? super T>> localUsersMembersInjectors = userMembersInjectors;
try { if (localUsersMembersInjectors != null) {
userMembersInjector.injectMembers(t); // optimization: use manual for/each to save allocating an iterator here
} catch (RuntimeException e) { for (int i = 0; i < localUsersMembersInjectors.size(); i++) {
errors.errorInUserInjector(userMembersInjector, typeLiteral, e); MembersInjector<? super T> userMembersInjector = localUsersMembersInjectors.get(i);
try {
userMembersInjector.injectMembers(t);
} catch (RuntimeException e) {
throw InternalProvisionException.errorInUserInjector(
userMembersInjector, typeLiteral, e);
}
} }
} }
} }
@ -128,11 +144,15 @@ final class MembersInjectorImpl<T> implements MembersInjector<T> {
} }
public ImmutableSet<InjectionPoint> getInjectionPoints() { public ImmutableSet<InjectionPoint> getInjectionPoints() {
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder(); ImmutableList<SingleMemberInjector> localMemberInjectors = memberInjectors;
for (SingleMemberInjector memberInjector : memberInjectors) { if (localMemberInjectors != null) {
builder.add(memberInjector.getInjectionPoint()); ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
for (SingleMemberInjector memberInjector : localMemberInjectors) {
builder.add(memberInjector.getInjectionPoint());
}
return builder.build();
} }
return builder.build(); return ImmutableSet.of();
} }
} }

View file

@ -1,6 +1,9 @@
package com.google.inject.internal; package com.google.inject.internal;
import static com.google.common.collect.ImmutableListMultimap.flatteningToImmutableListMultimap;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.inject.ConfigurationException; import com.google.inject.ConfigurationException;
@ -11,24 +14,25 @@ import com.google.inject.spi.TypeListenerBinding;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
* Members injectors by type. * Members injectors by type.
*/ */
final class MembersInjectorStore { final class MembersInjectorStore {
private final InjectorImpl injector; private final InjectorImpl injector;
private final ImmutableList<TypeListenerBinding> typeListenerBindings; private final ImmutableList<TypeListenerBinding> typeListenerBindings;
private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache private final FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>> cache = new FailableCache<>() {
= new FailableCache<TypeLiteral<?>, MembersInjectorImpl<?>>() { @Override
@Override protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors)
protected MembersInjectorImpl<?> create(TypeLiteral<?> type, Errors errors) throws ErrorsException {
throws ErrorsException { return createWithListeners(type, errors);
return createWithListeners(type, errors); }
} };
};
MembersInjectorStore(InjectorImpl injector, MembersInjectorStore(InjectorImpl injector,
List<TypeListenerBinding> typeListenerBindings) { List<TypeListenerBinding> typeListenerBindings) {
this.injector = injector; this.injector = injector;
@ -39,7 +43,7 @@ final class MembersInjectorStore {
* Returns true if any type listeners are installed. Other code may take shortcuts when there * Returns true if any type listeners are installed. Other code may take shortcuts when there
* aren't any type listeners. * aren't any type listeners.
*/ */
public boolean hasTypeListeners() { boolean hasTypeListeners() {
return !typeListenerBindings.isEmpty(); return !typeListenerBindings.isEmpty();
} }
@ -81,7 +85,7 @@ final class MembersInjectorStore {
ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors); ImmutableList<SingleMemberInjector> injectors = getInjectors(injectionPoints, errors);
errors.throwIfNewErrors(numErrorsBefore); errors.throwIfNewErrors(numErrorsBefore);
EncounterImpl<T> encounter = new EncounterImpl<T>(errors, injector.lookups); EncounterImpl<T> encounter = new EncounterImpl<>(errors, injector.lookups);
Set<TypeListener> alreadySeenListeners = Sets.newHashSet(); Set<TypeListener> alreadySeenListeners = Sets.newHashSet();
for (TypeListenerBinding binding : typeListenerBindings) { for (TypeListenerBinding binding : typeListenerBindings) {
TypeListener typeListener = binding.getListener(); TypeListener typeListener = binding.getListener();
@ -97,7 +101,7 @@ final class MembersInjectorStore {
encounter.invalidate(); encounter.invalidate();
errors.throwIfNewErrors(numErrorsBefore); errors.throwIfNewErrors(numErrorsBefore);
return new MembersInjectorImpl<T>(injector, type, encounter, injectors); return new MembersInjectorImpl<>(injector, type, encounter, injectors);
} }
/** /**
@ -121,4 +125,11 @@ final class MembersInjectorStore {
} }
return ImmutableList.copyOf(injectors); return ImmutableList.copyOf(injectors);
} }
ImmutableListMultimap<TypeLiteral<?>, InjectionPoint> getAllInjectionPoints() {
return cache.asMap().entrySet().stream()
.collect(
flatteningToImmutableListMultimap(
Map.Entry::getKey, entry -> entry.getValue().getInjectionPoints().stream()));
}
} }

View file

@ -12,20 +12,8 @@ final class MessageProcessor extends AbstractProcessor {
super(errors); super(errors);
} }
public static String getRootMessage(Throwable t) {
Throwable cause = t.getCause();
return cause == null ? t.toString() : getRootMessage(cause);
}
@Override @Override
public Boolean visit(Message message) { public Boolean visit(Message message) {
if (message.getCause() != null) {
String rootMessage = getRootMessage(message.getCause());
/*logger.log(Level.INFO,
"An exception was caught and reported. Message: " + rootMessage,
message.getCause());*/
}
errors.addMessage(message); errors.addMessage(message);
return true; return true;
} }

View file

@ -0,0 +1,391 @@
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.Equivalence;
import com.google.common.base.Objects;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes;
import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ElementSource;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.util.Arrays;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
/**
* Utility methods for {@link Message} objects
*/
public final class Messages {
private Messages() {
}
/**
* Prepends the list of sources to the given {@link Message}
*/
static Message mergeSources(List<Object> sources, Message message) {
List<Object> messageSources = message.getSources();
// It is possible that the end of getSources() and the beginning of message.getSources() are
// equivalent, in this case we should drop the repeated source when joining the lists. The
// most likely scenario where this would happen is when a scoped binding throws an exception,
// due to the fact that InternalFactoryToProviderAdapter applies the binding source when
// merging errors.
if (!sources.isEmpty() && !messageSources.isEmpty() &&
Objects.equal(messageSources.get(0), sources.get(sources.size() - 1))) {
messageSources = messageSources.subList(1, messageSources.size());
}
return new Message(ImmutableList.builder().addAll(sources).addAll(messageSources).build(),
message.getMessage(), message.getCause());
}
/**
* Calls {@link String#format} after converting the arguments using some standard guice formatting
* for {@link Key}, {@link Class} and {@link Member} objects.
*/
public static String format(String messageFormat, Object... arguments) {
for (int i = 0; i < arguments.length; i++) {
arguments[i] = convert(arguments[i]);
}
return String.format(messageFormat, arguments);
}
/**
* Returns the formatted message for an exception with the specified messages.
*/
public static String formatMessages(String heading, Collection<Message> errorMessages) {
Formatter fmt = new Formatter().format(heading).format(":%n%n");
int index = 1;
boolean displayCauses = getOnlyCause(errorMessages) == null;
Map<Equivalence.Wrapper<Throwable>, Integer> causes = Maps.newHashMap();
for (Message errorMessage : errorMessages) {
int thisIdx = index++;
fmt.format("%s) %s%n", thisIdx, errorMessage.getMessage());
List<Object> dependencies = errorMessage.getSources();
for (int i = dependencies.size() - 1; i >= 0; i--) {
Object source = dependencies.get(i);
formatSource(fmt, source);
}
Throwable cause = errorMessage.getCause();
if (displayCauses && cause != null) {
Equivalence.Wrapper<Throwable> causeEquivalence = ThrowableEquivalence.INSTANCE.wrap(cause);
if (!causes.containsKey(causeEquivalence)) {
causes.put(causeEquivalence, thisIdx);
fmt.format("Caused by: %s", Throwables.getStackTraceAsString(cause));
} else {
int causeIdx = causes.get(causeEquivalence);
fmt.format(
"Caused by: %s (same stack trace as error #%s)",
cause.getClass().getName(), causeIdx);
}
}
fmt.format("%n");
}
if (errorMessages.size() == 1) {
fmt.format("1 error");
} else {
fmt.format("%s errors", errorMessages.size());
}
return fmt.toString();
}
/**
* Creates a new Message without a cause.
*
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(String messageFormat, Object... arguments) {
return create(null, messageFormat, arguments);
}
/**
* Creates a new Message with the given cause.
*
* @param cause The exception that caused the error
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(Throwable cause, String messageFormat, Object... arguments) {
return create(cause, ImmutableList.of(), messageFormat, arguments);
}
/**
* Creates a new Message with the given cause and a binding source stack.
*
* @param cause The exception that caused the error
* @param sources The binding sources for the source stack
* @param messageFormat Format string
* @param arguments format string arguments
*/
public static Message create(Throwable cause, List<Object> sources, String messageFormat, Object... arguments) {
String message = format(messageFormat, arguments);
return new Message(sources, message, cause);
}
/**
* Formats an object in a user friendly way.
*/
public static Object convert(Object o) {
ElementSource source = null;
if (o instanceof ElementSource) {
source = (ElementSource) o;
o = source.getDeclaringSource();
}
return convert(o, source);
}
public static Object convert(Object o, ElementSource source) {
for (Converter<?> converter : converters) {
if (converter.appliesTo(o)) {
return appendModules(converter.convert(o), source);
}
}
return appendModules(o, source);
}
private static Object appendModules(Object source, ElementSource elementSource) {
String modules = moduleSourceString(elementSource);
if (modules.length() == 0) {
return source;
} else {
return source + modules;
}
}
private static String moduleSourceString(ElementSource elementSource) {
// if we only have one module (or don't know what they are), then don't bother
// reporting it, because the source already is going to report exactly that module.
if (elementSource == null) {
return "";
}
List<String> modules = Lists.newArrayList(elementSource.getModuleClassNames());
// Insert any original element sources w/ module info into the path.
while (elementSource.getOriginalElementSource() != null) {
elementSource = elementSource.getOriginalElementSource();
modules.addAll(0, elementSource.getModuleClassNames());
}
if (modules.size() <= 1) {
return "";
}
// Ideally we'd do:
// return Joiner.on(" -> ")
// .appendTo(new StringBuilder(" (via modules: "), Lists.reverse(modules))
// .append(")").toString();
// ... but for some reason we can't find Lists.reverse, so do it the boring way.
StringBuilder builder = new StringBuilder(" (via modules: ");
for (int i = modules.size() - 1; i >= 0; i--) {
builder.append(modules.get(i));
if (i != 0) {
builder.append(" -> ");
}
}
builder.append(")");
return builder.toString();
}
static void formatSource(Formatter formatter, Object source) {
ElementSource elementSource = null;
if (source instanceof ElementSource) {
elementSource = (ElementSource) source;
source = elementSource.getDeclaringSource();
}
formatSource(formatter, source, elementSource);
}
static void formatSource(Formatter formatter, Object source, ElementSource elementSource) {
String modules = moduleSourceString(elementSource);
if (source instanceof Dependency) {
Dependency<?> dependency = (Dependency<?>) source;
InjectionPoint injectionPoint = dependency.getInjectionPoint();
if (injectionPoint != null) {
formatInjectionPoint(formatter, dependency, injectionPoint, elementSource);
} else {
formatSource(formatter, dependency.getKey(), elementSource);
}
} else if (source instanceof InjectionPoint) {
formatInjectionPoint(formatter, null, (InjectionPoint) source, elementSource);
} else if (source instanceof Class) {
formatter.format(" at %s%s%n", StackTraceElements.forType((Class<?>) source), modules);
} else if (source instanceof Member) {
formatter.format(" at %s%s%n", StackTraceElements.forMember((Member) source), modules);
} else if (source instanceof TypeLiteral) {
formatter.format(" while locating %s%s%n", source, modules);
} else if (source instanceof Key) {
Key<?> key = (Key<?>) source;
formatter.format(" while locating %s%n", convert(key, elementSource));
} else if (source instanceof Thread) {
formatter.format(" in thread %s%n", source);
} else {
formatter.format(" at %s%s%n", source, modules);
}
}
private static void formatInjectionPoint(
Formatter formatter,
Dependency<?> dependency,
InjectionPoint injectionPoint,
ElementSource elementSource) {
Member member = injectionPoint.getMember();
Class<? extends Member> memberType = Classes.memberType(member);
if (memberType == Field.class) {
dependency = injectionPoint.getDependencies().get(0);
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
formatter.format(" for field at %s%n", StackTraceElements.forMember(member));
} else if (dependency != null) {
formatter.format(" while locating %s%n", convert(dependency.getKey(), elementSource));
formatter.format(" for %s%n", formatParameter(dependency));
} else {
formatSource(formatter, injectionPoint.getMember());
}
}
static String formatParameter(Dependency<?> dependency) {
int ordinal = dependency.getParameterIndex() + 1;
return String.format(
"the %s%s parameter of %s",
ordinal,
getOrdinalSuffix(ordinal),
StackTraceElements.forMember(dependency.getInjectionPoint().getMember()));
}
/**
* Maps {@code 1} to the string {@code "1st"} ditto for all non-negative numbers
*
* @see <a href="https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers">
* https://en.wikipedia.org/wiki/English_numerals#Ordinal_numbers</a>
*/
private static String getOrdinalSuffix(int ordinal) {
// negative ordinals don't make sense, we allow zero though because we are programmers
checkArgument(ordinal >= 0);
if ((ordinal / 10) % 10 == 1) {
// all the 'teens' are weird
return "th";
} else {
// could use a lookup table? any better?
switch (ordinal % 10) {
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}
}
private abstract static class Converter<T> {
final Class<T> type;
Converter(Class<T> type) {
this.type = type;
}
boolean appliesTo(Object o) {
return o != null && type.isAssignableFrom(o.getClass());
}
String convert(Object o) {
return toString(type.cast(o));
}
abstract String toString(T t);
}
@SuppressWarnings({"rawtypes"}) // rawtypes aren't avoidable
private static final Collection<Converter<?>> converters =
ImmutableList.of(
new Converter<>(Class.class) {
@Override
public String toString(Class c) {
return c.getName();
}
},
new Converter<>(Member.class) {
@Override
public String toString(Member member) {
return Classes.toString(member);
}
},
new Converter<>(Key.class) {
@Override
public String toString(Key key) {
if (key.getAnnotationType() != null) {
return key.getTypeLiteral()
+ " annotated with "
+ (key.getAnnotation() != null ? key.getAnnotation() : key.getAnnotationType());
} else {
return key.getTypeLiteral().toString();
}
}
});
/**
* Returns the cause throwable if there is exactly one cause in {@code messages}. If there are
* zero or multiple messages with causes, null is returned.
*/
public static Throwable getOnlyCause(Collection<Message> messages) {
Throwable onlyCause = null;
for (Message message : messages) {
Throwable messageCause = message.getCause();
if (messageCause == null) {
continue;
}
if (onlyCause != null && !ThrowableEquivalence.INSTANCE.equivalent(onlyCause, messageCause)) {
return null;
}
onlyCause = messageCause;
}
return onlyCause;
}
private static final class ThrowableEquivalence extends Equivalence<Throwable> {
static final ThrowableEquivalence INSTANCE = new ThrowableEquivalence();
@Override
protected boolean doEquivalent(Throwable a, Throwable b) {
return a.getClass().equals(b.getClass())
&& Objects.equal(a.getMessage(), b.getMessage())
&& Arrays.equals(a.getStackTrace(), b.getStackTrace())
&& equivalent(a.getCause(), b.getCause());
}
@Override
protected int doHash(Throwable t) {
return Objects.hashCode(t.getClass().hashCode(), t.getMessage(), hash(t.getCause()));
}
}
}

View file

@ -264,7 +264,7 @@ public class MoreTypes {
// we skip searching through interfaces if unknown is an interface // we skip searching through interfaces if unknown is an interface
if (toResolve.isInterface()) { if (toResolve.isInterface()) {
Class[] interfaces = rawType.getInterfaces(); Class<?>[] interfaces = rawType.getInterfaces();
for (int i = 0, length = interfaces.length; i < length; i++) { for (int i = 0, length = interfaces.length; i < length; i++) {
if (interfaces[i] == toResolve) { if (interfaces[i] == toResolve) {
return rawType.getGenericInterfaces()[i]; return rawType.getGenericInterfaces()[i];
@ -291,7 +291,7 @@ public class MoreTypes {
return toResolve; return toResolve;
} }
public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable unknown) { public static Type resolveTypeVariable(Type type, Class<?> rawType, TypeVariable<?> unknown) {
Class<?> declaredByRaw = declaringClassOf(unknown); Class<?> declaredByRaw = declaringClassOf(unknown);
// we can't reduce this further // we can't reduce this further
@ -321,7 +321,7 @@ public class MoreTypes {
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
* a class. * a class.
*/ */
private static Class<?> declaringClassOf(TypeVariable typeVariable) { private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
return genericDeclaration instanceof Class return genericDeclaration instanceof Class
? (Class<?>) genericDeclaration ? (Class<?>) genericDeclaration
@ -365,7 +365,7 @@ public class MoreTypes {
private static void ensureOwnerType(Type ownerType, Type rawType) { private static void ensureOwnerType(Type ownerType, Type rawType) {
if (rawType instanceof Class<?>) { if (rawType instanceof Class<?>) {
Class rawTypeAsClass = (Class) rawType; Class<?> rawTypeAsClass = (Class) rawType;
checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null, checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null,
"No owner type for enclosed %s", rawType); "No owner type for enclosed %s", rawType);
checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null, checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null,
@ -373,18 +373,22 @@ public class MoreTypes {
} }
} }
@Override
public Type[] getActualTypeArguments() { public Type[] getActualTypeArguments() {
return typeArguments.clone(); return typeArguments.clone();
} }
@Override
public Type getRawType() { public Type getRawType() {
return rawType; return rawType;
} }
@Override
public Type getOwnerType() { public Type getOwnerType() {
return ownerType; return ownerType;
} }
@Override
public boolean isFullySpecified() { public boolean isFullySpecified() {
if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) { if (ownerType != null && !MoreTypes.isFullySpecified(ownerType)) {
return false; return false;
@ -441,10 +445,12 @@ public class MoreTypes {
this.componentType = canonicalize(componentType); this.componentType = canonicalize(componentType);
} }
@Override
public Type getGenericComponentType() { public Type getGenericComponentType() {
return componentType; return componentType;
} }
@Override
public boolean isFullySpecified() { public boolean isFullySpecified() {
return MoreTypes.isFullySpecified(componentType); return MoreTypes.isFullySpecified(componentType);
} }
@ -495,14 +501,17 @@ public class MoreTypes {
} }
} }
@Override
public Type[] getUpperBounds() { public Type[] getUpperBounds() {
return new Type[]{upperBound}; return new Type[]{upperBound};
} }
@Override
public Type[] getLowerBounds() { public Type[] getLowerBounds() {
return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY; return lowerBound != null ? new Type[]{lowerBound} : EMPTY_TYPE_ARRAY;
} }
@Override
public boolean isFullySpecified() { public boolean isFullySpecified() {
return MoreTypes.isFullySpecified(upperBound) return MoreTypes.isFullySpecified(upperBound)
&& (lowerBound == null || MoreTypes.isFullySpecified(lowerBound)); && (lowerBound == null || MoreTypes.isFullySpecified(lowerBound));

View file

@ -25,7 +25,7 @@ final class PrivateElementProcessor extends AbstractProcessor {
return true; return true;
} }
public List<InjectorShell.Builder> getInjectorShellBuilders() { List<InjectorShell.Builder> getInjectorShellBuilders() {
return injectorShellBuilders; return injectorShellBuilders;
} }
} }

View file

@ -49,10 +49,12 @@ public final class PrivateElementsImpl implements PrivateElements {
this.source = checkNotNull(source, "source"); this.source = checkNotNull(source, "source");
} }
@Override
public Object getSource() { public Object getSource() {
return source; return source;
} }
@Override
public List<Element> getElements() { public List<Element> getElements() {
if (elements == null) { if (elements == null) {
elements = ImmutableList.copyOf(elementsMutable); elements = ImmutableList.copyOf(elementsMutable);
@ -62,15 +64,17 @@ public final class PrivateElementsImpl implements PrivateElements {
return elements; return elements;
} }
@Override
public Injector getInjector() { public Injector getInjector() {
return injector; return injector;
} }
public void initInjector(Injector injector) { void initInjector(Injector injector) {
checkState(this.injector == null, "injector already initialized"); checkState(this.injector == null, "injector already initialized");
this.injector = checkNotNull(injector, "injector"); this.injector = checkNotNull(injector, "injector");
} }
@Override
public Set<Key<?>> getExposedKeys() { public Set<Key<?>> getExposedKeys() {
if (exposedKeysToSources == null) { if (exposedKeysToSources == null) {
Map<Key<?>, Object> exposedKeysToSourcesMutable = Maps.newLinkedHashMap(); Map<Key<?>, Object> exposedKeysToSourcesMutable = Maps.newLinkedHashMap();
@ -84,6 +88,7 @@ public final class PrivateElementsImpl implements PrivateElements {
return exposedKeysToSources.keySet(); return exposedKeysToSources.keySet();
} }
@Override
public <T> T acceptVisitor(ElementVisitor<T> visitor) { public <T> T acceptVisitor(ElementVisitor<T> visitor) {
return visitor.visit(this); return visitor.visit(this);
} }
@ -96,6 +101,7 @@ public final class PrivateElementsImpl implements PrivateElements {
exposureBuilders.add(exposureBuilder); exposureBuilders.add(exposureBuilder);
} }
@Override
public void applyTo(Binder binder) { public void applyTo(Binder binder) {
PrivateBinder privateBinder = binder.withSource(source).newPrivateBinder(); PrivateBinder privateBinder = binder.withSource(source).newPrivateBinder();
@ -109,6 +115,7 @@ public final class PrivateElementsImpl implements PrivateElements {
} }
} }
@Override
public Object getExposedSource(Key<?> key) { public Object getExposedSource(Key<?> key) {
getExposedKeys(); // ensure exposedKeysToSources is populated getExposedKeys(); // ensure exposedKeysToSources is populated
Object source = exposedKeysToSources.get(key); Object source = exposedKeysToSources.get(key);

View file

@ -11,8 +11,11 @@ import java.util.List;
class ProcessedBindingData { class ProcessedBindingData {
private final List<CreationListener> creationListeners = Lists.newArrayList(); private final List<CreationListener> creationListeners = Lists.newArrayList();
private final List<Runnable> uninitializedBindings = Lists.newArrayList(); private final List<Runnable> uninitializedBindings = Lists.newArrayList();
private final List<Runnable> delayedUninitializedBindings = Lists.newArrayList();
void addCreationListener(CreationListener listener) { void addCreationListener(CreationListener listener) {
creationListeners.add(listener); creationListeners.add(listener);
} }
@ -21,6 +24,10 @@ class ProcessedBindingData {
uninitializedBindings.add(runnable); uninitializedBindings.add(runnable);
} }
void addDelayedUninitializedBinding(Runnable runnable) {
delayedUninitializedBindings.add(runnable);
}
void initializeBindings() { void initializeBindings() {
for (Runnable initializer : uninitializedBindings) { for (Runnable initializer : uninitializedBindings) {
initializer.run(); initializer.run();
@ -33,4 +40,14 @@ class ProcessedBindingData {
} }
} }
/**
* Initialized bindings that need to be delayed until after all injection points and other
* bindings are processed. The main current usecase for this is resolving Optional dependencies
* for OptionalBinder bindings.
*/
void initializeDelayedBindings() {
for (Runnable initializer : delayedUninitializedBindings) {
initializer.run();
}
}
} }

View file

@ -2,11 +2,9 @@ package com.google.inject.internal;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.ProvidedBy; import com.google.inject.ProvidedBy;
import com.google.inject.Provider;
import com.google.inject.internal.InjectorImpl.JitLimitation; import com.google.inject.internal.InjectorImpl.JitLimitation;
import com.google.inject.spi.Dependency; import com.google.inject.spi.Dependency;
import javax.inject.Provider;
import static com.google.common.base.Preconditions.checkState;
/** /**
* An {@link InternalFactory} for {@literal @}{@link ProvidedBy} bindings. * An {@link InternalFactory} for {@literal @}{@link ProvidedBy} bindings.
@ -34,40 +32,48 @@ class ProvidedByInternalFactory<T> extends ProviderInternalFactory<T>
provisionCallback = listener; provisionCallback = listener;
} }
@Override
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException { public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
providerBinding = providerBinding =
injector.getBindingOrThrow(providerKey, errors, JitLimitation.NEW_OR_EXISTING_JIT); injector.getBindingOrThrow(providerKey, errors, JitLimitation.NEW_OR_EXISTING_JIT);
} }
public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked) @Override
throws ErrorsException { public T get(InternalContext context, Dependency<?> dependency, boolean linked)
checkState(providerBinding != null, "not initialized"); throws InternalProvisionException {
BindingImpl<? extends Provider<T>> localProviderBinding = providerBinding;
if (localProviderBinding == null) {
throw new IllegalStateException("not initialized");
}
Key<? extends Provider<T>> localProviderKey = providerKey;
context.pushState(localProviderKey, localProviderBinding.getSource());
context.pushState(providerKey, providerBinding.getSource());
try { try {
errors = errors.withSource(providerKey); Provider<? extends T> provider =
Provider<? extends T> provider = providerBinding.getInternalFactory().get( localProviderBinding.getInternalFactory().get(context, dependency, true);
errors, context, dependency, true); return circularGet(provider, context, dependency, provisionCallback);
return circularGet(provider, errors, context, dependency, provisionCallback); } catch (InternalProvisionException ipe) {
throw ipe.addSource(localProviderKey);
} finally { } finally {
context.popState(); context.popState();
} }
} }
@Override @Override
protected T provision(javax.inject.Provider<? extends T> provider, Errors errors, protected T provision(javax.inject.Provider<? extends T> provider,
Dependency<?> dependency, ConstructionContext<T> constructionContext) Dependency<?> dependency,
throws ErrorsException { ConstructionContext<T> constructionContext)
throws InternalProvisionException {
try { try {
Object o = super.provision(provider, errors, dependency, constructionContext); Object o = super.provision(provider, dependency, constructionContext);
if (o != null && !rawType.isInstance(o)) { if (o != null && !rawType.isInstance(o)) {
throw errors.subtypeNotProvided(providerType, rawType).toException(); throw InternalProvisionException.subtypeNotProvided(providerType, rawType);
} }
@SuppressWarnings("unchecked") // protected by isInstance() check above @SuppressWarnings("unchecked") // protected by isInstance() check above
T t = (T) o; T t = (T) o;
return t; return t;
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw errors.errorInProvider(e).toException(); throw InternalProvisionException.errorInProvider(e).addSource(source);
} }
} }
} }

View file

@ -14,13 +14,13 @@ import com.google.inject.spi.ProviderWithExtensionVisitor;
import java.util.Set; import java.util.Set;
final class ProviderInstanceBindingImpl<T> extends BindingImpl<T> class ProviderInstanceBindingImpl<T> extends BindingImpl<T> implements ProviderInstanceBinding<T> {
implements ProviderInstanceBinding<T> {
final javax.inject.Provider<? extends T> providerInstance; private final javax.inject.Provider<? extends T> providerInstance;
final ImmutableSet<InjectionPoint> injectionPoints;
public ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key, private final ImmutableSet<InjectionPoint> injectionPoints;
ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key,
Object source, InternalFactory<? extends T> internalFactory, Scoping scoping, Object source, InternalFactory<? extends T> internalFactory, Scoping scoping,
javax.inject.Provider<? extends T> providerInstance, javax.inject.Provider<? extends T> providerInstance,
Set<InjectionPoint> injectionPoints) { Set<InjectionPoint> injectionPoints) {
@ -29,7 +29,7 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
this.injectionPoints = ImmutableSet.copyOf(injectionPoints); this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
} }
public ProviderInstanceBindingImpl(Object source, Key<T> key, Scoping scoping, ProviderInstanceBindingImpl(Object source, Key<T> key, Scoping scoping,
Set<InjectionPoint> injectionPoints, javax.inject.Provider<? extends T> providerInstance) { Set<InjectionPoint> injectionPoints, javax.inject.Provider<? extends T> providerInstance) {
super(source, key, scoping); super(source, key, scoping);
this.injectionPoints = ImmutableSet.copyOf(injectionPoints); this.injectionPoints = ImmutableSet.copyOf(injectionPoints);
@ -61,13 +61,11 @@ final class ProviderInstanceBindingImpl<T> extends BindingImpl<T>
} }
public BindingImpl<T> withScoping(Scoping scoping) { public BindingImpl<T> withScoping(Scoping scoping) {
return new ProviderInstanceBindingImpl<T>( return new ProviderInstanceBindingImpl<>(getSource(), getKey(), scoping, injectionPoints, providerInstance);
getSource(), getKey(), scoping, injectionPoints, providerInstance);
} }
public BindingImpl<T> withKey(Key<T> key) { public BindingImpl<T> withKey(Key<T> key) {
return new ProviderInstanceBindingImpl<T>( return new ProviderInstanceBindingImpl<>(getSource(), key, getScoping(), injectionPoints, providerInstance);
getSource(), key, getScoping(), injectionPoints, providerInstance);
} }
public void applyTo(Binder binder) { public void applyTo(Binder binder) {

View file

@ -1,6 +1,5 @@
package com.google.inject.internal; package com.google.inject.internal;
import com.google.inject.internal.ProvisionListenerStackCallback.ProvisionCallback;
import com.google.inject.spi.Dependency; import com.google.inject.spi.Dependency;
import javax.inject.Provider; import javax.inject.Provider;
@ -20,33 +19,28 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
this.source = checkNotNull(source, "source"); this.source = checkNotNull(source, "source");
} }
protected T circularGet(final Provider<? extends T> provider, final Errors errors, protected T circularGet(final Provider<? extends T> provider,
InternalContext context, final Dependency<?> dependency, InternalContext context,
final Dependency<?> dependency,
ProvisionListenerStackCallback<T> provisionCallback) ProvisionListenerStackCallback<T> provisionCallback)
throws ErrorsException { throws InternalProvisionException {
final ConstructionContext<T> constructionContext = context.getConstructionContext(this); final ConstructionContext<T> constructionContext = context.getConstructionContext(this);
// We have a circular reference between constructors. Return a proxy. // We have a circular reference between constructors. Return a proxy.
if (constructionContext.isConstructing()) { if (constructionContext.isConstructing()) {
Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType(); Class<?> expectedType = dependency.getKey().getTypeLiteral().getRawType();
// TODO: if we can't proxy this object, can we proxy the other object? // TODO: if we can't proxy this object, can we proxy the other object?
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
T proxyType = (T) constructionContext.createProxy( T proxyType = (T) constructionContext.createProxy(context.getInjectorOptions(), expectedType);
errors, context.getInjectorOptions(), expectedType);
return proxyType; return proxyType;
} }
// Optimization: Don't go through the callback stack if no one's listening. // Optimization: Don't go through the callback stack if no one's listening.
constructionContext.startConstruction(); constructionContext.startConstruction();
try { try {
if (!provisionCallback.hasListeners()) { if (!provisionCallback.hasListeners()) {
return provision(provider, errors, dependency, constructionContext); return provision(provider, dependency, constructionContext);
} else { } else {
return provisionCallback.provision(errors, context, new ProvisionCallback<T>() { return provisionCallback.provision(context, () ->
public T call() throws ErrorsException { provision(provider, dependency, constructionContext));
return provision(provider, errors, dependency, constructionContext);
}
});
} }
} finally { } finally {
constructionContext.removeCurrentReference(); constructionContext.removeCurrentReference();
@ -58,9 +52,12 @@ abstract class ProviderInternalFactory<T> implements InternalFactory<T> {
* Provisions a new instance. Subclasses should override this to catch * Provisions a new instance. Subclasses should override this to catch
* exceptions & rethrow as ErrorsExceptions. * exceptions & rethrow as ErrorsExceptions.
*/ */
protected T provision(Provider<? extends T> provider, Errors errors, Dependency<?> dependency, protected T provision(Provider<? extends T> provider, Dependency<?> dependency,
ConstructionContext<T> constructionContext) throws ErrorsException { ConstructionContext<T> constructionContext) throws InternalProvisionException {
T t = errors.checkForNull(provider.get(), source, dependency); T t = provider.get();
if (t == null && !dependency.isNullable()) {
InternalProvisionException.onNullInjectedIntoNonNullableDependency(source, dependency);
}
constructionContext.setProxyDelegates(t); constructionContext.setProxyDelegates(t);
return t; return t;
} }

View file

@ -6,7 +6,6 @@ import com.google.inject.Binder;
import com.google.inject.Exposed; import com.google.inject.Exposed;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.PrivateBinder; import com.google.inject.PrivateBinder;
import com.google.inject.Provider;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.internal.util.StackTraceElements; import com.google.inject.internal.util.StackTraceElements;
import com.google.inject.spi.BindingTargetVisitor; import com.google.inject.spi.BindingTargetVisitor;
@ -21,36 +20,50 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
* A provider that invokes a method and returns its result. * A provider that invokes a method and returns its result.
* *
*/ */
public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<T>, HasDependencies, public abstract class ProviderMethod<T>
ProvidesMethodBinding<T> { extends InternalProviderInstanceBindingImpl.CyclicFactory<T>
implements HasDependencies, ProviderWithExtensionVisitor<T>, ProvidesMethodBinding<T> {
protected final Object instance; protected final Object instance;
protected final Method method; protected final Method method;
private final Key<T> key; private final Key<T> key;
private final Class<? extends Annotation> scopeAnnotation; private final Class<? extends Annotation> scopeAnnotation;
private final ImmutableSet<Dependency<?>> dependencies; private final ImmutableSet<Dependency<?>> dependencies;
private final List<Provider<?>> parameterProviders;
private final boolean exposed; private final boolean exposed;
private final Annotation annotation; private final Annotation annotation;
/**
* Set by {@link #initialize(InjectorImpl, Errors)} so it is always available prior to injection.
*/
private SingleParameterInjector<?>[] parameterInjectors;
/** /**
* @param method the method to invoke. It's return type must be the same type as {@code key}. * @param method the method to invoke. It's return type must be the same type as {@code key}.
*/ */
private ProviderMethod(Key<T> key, Method method, Object instance, private ProviderMethod(Key<T> key,
ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders, Method method,
Class<? extends Annotation> scopeAnnotation, Annotation annotation) { Object instance,
ImmutableSet<Dependency<?>> dependencies,
Class<? extends Annotation> scopeAnnotation,
Annotation annotation) {
super(InternalProviderInstanceBindingImpl.InitializationTiming.EAGER);
this.key = key; this.key = key;
this.scopeAnnotation = scopeAnnotation; this.scopeAnnotation = scopeAnnotation;
this.instance = instance; this.instance = instance;
this.dependencies = dependencies; this.dependencies = dependencies;
this.method = method; this.method = method;
this.parameterProviders = parameterProviders;
this.exposed = method.isAnnotationPresent(Exposed.class); this.exposed = method.isAnnotationPresent(Exposed.class);
this.annotation = annotation; this.annotation = annotation;
} }
@ -58,24 +71,19 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
/** /**
* Creates a {@link ProviderMethod}. * Creates a {@link ProviderMethod}.
*/ */
static <T> ProviderMethod<T> create(Key<T> key, Method method, Object instance, static <T> ProviderMethod<T> create(Key<T> key,
ImmutableSet<Dependency<?>> dependencies, List<Provider<?>> parameterProviders, Method method,
Class<? extends Annotation> scopeAnnotation, boolean skipFastClassGeneration, Object instance,
ImmutableSet<Dependency<?>> dependencies,
Class<? extends Annotation> scopeAnnotation,
boolean skipFastClassGeneration,
Annotation annotation) { Annotation annotation) {
int modifiers = method.getModifiers(); int modifiers = method.getModifiers();
if (!Modifier.isPublic(modifiers) || if (!Modifier.isPublic(modifiers) ||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) { !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
method.setAccessible(true); method.setAccessible(true);
} }
return new ReflectionProviderMethod<>(key, method, instance, dependencies, scopeAnnotation, annotation);
return new ReflectionProviderMethod<T>(key,
method,
instance,
dependencies,
parameterProviders,
scopeAnnotation,
annotation);
} }
@Override @Override
@ -120,27 +128,31 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
} }
@Override @Override
public T get() { void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
Object[] parameters = new Object[parameterProviders.size()]; parameterInjectors = injector.getParametersInjectors(dependencies.asList(), errors);
for (int i = 0; i < parameters.length; i++) { }
parameters[i] = parameterProviders.get(i).get();
}
@Override
protected T doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException {
try { try {
@SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"}) T t = doProvision(SingleParameterInjector.getAll(context, parameterInjectors));
T result = (T) doProvision(parameters); if (t == null && !dependency.isNullable()) {
return result; InternalProvisionException.onNullInjectedIntoNonNullableDependency(getMethod(), dependency);
}
return t;
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new AssertionError(e); throw new AssertionError(e);
} catch (InvocationTargetException e) { } catch (InvocationTargetException userException) {
throw Exceptions.rethrowCause(e); Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
throw InternalProvisionException.errorInProvider(cause).addSource(getSource());
} }
} }
/** /**
* Extension point for our subclasses to implement the provisioning strategy. * Extension point for our subclasses to implement the provisioning strategy.
*/ */
abstract Object doProvision(Object[] parameters) abstract T doProvision(Object[] parameters)
throws IllegalAccessException, InvocationTargetException; throws IllegalAccessException, InvocationTargetException;
@Override @Override
@ -199,22 +211,21 @@ public abstract class ProviderMethod<T> implements ProviderWithExtensionVisitor<
Method method, Method method,
Object instance, Object instance,
ImmutableSet<Dependency<?>> dependencies, ImmutableSet<Dependency<?>> dependencies,
List<Provider<?>> parameterProviders,
Class<? extends Annotation> scopeAnnotation, Class<? extends Annotation> scopeAnnotation,
Annotation annotation) { Annotation annotation) {
super(key, super(key,
method, method,
instance, instance,
dependencies, dependencies,
parameterProviders,
scopeAnnotation, scopeAnnotation,
annotation); annotation);
} }
@SuppressWarnings("unchecked")
@Override @Override
Object doProvision(Object[] parameters) throws IllegalAccessException, T doProvision(Object[] parameters) throws IllegalAccessException,
InvocationTargetException { InvocationTargetException {
return method.invoke(instance, parameters); return (T) method.invoke(instance, parameters);
} }
} }
} }

View file

@ -1,17 +1,14 @@
package com.google.inject.internal; package com.google.inject.internal;
import com.google.common.base.Optional;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.inject.Binder; import com.google.inject.Binder;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Provides; import com.google.inject.Provides;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint; import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.Message; import com.google.inject.spi.Message;
import com.google.inject.spi.ModuleAnnotatedMethodScanner; import com.google.inject.spi.ModuleAnnotatedMethodScanner;
@ -21,11 +18,12 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Member; import java.lang.reflect.Member;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/** /**
* Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and * Creates bindings to methods annotated with {@literal @}{@link Provides}. Use the scope and
@ -34,23 +32,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
*/ */
public final class ProviderMethodsModule implements Module { public final class ProviderMethodsModule implements Module {
private static ModuleAnnotatedMethodScanner PROVIDES_BUILDER =
new ModuleAnnotatedMethodScanner() {
@Override
public <T> Key<T> prepareMethod(
Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) {
return key;
}
@Override
public Set<? extends Class<? extends Annotation>> annotationClasses() {
return ImmutableSet.of(Provides.class);
}
};
private final Object delegate; private final Object delegate;
private final TypeLiteral<?> typeLiteral; private final TypeLiteral<?> typeLiteral;
private final boolean skipFastClassGeneration; private final boolean skipFastClassGeneration;
private final ModuleAnnotatedMethodScanner scanner; private final ModuleAnnotatedMethodScanner scanner;
private ProviderMethodsModule(Object delegate, boolean skipFastClassGeneration, private ProviderMethodsModule(Object delegate, boolean skipFastClassGeneration,
@ -65,7 +52,7 @@ public final class ProviderMethodsModule implements Module {
* Returns a module which creates bindings for provider methods from the given module. * Returns a module which creates bindings for provider methods from the given module.
*/ */
public static Module forModule(Module module) { public static Module forModule(Module module) {
return forObject(module, false, PROVIDES_BUILDER); return forObject(module, false, ProvidesMethodScanner.INSTANCE);
} }
/** /**
@ -83,7 +70,7 @@ public final class ProviderMethodsModule implements Module {
* are only interested in Module metadata. * are only interested in Module metadata.
*/ */
public static Module forObject(Object object) { public static Module forObject(Object object) {
return forObject(object, true, PROVIDES_BUILDER); return forObject(object, true, ProvidesMethodScanner.INSTANCE);
} }
private static Module forObject(Object object, boolean skipFastClassGeneration, private static Module forObject(Object object, boolean skipFastClassGeneration,
@ -92,143 +79,143 @@ public final class ProviderMethodsModule implements Module {
if (object instanceof ProviderMethodsModule) { if (object instanceof ProviderMethodsModule) {
return Modules.EMPTY_MODULE; return Modules.EMPTY_MODULE;
} }
return new ProviderMethodsModule(object, skipFastClassGeneration, scanner); return new ProviderMethodsModule(object, skipFastClassGeneration, scanner);
} }
/** public Class<?> getDelegateModuleClass() {
* Returns true if a overrides b, assumes that the signatures match return isStaticModule() ? (Class<?>) delegate : delegate.getClass();
*/ }
private static boolean overrides(Method a, Method b) {
// See JLS section 8.4.8.1 @Override
int modifiers = b.getModifiers(); public void configure(Binder binder) {
if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) { for (ProviderMethod<?> providerMethod : getProviderMethods(binder)) {
return true; providerMethod.configure(binder);
} }
if (Modifier.isPrivate(modifiers)) { }
return false;
} private boolean isStaticModule() {
// b must be package-private return delegate instanceof Class;
return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
} }
public Object getDelegateModule() { public Object getDelegateModule() {
return delegate; return delegate;
} }
@Override
public synchronized void configure(Binder binder) {
for (ProviderMethod<?> providerMethod : getProviderMethods(binder)) {
providerMethod.configure(binder);
}
}
public List<ProviderMethod<?>> getProviderMethods(Binder binder) { public List<ProviderMethod<?>> getProviderMethods(Binder binder) {
List<ProviderMethod<?>> result = Lists.newArrayList(); List<ProviderMethod<?>> result = null;
Multimap<Signature, Method> methodsBySignature = HashMultimap.create(); List<MethodAndAnnotation> methodsAndAnnotations = null;
for (Class<?> c = delegate.getClass(); c != Object.class; c = c.getSuperclass()) { // The highest class in the type hierarchy that contained a provider method definition.
for (Method method : c.getDeclaredMethods()) { Class<?> superMostClass = getDelegateModuleClass();
// private/static methods cannot override or be overridden by other methods, so there is no for (Class<?> c = superMostClass; c != Object.class && c != null; c = c.getSuperclass()) {
// point in indexing them. for (Method method : DeclaredMembers.getDeclaredMethods(c)) {
// Skip synthetic methods and bridge methods since java will automatically generate Annotation annotation = getAnnotation(binder, method);
// synthetic overrides in some cases where we don't want to generate an error (e.g. if (annotation != null) {
// increasing visibility of a subclass). if (isStaticModule()
if (((method.getModifiers() & (Modifier.PRIVATE | Modifier.STATIC)) == 0) && !Modifier.isStatic(method.getModifiers())
&& !method.isBridge() && !method.isSynthetic()) { && !Modifier.isAbstract(method.getModifiers())) {
methodsBySignature.put(new Signature(method), method); binder
} .skipSources(ProviderMethodsModule.class)
Optional<Annotation> annotation = isProvider(binder, method); .addError(
if (annotation.isPresent()) { "%s is an instance method, but a class literal was passed. Make this method"
result.add(createProviderMethod(binder, method, annotation.get())); + " static or pass an instance of the module instead.",
method);
continue;
}
if (result == null) {
result = new ArrayList<>();
methodsAndAnnotations = new ArrayList<>();
}
ProviderMethod<Object> providerMethod = createProviderMethod(binder, method, annotation);
if (providerMethod != null) {
result.add(providerMethod);
}
methodsAndAnnotations.add(new MethodAndAnnotation(method, annotation));
superMostClass = c;
} }
} }
} }
// we have found all the providers and now need to identify if any were overridden if (result == null) {
// In the worst case this will have O(n^2) in the number of @Provides methods, but that is only // We didn't find anything
// assuming that every method is an override, in general it should be very quick. return ImmutableList.of();
for (ProviderMethod<?> provider : result) { }
Method method = provider.getMethod(); // We have found some provider methods, now we need to check if any were overridden.
for (Method matchingSignature : methodsBySignature.get(new Signature(method))) { // We do this as a separate pass to avoid calculating all the signatures when there are no
// matching signature is in the same class or a super class, therefore method cannot be // provides methods, or when all provides methods are defined in a single class.
// overridding it. Multimap<Signature, Method> methodsBySignature = null;
if (matchingSignature.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) { // We can stop scanning when we see superMostClass, since no superclass method can override
continue; // a method in a subclass. Corrollary, if superMostClass == getDelegateModuleClass(), there can
// be no overrides of a provides method.
for (Class<?> c = getDelegateModuleClass(); c != superMostClass; c = c.getSuperclass()) {
for (Method method : c.getDeclaredMethods()) {
if (((method.getModifiers() & (Modifier.PRIVATE | Modifier.STATIC)) == 0)
&& !method.isBridge()
&& !method.isSynthetic()) {
if (methodsBySignature == null) {
methodsBySignature = HashMultimap.create();
}
methodsBySignature.put(new Signature(typeLiteral, method), method);
} }
// now we know matching signature is in a subtype of method.getDeclaringClass() }
if (overrides(matchingSignature, method)) { }
String annotationString = provider.getAnnotation().annotationType() == Provides.class if (methodsBySignature != null) {
? "@Provides" : "@" + provider.getAnnotation().annotationType().getCanonicalName(); // we have found all the signatures and now need to identify if any were overridden
binder.addError( // In the worst case this will have O(n^2) in the number of @Provides methods, but that is
"Overriding " + annotationString + " methods is not allowed." // only assuming that every method is an override, in general it should be very quick.
+ "\n\t" + annotationString + " method: %s\n\toverridden by: %s", for (MethodAndAnnotation methodAndAnnotation : methodsAndAnnotations) {
method, Method method = methodAndAnnotation.method;
matchingSignature); Annotation annotation = methodAndAnnotation.annotation;
break;
for (Method matchingSignature :
methodsBySignature.get(new Signature(typeLiteral, method))) {
// matching signature is in the same class or a super class, therefore method cannot be
// overridding it.
if (matchingSignature.getDeclaringClass().isAssignableFrom(method.getDeclaringClass())) {
continue;
}
// now we know matching signature is in a subtype of method.getDeclaringClass()
if (overrides(matchingSignature, method)) {
String annotationString =
annotation.annotationType() == Provides.class
? "@Provides"
: "@" + annotation.annotationType().getCanonicalName();
binder.addError(
"Overriding "
+ annotationString
+ " methods is not allowed."
+ "\n\t"
+ annotationString
+ " method: %s\n\toverridden by: %s",
method,
matchingSignature);
break;
}
} }
} }
} }
return result; return result;
} }
/** /** Returns the annotation that is claimed by the scanner, or null if there is none. */
* Returns true if the method is a provider. private Annotation getAnnotation(Binder binder, Method method) {
*
* Synthetic bridge methods are excluded. Starting with JDK 8, javac copies annotations onto
* bridge methods (which always have erased signatures).
*/
private Optional<Annotation> isProvider(Binder binder, Method method) {
if (method.isBridge() || method.isSynthetic()) { if (method.isBridge() || method.isSynthetic()) {
return Optional.absent(); return null;
} }
Annotation annotation = null; Annotation annotation = null;
for (Class<? extends Annotation> annotationClass : scanner.annotationClasses()) { for (Class<? extends Annotation> annotationClass : scanner.annotationClasses()) {
Annotation foundAnnotation = method.getAnnotation(annotationClass); Annotation foundAnnotation = method.getAnnotation(annotationClass);
if (foundAnnotation != null) { if (foundAnnotation != null) {
if (annotation != null) { if (annotation != null) {
binder.addError("More than one annotation claimed by %s on method %s." binder.addError(
"More than one annotation claimed by %s on method %s."
+ " Methods can only have one annotation claimed per scanner.", + " Methods can only have one annotation claimed per scanner.",
scanner, method); scanner, method);
return Optional.absent(); return null;
} }
annotation = foundAnnotation; annotation = foundAnnotation;
} }
} }
return Optional.fromNullable(annotation); return annotation;
}
private <T> ProviderMethod<T> createProviderMethod(Binder binder, Method method,
Annotation annotation) {
binder = binder.withSource(method);
Errors errors = new Errors(method);
// prepare the parameter providers
InjectionPoint point = InjectionPoint.forMethod(method, typeLiteral);
List<Dependency<?>> dependencies = point.getDependencies();
List<Provider<?>> parameterProviders = Lists.newArrayList();
for (Dependency<?> dependency : point.getDependencies()) {
parameterProviders.add(binder.getProvider(dependency));
}
@SuppressWarnings("unchecked") // Define T as the method's return type.
TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method);
Key<T> key = getKey(errors, returnType, method, method.getAnnotations());
try {
key = scanner.prepareMethod(binder, annotation, key, point);
} catch (Throwable t) {
binder.addError(t);
}
Class<? extends Annotation> scopeAnnotation
= Annotations.findScopeAnnotation(errors, method.getAnnotations());
for (Message message : errors.getMessages()) {
binder.addError(message);
}
return ProviderMethod.create(key, method, delegate, ImmutableSet.copyOf(dependencies),
parameterProviders, scopeAnnotation, skipFastClassGeneration, annotation);
}
<T> Key<T> getKey(Errors errors, TypeLiteral<T> type, Member member, Annotation[] annotations) {
Annotation bindingAnnotation = Annotations.findBindingAnnotation(errors, member, annotations);
return bindingAnnotation == null ? Key.get(type) : Key.get(type, bindingAnnotation);
} }
@Override @Override
@ -243,12 +230,23 @@ public final class ProviderMethodsModule implements Module {
return delegate.hashCode(); return delegate.hashCode();
} }
private final class Signature { private static class MethodAndAnnotation {
final Method method;
final Annotation annotation;
MethodAndAnnotation(Method method, Annotation annotation) {
this.method = method;
this.annotation = annotation;
}
}
private static class Signature {
final Class<?>[] parameters; final Class<?>[] parameters;
final String name; final String name;
final int hashCode; final int hashCode;
Signature(Method method) { Signature(TypeLiteral<?> typeLiteral, Method method) {
this.name = method.getName(); this.name = method.getName();
// We need to 'resolve' the parameters against the actual class type in case this method uses // We need to 'resolve' the parameters against the actual class type in case this method uses
// type parameters. This is so we can detect overrides of generic superclass methods where // type parameters. This is so we can detect overrides of generic superclass methods where
@ -278,4 +276,75 @@ public final class ProviderMethodsModule implements Module {
return hashCode; return hashCode;
} }
} }
/** Returns true if a overrides b, assumes that the signatures match */
private static boolean overrides(Method a, Method b) {
// See JLS section 8.4.8.1
int modifiers = b.getModifiers();
if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
return true;
}
if (Modifier.isPrivate(modifiers)) {
return false;
}
// b must be package-private
return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
}
private <T> ProviderMethod<T> createProviderMethod(
Binder binder, Method method, Annotation annotation) {
binder = binder.withSource(method);
Errors errors = new Errors(method);
// prepare the parameter providers
InjectionPoint point = InjectionPoint.forMethod(method, typeLiteral);
@SuppressWarnings("unchecked") // Define T as the method's return type.
TypeLiteral<T> returnType = (TypeLiteral<T>) typeLiteral.getReturnType(method);
Key<T> key = getKey(errors, returnType, method, method.getAnnotations());
try {
key = scanner.prepareMethod(binder, annotation, key, point);
} catch (Throwable t) {
binder.addError(t);
}
if (Modifier.isAbstract(method.getModifiers())) {
checkState(
key == null,
"%s returned a non-null key (%s) for %s. prepareMethod() must return null for abstract"
+ " methods",
scanner,
key,
method);
return null;
} else {
checkState(
key != null,
"%s returned a null key for %s. prepareMethod() can only return null for abstract"
+ " methods",
scanner,
method);
}
Class<? extends Annotation> scopeAnnotation =
Annotations.findScopeAnnotation(errors, method.getAnnotations());
for (Message message : errors.getMessages()) {
binder.addError(message);
}
return ProviderMethod.create(
key,
method,
isStaticModule() ? null : delegate,
ImmutableSet.copyOf(point.getDependencies()),
scopeAnnotation,
skipFastClassGeneration,
annotation);
}
<T> Key<T> getKey(Errors errors, TypeLiteral<T> type, Member member, Annotation[] annotations) {
Annotation bindingAnnotation = Annotations.findBindingAnnotation(errors, member, annotations);
return bindingAnnotation == null ? Key.get(type) : Key.get(type, bindingAnnotation);
}
} }

View file

@ -1,12 +1,11 @@
package com.google.inject.internal; package com.google.inject.internal;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.spi.Dependency;
final class ProviderToInternalFactoryAdapter<T> implements Provider<T> { final class ProviderToInternalFactoryAdapter<T> implements Provider<T> {
private final InjectorImpl injector; private final InjectorImpl injector;
private final InternalFactory<? extends T> internalFactory; private final InternalFactory<? extends T> internalFactory;
public ProviderToInternalFactoryAdapter(InjectorImpl injector, public ProviderToInternalFactoryAdapter(InjectorImpl injector,
@ -15,25 +14,23 @@ final class ProviderToInternalFactoryAdapter<T> implements Provider<T> {
this.internalFactory = internalFactory; this.internalFactory = internalFactory;
} }
@Override
public T get() { public T get() {
final Errors errors = new Errors(); try (InternalContext context = injector.enterContext()) {
try { // Always pretend that we are a linked binding, to support
T t = injector.callInContext(new ContextualCallable<T>() { // scoping implicit bindings. If we are not actually a linked
public T call(InternalContext context) throws ErrorsException { // binding, we'll fail properly elsewhere in the chain.
Dependency dependency = context.getDependency(); return internalFactory.get(context, context.getDependency(), true);
// Always pretend that we are a linked binding, to support } catch (InternalProvisionException e) {
// scoping implicit bindings. If we are not actually a linked throw e.toProvisionException();
// binding, we'll fail properly elsewhere in the chain.
return internalFactory.get(errors, context, dependency, true);
}
});
errors.throwIfNewErrors(0);
return t;
} catch (ErrorsException e) {
throw new ProvisionException(errors.merge(e.getErrors()).getMessages());
} }
} }
/** Exposed for SingletonScope. */
InjectorImpl getInjector() {
return injector;
}
@Override @Override
public String toString() { public String toString() {
return internalFactory.toString(); return internalFactory.toString();

View file

@ -0,0 +1,159 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.MapKey;
import com.google.inject.multibindings.ProvidesIntoMap;
import com.google.inject.multibindings.ProvidesIntoOptional;
import com.google.inject.multibindings.ProvidesIntoSet;
import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.ModuleAnnotatedMethodScanner;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
/**
* A {@link ModuleAnnotatedMethodScanner} that handles the {@link Provides}, {@link ProvidesIntoSet},
* {@link ProvidesIntoMap} and {@link ProvidesIntoOptional} annotations.
*
* <p>This is the default scanner used by ProviderMethodsModule and handles all the built in
* annotations.
*/
final class ProvidesMethodScanner extends ModuleAnnotatedMethodScanner {
static final ProvidesMethodScanner INSTANCE = new ProvidesMethodScanner();
private static final ImmutableSet<Class<? extends Annotation>> ANNOTATIONS =
ImmutableSet.of(Provides.class, ProvidesIntoSet.class, ProvidesIntoMap.class, ProvidesIntoOptional.class);
private ProvidesMethodScanner() {}
@Override
public Set<? extends Class<? extends Annotation>> annotationClasses() {
return ANNOTATIONS;
}
@SuppressWarnings({"unchecked", "rawtypes"}) // mapKey doesn't know its key type
@Override
public <T> Key<T> prepareMethod(
Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) {
Method method = (Method) injectionPoint.getMember();
AnnotationOrError mapKey = findMapKeyAnnotation(binder, method);
if (annotation instanceof Provides) {
if (mapKey.annotation != null) {
binder.addError("Found a MapKey annotation on non map binding at %s.", method);
}
// no key rewriting for plain old @Provides
return key;
}
if (annotation instanceof ProvidesIntoSet) {
if (mapKey.annotation != null) {
binder.addError("Found a MapKey annotation on non map binding at %s.", method);
}
return RealMultibinder.newRealSetBinder(binder, key).getKeyForNewItem();
} else if (annotation instanceof ProvidesIntoMap) {
if (mapKey.error) {
// Already failed on the MapKey, don't bother doing more work.
return key;
}
if (mapKey.annotation == null) {
// If no MapKey, make an error and abort.
binder.addError("No MapKey found for map binding at %s.", method);
return key;
}
TypeAndValue typeAndValue = typeAndValueOfMapKey(mapKey.annotation);
return RealMapBinder.newRealMapBinder(binder, typeAndValue.type, key)
.getKeyForNewValue(typeAndValue.value);
} else if (annotation instanceof ProvidesIntoOptional) {
if (mapKey.annotation != null) {
binder.addError("Found a MapKey annotation on non map binding at %s.", method);
}
switch (((ProvidesIntoOptional) annotation).value()) {
case DEFAULT:
return RealOptionalBinder.newRealOptionalBinder(binder, key).getKeyForDefaultBinding();
case ACTUAL:
return RealOptionalBinder.newRealOptionalBinder(binder, key).getKeyForActualBinding();
}
}
throw new IllegalStateException("Invalid annotation: " + annotation);
}
private static class AnnotationOrError {
final Annotation annotation;
final boolean error;
AnnotationOrError(Annotation annotation, boolean error) {
this.annotation = annotation;
this.error = error;
}
static AnnotationOrError forPossiblyNullAnnotation(Annotation annotation) {
return new AnnotationOrError(annotation, false);
}
static AnnotationOrError forError() {
return new AnnotationOrError(null, true);
}
}
private static AnnotationOrError findMapKeyAnnotation(Binder binder, Method method) {
Annotation foundAnnotation = null;
for (Annotation annotation : method.getAnnotations()) {
MapKey mapKey = annotation.annotationType().getAnnotation(MapKey.class);
if (mapKey != null) {
if (foundAnnotation != null) {
binder.addError("Found more than one MapKey annotations on %s.", method);
return AnnotationOrError.forError();
}
if (mapKey.unwrapValue()) {
try {
// validate there's a declared method called "value"
Method valueMethod = annotation.annotationType().getDeclaredMethod("value");
if (valueMethod.getReturnType().isArray()) {
binder.addError(
"Array types are not allowed in a MapKey with unwrapValue=true: %s",
annotation.annotationType());
return AnnotationOrError.forError();
}
} catch (NoSuchMethodException invalid) {
binder.addError(
"No 'value' method in MapKey with unwrapValue=true: %s",
annotation.annotationType());
return AnnotationOrError.forError();
}
}
foundAnnotation = annotation;
}
}
return AnnotationOrError.forPossiblyNullAnnotation(foundAnnotation);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static TypeAndValue<?> typeAndValueOfMapKey(Annotation mapKeyAnnotation) {
if (!mapKeyAnnotation.annotationType().getAnnotation(MapKey.class).unwrapValue()) {
return new TypeAndValue(TypeLiteral.get(mapKeyAnnotation.annotationType()), mapKeyAnnotation);
} else {
try {
Method valueMethod = mapKeyAnnotation.annotationType().getDeclaredMethod("value");
valueMethod.setAccessible(true);
TypeLiteral<?> returnType =
TypeLiteral.get(mapKeyAnnotation.annotationType()).getReturnType(valueMethod);
return new TypeAndValue(returnType, valueMethod.invoke(mapKeyAnnotation));
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
}
private static class TypeAndValue<T> {
final TypeLiteral<T> type;
final T value;
TypeAndValue(TypeLiteral<T> type, T value) {
this.type = type;
this.value = value;
}
}
}

View file

@ -29,9 +29,9 @@ final class ProvisionListenerCallbackStore {
private final ImmutableList<ProvisionListenerBinding> listenerBindings; private final ImmutableList<ProvisionListenerBinding> listenerBindings;
private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache private final LoadingCache<KeyBinding, ProvisionListenerStackCallback<?>> cache = CacheBuilder.newBuilder().build(
= CacheBuilder.newBuilder().build( new CacheLoader<>() {
new CacheLoader<KeyBinding, ProvisionListenerStackCallback<?>>() { @Override
public ProvisionListenerStackCallback<?> load(KeyBinding key) { public ProvisionListenerStackCallback<?> load(KeyBinding key) {
return create(key.binding); return create(key.binding);
} }

View file

@ -3,8 +3,6 @@ package com.google.inject.internal;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.inject.Binding; import com.google.inject.Binding;
import com.google.inject.ProvisionException;
import com.google.inject.spi.DependencyAndSource;
import com.google.inject.spi.ProvisionListener; import com.google.inject.spi.ProvisionListener;
import java.util.List; import java.util.List;
@ -16,7 +14,8 @@ import java.util.Set;
*/ */
final class ProvisionListenerStackCallback<T> { final class ProvisionListenerStackCallback<T> {
private static final ProvisionListener EMPTY_LISTENER[] = new ProvisionListener[0]; private static final ProvisionListener[] EMPTY_LISTENER = new ProvisionListener[0];
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
private static final ProvisionListenerStackCallback<?> EMPTY_CALLBACK = private static final ProvisionListenerStackCallback<?> EMPTY_CALLBACK =
new ProvisionListenerStackCallback(null /* unused, so ok */, ImmutableList.of()); new ProvisionListenerStackCallback(null /* unused, so ok */, ImmutableList.of());
@ -43,9 +42,9 @@ final class ProvisionListenerStackCallback<T> {
return listeners.length > 0; return listeners.length > 0;
} }
public T provision(Errors errors, InternalContext context, ProvisionCallback<T> callable) public T provision(InternalContext context, ProvisionCallback<T> callable)
throws ErrorsException { throws InternalProvisionException {
Provision provision = new Provision(errors, context, callable); Provision provision = new Provision(context, callable);
RuntimeException caught = null; RuntimeException caught = null;
try { try {
provision.provision(); provision.provision();
@ -56,12 +55,14 @@ final class ProvisionListenerStackCallback<T> {
if (provision.exceptionDuringProvision != null) { if (provision.exceptionDuringProvision != null) {
throw provision.exceptionDuringProvision; throw provision.exceptionDuringProvision;
} else if (caught != null) { } else if (caught != null) {
Object listener = provision.erredListener != null ? Object listener =
provision.erredListener.getClass() : "(unknown)"; provision.erredListener != null ? provision.erredListener.getClass() : "(unknown)";
throw errors throw InternalProvisionException.errorInUserCode(
.errorInUserCode(caught, "Error notifying ProvisionListener %s of %s.%n" caught,
+ " Reason: %s", listener, binding.getKey(), caught) "Error notifying ProvisionListener %s of %s.%n Reason: %s",
.toException(); listener,
binding.getKey(),
caught);
} else { } else {
return provision.result; return provision.result;
} }
@ -69,25 +70,21 @@ final class ProvisionListenerStackCallback<T> {
// TODO(sameb): Can this be more InternalFactory-like? // TODO(sameb): Can this be more InternalFactory-like?
public interface ProvisionCallback<T> { public interface ProvisionCallback<T> {
T call() throws ErrorsException; T call() throws InternalProvisionException;
} }
private class Provision extends ProvisionListener.ProvisionInvocation<T> { private class Provision extends ProvisionListener.ProvisionInvocation<T> {
final Errors errors;
final int numErrorsBefore;
final InternalContext context; final InternalContext context;
final ProvisionCallback<T> callable; final ProvisionCallback<T> callable;
int index = -1; int index = -1;
T result; T result;
ErrorsException exceptionDuringProvision; InternalProvisionException exceptionDuringProvision;
ProvisionListener erredListener; ProvisionListener erredListener;
public Provision(Errors errors, InternalContext context, ProvisionCallback<T> callable) { public Provision(InternalContext context, ProvisionCallback<T> callable) {
this.callable = callable; this.callable = callable;
this.context = context; this.context = context;
this.errors = errors;
this.numErrorsBefore = errors.size();
} }
@Override @Override
@ -96,12 +93,9 @@ final class ProvisionListenerStackCallback<T> {
if (index == listeners.length) { if (index == listeners.length) {
try { try {
result = callable.call(); result = callable.call();
// Make sure we don't return the provisioned object if there were any errors } catch (InternalProvisionException ipe) {
// injecting its field/method dependencies. exceptionDuringProvision = ipe;
errors.throwIfNewErrors(numErrorsBefore); throw ipe.toProvisionException();
} catch (ErrorsException ee) {
exceptionDuringProvision = ee;
throw new ProvisionException(errors.merge(ee.getErrors()).getMessages());
} }
} else if (index < listeners.length) { } else if (index < listeners.length) {
int currentIdx = index; int currentIdx = index;
@ -128,10 +122,5 @@ final class ProvisionListenerStackCallback<T> {
// if someone calls that they'll get strange errors. // if someone calls that they'll get strange errors.
return binding; return binding;
} }
@Override
public List<DependencyAndSource> getDependencyChain() {
return context.getDependencyChain();
}
} }
} }

View file

@ -1,7 +1,6 @@
package com.google.inject.multibindings; package com.google.inject.internal;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.internal.Annotations;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,611 @@
package com.google.inject.internal;
import static com.google.inject.internal.Element.Type.MULTIBINDER;
import static com.google.inject.internal.Errors.checkConfiguration;
import static com.google.inject.internal.Errors.checkNotNull;
import static com.google.inject.name.Names.named;
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
import com.google.inject.multibindings.MultibinderBinding;
import com.google.inject.multibindings.MultibindingsTargetVisitor;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.util.Types;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* The actual multibinder plays several roles:
*
* <p>As a Multibinder, it acts as a factory for LinkedBindingBuilders for each of the set's
* elements. Each binding is given an annotation that identifies it as a part of this set.
*
* <p>As a Module, it installs the binding to the set itself. As a module, this implements equals()
* and hashcode() in order to trick Guice into executing its configure() method only once. That
* makes it so that multiple multibinders can be created for the same target collection, but only
* one is bound. Since the list of bindings is retrieved from the injector itself (and not the
* multibinder), each multibinder has access to all contributions from all multibinders.
*
* <p>As a Provider, this constructs the set instances.
*
* <p>We use a subclass to hide 'implements Module, Provider' from the public API.
*/
public final class RealMultibinder<T> implements Module {
/** Implementation of newSetBinder. */
public static <T> RealMultibinder<T> newRealSetBinder(Binder binder, Key<T> key) {
binder = binder.skipSources(RealMultibinder.class);
RealMultibinder<T> result = new RealMultibinder<>(binder, key);
binder.install(result);
return result;
}
@SuppressWarnings("unchecked") // wrapping a T in a Set safely returns a Set<T>
static <T> TypeLiteral<Set<T>> setOf(TypeLiteral<T> elementType) {
Type type = Types.setOf(elementType.getType());
return (TypeLiteral<Set<T>>) TypeLiteral.get(type);
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Collection<Provider<T>>> collectionOfProvidersOf(
TypeLiteral<T> elementType) {
Type providerType = Types.providerOf(elementType.getType());
Type type = Types.collectionOf(providerType);
return (TypeLiteral<Collection<Provider<T>>>) TypeLiteral.get(type);
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersOf(
TypeLiteral<T> elementType) {
Type providerType =
Types.newParameterizedType(javax.inject.Provider.class, elementType.getType());
Type type = Types.collectionOf(providerType);
return (TypeLiteral<Collection<javax.inject.Provider<T>>>) TypeLiteral.get(type);
}
private final BindingSelection<T> bindingSelection;
private final Binder binder;
RealMultibinder(Binder binder, Key<T> key) {
this.binder = checkNotNull(binder, "binder");
this.bindingSelection = new BindingSelection<>(key);
}
@Override
public void configure(Binder binder) {
checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
binder
.bind(bindingSelection.getSetKey())
.toProvider(new RealMultibinderProvider<T>(bindingSelection));
Provider<Collection<Provider<T>>> collectionOfProvidersProvider =
new RealMultibinderCollectionOfProvidersProvider<T>(bindingSelection);
binder
.bind(bindingSelection.getCollectionOfProvidersKey())
.toProvider(collectionOfProvidersProvider);
// The collection this exposes is internally an ImmutableList, so it's OK to massage
// the guice Provider to javax Provider in the value (since the guice Provider implements
// javax Provider).
@SuppressWarnings("unchecked")
Provider<Collection<javax.inject.Provider<T>>> javaxProvider =
(Provider) collectionOfProvidersProvider;
binder.bind(bindingSelection.getCollectionOfJavaxProvidersKey()).toProvider(javaxProvider);
}
public void permitDuplicates() {
binder.install(new PermitDuplicatesModule(bindingSelection.getPermitDuplicatesKey()));
}
/** Adds a new entry to the set and returns the key for it. */
Key<T> getKeyForNewItem() {
checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
return Key.get(
bindingSelection.getElementTypeLiteral(),
new RealElement(bindingSelection.getSetName(), MULTIBINDER, ""));
}
public LinkedBindingBuilder<T> addBinding() {
return binder.bind(getKeyForNewItem());
}
// These methods are used by RealMapBinder
Key<Set<T>> getSetKey() {
return bindingSelection.getSetKey();
}
TypeLiteral<T> getElementTypeLiteral() {
return bindingSelection.getElementTypeLiteral();
}
String getSetName() {
return bindingSelection.getSetName();
}
boolean permitsDuplicates(Injector injector) {
return bindingSelection.permitsDuplicates(injector);
}
boolean containsElement(com.google.inject.spi.Element element) {
return bindingSelection.containsElement(element);
}
private static final class RealMultibinderProvider<T>
extends InternalProviderInstanceBindingImpl.Factory<Set<T>>
implements ProviderWithExtensionVisitor<Set<T>>, MultibinderBinding<Set<T>> {
private final BindingSelection<T> bindingSelection;
private List<Binding<T>> bindings;
private SingleParameterInjector<T>[] injectors;
private boolean permitDuplicates;
RealMultibinderProvider(BindingSelection<T> bindingSelection) {
// While Multibinders only depend on bindings created in modules so we could theoretically
// initialize eagerly, they also depend on
// 1. findBindingsByType returning results
// 2. being able to call BindingImpl.acceptTargetVisitor
// neither of those is available during eager initialization, so we use DELAYED
super(InitializationTiming.DELAYED);
this.bindingSelection = bindingSelection;
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.getDependencies();
}
@Override
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
bindingSelection.initialize(injector, errors);
this.bindings = bindingSelection.getBindings();
this.injectors = bindingSelection.getParameterInjectors();
this.permitDuplicates = bindingSelection.permitsDuplicates();
}
@Override
protected Set<T> doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException {
SingleParameterInjector<T>[] localInjectors = injectors;
if (localInjectors == null) {
// if localInjectors == null, then we have no bindings so return the empty set.
return ImmutableSet.of();
}
// Ideally we would just add to an ImmutableSet.Builder, but if we did that and there were
// duplicates we wouldn't be able to tell which one was the duplicate. So to manage this we
// first put everything into an array and then construct the set. This way if something gets
// dropped we can figure out what it is.
@SuppressWarnings("unchecked")
T[] values = (T[]) new Object[localInjectors.length];
for (int i = 0; i < localInjectors.length; i++) {
SingleParameterInjector<T> parameterInjector = localInjectors[i];
T newValue = parameterInjector.inject(context);
if (newValue == null) {
throw newNullEntryException(i);
}
values[i] = newValue;
}
ImmutableSet<T> set = ImmutableSet.copyOf(values);
// There are fewer items in the set than the array. Figure out which one got dropped.
if (!permitDuplicates && set.size() < values.length) {
throw newDuplicateValuesException(set, values);
}
return set;
}
private InternalProvisionException newNullEntryException(int i) {
return InternalProvisionException.create(
"Set injection failed due to null element bound at: %s", bindings.get(i).getSource());
}
@SuppressWarnings("unchecked")
@Override
public <B, V> V acceptExtensionVisitor(
BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof MultibindingsTargetVisitor) {
return ((MultibindingsTargetVisitor<Set<T>, V>) visitor).visit(this);
} else {
return visitor.visit(binding);
}
}
private InternalProvisionException newDuplicateValuesException(
ImmutableSet<T> set, T[] values) {
// TODO(lukes): consider reporting all duplicate values, the easiest way would be to rebuild
// a new set and detect dupes as we go
// Find the duplicate binding
// To do this we take advantage of the fact that set, values and bindings all have the same
// ordering for a non-empty prefix of the set.
// First we scan for the first item dropped from the set.
int newBindingIndex = 0;
for (T item : set) {
if (item != values[newBindingIndex]) {
break;
}
newBindingIndex++;
}
// once we exit the loop newBindingIndex will point at the first item in values that was
// dropped.
Binding<T> newBinding = bindings.get(newBindingIndex);
T newValue = values[newBindingIndex];
// Now we scan again to find the index of the value, we are guaranteed to find it.
int oldBindingIndex = set.asList().indexOf(newValue);
T oldValue = values[oldBindingIndex];
Binding<T> duplicateBinding = bindings.get(oldBindingIndex);
String oldString = oldValue.toString();
String newString = newValue.toString();
if (Objects.equal(oldString, newString)) {
// When the value strings match, just show the source of the bindings
return InternalProvisionException.create(
"Set injection failed due to duplicated element \"%s\""
+ "\n Bound at %s\n Bound at %s",
newValue, duplicateBinding.getSource(), newBinding.getSource());
} else {
// When the value strings don't match, include them both as they may be useful for debugging
return InternalProvisionException.create(
"Set injection failed due to multiple elements comparing equal:"
+ "\n \"%s\"\n bound at %s"
+ "\n \"%s\"\n bound at %s",
oldValue, duplicateBinding.getSource(), newValue, newBinding.getSource());
}
}
@Override
public boolean equals(Object obj) {
return obj instanceof RealMultibinderProvider
&& bindingSelection.equals(((RealMultibinderProvider<?>) obj).bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
@Override
public Key<Set<T>> getSetKey() {
return bindingSelection.getSetKey();
}
@Override
public Set<Key<?>> getAlternateSetKeys() {
return ImmutableSet.of(
(Key<?>) bindingSelection.getCollectionOfProvidersKey(),
(Key<?>) bindingSelection.getCollectionOfJavaxProvidersKey());
}
@Override
public TypeLiteral<?> getElementTypeLiteral() {
return bindingSelection.getElementTypeLiteral();
}
@Override
public List<Binding<?>> getElements() {
return bindingSelection.getElements();
}
@Override
public boolean permitsDuplicates() {
return bindingSelection.permitsDuplicates();
}
@Override
public boolean containsElement(com.google.inject.spi.Element element) {
return bindingSelection.containsElement(element);
}
}
private static final class BindingSelection<T> {
// prior to initialization we declare just a dependency on the injector, but as soon as we are
// initialized we swap to dependencies on the elements.
private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
private final TypeLiteral<T> elementType;
private final Key<Set<T>> setKey;
// these are all lazily allocated
private String setName;
private Key<Collection<Provider<T>>> collectionOfProvidersKey;
private Key<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersKey;
private Key<Boolean> permitDuplicatesKey;
private boolean isInitialized;
/* a binding for each element in the set. null until initialization, non-null afterwards */
private ImmutableList<Binding<T>> bindings;
// Starts out as Injector and gets set up properly after initialization
private ImmutableSet<Dependency<?>> dependencies = MODULE_DEPENDENCIES;
private ImmutableSet<Dependency<?>> providerDependencies = MODULE_DEPENDENCIES;
/** whether duplicates are allowed. Possibly configured by a different instance */
private boolean permitDuplicates;
private SingleParameterInjector<T>[] parameterinjectors;
BindingSelection(Key<T> key) {
this.setKey = key.ofType(setOf(key.getTypeLiteral()));
this.elementType = key.getTypeLiteral();
}
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
// This will be called multiple times, once by each Factory. We only want
// to do the work to initialize everything once, so guard this code with
// isInitialized.
if (isInitialized) {
return;
}
List<Binding<T>> bindings = Lists.newArrayList();
Set<Indexer.IndexedBinding> index = Sets.newHashSet();
Indexer indexer = new Indexer(injector);
List<Dependency<?>> dependencies = Lists.newArrayList();
List<Dependency<?>> providerDependencies = Lists.newArrayList();
for (Binding<?> entry : injector.findBindingsByType(elementType)) {
if (keyMatches(entry.getKey())) {
@SuppressWarnings("unchecked") // protected by findBindingsByType()
Binding<T> binding = (Binding<T>) entry;
if (index.add(binding.acceptTargetVisitor(indexer))) {
// TODO(lukes): most of these are linked bindings since user bindings are linked to
// a user binding through the @Element annotation. Since this is an implementation
// detail we could 'dereference' the @Element if it is a LinkedBinding and avoid
// provisioning through the FactoryProxy at runtime.
// Ditto for OptionalBinder/MapBinder
bindings.add(binding);
Key<T> key = binding.getKey();
// TODO(lukes): we should mark this as a non-nullable dependency since we don't accept
// null.
// Add a dependency on Key<T>
dependencies.add(Dependency.get(key));
// and add a dependency on Key<Provider<T>>
providerDependencies.add(
Dependency.get(key.ofType(Types.providerOf(key.getTypeLiteral().getType()))));
}
}
}
this.bindings = ImmutableList.copyOf(bindings);
this.dependencies = ImmutableSet.copyOf(dependencies);
this.providerDependencies = ImmutableSet.copyOf(providerDependencies);
this.permitDuplicates = permitsDuplicates(injector);
// This is safe because all our dependencies are assignable to T and we never assign to
// elements of this array.
@SuppressWarnings("unchecked")
SingleParameterInjector<T>[] typed =
(SingleParameterInjector<T>[]) injector.getParametersInjectors(dependencies, errors);
this.parameterinjectors = typed;
isInitialized = true;
}
boolean permitsDuplicates(Injector injector) {
return injector.getBindings().containsKey(getPermitDuplicatesKey());
}
ImmutableList<Binding<T>> getBindings() {
checkConfiguration(isInitialized, "not initialized");
return bindings;
}
SingleParameterInjector<T>[] getParameterInjectors() {
checkConfiguration(isInitialized, "not initialized");
return parameterinjectors;
}
ImmutableSet<Dependency<?>> getDependencies() {
return dependencies;
}
ImmutableSet<Dependency<?>> getProviderDependencies() {
return providerDependencies;
}
String getSetName() {
// lazily initialized since most selectors don't survive module installation.
if (setName == null) {
setName = Annotations.nameOf(setKey);
}
return setName;
}
Key<Boolean> getPermitDuplicatesKey() {
Key<Boolean> local = permitDuplicatesKey;
if (local == null) {
local =
permitDuplicatesKey = Key.get(Boolean.class, named(toString() + " permits duplicates"));
}
return local;
}
Key<Collection<Provider<T>>> getCollectionOfProvidersKey() {
Key<Collection<Provider<T>>> local = collectionOfProvidersKey;
if (local == null) {
local = collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
}
return local;
}
Key<Collection<javax.inject.Provider<T>>> getCollectionOfJavaxProvidersKey() {
Key<Collection<javax.inject.Provider<T>>> local = collectionOfJavaxProvidersKey;
if (local == null) {
local =
collectionOfJavaxProvidersKey =
setKey.ofType(collectionOfJavaxProvidersOf(elementType));
}
return local;
}
boolean isInitialized() {
return isInitialized;
}
// MultibinderBinding API methods
TypeLiteral<T> getElementTypeLiteral() {
return elementType;
}
Key<Set<T>> getSetKey() {
return setKey;
}
@SuppressWarnings("unchecked")
List<Binding<?>> getElements() {
if (isInitialized()) {
return (List<Binding<?>>) (List<?>) bindings; // safe because bindings is immutable.
} else {
throw new UnsupportedOperationException("getElements() not supported for module bindings");
}
}
boolean permitsDuplicates() {
if (isInitialized()) {
return permitDuplicates;
} else {
throw new UnsupportedOperationException(
"permitsDuplicates() not supported for module bindings");
}
}
boolean containsElement(com.google.inject.spi.Element element) {
if (element instanceof Binding) {
Binding<?> binding = (Binding<?>) element;
return keyMatches(binding.getKey())
|| binding.getKey().equals(getPermitDuplicatesKey())
|| binding.getKey().equals(setKey)
|| binding.getKey().equals(collectionOfProvidersKey)
|| binding.getKey().equals(collectionOfJavaxProvidersKey);
} else {
return false;
}
}
private boolean keyMatches(Key<?> key) {
return key.getTypeLiteral().equals(elementType)
&& key.getAnnotation() instanceof Element
&& ((Element) key.getAnnotation()).setName().equals(getSetName())
&& ((Element) key.getAnnotation()).type() == MULTIBINDER;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof BindingSelection) {
return setKey.equals(((BindingSelection<?>) obj).setKey);
}
return false;
}
@Override
public int hashCode() {
return setKey.hashCode();
}
@Override
public String toString() {
return (getSetName().isEmpty() ? "" : getSetName() + " ")
+ "Multibinder<"
+ elementType
+ ">";
}
}
@Override
public boolean equals(Object o) {
return o instanceof RealMultibinder
&& ((RealMultibinder<?>) o).bindingSelection.equals(bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
private static final class RealMultibinderCollectionOfProvidersProvider<T>
extends InternalProviderInstanceBindingImpl.Factory<Collection<Provider<T>>> {
private final BindingSelection<T> bindingSelection;
private ImmutableList<Provider<T>> collectionOfProviders;
RealMultibinderCollectionOfProvidersProvider(BindingSelection<T> bindingSelection) {
super(InitializationTiming.DELAYED); // See comment in RealMultibinderProvider
this.bindingSelection = bindingSelection;
}
@Override
void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
bindingSelection.initialize(injector, errors);
ImmutableList.Builder<Provider<T>> providers = ImmutableList.builder();
for (Binding<T> binding : bindingSelection.getBindings()) {
providers.add(binding.getProvider());
}
this.collectionOfProviders = providers.build();
}
@Override
protected Collection<Provider<T>> doProvision(
InternalContext context, Dependency<?> dependency) {
return collectionOfProviders;
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.getProviderDependencies();
}
@Override
public boolean equals(Object obj) {
return obj instanceof RealMultibinderCollectionOfProvidersProvider
&& bindingSelection.equals(
((RealMultibinderCollectionOfProvidersProvider<?>) obj).bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
}
/**
* We install the permit duplicates configuration as its own binding, all by itself. This way, if
* only one of a multibinder's users remember to call permitDuplicates(), they're still permitted.
*
* <p>This is like setting a global variable in the injector so that each instance of the
* multibinder will have the same value for permitDuplicates, even if it is only set on one of
* them.
*/
private static class PermitDuplicatesModule extends AbstractModule {
private final Key<Boolean> key;
PermitDuplicatesModule(Key<Boolean> key) {
this.key = key;
}
@Override
protected void configure() {
bind(key).toInstance(true);
}
@Override
public boolean equals(Object o) {
return o instanceof PermitDuplicatesModule && ((PermitDuplicatesModule) o).key.equals(key);
}
@Override
public int hashCode() {
return getClass().hashCode() ^ key.hashCode();
}
}
}

View file

@ -0,0 +1,793 @@
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.internal.Errors.checkConfiguration;
import static com.google.inject.util.Types.newParameterizedType;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
import com.google.inject.multibindings.MultibindingsTargetVisitor;
import com.google.inject.multibindings.OptionalBinderBinding;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.util.Types;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.reflect.Type;
import java.util.Set;
import javax.inject.Qualifier;
/**
* The actual OptionalBinder plays several roles. It implements Module to hide that fact from the
* public API, and installs the various bindings that are exposed to the user.
*/
public final class RealOptionalBinder<T> implements Module {
public static <T> RealOptionalBinder<T> newRealOptionalBinder(Binder binder, Key<T> type) {
binder = binder.skipSources(RealOptionalBinder.class);
RealOptionalBinder<T> optionalBinder = new RealOptionalBinder<>(binder, type);
binder.install(optionalBinder);
return optionalBinder;
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Optional<T>> optionalOf(TypeLiteral<T> type) {
return (TypeLiteral<Optional<T>>)
TypeLiteral.get(Types.newParameterizedType(Optional.class, type.getType()));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<java.util.Optional<T>> javaOptionalOf(TypeLiteral<T> type) {
return (TypeLiteral<java.util.Optional<T>>)
TypeLiteral.get(Types.newParameterizedType(java.util.Optional.class, type.getType()));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Optional<javax.inject.Provider<T>>> optionalOfJavaxProvider(
TypeLiteral<T> type) {
return (TypeLiteral<Optional<javax.inject.Provider<T>>>)
TypeLiteral.get(
Types.newParameterizedType(
Optional.class, newParameterizedType(javax.inject.Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<java.util.Optional<javax.inject.Provider<T>>> javaOptionalOfJavaxProvider(
TypeLiteral<T> type) {
return (TypeLiteral<java.util.Optional<javax.inject.Provider<T>>>)
TypeLiteral.get(
Types.newParameterizedType(
java.util.Optional.class,
newParameterizedType(javax.inject.Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<Optional<Provider<T>>> optionalOfProvider(TypeLiteral<T> type) {
return (TypeLiteral<Optional<Provider<T>>>)
TypeLiteral.get(
Types.newParameterizedType(
Optional.class, newParameterizedType(Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> TypeLiteral<java.util.Optional<Provider<T>>> javaOptionalOfProvider(
TypeLiteral<T> type) {
return (TypeLiteral<java.util.Optional<Provider<T>>>)
TypeLiteral.get(
Types.newParameterizedType(
java.util.Optional.class, newParameterizedType(Provider.class, type.getType())));
}
@SuppressWarnings("unchecked")
static <T> Key<Provider<T>> providerOf(Key<T> key) {
Type providerT = Types.providerOf(key.getTypeLiteral().getType());
return (Key<Provider<T>>) key.ofType(providerT);
}
enum Source {
DEFAULT,
ACTUAL
}
@Retention(RUNTIME)
@Qualifier
@interface Default {
String value();
}
@Retention(RUNTIME)
@Qualifier
@interface Actual {
String value();
}
private final BindingSelection<T> bindingSelection;
private final Binder binder;
private RealOptionalBinder(Binder binder, Key<T> typeKey) {
this.bindingSelection = new BindingSelection<>(typeKey);
this.binder = binder;
}
/**
* Adds a binding for T. Multiple calls to this are safe, and will be collapsed as duplicate
* bindings.
*/
private void addDirectTypeBinding(Binder binder) {
binder
.bind(bindingSelection.getDirectKey())
.toProvider(new RealDirectTypeProvider<T>(bindingSelection));
}
/**
* Returns the key to use for the default binding.
*
* <p>As a side effect this installs support for the 'direct type', so a binding for {@code T}
* will be made available.
*/
Key<T> getKeyForDefaultBinding() {
bindingSelection.checkNotInitialized();
addDirectTypeBinding(binder);
return bindingSelection.getKeyForDefaultBinding();
}
public LinkedBindingBuilder<T> setDefault() {
return binder.bind(getKeyForDefaultBinding());
}
/**
* Returns the key to use for the actual binding, overrides the default if set.
*
* <p>As a side effect this installs support for the 'direct type', so a binding for {@code T}
* will be made available.
*/
Key<T> getKeyForActualBinding() {
bindingSelection.checkNotInitialized();
addDirectTypeBinding(binder);
return bindingSelection.getKeyForActualBinding();
}
public LinkedBindingBuilder<T> setBinding() {
return binder.bind(getKeyForActualBinding());
}
@Override
public void configure(Binder binder) {
bindingSelection.checkNotInitialized();
Key<T> key = bindingSelection.getDirectKey();
TypeLiteral<T> typeLiteral = key.getTypeLiteral();
// Every OptionalBinder gets the following types bound
// * {cgcb,ju}.Optional<Provider<T>>
// * {cgcb,ju}.Optional<javax.inject.Provider<T>>
// * {cgcb,ju}.Optional<T>
// If setDefault() or setBinding() is called then also
// * T is bound
// cgcb.Optional<Provider<T>>
InternalProviderInstanceBindingImpl.Factory<Optional<Provider<T>>> optionalProviderFactory =
new RealOptionalProviderProvider<>(bindingSelection);
binder.bind(key.ofType(optionalOfProvider(typeLiteral))).toProvider(optionalProviderFactory);
// ju.Optional<Provider<T>>
InternalProviderInstanceBindingImpl.Factory<java.util.Optional<Provider<T>>>
javaOptionalProviderFactory = new JavaOptionalProviderProvider<>(bindingSelection);
binder
.bind(key.ofType(javaOptionalOfProvider(typeLiteral)))
.toProvider(javaOptionalProviderFactory);
// Provider is assignable to javax.inject.Provider and the provider that the factory contains
// cannot be modified so we can use some rawtypes hackery to share the same implementation.
// cgcb.Optional<ji.Provider<T>>
@SuppressWarnings("unchecked")
InternalProviderInstanceBindingImpl.Factory<Optional<javax.inject.Provider<T>>>
optionalJavaxProviderFactory =
(InternalProviderInstanceBindingImpl.Factory) optionalProviderFactory;
binder
.bind(key.ofType(optionalOfJavaxProvider(typeLiteral)))
.toProvider(optionalJavaxProviderFactory);
// ju.Optional<ji.Provider<T>>
@SuppressWarnings("unchecked")
InternalProviderInstanceBindingImpl.Factory<java.util.Optional<javax.inject.Provider<T>>>
javaOptionalJavaxProviderFactory =
(InternalProviderInstanceBindingImpl.Factory) javaOptionalProviderFactory;
binder
.bind(key.ofType(javaOptionalOfJavaxProvider(typeLiteral)))
.toProvider(javaOptionalJavaxProviderFactory);
// cgcb.Optional<T>
Key<Optional<T>> optionalKey = key.ofType(optionalOf(typeLiteral));
binder
.bind(optionalKey)
.toProvider(new RealOptionalKeyProvider<>(bindingSelection, optionalKey));
// ju.Optional<T>
Key<java.util.Optional<T>> javaOptionalKey = key.ofType(javaOptionalOf(typeLiteral));
binder
.bind(javaOptionalKey)
.toProvider(new JavaOptionalProvider<>(bindingSelection, javaOptionalKey));
}
/** Provides the binding for java.util.Optional<T>. */
private static final class JavaOptionalProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, java.util.Optional<T>>
implements ProviderWithExtensionVisitor<java.util.Optional<T>>,
OptionalBinderBinding<java.util.Optional<T>> {
private final Key<java.util.Optional<T>> optionalKey;
private Dependency<?> targetDependency;
private InternalFactory<? extends T> target;
JavaOptionalProvider(
BindingSelection<T> bindingSelection, Key<java.util.Optional<T>> optionalKey) {
super(bindingSelection);
this.optionalKey = optionalKey;
}
@Override
void doInitialize() {
if (bindingSelection.getBinding() != null) {
target = bindingSelection.getBinding().getInternalFactory();
targetDependency = bindingSelection.getDependency();
}
}
@Override
protected java.util.Optional<T> doProvision(
InternalContext context, Dependency<?> currentDependency)
throws InternalProvisionException {
InternalFactory<? extends T> local = target;
if (local == null) {
return java.util.Optional.empty();
}
Dependency<?> localDependency = targetDependency;
T result;
Dependency<?> previous = context.pushDependency(localDependency, getSource());
try {
// See comments in RealOptionalKeyProvider, about how localDependency may be more specific
// than what we actually need.
result = local.get(context, localDependency, false);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localDependency);
} finally {
context.popStateAndSetDependency(previous);
}
return java.util.Optional.ofNullable(result);
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.dependencies;
}
@SuppressWarnings("unchecked")
@Override
public <B, R> R acceptExtensionVisitor(
BindingTargetVisitor<B, R> visitor, ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof MultibindingsTargetVisitor) {
return ((MultibindingsTargetVisitor<java.util.Optional<T>, R>) visitor).visit(this);
} else {
return visitor.visit(binding);
}
}
@Override
public boolean containsElement(Element element) {
return bindingSelection.containsElement(element);
}
@Override
public Binding<?> getActualBinding() {
return bindingSelection.getActualBinding();
}
@Override
public Binding<?> getDefaultBinding() {
return bindingSelection.getDefaultBinding();
}
@Override
public Key<java.util.Optional<T>> getKey() {
return optionalKey;
}
@Override
public Set<Key<?>> getAlternateKeys() {
Key<?> key = bindingSelection.getDirectKey();
TypeLiteral<?> typeLiteral = key.getTypeLiteral();
return ImmutableSet.of(
(Key<?>) key.ofType(javaOptionalOfProvider(typeLiteral)),
(Key<?>) key.ofType(javaOptionalOfJavaxProvider(typeLiteral)));
}
}
/** Provides the binding for java.util.Optional<Provider<T>>. */
private static final class JavaOptionalProviderProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, java.util.Optional<Provider<T>>> {
private java.util.Optional<Provider<T>> value;
JavaOptionalProviderProvider(BindingSelection<T> bindingSelection) {
super(bindingSelection);
}
@Override
void doInitialize() {
if (bindingSelection.getBinding() == null) {
value = java.util.Optional.empty();
} else {
value = java.util.Optional.of(bindingSelection.getBinding().getProvider());
}
}
@Override
protected java.util.Optional<Provider<T>> doProvision(
InternalContext context, Dependency<?> dependency) {
return value;
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.providerDependencies();
}
}
/** Provides the binding for T, conditionally installed by calling setBinding/setDefault. */
private static final class RealDirectTypeProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, T> {
private Key<? extends T> targetKey;
private Object targetSource;
private InternalFactory<? extends T> targetFactory;
RealDirectTypeProvider(BindingSelection<T> bindingSelection) {
super(bindingSelection);
}
@Override
void doInitialize() {
BindingImpl<T> targetBinding = bindingSelection.getBinding();
// we only install this factory if they call setBinding()/setDefault() so we know that
// targetBinding will be non-null.
this.targetKey = targetBinding.getKey();
this.targetSource = targetBinding.getSource();
this.targetFactory = targetBinding.getInternalFactory();
}
@Override
protected T doProvision(InternalContext context, Dependency<?> dependency)
throws InternalProvisionException {
// This is what linked bindings do (see FactoryProxy), and we are pretty similar.
context.pushState(targetKey, targetSource);
try {
return targetFactory.get(context, dependency, true);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(targetKey);
} finally {
context.popState();
}
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.dependencies;
}
}
/** Provides the binding for Optional<Provider<T>>. */
private static final class RealOptionalProviderProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, Optional<Provider<T>>> {
private Optional<Provider<T>> value;
RealOptionalProviderProvider(BindingSelection<T> bindingSelection) {
super(bindingSelection);
}
@Override
void doInitialize() {
if (bindingSelection.getBinding() == null) {
value = Optional.absent();
} else {
value = Optional.of(bindingSelection.getBinding().getProvider());
}
}
@Override
protected Optional<Provider<T>> doProvision(InternalContext context, Dependency<?> dependency) {
return value;
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.providerDependencies();
}
}
/** Provides the binding for Optional<T>. */
private static final class RealOptionalKeyProvider<T>
extends RealOptionalBinderProviderWithDependencies<T, Optional<T>>
implements ProviderWithExtensionVisitor<Optional<T>>, OptionalBinderBinding<Optional<T>> {
private final Key<Optional<T>> optionalKey;
// These are assigned to non-null values during initialization if and only if we have a binding
// to delegate to.
private Dependency<?> targetDependency;
private InternalFactory<? extends T> delegate;
RealOptionalKeyProvider(BindingSelection<T> bindingSelection, Key<Optional<T>> optionalKey) {
super(bindingSelection);
this.optionalKey = optionalKey;
}
@Override
void doInitialize() {
if (bindingSelection.getBinding() != null) {
delegate = bindingSelection.getBinding().getInternalFactory();
targetDependency = bindingSelection.getDependency();
}
}
@Override
protected Optional<T> doProvision(InternalContext context, Dependency<?> currentDependency)
throws InternalProvisionException {
InternalFactory<? extends T> local = delegate;
if (local == null) {
return Optional.absent();
}
Dependency<?> localDependency = targetDependency;
T result;
Dependency<?> previous = context.pushDependency(localDependency, getSource());
try {
// currentDependency is Optional<? super T>, so we really just need to set the target
// dependency to ? super T, but we are currently setting it to T. We could hypothetically
// make it easier for our delegate to generate proxies by modifying the dependency, but that
// would also require us to rewrite the key on each call. So for now we don't do it.
result = local.get(context, localDependency, false);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localDependency);
} finally {
context.popStateAndSetDependency(previous);
}
return Optional.fromNullable(result);
}
@Override
public Set<Dependency<?>> getDependencies() {
return bindingSelection.dependencies();
}
@SuppressWarnings("unchecked")
@Override
public <B, R> R acceptExtensionVisitor(
BindingTargetVisitor<B, R> visitor, ProviderInstanceBinding<? extends B> binding) {
if (visitor instanceof MultibindingsTargetVisitor) {
return ((MultibindingsTargetVisitor<Optional<T>, R>) visitor).visit(this);
} else {
return visitor.visit(binding);
}
}
@Override
public Key<Optional<T>> getKey() {
return optionalKey;
}
@Override
public Set<Key<?>> getAlternateKeys() {
Key<?> key = bindingSelection.getDirectKey();
TypeLiteral<?> typeLiteral = key.getTypeLiteral();
return ImmutableSet.of(
(Key<?>) key.ofType(optionalOfProvider(typeLiteral)),
(Key<?>) key.ofType(optionalOfJavaxProvider(typeLiteral)));
}
@Override
public Binding<?> getActualBinding() {
return bindingSelection.getActualBinding();
}
@Override
public Binding<?> getDefaultBinding() {
return bindingSelection.getDefaultBinding();
}
@Override
public boolean containsElement(Element element) {
return bindingSelection.containsElement(element);
}
}
/**
* A helper object that implements the core logic for deciding what the implementation of the
* binding will be.
*
* <p>This also implements the main OptionalBinderBinding logic.
*/
private static final class BindingSelection<T> {
private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
/*@Nullable */ BindingImpl<T> actualBinding;
/*@Nullable */ BindingImpl<T> defaultBinding;
/*@Nullable */ BindingImpl<T> binding;
private boolean initialized;
private final Key<T> key;
// Until the injector initializes us, we don't know what our dependencies are,
// so initialize to the whole Injector (like Multibinder, and MapBinder indirectly).
private ImmutableSet<Dependency<?>> dependencies = MODULE_DEPENDENCIES;
private ImmutableSet<Dependency<?>> providerDependencies = MODULE_DEPENDENCIES;
/** lazily allocated, by {@link #getBindingName}. */
private String bindingName;
/** lazily allocated, by {@link #getKeyForDefaultBinding}. */
private Key<T> defaultBindingKey;
/** lazily allocated, by {@link #getKeyForActualBinding}. */
private Key<T> actualBindingKey;
BindingSelection(Key<T> key) {
this.key = key;
}
void checkNotInitialized() {
checkConfiguration(!initialized, "already initialized");
}
void initialize(InjectorImpl injector) {
// Every one of our providers will call this method, so only execute the logic once.
if (initialized) {
return;
}
actualBinding = injector.getExistingBinding(getKeyForActualBinding());
defaultBinding = injector.getExistingBinding(getKeyForDefaultBinding());
// We should never create Jit bindings, but we can use them if some other binding created it.
BindingImpl<T> userBinding = injector.getExistingBinding(key);
if (actualBinding != null) {
// TODO(sameb): Consider exposing an option that will allow
// ACTUAL to fallback to DEFAULT if ACTUAL's provider returns null.
// Right now, an ACTUAL binding can convert from present -> absent
// if it's bound to a provider that returns null.
binding = actualBinding;
} else if (defaultBinding != null) {
binding = defaultBinding;
} else if (userBinding != null) {
// If neither the actual or default is set, then we fallback
// to the value bound to the type itself and consider that the
// "actual binding" for the SPI.
binding = userBinding;
actualBinding = userBinding;
}
if (binding != null) {
dependencies = ImmutableSet.<Dependency<?>>of(Dependency.get(binding.getKey()));
providerDependencies =
ImmutableSet.<Dependency<?>>of(Dependency.get(providerOf(binding.getKey())));
} else {
dependencies = ImmutableSet.of();
providerDependencies = ImmutableSet.of();
}
initialized = true;
}
Key<T> getKeyForDefaultBinding() {
if (defaultBindingKey == null) {
defaultBindingKey = Key.get(key.getTypeLiteral(), new DefaultImpl(getBindingName()));
}
return defaultBindingKey;
}
Key<T> getKeyForActualBinding() {
if (actualBindingKey == null) {
actualBindingKey = Key.get(key.getTypeLiteral(), new ActualImpl(getBindingName()));
}
return actualBindingKey;
}
Key<T> getDirectKey() {
return key;
}
private String getBindingName() {
// Lazily allocated, most instantiations will never need this because they are deduped during
// module installation.
if (bindingName == null) {
bindingName = Annotations.nameOf(key);
}
return bindingName;
}
BindingImpl<T> getBinding() {
return binding;
}
// Provide default implementations for most of the OptionalBinderBinding interface
BindingImpl<T> getDefaultBinding() {
return defaultBinding;
}
BindingImpl<T> getActualBinding() {
return actualBinding;
}
ImmutableSet<Dependency<?>> providerDependencies() {
return providerDependencies;
}
ImmutableSet<Dependency<?>> dependencies() {
return dependencies;
}
/**
* Returns the Dependency for the target binding, throws NoSuchElementException if no target
* exists.
*
* <p>Calls to this method should typically be guarded by checking if {@link #getBinding()}
* returns {@code null}.
*/
Dependency<?> getDependency() {
return Iterables.getOnlyElement(dependencies);
}
/** Implementation of {@link OptionalBinderBinding#containsElement}. */
boolean containsElement(Element element) {
// All of our bindings are ProviderInstanceBindings whose providers extend
// RealOptionalBinderProviderWithDependencies and have 'this' as its binding selection.
if (element instanceof ProviderInstanceBinding) {
javax.inject.Provider<?> providerInstance =
((ProviderInstanceBinding<?>) element).getUserSuppliedProvider();
if (providerInstance instanceof RealOptionalBinderProviderWithDependencies) {
return ((RealOptionalBinderProviderWithDependencies<?, ?>) providerInstance)
.bindingSelection.equals(this);
}
}
if (element instanceof Binding) {
Key<?> elementKey = ((Binding) element).getKey();
// if it isn't one of the things we bound directly it might be an actual or default key
return elementKey.equals(getKeyForActualBinding())
|| elementKey.equals(getKeyForDefaultBinding());
}
return false; // cannot match;
}
@Override
public boolean equals(Object o) {
return o instanceof BindingSelection && ((BindingSelection) o).key.equals(key);
}
@Override
public int hashCode() {
return key.hashCode();
}
}
@Override
public boolean equals(Object o) {
return o instanceof RealOptionalBinder
&& ((RealOptionalBinder<?>) o).bindingSelection.equals(bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
/** A base class for ProviderWithDependencies that need equality based on a specific object. */
private abstract static class RealOptionalBinderProviderWithDependencies<T, P>
extends InternalProviderInstanceBindingImpl.Factory<P> {
protected final BindingSelection<T> bindingSelection;
RealOptionalBinderProviderWithDependencies(BindingSelection<T> bindingSelection) {
// We need delayed initialization so we can detect jit bindings created by other bindings
// while not also creating jit bindings ourselves. This ensures we only pick up user bindings
// if the binding would have existed in the injector statically.
super(InitializationTiming.DELAYED);
this.bindingSelection = bindingSelection;
}
@Override
final void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
bindingSelection.initialize(injector);
doInitialize();
}
/**
* Initialize the factory. BindingSelection is guaranteed to be initialized at this point and
* this will be called prior to any provisioning.
*/
abstract void doInitialize();
@Override
public boolean equals(Object obj) {
return obj != null
&& this.getClass() == obj.getClass()
&& bindingSelection.equals(
((RealOptionalBinderProviderWithDependencies<?, ?>) obj).bindingSelection);
}
@Override
public int hashCode() {
return bindingSelection.hashCode();
}
}
@SuppressWarnings("serial")
static class DefaultImpl extends BaseAnnotation implements Default {
public DefaultImpl(String value) {
super(Default.class, value);
}
}
@SuppressWarnings("serial")
static class ActualImpl extends BaseAnnotation implements Actual {
public ActualImpl(String value) {
super(Actual.class, value);
}
}
abstract static class BaseAnnotation implements Serializable, Annotation {
private final String value;
private final Class<? extends Annotation> clazz;
BaseAnnotation(Class<? extends Annotation> clazz, String value) {
this.clazz = checkNotNull(clazz, "clazz");
this.value = checkNotNull(value, "value");
}
public String value() {
return this.value;
}
@Override
public int hashCode() {
// This is specified in java.lang.Annotation.
return (127 * "value".hashCode()) ^ value.hashCode();
}
@Override
public boolean equals(Object o) {
// We check against each annotation type instead of BaseAnnotation
// so that we can compare against generated annotation implementations.
if (o instanceof Actual && clazz == Actual.class) {
Actual other = (Actual) o;
return value.equals(other.value());
} else if (o instanceof Default && clazz == Default.class) {
Default other = (Default) o;
return value.equals(other.value());
}
return false;
}
@Override
public String toString() {
return "@" + clazz.getName() + (value.isEmpty() ? "" : "(value=" + value + ")");
}
@Override
public Class<? extends Annotation> annotationType() {
return clazz;
}
private static final long serialVersionUID = 0;
}
}

View file

@ -184,8 +184,7 @@ public abstract class Scoping {
Scope scope = scoping.getScopeInstance(); Scope scope = scoping.getScopeInstance();
Provider<T> scoped Provider<T> scoped = scope.scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
= scope.scope(key, new ProviderToInternalFactoryAdapter<T>(injector, creator));
return new InternalFactoryToProviderAdapter<T>(scoped, source); return new InternalFactoryToProviderAdapter<T>(scoped, source);
} }

View file

@ -10,35 +10,38 @@ import java.lang.reflect.Field;
* Sets an injectable field. * Sets an injectable field.
*/ */
final class SingleFieldInjector implements SingleMemberInjector { final class SingleFieldInjector implements SingleMemberInjector {
final Field field;
final InjectionPoint injectionPoint;
final Dependency<?> dependency;
final BindingImpl<?> binding;
public SingleFieldInjector(InjectorImpl injector, InjectionPoint injectionPoint, Errors errors) private final Field field;
private final InjectionPoint injectionPoint;
private final Dependency<?> dependency;
private final BindingImpl<?> binding;
SingleFieldInjector(InjectorImpl injector, InjectionPoint injectionPoint, Errors errors)
throws ErrorsException { throws ErrorsException {
this.injectionPoint = injectionPoint; this.injectionPoint = injectionPoint;
this.field = (Field) injectionPoint.getMember(); this.field = (Field) injectionPoint.getMember();
this.dependency = injectionPoint.getDependencies().get(0); this.dependency = injectionPoint.getDependencies().get(0);
// Ewwwww... // Ewwwww...
field.setAccessible(true); field.setAccessible(true);
binding = injector.getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT); binding = injector.getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT);
} }
@Override
public InjectionPoint getInjectionPoint() { public InjectionPoint getInjectionPoint() {
return injectionPoint; return injectionPoint;
} }
public void inject(Errors errors, InternalContext context, Object o) { @Override
errors = errors.withSource(dependency); public void inject(InternalContext context, Object o) throws InternalProvisionException {
Dependency<?> previous = context.pushDependency(dependency, binding.getSource());
Dependency previous = context.pushDependency(dependency, binding.getSource());
try { try {
Object value = binding.getInternalFactory().get(errors, context, dependency, false); Object value = binding.getInternalFactory().get(context, dependency, false);
field.set(o, value); field.set(o, value);
} catch (ErrorsException e) { } catch (InternalProvisionException e) {
errors.withSource(injectionPoint).merge(e.getErrors()); throw e.addSource(dependency);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new AssertionError(e); // a security manager is blocking us, we're hosed throw new AssertionError(e); // a security manager is blocking us, we're hosed
} finally { } finally {

View file

@ -6,7 +6,7 @@ import com.google.inject.spi.InjectionPoint;
* Injects a field or method of a given object. * Injects a field or method of a given object.
*/ */
interface SingleMemberInjector { interface SingleMemberInjector {
void inject(Errors errors, InternalContext context, Object o); void inject(InternalContext context, Object o) throws InternalProvisionException;
InjectionPoint getInjectionPoint(); InjectionPoint getInjectionPoint();
} }

View file

@ -29,37 +29,23 @@ final class SingleMethodInjector implements SingleMemberInjector {
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) { !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
method.setAccessible(true); method.setAccessible(true);
} }
return method::invoke;
return new MethodInvoker() {
public Object invoke(Object target, Object... parameters)
throws IllegalAccessException, InvocationTargetException {
return method.invoke(target, parameters);
}
};
} }
public InjectionPoint getInjectionPoint() { public InjectionPoint getInjectionPoint() {
return injectionPoint; return injectionPoint;
} }
public void inject(Errors errors, InternalContext context, Object o) { @Override
Object[] parameters; public void inject(InternalContext context, Object o) throws InternalProvisionException {
try { Object[] parameters = SingleParameterInjector.getAll(context, parameterInjectors);
parameters = SingleParameterInjector.getAll(errors, context, parameterInjectors);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
return;
}
try { try {
methodInvoker.invoke(o, parameters); methodInvoker.invoke(o, parameters);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new AssertionError(e); // a security manager is blocking us, we're hosed throw new AssertionError(e); // a security manager is blocking us, we're hosed
} catch (InvocationTargetException userException) { } catch (InvocationTargetException userException) {
Throwable cause = userException.getCause() != null Throwable cause = userException.getCause() != null ? userException.getCause() : userException;
? userException.getCause() throw InternalProvisionException.errorInjectingMethod(cause).addSource(injectionPoint);
: userException;
errors.withSource(injectionPoint).errorInjectingMethod(cause);
} }
} }
} }

View file

@ -6,48 +6,47 @@ import com.google.inject.spi.Dependency;
* Resolves a single parameter, to be used in a constructor or method invocation. * Resolves a single parameter, to be used in a constructor or method invocation.
*/ */
final class SingleParameterInjector<T> { final class SingleParameterInjector<T> {
private static final Object[] NO_ARGUMENTS = {}; private static final Object[] NO_ARGUMENTS = {};
private final Dependency<T> dependency; private final Dependency<T> dependency;
private final BindingImpl<? extends T> binding;
private final Object source;
private final InternalFactory<? extends T> factory;
SingleParameterInjector(Dependency<T> dependency, BindingImpl<? extends T> binding) { SingleParameterInjector(Dependency<T> dependency, BindingImpl<? extends T> binding) {
this.dependency = dependency; this.dependency = dependency;
this.binding = binding; this.source = binding.getSource();
this.factory = binding.getInternalFactory();
} }
/** /**
* Returns an array of parameter values. * Returns an array of parameter values.
*/ */
static Object[] getAll(Errors errors, InternalContext context, static Object[] getAll(InternalContext context,
SingleParameterInjector<?>[] parameterInjectors) throws ErrorsException { SingleParameterInjector<?>[] parameterInjectors) throws InternalProvisionException {
if (parameterInjectors == null) { if (parameterInjectors == null) {
return NO_ARGUMENTS; return NO_ARGUMENTS;
} }
int numErrorsBefore = errors.size();
int size = parameterInjectors.length; int size = parameterInjectors.length;
Object[] parameters = new Object[size]; Object[] parameters = new Object[size];
// optimization: use manual for/each to save allocating an iterator here // optimization: use manual for/each to save allocating an iterator here
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
SingleParameterInjector<?> parameterInjector = parameterInjectors[i]; parameters[i] = parameterInjectors[i].inject(context);
try {
parameters[i] = parameterInjector.inject(errors, context);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
} }
errors.throwIfNewErrors(numErrorsBefore);
return parameters; return parameters;
} }
private T inject(Errors errors, InternalContext context) throws ErrorsException { T inject(InternalContext context) throws InternalProvisionException {
Dependency previous = context.pushDependency(dependency, binding.getSource()); Dependency<T> localDependency = dependency;
Dependency<?> previous = context.pushDependency(localDependency, source);
try { try {
return binding.getInternalFactory().get(errors.withSource(dependency), context, dependency, false); return factory.get(context, localDependency, false);
} catch (InternalProvisionException ipe) {
throw ipe.addSource(localDependency);
} finally { } finally {
context.popStateAndSetDependency(previous); context.popStateAndSetDependency(previous);
} }

Some files were not shown because too many files have changed in this diff Show more