Compare commits

..

10 commits

276 changed files with 45357 additions and 33007 deletions

View file

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

View file

@ -1,24 +1,25 @@
# Guice = Guice - simplified and reduced version
This is a xbib Guice, a build of Guice with the following differences to the This is xbib Guice, a build of Google Guice with the following differences to the
original [Google Guice Core Library](https://github.com/google/guice): original [Google Guice Core Library](https://github.com/google/guice):
- only external dependencies are - only external dependencies are
- javax.inject:javax.inject:1 - javax.inject:javax.inject:1
- javax.annotation:javax.annotation-api:1.2 - org.xbib:jsr-305:1.0.0
- com.google.guava:guava:19.0 - com.google.guava:guava:30.1
- 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 11, no support for Java 6/7
- introduced Gradle as build system - introduced Gradle as build system
- junit tests made compatible to junit 4.12 - junit tests made compatible to junit 4
- assistedinject and multibindings extension included - assistedinject and multibindings extensions are included
All credits belong to the original authors, especially Bob Lee http://blog.crazybob.org/ All credits belong to the original authors, especially Bob Lee http://blog.crazybob.org/
## License == License
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View file

@ -1,85 +1,51 @@
plugins {
group = 'org.xbib' //id "checkstyle"
version = '4.0' //id "pmd"
id 'maven-publish'
apply plugin: 'java' id 'signing'
apply plugin: 'maven' id "io.github.gradle-nexus.publish-plugin" version "1.3.0"
//id "com.github.spotbugs" version "5.0.14"
repositories { id "org.cyclonedx.bom" version "1.7.2"
mavenCentral() //id "org.xbib.gradle.plugin.asciidoctor" version "3.0.0"
mavenLocal()
jcenter()
maven {
url "http://xbib.org/repository"
}
} }
configurations { wrapper {
wagon gradleVersion = libs.versions.gradle.get()
distributionType = Wrapper.DistributionType.ALL
} }
ext {
user = 'joerg'
name = 'guice'
description = 'Guice implementation with named modules for Java 11+'
inceptionYear = '2012'
url = 'https://xbib.org/' + user + '/' + name
scmUrl = 'https://xbib.org/' + user + '/' + name
scmConnection = 'scm:git:git://xbib.org/' + user + '/' + name + '.git'
scmDeveloperConnection = 'scm:git:ssh://forgejo@xbib.org:' + user + '/' + name + '.git'
issueManagementSystem = 'Github'
issueManagementUrl = ext.scmUrl + '/issues'
licenseName = 'The Apache License, Version 2.0'
licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
apply plugin: 'java-library'
apply from: rootProject.file('gradle/init/banner.gradle')
apply from: rootProject.file('gradle/ide/idea.gradle')
apply from: rootProject.file('gradle/repositories/maven.gradle')
apply from: rootProject.file('gradle/compile/java.gradle')
apply from: rootProject.file('gradle/test/junit5.gradle')
apply from: rootProject.file('gradle/quality/cyclonedx.gradle')
//apply from: rootProject.file('gradle/quality/spotbugs.gradle')
//apply from: rootProject.file('gradle/quality/checkstyle.gradle')
//apply from: rootProject.file('gradle/quality/pmd.gradle')
apply from: rootProject.file('gradle/publish/sonatype.gradle')
apply from: rootProject.file('gradle/publish/forgejo.gradle')
dependencies { dependencies {
compile "javax.inject:javax.inject:1" api libs.javax.inject
compile 'javax.annotation:javax.annotation-api:1.2' api libs.guava
compile "com.google.guava:guava:19.0" testImplementation libs.junit4
testCompile "junit:junit:4.12" // Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester
testCompile "org.apache.logging.log4j:log4j-slf4j-impl:2.5" testImplementation libs.guava.testlib
testCompile "org.apache.logging.log4j:log4j-core:2.5"
testCompile "javax.inject:javax.inject-tck:1"
testCompile "com.google.guava:guava-testlib:19.0"
wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
[compileJava, compileTestJava]*.options.collect { options ->
options.encoding = 'UTF-8'
options.compilerArgs << '-Xlint:-serial,-path,-rawtypes,-unchecked'
}
test {
exclude '*$*'
exclude '**/ErrorHandlingTest*'
exclude '**/OSGiContainerTest*'
exclude '**/ScopesTest*'
exclude '**/TypeConversionTest*'
testLogging {
showStandardStreams = false
exceptionFormat = 'full'
}
}
task sourcesJar(type: Jar, dependsOn: classes) {
from sourceSets.main.allSource
classifier 'sources'
}
artifacts {
archives sourcesJar
}
uploadArchives {
repositories {
if (project.hasProperty("xbibUsername")) {
mavenDeployer {
configuration = configurations.wagon
repository(
id: 'xbib.org',
url: uri('scpexe://xbib.org/repository'),
authentication: [userName: xbibUsername, privateKey: xbibPrivateKey]
)
pom.project {
inceptionYear '2016'
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
distribution 'repo'
}
}
}
}
}
}
} }

5
gradle.properties Normal file
View file

@ -0,0 +1,5 @@
group = org.xbib
name = guice
version = 5.0.1.0
org.gradle.warning.mode = ALL

View file

@ -0,0 +1,33 @@
apply plugin: 'java-library'
java {
modularity.inferModulePath.set(true)
withSourcesJar()
withJavadocJar()
}
compileJava {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
compileTestJava {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
jar {
manifest {
attributes('Implementation-Version': project.version)
}
}
tasks.withType(JavaCompile) {
options.encoding('UTF-8')
options.compilerArgs << '-Xlint:all'
}
javadoc {
options.addStringOption('Xdoclint:none', '-quiet')
}

View file

@ -0,0 +1,55 @@
apply plugin: 'org.xbib.gradle.plugin.asciidoctor'
configurations {
asciidoclet
}
dependencies {
asciidoclet "org.asciidoctor:asciidoclet:${project.property('asciidoclet.version')}"
}
asciidoctor {
backends 'html5'
outputDir = file("${rootProject.projectDir}/docs")
separateOutputDirs = false
attributes 'source-highlighter': 'coderay',
idprefix: '',
idseparator: '-',
toc: 'left',
doctype: 'book',
icons: 'font',
encoding: 'utf-8',
sectlink: true,
sectanchors: true,
linkattrs: true,
imagesdir: 'img',
stylesheet: "${projectDir}/src/docs/asciidoc/css/foundation.css"
}
/*javadoc {
options.docletpath = configurations.asciidoclet.files.asType(List)
options.doclet = 'org.asciidoctor.Asciidoclet'
//options.overview = "src/docs/asciidoclet/overview.adoc"
options.addStringOption "-base-dir", "${projectDir}"
options.addStringOption "-attribute",
"name=${project.name},version=${project.version},title-link=https://github.com/xbib/${project.name}"
configure(options) {
noTimestamp = true
}
}*/
/*javadoc {
options.docletpath = configurations.asciidoclet.files.asType(List)
options.doclet = 'org.asciidoctor.Asciidoclet'
options.overview = "${rootProject.projectDir}/src/docs/asciidoclet/overview.adoc"
options.addStringOption "-base-dir", "${projectDir}"
options.addStringOption "-attribute",
"name=${project.name},version=${project.version},title-link=https://github.com/xbib/${project.name}"
options.destinationDirectory(file("${projectDir}/docs/javadoc"))
configure(options) {
noTimestamp = true
}
}*/

12
gradle/ide/idea.gradle Normal file
View file

@ -0,0 +1,12 @@
apply plugin: 'idea'
idea {
module {
outputDir file('build/classes/java/main')
testOutputDir file('build/classes/java/test')
}
}
clean {
delete 'out'
}

14
gradle/init/banner.gradle Normal file
View file

@ -0,0 +1,14 @@
gradle.projectsLoaded { g ->
printf "Host: %s\nOS: %s %s %s\nJVM: %s %s %s %s\nGradle: %s Groovy: %s Java: %s\n" +
"Build: group: %s name: %s version: %s\n",
InetAddress.getLocalHost(),
System.getProperty("os.name"),
System.getProperty("os.arch"),
System.getProperty("os.version"),
System.getProperty("java.version"),
System.getProperty("java.vm.version"),
System.getProperty("java.vm.vendor"),
System.getProperty("java.vm.name"),
gradle.gradleVersion, GroovySystem.getVersion(), JavaVersion.current(),
gradle.rootProject.group, gradle.rootProject.name, gradle.rootProject.version
}

View file

@ -0,0 +1,16 @@
if (project.hasProperty('forgeJoToken')) {
publishing {
repositories {
maven {
url 'https://xbib.org/api/packages/joerg/maven'
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = "token ${project.property('forgeJoToken')}"
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
}
}

27
gradle/publish/ivy.gradle Normal file
View file

@ -0,0 +1,27 @@
apply plugin: 'ivy-publish'
publishing {
repositories {
ivy {
url = "https://xbib.org/repo"
}
}
publications {
ivy(IvyPublication) {
from components.java
descriptor {
license {
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
author {
name = 'Jörg Prante'
url = 'https://xbib.org/joerg'
}
descriptor.description {
text = rootProject.ext.description
}
}
}
}
}

View file

@ -0,0 +1,51 @@
publishing {
publications {
"${project.name}"(MavenPublication) {
from components.java
pom {
artifactId = project.name
name = project.name
description = rootProject.ext.description
url = rootProject.ext.url
inceptionYear = rootProject.ext.inceptionYear
packaging = 'jar'
organization {
name = 'xbib'
url = 'https://xbib.org'
}
developers {
developer {
id = 'joerg'
name = 'Jörg Prante'
email = 'joergprante@gmail.com'
url = 'https://xbib.org/joerg'
}
}
scm {
url = rootProject.ext.scmUrl
connection = rootProject.ext.scmConnection
developerConnection = rootProject.ext.scmDeveloperConnection
}
issueManagement {
system = rootProject.ext.issueManagementSystem
url = rootProject.ext.issueManagementUrl
}
licenses {
license {
name = rootProject.ext.licenseName
url = rootProject.ext.licenseUrl
distribution = 'repo'
}
}
}
}
}
}
if (project.hasProperty("signing.keyId")) {
apply plugin: 'signing'
signing {
sign publishing.publications."${project.name}"
}
}

View file

@ -0,0 +1,11 @@
if (project.hasProperty('ossrhUsername') && project.hasProperty('ossrhPassword')) {
nexusPublishing {
repositories {
sonatype {
username = project.property('ossrhUsername')
password = project.property('ossrhPassword')
packageGroup = "org.xbib"
}
}
}
}

View file

@ -0,0 +1,19 @@
apply plugin: 'checkstyle'
tasks.withType(Checkstyle) {
ignoreFailures = true
reports {
xml.getRequired().set(true)
html.getRequired().set(true)
}
}
checkstyle {
configFile = rootProject.file('gradle/quality/checkstyle.xml')
ignoreFailures = true
showViolations = true
checkstyleMain {
source = sourceSets.main.allSource
}
}

View file

@ -0,0 +1,333 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<!-- This is a checkstyle configuration file. For descriptions of
what the following rules do, please see the checkstyle configuration
page at http://checkstyle.sourceforge.net/config.html -->
<module name="Checker">
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value=".*(Example|Test|module-info)(\$.*)?"/>
</module>
<module name="FileTabCharacter">
<!-- Checks that there are no tab characters in the file.
-->
</module>
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf"/>
</module>
<module name="RegexpSingleline">
<!-- Checks that FIXME is not used in comments. TODO is preferred.
-->
<property name="format" value="((//.*)|(\*.*))FIXME" />
<property name="message" value='TODO is preferred to FIXME. e.g. "TODO(johndoe): Refactor when v2 is released."' />
</module>
<module name="RegexpSingleline">
<!-- Checks that TODOs are named. (Actually, just that they are followed
by an open paren.)
-->
<property name="format" value="((//.*)|(\*.*))TODO[^(]" />
<property name="message" value='All TODOs should be named. e.g. "TODO(johndoe): Refactor when v2 is released."' />
</module>
<module name="JavadocPackage">
<!-- Checks that each Java package has a Javadoc file used for commenting.
Only allows a package-info.java, not package.html. -->
</module>
<!-- All Java AST specific tests live under TreeWalker module. -->
<module name="TreeWalker">
<!--
IMPORT CHECKS
-->
<module name="RedundantImport">
<!-- Checks for redundant import statements. -->
<property name="severity" value="error"/>
</module>
<module name="ImportOrder">
<!-- Checks for out of order import statements. -->
<property name="severity" value="warning"/>
<!-- <property name="tokens" value="IMPORT, STATIC_IMPORT"/> -->
<property name="separated" value="false"/>
<property name="groups" value="*"/>
<!-- <property name="option" value="above"/> -->
<property name="sortStaticImportsAlphabetically" value="true"/>
</module>
<module name="CustomImportOrder">
<!-- <property name="customImportOrderRules" value="THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###STATIC"/> -->
<!-- <property name="specialImportsRegExp" value="^javax\."/> -->
<!-- <property name="standardPackageRegExp" value="^java\."/> -->
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="false"/>
</module>
<!--
JAVADOC CHECKS
-->
<!-- Checks for Javadoc comments. -->
<!-- See http://checkstyle.sf.net/config_javadoc.html -->
<module name="JavadocMethod">
<property name="accessModifiers" value="protected"/>
<property name="severity" value="warning"/>
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
</module>
<module name="JavadocType">
<property name="scope" value="protected"/>
<property name="severity" value="error"/>
</module>
<module name="JavadocStyle">
<property name="severity" value="warning"/>
</module>
<!--
NAMING CHECKS
-->
<!-- Item 38 - Adhere to generally accepted naming conventions -->
<module name="PackageName">
<!-- Validates identifiers for package names against the
supplied expression. -->
<!-- Here the default checkstyle rule restricts package name parts to
seven characters, this is not in line with common practice at Google.
-->
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,})*$"/>
<property name="severity" value="warning"/>
</module>
<module name="TypeNameCheck">
<!-- Validates static, final fields against the
expression "^[A-Z][a-zA-Z0-9]*$". -->
<metadata name="altname" value="TypeName"/>
<property name="severity" value="warning"/>
</module>
<module name="ConstantNameCheck">
<!-- Validates non-private, static, final fields against the supplied
public/package final fields "^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$". -->
<metadata name="altname" value="ConstantName"/>
<property name="applyToPublic" value="true"/>
<property name="applyToProtected" value="true"/>
<property name="applyToPackage" value="true"/>
<property name="applyToPrivate" value="false"/>
<property name="format" value="^([A-Z][A-Z0-9]*(_[A-Z0-9]+)*|FLAG_.*)$"/>
<message key="name.invalidPattern"
value="Variable ''{0}'' should be in ALL_CAPS (if it is a constant) or be private (otherwise)."/>
<property name="severity" value="warning"/>
</module>
<module name="StaticVariableNameCheck">
<!-- Validates static, non-final fields against the supplied
expression "^[a-z][a-zA-Z0-9]*_?$". -->
<metadata name="altname" value="StaticVariableName"/>
<property name="applyToPublic" value="true"/>
<property name="applyToProtected" value="true"/>
<property name="applyToPackage" value="true"/>
<property name="applyToPrivate" value="true"/>
<property name="format" value="^[a-z][a-zA-Z0-9]*_?$"/>
<property name="severity" value="warning"/>
</module>
<module name="MemberNameCheck">
<!-- Validates non-static members against the supplied expression. -->
<metadata name="altname" value="MemberName"/>
<property name="applyToPublic" value="true"/>
<property name="applyToProtected" value="true"/>
<property name="applyToPackage" value="true"/>
<property name="applyToPrivate" value="true"/>
<property name="format" value="^[a-z][a-zA-Z0-9]*$"/>
<property name="severity" value="warning"/>
</module>
<module name="MethodNameCheck">
<!-- Validates identifiers for method names. -->
<metadata name="altname" value="MethodName"/>
<property name="format" value="^[a-z][a-zA-Z0-9]*(_[a-zA-Z0-9]+)*$"/>
<property name="severity" value="warning"/>
</module>
<module name="ParameterName">
<!-- Validates identifiers for method parameters against the
expression "^[a-z][a-zA-Z0-9]*$". -->
<property name="severity" value="warning"/>
</module>
<module name="LocalFinalVariableName">
<!-- Validates identifiers for local final variables against the
expression "^[a-z][a-zA-Z0-9]*$". -->
<property name="severity" value="warning"/>
</module>
<module name="LocalVariableName">
<!-- Validates identifiers for local variables against the
expression "^[a-z][a-zA-Z0-9]*$". -->
<property name="severity" value="warning"/>
</module>
<!--
LENGTH and CODING CHECKS
-->
<module name="LeftCurly">
<!-- Checks for placement of the left curly brace ('{'). -->
<property name="severity" value="warning"/>
</module>
<module name="RightCurly">
<!-- Checks right curlies on CATCH, ELSE, and TRY blocks are on
the same line. e.g., the following example is fine:
<pre>
if {
...
} else
</pre>
-->
<!-- This next example is not fine:
<pre>
if {
...
}
else
</pre>
-->
<property name="option" value="same"/>
<property name="severity" value="warning"/>
</module>
<!-- Checks for braces around if and else blocks -->
<module name="NeedBraces">
<property name="severity" value="warning"/>
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
</module>
<module name="UpperEll">
<!-- Checks that long constants are defined with an upper ell.-->
<property name="severity" value="error"/>
</module>
<module name="FallThrough">
<!-- Warn about falling through to the next case statement. Similar to
javac -Xlint:fallthrough, but the check is suppressed if a single-line comment
on the last non-blank line preceding the fallen-into case contains 'fall through' (or
some other variants which we don't publicized to promote consistency).
-->
<property name="reliefPattern"
value="fall through|Fall through|fallthru|Fallthru|falls through|Falls through|fallthrough|Fallthrough|No break|NO break|no break|continue on"/>
<property name="severity" value="error"/>
</module>
<!--
MODIFIERS CHECKS
-->
<module name="ModifierOrder">
<!-- Warn if modifier order is inconsistent with JLS3 8.1.1, 8.3.1, and
8.4.3. The prescribed order is:
public, protected, private, abstract, static, final, transient, volatile,
synchronized, native, strictfp
-->
</module>
<!--
WHITESPACE CHECKS
-->
<module name="WhitespaceAround">
<!-- Checks that various tokens are surrounded by whitespace.
This includes most binary operators and keywords followed
by regular or curly braces.
-->
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR,
BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN,
EQUAL, GE, GT, LAND, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE,
LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, LITERAL_RETURN,
LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS,
MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION,
SL, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/>
<property name="severity" value="error"/>
</module>
<module name="WhitespaceAfter">
<!-- Checks that commas, semicolons and typecasts are followed by
whitespace.
-->
<property name="tokens" value="COMMA, SEMI, TYPECAST"/>
</module>
<module name="NoWhitespaceAfter">
<!-- Checks that there is no whitespace after various unary operators.
Linebreaks are allowed.
-->
<property name="tokens" value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS,
UNARY_PLUS"/>
<property name="allowLineBreaks" value="true"/>
<property name="severity" value="error"/>
</module>
<module name="NoWhitespaceBefore">
<!-- Checks that there is no whitespace before various unary operators.
Linebreaks are allowed.
-->
<property name="tokens" value="SEMI, DOT, POST_DEC, POST_INC"/>
<property name="allowLineBreaks" value="true"/>
<property name="severity" value="error"/>
</module>
<module name="ParenPad">
<!-- Checks that there is no whitespace before close parens or after
open parens.
-->
<property name="severity" value="warning"/>
</module>
</module>
<module name="LineLength">
<!-- Checks if a line is too long. -->
<property name="max" value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.max}" default="128"/>
<property name="severity" value="error"/>
<!--
The default ignore pattern exempts the following elements:
- import statements
- long URLs inside comments
-->
<property name="ignorePattern"
value="${com.puppycrawl.tools.checkstyle.checks.sizes.LineLength.ignorePattern}"
default="^(package .*;\s*)|(import .*;\s*)|( *(\*|//).*https?://.*)$"/>
</module>
</module>

View file

@ -0,0 +1,11 @@
cyclonedxBom {
includeConfigs = [ 'runtimeClasspath' ]
skipConfigs = [ 'compileClasspath', 'testCompileClasspath' ]
projectType = "library"
schemaVersion = "1.4"
destination = file("build/reports")
outputName = "bom"
outputFormat = "json"
includeBomSerialNumber = true
componentVersion = "2.0.0"
}

17
gradle/quality/pmd.gradle Normal file
View file

@ -0,0 +1,17 @@
apply plugin: 'pmd'
tasks.withType(Pmd) {
ignoreFailures = true
reports {
xml.getRequired().set(true)
html.getRequired().set(true)
}
}
pmd {
ignoreFailures = true
consoleOutput = false
toolVersion = "6.51.0"
ruleSetFiles = rootProject.files('gradle/quality/pmd/category/java/bestpractices.xml')
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,10 @@
rulesets.filenames=\
category/java/bestpractices.xml,\
category/java/codestyle.xml,\
category/java/design.xml,\
category/java/documentation.xml,\
category/java/errorprone.xml,\
category/java/multithreading.xml,\
category/java/performance.xml,\
category/java/security.xml

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,144 @@
<?xml version="1.0"?>
<ruleset name="Documentation"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Rules that are related to code documentation.
</description>
<rule name="CommentContent"
since="5.0"
message="Invalid words or phrases found"
class="net.sourceforge.pmd.lang.java.rule.documentation.CommentContentRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentcontent">
<description>
A rule for the politically correct... we don't want to offend anyone.
</description>
<priority>3</priority>
<example>
<![CDATA[
//OMG, this is horrible, Bob is an idiot !!!
]]>
</example>
</rule>
<rule name="CommentRequired"
since="5.1"
message="Comment is required"
class="net.sourceforge.pmd.lang.java.rule.documentation.CommentRequiredRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentrequired">
<description>
Denotes whether comments are required (or unwanted) for specific language elements.
</description>
<priority>3</priority>
<example>
<![CDATA[
/**
*
*
* @author Jon Doe
*/
]]>
</example>
</rule>
<rule name="CommentSize"
since="5.0"
message="Comment is too large"
class="net.sourceforge.pmd.lang.java.rule.documentation.CommentSizeRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentsize">
<description>
Determines whether the dimensions of non-header comments found are within the specified limits.
</description>
<priority>3</priority>
<example>
<![CDATA[
/**
*
* too many lines!
*
*
*
*
*
*
*
*
*
*
*
*
*/
]]>
</example>
</rule>
<rule name="UncommentedEmptyConstructor"
language="java"
since="3.4"
message="Document empty constructor"
class="net.sourceforge.pmd.lang.rule.XPathRule"
typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#uncommentedemptyconstructor">
<description>
Uncommented Empty Constructor finds instances where a constructor does not
contain statements, but there is no comment. By explicitly commenting empty
constructors it is easier to distinguish between intentional (commented)
and unintentional empty constructors.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//ConstructorDeclaration[@Private='false']
[count(BlockStatement) = 0 and ($ignoreExplicitConstructorInvocation = 'true' or not(ExplicitConstructorInvocation)) and @containsComment = 'false']
[not(../Annotation/MarkerAnnotation/Name[pmd-java:typeIs('javax.inject.Inject')])]
]]>
</value>
</property>
<property name="ignoreExplicitConstructorInvocation" type="Boolean" description="Ignore explicit constructor invocation when deciding whether constructor is empty or not" value="false"/>
</properties>
<example>
<![CDATA[
public Foo() {
// This constructor is intentionally empty. Nothing special is needed here.
}
]]>
</example>
</rule>
<rule name="UncommentedEmptyMethodBody"
language="java"
since="3.4"
message="Document empty method body"
class="net.sourceforge.pmd.lang.rule.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#uncommentedemptymethodbody">
<description>
Uncommented Empty Method Body finds instances where a method body does not contain
statements, but there is no comment. By explicitly commenting empty method bodies
it is easier to distinguish between intentional (commented) and unintentional
empty methods.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//MethodDeclaration/Block[count(BlockStatement) = 0 and @containsComment = 'false']
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
public void doSomething() {
}
]]>
</example>
</rule>
</ruleset>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,393 @@
<?xml version="1.0"?>
<ruleset name="Multithreading"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Rules that flag issues when dealing with multiple threads of execution.
</description>
<rule name="AvoidSynchronizedAtMethodLevel"
language="java"
since="3.0"
message="Use block level rather than method level synchronization"
class="net.sourceforge.pmd.lang.rule.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#avoidsynchronizedatmethodlevel">
<description>
Method-level synchronization can cause problems when new code is added to the method.
Block-level synchronization helps to ensure that only the code that needs synchronization
gets it.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>//MethodDeclaration[@Synchronized='true']</value>
</property>
</properties>
<example>
<![CDATA[
public class Foo {
// Try to avoid this:
synchronized void foo() {
}
// Prefer this:
void bar() {
synchronized(this) {
}
}
// Try to avoid this for static methods:
static synchronized void fooStatic() {
}
// Prefer this:
static void barStatic() {
synchronized(Foo.class) {
}
}
}
]]>
</example>
</rule>
<rule name="AvoidThreadGroup"
language="java"
since="3.6"
message="Avoid using java.lang.ThreadGroup; it is not thread safe"
class="net.sourceforge.pmd.lang.rule.XPathRule"
typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#avoidthreadgroup">
<description>
Avoid using java.lang.ThreadGroup; although it is intended to be used in a threaded environment
it contains methods that are not thread-safe.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//AllocationExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.ThreadGroup')]|
//PrimarySuffix[contains(@Image, 'getThreadGroup')]
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
public class Bar {
void buz() {
ThreadGroup tg = new ThreadGroup("My threadgroup");
tg = new ThreadGroup(tg, "my thread group");
tg = Thread.currentThread().getThreadGroup();
tg = System.getSecurityManager().getThreadGroup();
}
}
]]>
</example>
</rule>
<rule name="AvoidUsingVolatile"
language="java"
since="4.1"
class="net.sourceforge.pmd.lang.rule.XPathRule"
message="Use of modifier volatile is not recommended."
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#avoidusingvolatile">
<description>
Use of the keyword 'volatile' is generally used to fine tune a Java application, and therefore, requires
a good expertise of the Java Memory Model. Moreover, its range of action is somewhat misknown. Therefore,
the volatile keyword should not be used for maintenance purpose and portability.
</description>
<priority>2</priority>
<properties>
<property name="xpath">
<value>//FieldDeclaration[contains(@Volatile,'true')]</value>
</property>
</properties>
<example>
<![CDATA[
public class ThrDeux {
private volatile String var1; // not suggested
private String var2; // preferred
}
]]>
</example>
</rule>
<rule name="DoNotUseThreads"
language="java"
since="4.1"
class="net.sourceforge.pmd.lang.rule.XPathRule"
message="To be compliant to J2EE, a webapp should not use any thread."
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#donotusethreads">
<description>
The J2EE specification explicitly forbids the use of threads.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>//ClassOrInterfaceType[@Image = 'Thread' or @Image = 'Runnable']</value>
</property>
</properties>
<example>
<![CDATA[
// This is not allowed
public class UsingThread extends Thread {
}
// Neither this,
public class OtherThread implements Runnable {
// Nor this ...
public void methode() {
Runnable thread = new Thread(); thread.run();
}
}
]]>
</example>
</rule>
<rule name="DontCallThreadRun"
language="java"
since="4.3"
message="Don't call Thread.run() explicitly, use Thread.start()"
class="net.sourceforge.pmd.lang.rule.XPathRule"
typeResolution="true"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#dontcallthreadrun">
<description>
Explicitly calling Thread.run() method will execute in the caller's thread of control. Instead, call Thread.start() for the intended behavior.
</description>
<priority>4</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//StatementExpression/PrimaryExpression
[
PrimaryPrefix
[
./Name[ends-with(@Image, '.run') or @Image = 'run']
and substring-before(Name/@Image, '.') =//VariableDeclarator/VariableDeclaratorId/@Image
[../../../Type/ReferenceType/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]]
or (./AllocationExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.Thread')]
and ../PrimarySuffix[@Image = 'run'])
]
]
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
Thread t = new Thread();
t.run(); // use t.start() instead
new Thread().run(); // same violation
]]>
</example>
</rule>
<rule name="DoubleCheckedLocking"
language="java"
since="1.04"
message="Double checked locking is not thread safe in Java."
class="net.sourceforge.pmd.lang.java.rule.multithreading.DoubleCheckedLockingRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#doublecheckedlocking">
<description>
Partially created objects can be returned by the Double Checked Locking pattern when used in Java.
An optimizing JRE may assign a reference to the baz variable before it calls the constructor of the object the
reference points to.
Note: With Java 5, you can make Double checked locking work, if you declare the variable to be `volatile`.
For more details refer to: &lt;http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html>
or &lt;http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html>
</description>
<priority>1</priority>
<example>
<![CDATA[
public class Foo {
/*volatile */ Object baz = null; // fix for Java5 and later: volatile
Object bar() {
if (baz == null) { // baz may be non-null yet not fully created
synchronized(this) {
if (baz == null) {
baz = new Object();
}
}
}
return baz;
}
}
]]>
</example>
</rule>
<rule name="NonThreadSafeSingleton"
since="3.4"
message="Singleton is not thread safe"
class="net.sourceforge.pmd.lang.java.rule.multithreading.NonThreadSafeSingletonRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#nonthreadsafesingleton">
<description>
Non-thread safe singletons can result in bad state changes. Eliminate
static singletons if possible by instantiating the object directly. Static
singletons are usually not needed as only a single instance exists anyway.
Other possible fixes are to synchronize the entire method or to use an
[initialize-on-demand holder class](https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom).
Refrain from using the double-checked locking pattern. The Java Memory Model doesn't
guarantee it to work unless the variable is declared as `volatile`, adding an uneeded
performance penalty. [Reference](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)
See Effective Java, item 48.
</description>
<priority>3</priority>
<example>
<![CDATA[
private static Foo foo = null;
//multiple simultaneous callers may see partially initialized objects
public static Foo getFoo() {
if (foo==null) {
foo = new Foo();
}
return foo;
}
]]>
</example>
</rule>
<rule name="UnsynchronizedStaticDateFormatter"
since="3.6"
deprecated="true"
message="Static DateFormatter objects should be accessed in a synchronized manner"
class="net.sourceforge.pmd.lang.java.rule.multithreading.UnsynchronizedStaticDateFormatterRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#unsynchronizedstaticdateformatter">
<description>
SimpleDateFormat instances are not synchronized. Sun recommends using separate format instances
for each thread. If multiple threads must access a static formatter, the formatter must be
synchronized either on method or block level.
This rule has been deprecated in favor of the rule {% rule UnsynchronizedStaticFormatter %}.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo {
private static final SimpleDateFormat sdf = new SimpleDateFormat();
void bar() {
sdf.format(); // poor, no thread-safety
}
synchronized void foo() {
sdf.format(); // preferred
}
}
]]>
</example>
</rule>
<rule name="UnsynchronizedStaticFormatter"
since="6.11.0"
message="Static Formatter objects should be accessed in a synchronized manner"
class="net.sourceforge.pmd.lang.java.rule.multithreading.UnsynchronizedStaticFormatterRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#unsynchronizedstaticformatter">
<description>
Instances of `java.text.Format` are generally not synchronized.
Sun recommends using separate format instances for each thread.
If multiple threads must access a static formatter, the formatter must be
synchronized either on method or block level.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo {
private static final SimpleDateFormat sdf = new SimpleDateFormat();
void bar() {
sdf.format(); // poor, no thread-safety
}
synchronized void foo() {
sdf.format(); // preferred
}
}
]]>
</example>
</rule>
<rule name="UseConcurrentHashMap"
language="java"
minimumLanguageVersion="1.5"
since="4.2.6"
message="If you run in Java5 or newer and have concurrent access, you should use the ConcurrentHashMap implementation"
class="net.sourceforge.pmd.lang.rule.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#useconcurrenthashmap">
<description>
Since Java5 brought a new implementation of the Map designed for multi-threaded access, you can
perform efficient map reads without blocking other threads.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//Type[../VariableDeclarator/VariableInitializer//AllocationExpression/ClassOrInterfaceType[@Image != 'ConcurrentHashMap']]
/ReferenceType/ClassOrInterfaceType[@Image = 'Map']
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
public class ConcurrentApp {
public void getMyInstance() {
Map map1 = new HashMap(); // fine for single-threaded access
Map map2 = new ConcurrentHashMap(); // preferred for use with multiple threads
// the following case will be ignored by this rule
Map map3 = someModule.methodThatReturnMap(); // might be OK, if the returned map is already thread-safe
}
}
]]>
</example>
</rule>
<rule name="UseNotifyAllInsteadOfNotify"
language="java"
since="3.0"
message="Call Thread.notifyAll() rather than Thread.notify()"
class="net.sourceforge.pmd.lang.rule.XPathRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_multithreading.html#usenotifyallinsteadofnotify">
<description>
Thread.notify() awakens a thread monitoring the object. If more than one thread is monitoring, then only
one is chosen. The thread chosen is arbitrary; thus its usually safer to call notifyAll() instead.
</description>
<priority>3</priority>
<properties>
<property name="xpath">
<value>
<![CDATA[
//StatementExpression/PrimaryExpression
[PrimarySuffix/Arguments[@ArgumentCount = '0']]
[
PrimaryPrefix[
./Name[@Image='notify' or ends-with(@Image,'.notify')]
or ../PrimarySuffix/@Image='notify'
or (./AllocationExpression and ../PrimarySuffix[@Image='notify'])
]
]
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
void bar() {
x.notify();
// If many threads are monitoring x, only one (and you won't know which) will be notified.
// use instead:
x.notifyAll();
}
]]>
</example>
</rule>
</ruleset>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,65 @@
<?xml version="1.0"?>
<ruleset name="Security" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Rules that flag potential security flaws.
</description>
<rule name="HardCodedCryptoKey"
since="6.4.0"
message="Do not use hard coded encryption keys"
class="net.sourceforge.pmd.lang.java.rule.security.HardCodedCryptoKeyRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_security.html#hardcodedcryptokey">
<description>
Do not use hard coded values for cryptographic operations. Please store keys outside of source code.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo {
void good() {
SecretKeySpec secretKeySpec = new SecretKeySpec(Properties.getKey(), "AES");
}
void bad() {
SecretKeySpec secretKeySpec = new SecretKeySpec("my secret here".getBytes(), "AES");
}
}
]]>
</example>
</rule>
<rule name="InsecureCryptoIv"
since="6.3.0"
message="Do not use hard coded initialization vector in crypto operations"
class="net.sourceforge.pmd.lang.java.rule.security.InsecureCryptoIvRule"
externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_security.html#insecurecryptoiv">
<description>
Do not use hard coded initialization vector in cryptographic operations. Please use a randomly generated IV.
</description>
<priority>3</priority>
<example>
<![CDATA[
public class Foo {
void good() {
SecureRandom random = new SecureRandom();
byte iv[] = new byte[16];
random.nextBytes(bytes);
}
void bad() {
byte[] iv = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, };
}
void alsoBad() {
byte[] iv = "secret iv in here".getBytes();
}
}
]]>
</example>
</rule>
</ruleset>

View file

@ -0,0 +1,37 @@
subprojects {
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.junit.reportsPath", "build/test-results/test/"
}
}
tasks.withType(Pmd) {
ignoreFailures = true
reports {
xml.enabled = true
html.enabled = true
}
}
spotbugs {
effort = "max"
reportLevel = "low"
//includeFilter = file("findbugs-exclude.xml")
}
tasks.withType(com.github.spotbugs.SpotBugsTask) {
ignoreFailures = true
reports {
xml.enabled = false
html.enabled = true
}
}
}

View file

@ -0,0 +1,15 @@
apply plugin: 'com.github.spotbugs'
spotbugs {
effort = "max"
reportLevel = "low"
ignoreFailures = true
}
spotbugsMain {
reports {
xml.getRequired().set(false)
html.getRequired().set(true)
}
}

View file

@ -0,0 +1,4 @@
repositories {
mavenLocal()
mavenCentral()
}

28
gradle/test/junit5.gradle Normal file
View file

@ -0,0 +1,28 @@
def junitVersion = project.hasProperty('junit.version')?project.property('junit.version'):'5.7.1'
def hamcrestVersion = project.hasProperty('hamcrest.version')?project.property('hamcrest.version'):'2.2'
dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}"
testImplementation "org.hamcrest:hamcrest-library:${hamcrestVersion}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junitVersion}"
}
test {
useJUnitPlatform()
failFast = false
testLogging {
events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED'
}
afterSuite { desc, result ->
if (!desc.parent) {
println "\nTest result: ${result.resultType}"
println "Test summary: ${result.testCount} tests, " +
"${result.successfulTestCount} succeeded, " +
"${result.failedTestCount} failed, " +
"${result.skippedTestCount} skipped"
}
}
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

245
gradlew vendored Executable file
View file

@ -0,0 +1,245 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original 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 POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

92
gradlew.bat vendored Normal file
View file

@ -0,0 +1,92 @@
@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
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@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="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -1 +1,49 @@
rootProject.name = 'guice' pluginManagement {
repositories {
mavenLocal()
mavenCentral {
metadataSources {
mavenPom()
artifact()
ignoreGradleMetadataRedirection()
}
}
gradlePluginPortal()
}
}
dependencyResolutionManagement {
versionCatalogs {
libs {
version('gradle', '8.1.1')
version('groovy', '3.0.10')
version('junit', '5.9.2')
library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit')
library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit')
library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2')
library('junit4', 'junit', 'junit').version('4.13.2')
library('javax.inject', 'org.xbib', 'javax-inject').version('1')
library('guava', 'org.xbib', 'guava').version('30.1')
library('guava.testlib', 'com.google.guava', 'guava-testlib').version('30.1-jre')
}
}
}
/*
gradle.wrapper.version = 6.6.1
javax-inject.version = 1
guava.version = 30.1
# test
junit.version = 5.7.2
junit4.version = 4.13.2
log4j.version = 2.14.1
api "org.xbib:javax-inject:${project.property('javax-inject.version')}"
api "org.xbib:guava:${project.property('guava.version')}"
testImplementation "junit:junit:${project.property('junit4.version')}"
// Helper for com.google.common.testing.GcFinalization, com.google.common.testing.EqualsTester
testImplementation "com.google.guava:guava-testlib:30.1-jre"
*/

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

@ -100,7 +100,7 @@ import java.lang.reflect.Proxy;
* contribute their own custom scopes for use here as well. * contribute their own custom scopes for use here as well.
* *
* <pre> * <pre>
* bind(new TypeLiteral&lt;PaymentService&lt;CreditCard>>() {}) * bind(new TypeLiteral&lt;PaymentService&lt;CreditCard&gt;&gt;() {})
* .to(CreditCardPaymentService.class);</pre> * .to(CreditCardPaymentService.class);</pre>
* *
* This admittedly odd construct is the way to bind a parameterized type. It * This admittedly odd construct is the way to bind a parameterized type. It
@ -159,7 +159,7 @@ import java.lang.reflect.Proxy;
* your application. * your application.
* *
* <pre> * <pre>
* Constructor<T> loneCtor = getLoneCtorFromServiceImplViaReflection(); * Constructor&lt;T&gt; loneCtor = getLoneCtorFromServiceImplViaReflection();
* bind(ServiceImpl.class) * bind(ServiceImpl.class)
* .toConstructor(loneCtor);</pre> * .toConstructor(loneCtor);</pre>
* *
@ -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

@ -1,24 +1,20 @@
package com.google.inject; package com.google.inject;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.inject.internal.Annotations;
import com.google.inject.internal.MoreTypes;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.inject.internal.Annotations.generateAnnotation; import static com.google.inject.internal.Annotations.generateAnnotation;
import static com.google.inject.internal.Annotations.isAllDefaultMethods; import static com.google.inject.internal.Annotations.isAllDefaultMethods;
import com.google.inject.internal.Annotations;
import com.google.inject.internal.MoreTypes;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
/** /**
* Binding key consisting of an injection type and an optional annotation. * Guice uses Key objects to identify a dependency that can be resolved by the Guice {@link
* Matches the type and annotation at a point of injection. * Injector}. A Guice key consists of an injection type and an optional annotation.
* *
* <p>For example, {@code Key.get(Service.class, Transactional.class)} will * <p>For example, {@code Key.get(Service.class, Transactional.class)} will match:
* match:
* *
* <pre> * <pre>
* {@literal @}Inject * {@literal @}Inject
@ -27,12 +23,13 @@ import static com.google.inject.internal.Annotations.isAllDefaultMethods;
* } * }
* </pre> * </pre>
* *
* <p>{@code Key} supports generic types via subclassing just like {@link * <p>{@code Key} supports generic types via subclassing just like {@link TypeLiteral}.
* TypeLiteral}.
* *
* <p>Keys do not differentiate between primitive types (int, char, etc.) and * <p>Keys do not differentiate between primitive types (int, char, etc.) and their corresponding
* their corresponding wrapper types (Integer, Character, etc.). Primitive * wrapper types (Integer, Character, etc.). Primitive types will be replaced with their wrapper
* types will be replaced with their wrapper types when keys are created. * types when keys are created.
*
* @author crazybob@google.com (Bob Lee)
*/ */
public class Key<T> { public class Key<T> {
@ -40,38 +37,37 @@ public class Key<T> {
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.
* *
* <p>Clients create an empty anonymous subclass. Doing so embeds the type * <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
* parameter in the anonymous class's type hierarchy so we can reconstitute it * anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
* at runtime despite erasure.
* *
* <p>Example usage for a binding of type {@code Foo} annotated with * <p>Example usage for a binding of type {@code Foo} annotated with {@code @Bar}:
* {@code @Bar}:
* *
* <p>{@code new Key<Foo>(Bar.class) {}}. * <p>{@code new Key<Foo>(Bar.class) {}}.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Key(Class<? extends Annotation> annotationType) { protected Key(Class<? extends Annotation> annotationType) {
this.annotationStrategy = strategyFor(annotationType); this.annotationStrategy = strategyFor(annotationType);
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();
} }
/** /**
* 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.
* *
* <p>Clients create an empty anonymous subclass. Doing so embeds the type * <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
* parameter in the anonymous class's type hierarchy so we can reconstitute it * anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
* at runtime despite erasure.
* *
* <p>Example usage for a binding of type {@code Foo} annotated with * <p>Example usage for a binding of type {@code Foo} annotated with {@code @Bar}:
* {@code @Bar}:
* *
* <p>{@code new Key<Foo>(new Bar()) {}}. * <p>{@code new Key<Foo>(new Bar()) {}}.
*/ */
@ -79,18 +75,17 @@ public class Key<T> {
protected Key(Annotation annotation) { protected Key(Annotation annotation) {
// no usages, not test-covered // no usages, not test-covered
this.annotationStrategy = strategyFor(annotation); this.annotationStrategy = strategyFor(annotation);
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();
} }
/** /**
* 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.
* *
* <p>Clients create an empty anonymous subclass. Doing so embeds the type * <p>Clients create an empty anonymous subclass. Doing so embeds the type parameter in the
* parameter in the anonymous class's type hierarchy so we can reconstitute it * anonymous class's type hierarchy so we can reconstitute it at runtime despite erasure.
* at runtime despite erasure.
* *
* <p>Example usage for a binding of type {@code Foo}: * <p>Example usage for a binding of type {@code Foo}:
* *
@ -99,191 +94,51 @@ public class Key<T> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected Key() { protected Key() {
this.annotationStrategy = NullAnnotationStrategy.INSTANCE; this.annotationStrategy = NullAnnotationStrategy.INSTANCE;
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();
} }
/** /** Unsafe. Constructs a key from a manually specified type. */
* Unsafe. Constructs a key from a manually specified type.
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Key(Type type, AnnotationStrategy annotationStrategy) { private Key(Type type, AnnotationStrategy annotationStrategy) {
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();
} }
/** /** Constructs a key from a manually specified type. */
* Constructs a key from a manually specified type.
*/
private Key(TypeLiteral<T> typeLiteral, AnnotationStrategy annotationStrategy) { private Key(TypeLiteral<T> typeLiteral, AnnotationStrategy annotationStrategy) {
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();
} }
/** /** Computes the hash code for this key. */
* Gets a key for an injection type and an annotation strategy.
*/
static <T> Key<T> get(Class<T> type,
AnnotationStrategy annotationStrategy) {
return new Key<T>(type, annotationStrategy);
}
/**
* Gets a key for an injection type.
*/
public static <T> Key<T> get(Class<T> type) {
return new Key<T>(type, NullAnnotationStrategy.INSTANCE);
}
/**
* Gets a key for an injection type and an annotation type.
*/
public static <T> Key<T> get(Class<T> type,
Class<? extends Annotation> annotationType) {
return new Key<T>(type, strategyFor(annotationType));
}
/**
* Gets a key for an injection type and an annotation.
*/
public static <T> Key<T> get(Class<T> type, Annotation annotation) {
return new Key<T>(type, strategyFor(annotation));
}
/**
* Gets a key for an injection type.
*/
public static Key<?> get(Type type) {
return new Key<Object>(type, NullAnnotationStrategy.INSTANCE);
}
/**
* Gets a key for an injection type and an annotation type.
*/
public static Key<?> get(Type type,
Class<? extends Annotation> annotationType) {
return new Key<Object>(type, strategyFor(annotationType));
}
/**
* Gets a key for an injection type and an annotation.
*/
public static Key<?> get(Type type, Annotation annotation) {
return new Key<Object>(type, strategyFor(annotation));
}
/**
* Gets a key for an injection type.
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
return new Key<T>(typeLiteral, NullAnnotationStrategy.INSTANCE);
}
/**
* Gets a key for an injection type and an annotation type.
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
Class<? extends Annotation> annotationType) {
return new Key<T>(typeLiteral, strategyFor(annotationType));
}
/**
* Gets a key for an injection type and an annotation.
*/
public static <T> Key<T> get(TypeLiteral<T> typeLiteral,
Annotation annotation) {
return new Key<T>(typeLiteral, strategyFor(annotation));
}
/**
* Gets the strategy for an annotation.
*/
static AnnotationStrategy strategyFor(Annotation annotation) {
checkNotNull(annotation, "annotation");
Class<? extends Annotation> annotationType = annotation.annotationType();
ensureRetainedAtRuntime(annotationType);
ensureIsBindingAnnotation(annotationType);
if (Annotations.isMarker(annotationType)) {
return new AnnotationTypeStrategy(annotationType, annotation);
}
return new AnnotationInstanceStrategy(Annotations.canonicalizeIfNamed(annotation));
}
/**
* Gets the strategy for an annotation type.
*/
static AnnotationStrategy strategyFor(Class<? extends Annotation> annotationType) {
annotationType = Annotations.canonicalizeIfNamed(annotationType);
if (isAllDefaultMethods(annotationType)) {
return strategyFor(generateAnnotation(annotationType));
}
checkNotNull(annotationType, "annotation type");
ensureRetainedAtRuntime(annotationType);
ensureIsBindingAnnotation(annotationType);
return new AnnotationTypeStrategy(annotationType, null);
}
private static void ensureRetainedAtRuntime(
Class<? extends Annotation> annotationType) {
checkArgument(Annotations.isRetainedAtRuntime(annotationType),
"%s is not retained at runtime. Please annotate it with @Retention(RUNTIME).",
annotationType.getName());
}
private static void ensureIsBindingAnnotation(Class<? extends Annotation> annotationType) {
checkArgument(Annotations.isBindingAnnotation(annotationType),
"%s is not a binding annotation. Please annotate it with @BindingAnnotation.",
annotationType.getName());
}
/**
* Computes the hash code for this key.
*/
private int computeHashCode() { private int computeHashCode() {
return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode(); return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
} }
/** /** Gets the key type. */
* @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.
*/
public final TypeLiteral<T> getTypeLiteral() { public final TypeLiteral<T> getTypeLiteral() {
return typeLiteral; return typeLiteral;
} }
/** /** Gets the annotation type. Will be {@code null} if this key lacks an annotation. */
* Gets the annotation type.
*/
public final Class<? extends Annotation> getAnnotationType() { public final Class<? extends Annotation> getAnnotationType() {
return annotationStrategy.getAnnotationType(); return annotationStrategy.getAnnotationType();
} }
/** /**
* Gets the annotation. * Gets the annotation instance if available. Will be {@code null} if this key lacks an annotation
* <i>or</i> the key was constructed with a {@code Class<Annotation>}.
*
* <p><b>Warning:</b> this can return null even if this key is annotated. To check whether a
* {@code Key} has an annotation use {@link #hasAnnotationType} instead.
*/ */
// TODO(diamondm) consider deprecating this in favor of a method that ISEs if hasAnnotationType()
// is true but this would return null.
public final Annotation getAnnotation() { public final Annotation getAnnotation() {
return annotationStrategy.getAnnotation(); return annotationStrategy.getAnnotation();
} }
@ -306,9 +161,7 @@ public class Key<T> {
return typeLiteral.getRawType(); return typeLiteral.getRawType();
} }
/** /** Gets the key of this key's provider. */
* Gets the key of this key's provider.
*/
Key<Provider<T>> providerKey() { Key<Provider<T>> providerKey() {
return ofType(typeLiteral.providerType()); return ofType(typeLiteral.providerType());
} }
@ -333,73 +186,136 @@ 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;
} }
/** /** Gets a key for an injection type and an annotation strategy. */
* Returns a new key of the specified type with the same annotation as this static <T> Key<T> get(Class<T> type, AnnotationStrategy annotationStrategy) {
* key.
*/
public <T> Key<T> ofType(Class<T> type) {
return new Key<T>(type, annotationStrategy); return new Key<T>(type, annotationStrategy);
} }
/** Gets a key for an injection type. */
public static <T> Key<T> get(Class<T> type) {
return new Key<T>(type, NullAnnotationStrategy.INSTANCE);
}
/** Gets a key for an injection type and an annotation type. */
public static <T> Key<T> get(Class<T> type, Class<? extends Annotation> annotationType) {
return new Key<T>(type, strategyFor(annotationType));
}
/** Gets a key for an injection type and an annotation. */
public static <T> Key<T> get(Class<T> type, Annotation annotation) {
return new Key<T>(type, strategyFor(annotation));
}
/** Gets a key for an injection type. */
public static Key<?> get(Type type) {
return new Key<>(type, NullAnnotationStrategy.INSTANCE);
}
/** Gets a key for an injection type and an annotation type. */
public static Key<?> get(Type type, Class<? extends Annotation> annotationType) {
return new Key<>(type, strategyFor(annotationType));
}
/** Gets a key for an injection type and an annotation. */
public static Key<?> get(Type type, Annotation annotation) {
return new Key<>(type, strategyFor(annotation));
}
/** Gets a key for an injection type. */
public static <T> Key<T> get(TypeLiteral<T> typeLiteral) {
return new Key<T>(typeLiteral, NullAnnotationStrategy.INSTANCE);
}
/** Gets a key for an injection type and an annotation type. */
public static <T> Key<T> get(
TypeLiteral<T> typeLiteral, Class<? extends Annotation> annotationType) {
return new Key<T>(typeLiteral, strategyFor(annotationType));
}
/** Gets a key for an injection type and an annotation. */
public static <T> Key<T> get(TypeLiteral<T> typeLiteral, Annotation annotation) {
return new Key<T>(typeLiteral, strategyFor(annotation));
}
/** /**
* Returns a new key of the specified type with the same annotation as this * Returns a new key of the specified type with the same annotation as this key.
* key. *
* @since 3.0
*/
public <U> Key<U> ofType(Class<U> type) {
return new Key<>(type, annotationStrategy);
}
/**
* Returns a new key of the specified type with the same annotation as this key.
*
* @since 3.0
*/ */
public Key<?> ofType(Type type) { public Key<?> ofType(Type type) {
return new Key<Object>(type, annotationStrategy); return new Key<>(type, annotationStrategy);
} }
/** /**
* Returns a new key of the specified type with the same annotation as this * Returns a new key of the specified type with the same annotation as this key.
* key. *
* @since 3.0
*/ */
public <T> Key<T> ofType(TypeLiteral<T> type) { public <U> Key<U> ofType(TypeLiteral<U> type) {
return new Key<T>(type, annotationStrategy); return new Key<U>(type, annotationStrategy);
}
/**
* Returns a new key of the same type with the specified annotation.
*
* <p>This is equivalent to {@code Key.get(key.getTypeLiteral(), annotation)} but may be more
* convenient to use in certain cases.
*
* @since 5.0
*/
public Key<T> withAnnotation(Class<? extends Annotation> annotationType) {
return new Key<T>(typeLiteral, strategyFor(annotationType));
}
/**
* Returns a new key of the same type with the specified annotation.
*
* <p>This is equivalent to {@code Key.get(key.getTypeLiteral(), annotation)} but may be more
* convenient to use in certain cases.
*
* @since 5.0
*/
public Key<T> withAnnotation(Annotation annotation) {
return new Key<T>(typeLiteral, strategyFor(annotation));
} }
/** /**
* Returns true if this key has annotation attributes. * Returns true if this key has annotation attributes.
*
* @since 3.0
*/ */
public boolean hasAttributes() { public boolean hasAttributes() {
return annotationStrategy.hasAttributes(); return annotationStrategy.hasAttributes();
} }
/** /**
* Returns this key without annotation attributes, i.e. with only the * Returns this key without annotation attributes, i.e. with only the annotation type.
* annotation type. *
* @since 3.0
*/ */
public Key<T> withoutAttributes() { public Key<T> withoutAttributes() {
return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes()); return new Key<T>(typeLiteral, annotationStrategy.withoutAttributes());
} }
static enum NullAnnotationStrategy implements AnnotationStrategy {
INSTANCE;
public boolean hasAttributes() {
return false;
}
public AnnotationStrategy withoutAttributes() {
throw new UnsupportedOperationException("Key already has no attributes.");
}
public Annotation getAnnotation() {
return null;
}
public Class<? extends Annotation> getAnnotationType() {
return null;
}
@Override
public String toString() {
return "[none]";
}
}
interface AnnotationStrategy { interface AnnotationStrategy {
Annotation getAnnotation(); Annotation getAnnotation();
@ -410,6 +326,76 @@ public class Key<T> {
AnnotationStrategy withoutAttributes(); AnnotationStrategy withoutAttributes();
} }
/** Gets the strategy for an annotation. */
static AnnotationStrategy strategyFor(Annotation annotation) {
checkNotNull(annotation, "annotation");
Class<? extends Annotation> annotationType = annotation.annotationType();
ensureRetainedAtRuntime(annotationType);
ensureIsBindingAnnotation(annotationType);
if (Annotations.isMarker(annotationType)) {
return new AnnotationTypeStrategy(annotationType, annotation);
}
return new AnnotationInstanceStrategy(Annotations.canonicalizeIfNamed(annotation));
}
/** Gets the strategy for an annotation type. */
static AnnotationStrategy strategyFor(Class<? extends Annotation> annotationType) {
annotationType = Annotations.canonicalizeIfNamed(annotationType);
if (isAllDefaultMethods(annotationType)) {
return strategyFor(generateAnnotation(annotationType));
}
checkNotNull(annotationType, "annotation type");
ensureRetainedAtRuntime(annotationType);
ensureIsBindingAnnotation(annotationType);
return new AnnotationTypeStrategy(annotationType, null);
}
private static void ensureRetainedAtRuntime(Class<? extends Annotation> annotationType) {
checkArgument(
Annotations.isRetainedAtRuntime(annotationType),
"%s is not retained at runtime. Please annotate it with @Retention(RUNTIME).",
annotationType.getName());
}
private static void ensureIsBindingAnnotation(Class<? extends Annotation> annotationType) {
checkArgument(
Annotations.isBindingAnnotation(annotationType),
"%s is not a binding annotation. Please annotate it with @BindingAnnotation.",
annotationType.getName());
}
static enum NullAnnotationStrategy implements AnnotationStrategy {
INSTANCE;
@Override
public boolean hasAttributes() {
return false;
}
@Override
public AnnotationStrategy withoutAttributes() {
throw new UnsupportedOperationException("Key already has no attributes.");
}
@Override
public Annotation getAnnotation() {
return null;
}
@Override
public Class<? extends Annotation> getAnnotationType() {
return null;
}
@Override
public String toString() {
return "[none]";
}
}
// this class not test-covered // this class not test-covered
static class AnnotationInstanceStrategy implements AnnotationStrategy { static class AnnotationInstanceStrategy implements AnnotationStrategy {
@ -419,18 +405,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();
} }
@ -463,24 +453,27 @@ public class Key<T> {
// Keep the instance around if we have it so the client can request it. // Keep the instance around if we have it so the client can request it.
final Annotation annotation; final Annotation annotation;
AnnotationTypeStrategy(Class<? extends Annotation> annotationType, AnnotationTypeStrategy(Class<? extends Annotation> annotationType, Annotation annotation) {
Annotation annotation) {
this.annotationType = checkNotNull(annotationType, "annotation type"); this.annotationType = checkNotNull(annotationType, "annotation type");
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

@ -0,0 +1,120 @@
package com.google.inject;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Annotation restricting the binding of the target type to permitted sources.
*
* <p>Bindings restricted by this annotation may only be created by sources annotated with a permit
* from {@link #permits} -- otherwise, an error message including the {@link #explanation} is
* issued.
*
* <p>There are two kinds of binding source:
*
* <ol>
* <li>Module: a module is the source of a binding if it creates it (either directly, or
* indirectly by installing another module). For example: if module A creates restricted
* binding X, and module C installs module B that installs A; then all 3 modules C,B,A are
* sources of X, and it's enough for any one of them to be annotated with a permit from X's
* restriction.
* <li>Method Scanner ({@code ModuleAnnotatedMethodScanner}): If a binding was created by a
* scanner, then that scanner is also a source of the binding (in addition to the module
* sources) and a permit may be given to the scanner by annotating its class.
* </ol>
*
* <p>Bindings with qualifier annotations are restricted solely by the annotation on their qualifier
* (restrictions on the type are ignored for qualified bindings). Unqualified bindings are
* restricted by the annotation on their type.
*
* <p>This allows libraries to prevent their clients from binding their keys, similar to how
* declaring a class final prevents subtyping. For example, a library may want to prevent users from
* creating mock bindings for tests, using the {@link #explanation} - included in the error message
* - to point them to a supported testing module.
*
* <p>Example usage:
*
* <pre>{@code
* @RestrictedBindingSource.Permit
* @Retention(RetentionPolicy.RUNTIME)
* @interface NetworkPermit {}
*
* @RestrictedBindingSource(
* explanation = "Only NetworkModule can create network bindings.",
* permits = {NetworkPermit.class})
* @Qualifier
* @Retention(RetentionPolicy.RUNTIME)
* public @interface GatewayIpAdress {}
*
* @NetworkPermit
* public final class NetworkModule extends AbstractModule {
* @Provides
* @GatewayIpAdress // Allowed because the module is annotated with @NetworkPermit.
* int provideGatewayIp() { ... }
* }
* }</pre>
*
* @author vzm@google.com (Vladimir Makaric)
* @since 5.0
*/
@Inherited
@Retention(RUNTIME)
@Target(TYPE)
public @interface RestrictedBindingSource {
/**
* Explanation of why binding this target type is restricted.
*
* <p>Will appear as the error message if the target type is bound by non-allowed modules.
*/
String explanation();
/**
* Meta-annotation indicating that the target annotation is a permit for binding restricted
* bindings. Annotating a binding source (defined in top-level javadoc) with a permit gives it
* permission to bind the restricted bindings guarded by the permit (see {@link #permits}).
*
* @since 5.0
*/
@Documented
@Retention(RUNTIME)
@Target(ANNOTATION_TYPE)
public @interface Permit {}
/**
* List of {@code Permit} annotations (must be non-empty), one of which has has to be present on a
* restricted binding's source (defined in top-level javadoc).
*/
Class<? extends Annotation>[] permits();
/**
* Exempt modules whose fully qualified class names match this regex.
*
* <p>If any module on the binding's module stack matches this regex, the binding is allowed (no
* permit necessary). No module is exempt by default (empty string).
*
* <p>Inteded to be used when retrofitting a binding with this restriction. When restricting an
* existing binding, it's often practical to first restrict with exemptions for existing
* violations (to prevent new violations), before updating the code in violation to use the
* permitted module(s).
*/
String exemptModules() default "";
/**
* Level of restriction. Determines how violations are handled.
*
* @since 5.0
*/
public static enum RestrictionLevel {
WARNING,
ERROR;
}
RestrictionLevel restrictionLevel() default RestrictionLevel.ERROR;
}

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

@ -5,14 +5,15 @@ import com.google.inject.Key;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.Provider; import com.google.inject.Provider;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandles;
/** /**
* Provides a factory that combines the caller's arguments with injector-supplied values to * Provides a factory that combines the caller's arguments with injector-supplied values to
* construct objects. * construct objects.
* *
* <h3>Defining a factory</h3> * <h3>Defining a factory</h3>
*
* Create an interface whose methods return the constructed type, or any of its supertypes. The * Create an interface whose methods return the constructed type, or any of its supertypes. The
* method's parameters are the arguments required to build the constructed type. * method's parameters are the arguments required to build the constructed type.
* *
@ -24,6 +25,7 @@ import java.lang.annotation.Annotation;
* or <i>newPayment</i>. * or <i>newPayment</i>.
* *
* <h3>Creating a type that accepts factory parameters</h3> * <h3>Creating a type that accepts factory parameters</h3>
*
* {@code constructedType} is a concrete class with an {@literal @}{@link com.google.inject.Inject * {@code constructedType} is a concrete class with an {@literal @}{@link com.google.inject.Inject
* Inject}-annotated constructor. In addition to injector-supplied parameters, the constructor * Inject}-annotated constructor. In addition to injector-supplied parameters, the constructor
* should have parameters that match each of the factory method's parameters. Each factory-supplied * should have parameters that match each of the factory method's parameters. Each factory-supplied
@ -42,10 +44,10 @@ import java.lang.annotation.Annotation;
* }</pre> * }</pre>
* *
* <h3>Multiple factory methods for the same type</h3> * <h3>Multiple factory methods for the same type</h3>
*
* If the factory contains many methods that return the same type, you can create multiple * If the factory contains many methods that return the same type, you can create multiple
* constructors in your concrete class, each constructor marked with with * constructors in your concrete class, each constructor marked with with {@literal @}{@link
* {@literal @}{@link AssistedInject}, in order to match the different parameters types of the * AssistedInject}, in order to match the different parameters types of the factory methods.
* factory methods.
* *
* <pre>public interface PaymentFactory { * <pre>public interface PaymentFactory {
* Payment create(Date startDate, Money amount); * Payment create(Date startDate, Money amount);
@ -72,8 +74,8 @@ import java.lang.annotation.Annotation;
* }</pre> * }</pre>
* *
* <h3>Configuring simple factories</h3> * <h3>Configuring simple factories</h3>
* In your {@link Module module}, install a {@code FactoryModuleBuilder} that creates the *
* factory: * In your {@link Module module}, install a {@code FactoryModuleBuilder} that creates the factory:
* *
* <pre>install(new FactoryModuleBuilder() * <pre>install(new FactoryModuleBuilder()
* .implement(Payment.class, RealPayment.class) * .implement(Payment.class, RealPayment.class)
@ -83,8 +85,9 @@ import java.lang.annotation.Annotation;
* factory cannot be used until the injector has been initialized. * factory cannot be used until the injector has been initialized.
* *
* <h3>Configuring complex factories</h3> * <h3>Configuring complex factories</h3>
* Factories can create an arbitrary number of objects, one per each method. Each factory *
* method can be configured using <code>.implement</code>. * Factories can create an arbitrary number of objects, one per each method. Each factory method can
* be configured using <code>.implement</code>.
* *
* <pre>public interface OrderFactory { * <pre>public interface OrderFactory {
* Payment create(Date startDate, Money amount); * Payment create(Date startDate, Money amount);
@ -100,11 +103,13 @@ import java.lang.annotation.Annotation;
* // will be 'Shipment' itself, which is legal if it's not an interface. * // will be 'Shipment' itself, which is legal if it's not an interface.
* .implement(Receipt.class, RealReceipt.class) * .implement(Receipt.class, RealReceipt.class)
* .build(OrderFactory.class));</pre> * .build(OrderFactory.class));</pre>
*
* </pre> * </pre>
* *
* <h3>Using the factory</h3> * <h3>Using the factory</h3>
* Inject your factory into your application classes. When you use the factory, your arguments *
* will be combined with values from the injector to construct an instance. * Inject your factory into your application classes. When you use the factory, your arguments will
* be combined with values from the injector to construct an instance.
* *
* <pre>public class PaymentAction { * <pre>public class PaymentAction {
* {@literal @}Inject private PaymentFactory paymentFactory; * {@literal @}Inject private PaymentFactory paymentFactory;
@ -116,9 +121,10 @@ import java.lang.annotation.Annotation;
* }</pre> * }</pre>
* *
* <h3>Making parameter types distinct</h3> * <h3>Making parameter types distinct</h3>
* The types of the factory method's parameters must be distinct. To use multiple parameters of *
* the same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the * The types of the factory method's parameters must be distinct. To use multiple parameters of the
* parameters. The names must be applied to the factory method's parameters: * same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the parameters.
* The names must be applied to the factory method's parameters:
* *
* <pre>public interface PaymentFactory { * <pre>public interface PaymentFactory {
* Payment create( * Payment create(
@ -142,15 +148,17 @@ import java.lang.annotation.Annotation;
* }</pre> * }</pre>
* *
* <h3>Values are created by Guice</h3> * <h3>Values are created by Guice</h3>
*
* Returned factories use child injectors to create values. The values are eligible for method * Returned factories use child injectors to create values. The values are eligible for method
* interception. In addition, {@literal @}{@literal Inject} members will be injected before they are * interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
* returned. * returned.
* *
* <h3>More configuration options</h3> * <h3>More configuration options</h3>
*
* In addition to simply specifying an implementation class for any returned type, factories' return * In addition to simply specifying an implementation class for any returned type, factories' return
* values can be automatic or can be configured to use annotations: * values can be automatic or can be configured to use annotations:
* <p/> *
* If you just want to return the types specified in the factory, do not configure any * <p>If you just want to return the types specified in the factory, do not configure any
* implementations: * implementations:
* *
* <pre>public interface FruitFactory { * <pre>public interface FruitFactory {
@ -162,8 +170,8 @@ import java.lang.annotation.Annotation;
* }</pre> * }</pre>
* *
* Note that any type returned by the factory in this manner needs to be an implementation class. * Note that any type returned by the factory in this manner needs to be an implementation class.
* <p/> *
* To return two different implementations for the same interface from your factory, use binding * <p>To return two different implementations for the same interface from your factory, use binding
* annotations on your return types: * annotations on your return types:
* *
* <pre>interface CarFactory { * <pre>interface CarFactory {
@ -179,142 +187,138 @@ import java.lang.annotation.Annotation;
* }</pre> * }</pre>
* *
* <h3>Implementation limitations</h3> * <h3>Implementation limitations</h3>
* As a limitation of the implementation, it is prohibited to declare a factory method that *
* accepts a {@code Provider} as one of its arguments. * As a limitation of the implementation, it is prohibited to declare a factory method that accepts
* a {@code Provider} as one of its arguments.
*
* @since 3.0
* @author schmitt@google.com (Peter Schmitt)
*/ */
public final class FactoryModuleBuilder { public final class FactoryModuleBuilder {
private final BindingCollector bindings = new BindingCollector(); private final BindingCollector bindings = new BindingCollector();
private MethodHandles.Lookup lookups;
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}.
*/
public <T> FactoryModuleBuilder implement(Class<T> source, Class<? extends T> target) { public <T> FactoryModuleBuilder implement(Class<T> source, Class<? extends T> target) {
return implement(source, TypeLiteral.get(target)); return implement(source, TypeLiteral.get(target));
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}.
*/
public <T> FactoryModuleBuilder implement(Class<T> source, TypeLiteral<? extends T> target) { public <T> FactoryModuleBuilder implement(Class<T> source, TypeLiteral<? extends T> target) {
return implement(TypeLiteral.get(source), target); return implement(TypeLiteral.get(source), target);
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}.
*/
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Class<? extends T> target) { public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Class<? extends T> target) {
return implement(source, TypeLiteral.get(target)); return implement(source, TypeLiteral.get(target));
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}. public <T> FactoryModuleBuilder implement(
*/ TypeLiteral<T> source, TypeLiteral<? extends T> target) {
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source,
TypeLiteral<? extends T> target) {
return implement(Key.get(source), target); return implement(Key.get(source), target);
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}. public <T> FactoryModuleBuilder implement(
*/ Class<T> source, Annotation annotation, Class<? extends T> target) {
public <T> FactoryModuleBuilder implement(Class<T> source, Annotation annotation,
Class<? extends T> target) {
return implement(source, annotation, TypeLiteral.get(target)); return implement(source, annotation, TypeLiteral.get(target));
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}. public <T> FactoryModuleBuilder implement(
*/ Class<T> source, Annotation annotation, TypeLiteral<? extends T> target) {
public <T> FactoryModuleBuilder implement(Class<T> source, Annotation annotation,
TypeLiteral<? extends T> target) {
return implement(TypeLiteral.get(source), annotation, target); return implement(TypeLiteral.get(source), annotation, target);
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}. public <T> FactoryModuleBuilder implement(
*/ TypeLiteral<T> source, Annotation annotation, Class<? extends T> target) {
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Annotation annotation,
Class<? extends T> target) {
return implement(source, annotation, TypeLiteral.get(target)); return implement(source, annotation, TypeLiteral.get(target));
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}. public <T> FactoryModuleBuilder implement(
*/ TypeLiteral<T> source, Annotation annotation, TypeLiteral<? extends T> target) {
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Annotation annotation,
TypeLiteral<? extends T> target) {
return implement(Key.get(source, annotation), target); return implement(Key.get(source, annotation), target);
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}. public <T> FactoryModuleBuilder implement(
*/ Class<T> source, Class<? extends Annotation> annotationType, Class<? extends T> target) {
public <T> FactoryModuleBuilder implement(Class<T> source,
Class<? extends Annotation> annotationType, Class<? extends T> target) {
return implement(source, annotationType, TypeLiteral.get(target)); return implement(source, annotationType, TypeLiteral.get(target));
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}. public <T> FactoryModuleBuilder implement(
*/ Class<T> source,
public <T> FactoryModuleBuilder implement(Class<T> source, Class<? extends Annotation> annotationType,
Class<? extends Annotation> annotationType, TypeLiteral<? extends T> target) { TypeLiteral<? extends T> target) {
return implement(TypeLiteral.get(source), annotationType, target); return implement(TypeLiteral.get(source), annotationType, target);
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}. public <T> FactoryModuleBuilder implement(
*/ TypeLiteral<T> source,
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Class<? extends Annotation> annotationType,
Class<? extends Annotation> annotationType, Class<? extends T> target) { Class<? extends T> target) {
return implement(source, annotationType, TypeLiteral.get(target)); return implement(source, annotationType, TypeLiteral.get(target));
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}. public <T> FactoryModuleBuilder implement(
*/ TypeLiteral<T> source,
public <T> FactoryModuleBuilder implement(TypeLiteral<T> source, Class<? extends Annotation> annotationType,
Class<? extends Annotation> annotationType, TypeLiteral<? extends T> target) { TypeLiteral<? extends T> target) {
return implement(Key.get(source, annotationType), target); return implement(Key.get(source, annotationType), target);
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}.
*/
public <T> FactoryModuleBuilder implement(Key<T> source, Class<? extends T> target) { public <T> FactoryModuleBuilder implement(Key<T> source, Class<? extends T> target) {
return implement(source, TypeLiteral.get(target)); return implement(source, TypeLiteral.get(target));
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}.
*/
public <T> FactoryModuleBuilder implement(Key<T> source, TypeLiteral<? extends T> target) { public <T> FactoryModuleBuilder implement(Key<T> source, TypeLiteral<? extends T> target) {
bindings.addBinding(source, target); bindings.addBinding(source, target);
return this; return this;
} }
/** /**
* See the factory configuration examples at {@link FactoryModuleBuilder}. * Typically called via {@code withLookups(MethodHandles.lookup())}. Sets the MethodHandles.Lookup
* that the factory implementation will use to call default methods on the factory interface.
* While this is not always required, it is always OK to set it. It is required if the factory
* passed to {@link #build} is non-public and javac generated default methods while compiling it
* (which javac can sometimes do if the factory uses generic types).
*
* <p>Guice will try to work properly even if this method is not called (or called with a lookups
* that doesn't have access to the factory), but doing so requires reflection into the JDK, which
* may break at any time (and trigger unsafe access warnings).
*
* @since 5.0
*/ */
public <T> FactoryModuleBuilder withLookups(MethodHandles.Lookup lookups) {
this.lookups = lookups;
return this;
}
/** See the factory configuration examples at {@link FactoryModuleBuilder}. */
public <F> Module build(Class<F> factoryInterface) { public <F> Module build(Class<F> factoryInterface) {
return build(TypeLiteral.get(factoryInterface)); return build(TypeLiteral.get(factoryInterface));
} }
/** /** See the factory configuration examples at {@link FactoryModuleBuilder}. */
* See the factory configuration examples at {@link FactoryModuleBuilder}.
*/
public <F> Module build(TypeLiteral<F> factoryInterface) { public <F> Module build(TypeLiteral<F> factoryInterface) {
return build(Key.get(factoryInterface)); return build(Key.get(factoryInterface));
} }
public <F> Module build(final Key<F> factoryInterface) { public <F> Module build(final Key<F> factoryInterface) {
return new AbstractModule() { return new AbstractModule() {
@Override @Override
protected void configure() { protected void configure() {
Provider<F> provider = new FactoryProvider2<F>(factoryInterface, bindings); Provider<F> provider = new FactoryProvider2<>(factoryInterface, bindings, lookups);
bind(factoryInterface).toProvider(provider); binder().skipSources(this.getClass()).bind(factoryInterface).toProvider(provider);
} }
}; };
} }

View file

@ -1,5 +1,7 @@
package com.google.inject.assistedinject; package com.google.inject.assistedinject;
import static com.google.inject.internal.Annotations.getKey;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@ -16,7 +18,6 @@ import com.google.inject.internal.ErrorsException;
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;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
@ -27,8 +28,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static com.google.inject.internal.Annotations.getKey;
/** /**
* <strong>Obsolete.</strong> Prefer {@link FactoryModuleBuilder} for its more concise API and * <strong>Obsolete.</strong> Prefer {@link FactoryModuleBuilder} for its more concise API and
* additional capability. * additional capability.
@ -37,20 +36,25 @@ import static com.google.inject.internal.Annotations.getKey;
* construct objects. * construct objects.
* *
* <h3>Defining a factory</h3> * <h3>Defining a factory</h3>
*
* Create an interface whose methods return the constructed type, or any of its supertypes. The * Create an interface whose methods return the constructed type, or any of its supertypes. The
* method's parameters are the arguments required to build the constructed type. * method's parameters are the arguments required to build the constructed type.
*
* <pre>public interface PaymentFactory { * <pre>public interface PaymentFactory {
* Payment create(Date startDate, Money amount); * Payment create(Date startDate, Money amount);
* }</pre> * }</pre>
*
* You can name your factory methods whatever you like, such as <i>create</i>, <i>createPayment</i> * You can name your factory methods whatever you like, such as <i>create</i>, <i>createPayment</i>
* or <i>newPayment</i>. * or <i>newPayment</i>.
* *
* <h3>Creating a type that accepts factory parameters</h3> * <h3>Creating a type that accepts factory parameters</h3>
*
* {@code constructedType} is a concrete class with an {@literal @}{@link Inject}-annotated * {@code constructedType} is a concrete class with an {@literal @}{@link Inject}-annotated
* constructor. In addition to injector-supplied parameters, the constructor should have * constructor. In addition to injector-supplied parameters, the constructor should have parameters
* parameters that match each of the factory method's parameters. Each factory-supplied parameter * that match each of the factory method's parameters. Each factory-supplied parameter requires an
* requires an {@literal @}{@link Assisted} annotation. This serves to document that the parameter * {@literal @}{@link Assisted} annotation. This serves to document that the parameter is not bound
* is not bound by your application's modules. * by your application's modules.
*
* <pre>public class RealPayment implements Payment { * <pre>public class RealPayment implements Payment {
* {@literal @}Inject * {@literal @}Inject
* public RealPayment( * public RealPayment(
@ -61,19 +65,25 @@ import static com.google.inject.internal.Annotations.getKey;
* ... * ...
* } * }
* }</pre> * }</pre>
*
* Any parameter that permits a null value should also be annotated {@code @Nullable}. * Any parameter that permits a null value should also be annotated {@code @Nullable}.
* *
* <h3>Configuring factories</h3> * <h3>Configuring factories</h3>
*
* In your {@link com.google.inject.Module module}, bind the factory interface to the returned * In your {@link com.google.inject.Module module}, bind the factory interface to the returned
* factory: * factory:
*
* <pre>bind(PaymentFactory.class).toProvider( * <pre>bind(PaymentFactory.class).toProvider(
* FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</pre> * FactoryProvider.newFactory(PaymentFactory.class, RealPayment.class));</pre>
*
* As a side-effect of this binding, Guice will inject the factory to initialize it for use. The * As a side-effect of this binding, Guice will inject the factory to initialize it for use. The
* factory cannot be used until the injector has been initialized. * factory cannot be used until the injector has been initialized.
* *
* <h3>Using the factory</h3> * <h3>Using the factory</h3>
* Inject your factory into your application classes. When you use the factory, your arguments *
* will be combined with values from the injector to construct an instance. * Inject your factory into your application classes. When you use the factory, your arguments will
* be combined with values from the injector to construct an instance.
*
* <pre>public class PaymentAction { * <pre>public class PaymentAction {
* {@literal @}Inject private PaymentFactory paymentFactory; * {@literal @}Inject private PaymentFactory paymentFactory;
* *
@ -84,9 +94,10 @@ import static com.google.inject.internal.Annotations.getKey;
* }</pre> * }</pre>
* *
* <h3>Making parameter types distinct</h3> * <h3>Making parameter types distinct</h3>
* The types of the factory method's parameters must be distinct. To use multiple parameters of *
* the same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the * The types of the factory method's parameters must be distinct. To use multiple parameters of the
* parameters. The names must be applied to the factory method's parameters: * same type, use a named {@literal @}{@link Assisted} annotation to disambiguate the parameters.
* The names must be applied to the factory method's parameters:
* *
* <pre>public interface PaymentFactory { * <pre>public interface PaymentFactory {
* Payment create( * Payment create(
@ -94,7 +105,9 @@ import static com.google.inject.internal.Annotations.getKey;
* <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate, * <strong>{@literal @}Assisted("dueDate")</strong> Date dueDate,
* Money amount); * Money amount);
* } </pre> * } </pre>
*
* ...and to the concrete type's constructor parameters: * ...and to the concrete type's constructor parameters:
*
* <pre>public class RealPayment implements Payment { * <pre>public class RealPayment implements Payment {
* {@literal @}Inject * {@literal @}Inject
* public RealPayment( * public RealPayment(
@ -108,23 +121,27 @@ import static com.google.inject.internal.Annotations.getKey;
* }</pre> * }</pre>
* *
* <h3>Values are created by Guice</h3> * <h3>Values are created by Guice</h3>
*
* Returned factories use child injectors to create values. The values are eligible for method * Returned factories use child injectors to create values. The values are eligible for method
* interception. In addition, {@literal @}{@literal Inject} members will be injected before they are * interception. In addition, {@literal @}{@literal Inject} members will be injected before they are
* returned. * returned.
* *
* <h3>Backwards compatibility using {@literal @}AssistedInject</h3> * <h3>Backwards compatibility using {@literal @}AssistedInject</h3>
*
* Instead of the {@literal @}Inject annotation, you may annotate the constructed classes with * Instead of the {@literal @}Inject annotation, you may annotate the constructed classes with
* {@literal @}{@link AssistedInject}. This triggers a limited backwards-compatability mode. * {@literal @}{@link AssistedInject}. This triggers a limited backwards-compatability mode.
* *
* <p>Instead of matching factory method arguments to constructor parameters using their names, the * <p>Instead of matching factory method arguments to constructor parameters using their names, the
* <strong>parameters are matched by their order</strong>. The first factory method argument is * <strong>parameters are matched by their order</strong>. The first factory method argument is used
* used for the first {@literal @}Assisted constructor parameter, etc.. Annotation names have no * for the first {@literal @}Assisted constructor parameter, etc.. Annotation names have no effect.
* effect.
* *
* <p>Returned values are <strong>not created by Guice</strong>. These types are not eligible for * <p>Returned values are <strong>not created by Guice</strong>. These types are not eligible for
* method interception. They do receive post-construction member injection. * method interception. They do receive post-construction member injection.
* *
* @param <F> The factory interface * @param <F> The factory interface
* @author jmourits@google.com (Jerome Mourits)
* @author jessewilson@google.com (Jesse Wilson)
* @author dtm@google.com (Daniel Martin)
* @deprecated use {@link FactoryModuleBuilder} instead. * @deprecated use {@link FactoryModuleBuilder} instead.
*/ */
@Deprecated @Deprecated
@ -135,19 +152,11 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
* to factory methods. The new child injector implementation lives in FactoryProvider2. * to factory methods. The new child injector implementation lives in FactoryProvider2.
*/ */
private Injector injector;
private final TypeLiteral<F> factoryType; private final TypeLiteral<F> factoryType;
private final TypeLiteral<?> implementationType; private final TypeLiteral<?> implementationType;
private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor; private final Map<Method, AssistedConstructor<?>> factoryMethodToConstructor;
private Injector injector;
private FactoryProvider(TypeLiteral<F> factoryType,
TypeLiteral<?> implementationType,
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
this.factoryType = factoryType;
this.implementationType = implementationType;
this.factoryMethodToConstructor = factoryMethodToConstructor;
checkDeclaredExceptionsMatch();
}
public static <F> Provider<F> newFactory(Class<F> factoryType, Class<?> implementationType) { public static <F> Provider<F> newFactory(Class<F> factoryType, Class<?> implementationType) {
return newFactory(TypeLiteral.get(factoryType), TypeLiteral.get(implementationType)); return newFactory(TypeLiteral.get(factoryType), TypeLiteral.get(implementationType));
@ -155,8 +164,8 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
public static <F> Provider<F> newFactory( public static <F> Provider<F> newFactory(
TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) { TypeLiteral<F> factoryType, TypeLiteral<?> implementationType) {
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor Map<Method, AssistedConstructor<?>> factoryMethodToConstructor =
= createMethodMapping(factoryType, implementationType); createMethodMapping(factoryType, implementationType);
if (!factoryMethodToConstructor.isEmpty()) { if (!factoryMethodToConstructor.isEmpty()) {
return new FactoryProvider<F>(factoryType, implementationType, factoryMethodToConstructor); return new FactoryProvider<F>(factoryType, implementationType, factoryMethodToConstructor);
@ -170,8 +179,8 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
try { try {
for (Method method : factoryType.getRawType().getMethods()) { for (Method method : factoryType.getRawType().getMethods()) {
Key<?> returnType = getKey(factoryType.getReturnType(method), method, Key<?> returnType =
method.getAnnotations(), errors); getKey(factoryType.getReturnType(method), method, method.getAnnotations(), errors);
if (!implementationKey.equals(returnType)) { if (!implementationKey.equals(returnType)) {
collector.addBinding(returnType, implementationType); collector.addBinding(returnType, implementationType);
} }
@ -180,83 +189,18 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
throw new ConfigurationException(e.getErrors().getMessages()); throw new ConfigurationException(e.getErrors().getMessages());
} }
return new FactoryProvider2<F>(Key.get(factoryType), collector); return new FactoryProvider2<F>(Key.get(factoryType), collector, /* userLookups= */ null);
} }
} }
private static Map<Method, AssistedConstructor<?>> createMethodMapping( private FactoryProvider(
TypeLiteral<?> factoryType, TypeLiteral<?> implementationType) { TypeLiteral<F> factoryType,
List<AssistedConstructor<?>> constructors = Lists.newArrayList(); TypeLiteral<?> implementationType,
Map<Method, AssistedConstructor<?>> factoryMethodToConstructor) {
for (Constructor<?> constructor : implementationType.getRawType().getDeclaredConstructors()) { this.factoryType = factoryType;
if (constructor.isAnnotationPresent(AssistedInject.class)) { this.implementationType = implementationType;
AssistedConstructor<?> assistedConstructor = AssistedConstructor.create( this.factoryMethodToConstructor = factoryMethodToConstructor;
constructor, implementationType.getParameterTypes(constructor)); checkDeclaredExceptionsMatch();
constructors.add(assistedConstructor);
}
}
if (constructors.isEmpty()) {
return ImmutableMap.of();
}
Method[] factoryMethods = factoryType.getRawType().getMethods();
if (constructors.size() != factoryMethods.length) {
throw newConfigurationException("Constructor mismatch: %s has %s @AssistedInject "
+ "constructors, factory %s has %s creation methods", implementationType,
constructors.size(), factoryType, factoryMethods.length);
}
Map<ParameterListKey, AssistedConstructor<?>> paramsToConstructor = Maps.newHashMap();
for (AssistedConstructor<?> c : constructors) {
if (paramsToConstructor.containsKey(c.getAssistedParameters())) {
throw new RuntimeException("Duplicate constructor, " + c);
}
paramsToConstructor.put(c.getAssistedParameters(), c);
}
Map<Method, AssistedConstructor<?>> result = Maps.newHashMap();
for (Method method : factoryMethods) {
if (!method.getReturnType().isAssignableFrom(implementationType.getRawType())) {
throw newConfigurationException("Return type of method %s is not assignable from %s",
method, implementationType);
}
List<Type> parameterTypes = Lists.newArrayList();
for (TypeLiteral<?> parameterType : factoryType.getParameterTypes(method)) {
parameterTypes.add(parameterType.getType());
}
ParameterListKey methodParams = new ParameterListKey(parameterTypes);
if (!paramsToConstructor.containsKey(methodParams)) {
throw newConfigurationException("%s has no @AssistInject constructor that takes the "
+ "@Assisted parameters %s in that order. @AssistInject constructors are %s",
implementationType, methodParams, paramsToConstructor.values());
}
method.getParameterAnnotations();
for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
for (Annotation parameterAnnotation : parameterAnnotations) {
if (parameterAnnotation.annotationType() == Assisted.class) {
throw newConfigurationException("Factory method %s has an @Assisted parameter, which "
+ "is incompatible with the deprecated @AssistedInject annotation. Please replace "
+ "@AssistedInject with @Inject on the %s constructor.",
method, implementationType);
}
}
}
AssistedConstructor<?> matchingConstructor = paramsToConstructor.remove(methodParams);
result.put(method, matchingConstructor);
}
return result;
}
private static ConfigurationException newConfigurationException(String format, Object... args) {
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
} }
@Inject @Inject
@ -268,8 +212,10 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
// this is lame - we're not using the proper mechanism to add an // this is lame - we're not using the proper mechanism to add an
// error to the injector. Throughout this class we throw exceptions // error to the injector. Throughout this class we throw exceptions
// to add errors, which isn't really the best way in Guice // to add errors, which isn't really the best way in Guice
throw newConfigurationException("Parameter of type '%s' is not injectable or annotated " throw newConfigurationException(
+ "with @Assisted for Constructor '%s'", p, c); "Parameter of type '%s' is not injectable or annotated "
+ "with @Assisted for Constructor '%s'",
p, c);
} }
} }
} }
@ -280,8 +226,10 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) { for (Class<?> constructorException : entry.getValue().getDeclaredExceptions()) {
if (!isConstructorExceptionCompatibleWithFactoryExeception( if (!isConstructorExceptionCompatibleWithFactoryExeception(
constructorException, entry.getKey().getExceptionTypes())) { constructorException, entry.getKey().getExceptionTypes())) {
throw newConfigurationException("Constructor %s declares an exception, but no compatible " throw newConfigurationException(
+ "exception is thrown by the factory method %s", entry.getValue(), entry.getKey()); "Constructor %s declares an exception, but no compatible "
+ "exception is thrown by the factory method %s",
entry.getValue(), entry.getKey());
} }
} }
} }
@ -301,6 +249,82 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
return parameter.isBound(injector); return parameter.isBound(injector);
} }
private static Map<Method, AssistedConstructor<?>> createMethodMapping(
TypeLiteral<?> factoryType, TypeLiteral<?> implementationType) {
List<AssistedConstructor<?>> constructors = Lists.newArrayList();
for (Constructor<?> constructor : implementationType.getRawType().getDeclaredConstructors()) {
if (constructor.isAnnotationPresent(AssistedInject.class)) {
AssistedConstructor<?> assistedConstructor =
AssistedConstructor.create(
constructor, implementationType.getParameterTypes(constructor));
constructors.add(assistedConstructor);
}
}
if (constructors.isEmpty()) {
return ImmutableMap.of();
}
Method[] factoryMethods = factoryType.getRawType().getMethods();
if (constructors.size() != factoryMethods.length) {
throw newConfigurationException(
"Constructor mismatch: %s has %s @AssistedInject "
+ "constructors, factory %s has %s creation methods",
implementationType, constructors.size(), factoryType, factoryMethods.length);
}
Map<ParameterListKey, AssistedConstructor<?>> paramsToConstructor = Maps.newHashMap();
for (AssistedConstructor<?> c : constructors) {
if (paramsToConstructor.containsKey(c.getAssistedParameters())) {
throw new RuntimeException("Duplicate constructor, " + c);
}
paramsToConstructor.put(c.getAssistedParameters(), c);
}
Map<Method, AssistedConstructor<?>> result = Maps.newHashMap();
for (Method method : factoryMethods) {
if (!method.getReturnType().isAssignableFrom(implementationType.getRawType())) {
throw newConfigurationException(
"Return type of method %s is not assignable from %s", method, implementationType);
}
List<Type> parameterTypes = Lists.newArrayList();
for (TypeLiteral<?> parameterType : factoryType.getParameterTypes(method)) {
parameterTypes.add(parameterType.getType());
}
ParameterListKey methodParams = new ParameterListKey(parameterTypes);
if (!paramsToConstructor.containsKey(methodParams)) {
throw newConfigurationException(
"%s has no @AssistInject constructor that takes the "
+ "@Assisted parameters %s in that order. @AssistInject constructors are %s",
implementationType, methodParams, paramsToConstructor.values());
}
method.getParameterAnnotations();
for (Annotation[] parameterAnnotations : method.getParameterAnnotations()) {
for (Annotation parameterAnnotation : parameterAnnotations) {
if (parameterAnnotation.annotationType() == Assisted.class) {
throw newConfigurationException(
"Factory method %s has an @Assisted parameter, which is incompatible with the"
+ " deprecated @AssistedInject annotation. Please replace @AssistedInject with"
+ " @Inject on the %s constructor.",
method, implementationType);
}
}
}
AssistedConstructor<?> matchingConstructor = paramsToConstructor.remove(methodParams);
result.put(method, matchingConstructor);
}
return result;
}
@Override
public Set<Dependency<?>> getDependencies() { public Set<Dependency<?>> getDependencies() {
List<Dependency<?>> dependencies = Lists.newArrayList(); List<Dependency<?>> dependencies = Lists.newArrayList();
for (AssistedConstructor<?> constructor : factoryMethodToConstructor.values()) { for (AssistedConstructor<?> constructor : factoryMethodToConstructor.values()) {
@ -313,9 +337,13 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
return ImmutableSet.copyOf(dependencies); return ImmutableSet.copyOf(dependencies);
} }
@Override
public F get() { public F get() {
InvocationHandler invocationHandler = new InvocationHandler() { InvocationHandler invocationHandler =
public Object invoke(Object proxy, Method method, Object[] creationArgs) throws Throwable { new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] creationArgs)
throws Throwable {
// pass methods from Object.class to the proxy // pass methods from Object.class to the proxy
if (method.getDeclaringClass().equals(Object.class)) { if (method.getDeclaringClass().equals(Object.class)) {
if ("equals".equals(method.getName())) { if ("equals".equals(method.getName())) {
@ -335,8 +363,7 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
} }
public Object[] gatherArgsForConstructor( public Object[] gatherArgsForConstructor(
AssistedConstructor<?> constructor, AssistedConstructor<?> constructor, Object[] factoryArgs) {
Object[] factoryArgs) {
int numParams = constructor.getAllParameters().size(); int numParams = constructor.getAllParameters().size();
int argPosition = 0; int argPosition = 0;
Object[] result = new Object[numParams]; Object[] result = new Object[numParams];
@ -356,8 +383,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>) (Class<?>) factoryType.getRawType();
return factoryRawType.cast(Proxy.newProxyInstance(factoryRawType.getClassLoader(), return factoryRawType.cast(
new Class[]{factoryRawType}, invocationHandler)); Proxy.newProxyInstance(
factoryRawType.getClassLoader(), new Class<?>[] {factoryRawType}, invocationHandler));
} }
@Override @Override
@ -374,4 +402,8 @@ public class FactoryProvider<F> implements Provider<F>, HasDependencies {
return factoryType.equals(other.factoryType) return factoryType.equals(other.factoryType)
&& implementationType.equals(other.implementationType); && implementationType.equals(other.implementationType);
} }
private static ConfigurationException newConfigurationException(String format, Object... args) {
return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
}
} }

View file

@ -1,5 +1,9 @@
package com.google.inject.assistedinject; package com.google.inject.assistedinject;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -35,11 +39,12 @@ import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithExtensionVisitor; import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.Toolable; import com.google.inject.spi.Toolable;
import com.google.inject.util.Providers; import com.google.inject.util.Providers;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
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.lang.reflect.Proxy; import java.lang.reflect.Proxy;
@ -50,35 +55,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.atomic.AtomicReference;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getOnlyElement;
/** /**
* The newer implementation of factory provider. This implementation uses a child injector to * The newer implementation of factory provider. This implementation uses a child injector to create
* create values. * values.
*
* @author jessewilson@google.com (Jesse Wilson)
* @author dtm@google.com (Daniel Martin)
* @author schmitt@google.com (Peter Schmitt)
* @author sameb@google.com (Sam Berlin)
*/ */
final class FactoryProvider2<F> implements InvocationHandler, final class FactoryProvider2<F>
ProviderWithExtensionVisitor<F>, HasDependencies, AssistedInjectBinding<F> { implements InvocationHandler,
ProviderWithExtensionVisitor<F>,
HasDependencies,
AssistedInjectBinding<F> {
/** /** A constant annotation to denote the return value, instead of creating a new one each time. */
* A constant annotation to denote the return value, instead of creating a new one each time.
*/
static final Annotation RETURN_ANNOTATION = UniqueAnnotations.create(); static final Annotation RETURN_ANNOTATION = UniqueAnnotations.create();
// use the logger under a well-known name, not FactoryProvider2 // use the logger under a well-known name, not FactoryProvider2
static final Logger logger = Logger.getLogger(AssistedInject.class.getName()); static final Logger logger = Logger.getLogger(AssistedInject.class.getName());
/** /**
* if a factory method parameter isn't annotated, it gets this annotation. * A constant that determines if we allow fallback to reflection. Typically always true, but
* reflectively set to false in tests.
*/ */
static final Assisted DEFAULT_ANNOTATION = new Assisted() { private static boolean allowLookupReflection = true;
/** if a factory method parameter isn't annotated, it gets this annotation. */
static final Assisted DEFAULT_ANNOTATION =
new Assisted() {
@Override
public String value() { public String value() {
return ""; return "";
} }
@Override
public Class<? extends Annotation> annotationType() { public Class<? extends Annotation> annotationType() {
return Assisted.class; return Assisted.class;
} }
@ -95,40 +111,117 @@ final class FactoryProvider2<F> implements InvocationHandler,
@Override @Override
public String toString() { public String toString() {
return "@" + Assisted.class.getName() + "(value=)"; return "@"
+ Assisted.class.getName()
+ "("
+ Annotations.memberValueString("value", "")
+ ")";
} }
}; };
/**
* Mapping from method to the data about how the method will be assisted. /** All the data necessary to perform an assisted inject. */
*/ private static class AssistData implements AssistedMethod {
/** the constructor the implementation is constructed with. */
final Constructor<?> constructor;
/** the return type in the factory method that the constructor is bound to. */
final Key<?> returnType;
/** the parameters in the factory method associated with this data. */
final ImmutableList<Key<?>> paramTypes;
/** the type of the implementation constructed */
final TypeLiteral<?> implementationType;
/** All non-assisted dependencies required by this method. */
final Set<Dependency<?>> dependencies;
/** The factory method associated with this data */
final Method factoryMethod;
/** true if {@link #isValidForOptimizedAssistedInject} returned true. */
final boolean optimized;
/** the list of optimized providers, empty if not optimized. */
final List<ThreadLocalProvider> providers;
/** used to perform optimized factory creations. */
volatile Binding<?> cachedBinding; // TODO: volatile necessary?
AssistData(
Constructor<?> constructor,
Key<?> returnType,
ImmutableList<Key<?>> paramTypes,
TypeLiteral<?> implementationType,
Method factoryMethod,
Set<Dependency<?>> dependencies,
boolean optimized,
List<ThreadLocalProvider> providers) {
this.constructor = constructor;
this.returnType = returnType;
this.paramTypes = paramTypes;
this.implementationType = implementationType;
this.factoryMethod = factoryMethod;
this.dependencies = dependencies;
this.optimized = optimized;
this.providers = providers;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("ctor", constructor)
.add("return type", returnType)
.add("param type", paramTypes)
.add("implementation type", implementationType)
.add("dependencies", dependencies)
.add("factory method", factoryMethod)
.add("optimized", optimized)
.add("providers", providers)
.add("cached binding", cachedBinding)
.toString();
}
@Override
public Set<Dependency<?>> getDependencies() {
return dependencies;
}
@Override
public Method getFactoryMethod() {
return factoryMethod;
}
@Override
public Constructor<?> getImplementationConstructor() {
return constructor;
}
@Override
public TypeLiteral<?> getImplementationType() {
return implementationType;
}
}
/** Mapping from method to the data about how the method will be assisted. */
private final ImmutableMap<Method, AssistData> assistDataByMethod; private final ImmutableMap<Method, AssistData> assistDataByMethod;
/**
* Mapping from method to method handle, for generated default methods. /** Mapping from method to method handle, for generated default methods. */
*/ private final ImmutableMap<Method, MethodHandle> methodHandleByMethod;
private final ImmutableMap<Method, MethodHandleWrapper> methodHandleByMethod;
/** /** the hosting injector, or null if we haven't been initialized yet */
* the factory interface, implemented and provided
*/
private final F factory;
/**
* The key that this is bound to.
*/
private final Key<F> factoryKey;
/**
* The binding collector, for equality/hashing purposes.
*/
private final BindingCollector collector;
/**
* the hosting injector, or null if we haven't been initialized yet
*/
private Injector injector; private Injector injector;
/** the factory interface, implemented and provided */
private final F factory;
/** The key that this is bound to. */
private final Key<F> factoryKey;
/** The binding collector, for equality/hashing purposes. */
private final BindingCollector collector;
/** /**
* @param factoryKey a key for a Java interface that defines one or more create methods. * @param factoryKey a key for a Java interface that defines one or more create methods.
* @param collector binding configuration that maps method return types to * @param collector binding configuration that maps method return types to implementation types.
* implementation types. * @param userLookups user provided lookups, optional.
*/ */
FactoryProvider2(Key<F> factoryKey, BindingCollector collector) { FactoryProvider2(
Key<F> factoryKey, BindingCollector collector, MethodHandles.Lookup userLookups) {
this.factoryKey = factoryKey; this.factoryKey = factoryKey;
this.collector = collector; this.collector = collector;
@ -148,6 +241,11 @@ final class FactoryProvider2<F> implements InvocationHandler,
ImmutableMap.Builder<Method, AssistData> assistDataBuilder = ImmutableMap.builder(); ImmutableMap.Builder<Method, AssistData> assistDataBuilder = ImmutableMap.builder();
// TODO: also grab methods from superinterfaces // TODO: also grab methods from superinterfaces
for (Method method : factoryRawType.getMethods()) { for (Method method : factoryRawType.getMethods()) {
// Skip static methods
if (Modifier.isStatic(method.getModifiers())) {
continue;
}
// Skip default methods that java8 may have created. // Skip default methods that java8 may have created.
if (isDefault(method) && (method.isBridge() || method.isSynthetic())) { if (isDefault(method) && (method.isBridge() || method.isSynthetic())) {
// Even synthetic default methods need the return type validation... // Even synthetic default methods need the return type validation...
@ -161,7 +259,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
TypeLiteral<?> returnTypeLiteral = factoryType.getReturnType(method); TypeLiteral<?> returnTypeLiteral = factoryType.getReturnType(method);
Key<?> returnType; Key<?> returnType;
try { try {
returnType = Annotations.getKey(returnTypeLiteral, method, method.getAnnotations(), errors); returnType =
Annotations.getKey(returnTypeLiteral, method, method.getAnnotations(), errors);
} catch (ConfigurationException ce) { } catch (ConfigurationException ce) {
// If this was an error due to returnTypeLiteral not being specified, rephrase // If this was an error due to returnTypeLiteral not being specified, rephrase
// it as our factory not being specified, so it makes more sense to users. // it as our factory not being specified, so it makes more sense to users.
@ -181,7 +280,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
Class<?> underlylingType = paramKey.getTypeLiteral().getRawType(); Class<?> underlylingType = paramKey.getTypeLiteral().getRawType();
if (underlylingType.equals(Provider.class) if (underlylingType.equals(Provider.class)
|| underlylingType.equals(javax.inject.Provider.class)) { || underlylingType.equals(javax.inject.Provider.class)) {
errors.addMessage("A Provider may not be a type in a factory method of an AssistedInject." errors.addMessage(
"A Provider may not be a type in a factory method of an AssistedInject."
+ "\n Offending instance is parameter [%s] with key [%s] on method [%s]", + "\n Offending instance is parameter [%s] with key [%s] on method [%s]",
p, paramKey, method); p, paramKey, method);
} }
@ -197,7 +297,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
Class<? extends Annotation> scope = Class<? extends Annotation> scope =
Annotations.findScopeAnnotation(errors, implementation.getRawType()); Annotations.findScopeAnnotation(errors, implementation.getRawType());
if (scope != null) { if (scope != null) {
errors.addMessage("Found scope annotation [%s] on implementation class " errors.addMessage(
"Found scope annotation [%s] on implementation class "
+ "[%s] of AssistedInject factory [%s].\nThis is not allowed, please" + "[%s] of AssistedInject factory [%s].\nThis is not allowed, please"
+ " remove the scope annotation.", + " remove the scope annotation.",
scope, implementation.getRawType(), factoryType); scope, implementation.getRawType(), factoryType);
@ -206,7 +307,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
InjectionPoint ctorInjectionPoint; InjectionPoint ctorInjectionPoint;
try { try {
ctorInjectionPoint = ctorInjectionPoint =
findMatchingConstructorInjectionPoint(method, returnType, implementation, immutableParamList); findMatchingConstructorInjectionPoint(
method, returnType, implementation, immutableParamList);
} catch (ErrorsException ee) { } catch (ErrorsException ee) {
errors.merge(ee.getErrors()); errors.merge(ee.getErrors());
continue; continue;
@ -230,7 +332,9 @@ final class FactoryProvider2<F> implements InvocationHandler,
optimized = true; optimized = true;
} }
AssistData data = new AssistData(constructor, AssistData data =
new AssistData(
constructor,
returnType, returnType,
immutableParamList, immutableParamList,
implementation, implementation,
@ -241,25 +345,61 @@ final class FactoryProvider2<F> implements InvocationHandler,
assistDataBuilder.put(method, data); assistDataBuilder.put(method, data);
} }
factory = factoryRawType.cast(Proxy.newProxyInstance( factory =
factoryRawType.cast(
Proxy.newProxyInstance(
factoryRawType.getClassLoader(), new Class<?>[] {factoryRawType}, this)); factoryRawType.getClassLoader(), new Class<?>[] {factoryRawType}, this));
// Now go back through default methods. Try to use MethodHandles to make things // Now go back through default methods. Try to use MethodHandles to make things
// work. If that doesn't work, fallback to trying to find compatible method // work. If that doesn't work, fallback to trying to find compatible method
// signatures. // signatures.
Map<Method, AssistData> dataSoFar = assistDataBuilder.build(); Map<Method, AssistData> dataSoFar = assistDataBuilder.build();
ImmutableMap.Builder<Method, MethodHandleWrapper> methodHandleBuilder = ImmutableMap.builder(); ImmutableMap.Builder<Method, MethodHandle> methodHandleBuilder = ImmutableMap.builder();
boolean warnedAboutUserLookups = false;
for (Map.Entry<String, Method> entry : defaultMethods.entries()) { for (Map.Entry<String, Method> entry : defaultMethods.entries()) {
if (!warnedAboutUserLookups
&& userLookups == null
&& !Modifier.isPublic(factory.getClass().getModifiers())) {
warnedAboutUserLookups = true;
logger.log(
Level.WARNING,
"AssistedInject factory {0} is non-public and has javac-generated default methods. "
+ " Please pass a `MethodHandles.lookups()` with"
+ " FactoryModuleBuilder.withLookups when using this factory so that Guice can"
+ " properly call the default methods. Guice will try to work around the"
+ " problem, but doing so requires reflection into the JDK and may break at any"
+ " time.",
new Object[] {factoryType});
}
Method defaultMethod = entry.getValue(); Method defaultMethod = entry.getValue();
MethodHandleWrapper handle = MethodHandleWrapper.create(defaultMethod, factory); MethodHandle handle = null;
try {
// Note: this can return null if we fallback to reflecting the private lookup cxtor and it
// fails. In that case, we try the super hacky workaround below (w/ 'foundMatch').
// It can throw an exception if we're not doing private reflection, or if unreflectSpecial
// _still_ fails.
handle = superMethodHandle(defaultMethod, factory, userLookups);
} catch (ReflectiveOperationException roe) {
errors.addMessage(
new Message(
"Unable to use factory "
+ factoryRawType.getName()
+ ". Did you call FactoryModuleBuilder.withLookups(MethodHandles.lookups())"
+ " (with a lookups that has access to the factory)?"));
continue;
}
if (handle != null) { if (handle != null) {
methodHandleBuilder.put(defaultMethod, handle); methodHandleBuilder.put(defaultMethod, handle);
} else { } else {
// TODO: remove this workaround when Java8 support is dropped
boolean foundMatch = false; boolean foundMatch = false;
for (Method otherMethod : otherMethods.get(defaultMethod.getName())) { for (Method otherMethod : otherMethods.get(defaultMethod.getName())) {
if (dataSoFar.containsKey(otherMethod) && isCompatible(defaultMethod, otherMethod)) { if (dataSoFar.containsKey(otherMethod) && isCompatible(defaultMethod, otherMethod)) {
if (foundMatch) { if (foundMatch) {
errors.addMessage("Generated default method %s with parameters %s is" errors.addMessage(
"Generated default method %s with parameters %s is"
+ " signature-compatible with more than one non-default method." + " signature-compatible with more than one non-default method."
+ " Unable to create factory. As a workaround, remove the override" + " Unable to create factory. As a workaround, remove the override"
+ " so javac stops generating a default method.", + " so javac stops generating a default method.",
@ -276,7 +416,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
} }
} }
// If we generated any errors (from finding matching constructors, for instance), throw an exception. // If we generated any errors (from finding matching constructors, for instance), throw an
// exception.
if (errors.hasErrors()) { if (errors.hasErrors()) {
throw errors.toException(); throw errors.toException();
} }
@ -296,23 +437,6 @@ final class FactoryProvider2<F> implements InvocationHandler,
== Modifier.PUBLIC; == Modifier.PUBLIC;
} }
/**
* Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping.
*/
static boolean canRethrow(Method invoked, Throwable thrown) {
if (thrown instanceof Error || thrown instanceof RuntimeException) {
return true;
}
for (Class<?> declared : invoked.getExceptionTypes()) {
if (declared.isInstance(thrown)) {
return true;
}
}
return false;
}
private boolean isCompatible(Method src, Method dst) { private boolean isCompatible(Method src, Method dst) {
if (!src.getReturnType().isAssignableFrom(dst.getReturnType())) { if (!src.getReturnType().isAssignableFrom(dst.getReturnType())) {
return false; return false;
@ -330,32 +454,37 @@ final class FactoryProvider2<F> implements InvocationHandler,
return true; return true;
} }
@Override
public F get() { public F get() {
return factory; return factory;
} }
@Override
public Set<Dependency<?>> getDependencies() { public Set<Dependency<?>> getDependencies() {
Set<Dependency<?>> combinedDeps = new HashSet<Dependency<?>>(); Set<Dependency<?>> combinedDeps = new HashSet<>();
for (AssistData data : assistDataByMethod.values()) { for (AssistData data : assistDataByMethod.values()) {
combinedDeps.addAll(data.dependencies); combinedDeps.addAll(data.dependencies);
} }
return ImmutableSet.copyOf(combinedDeps); return ImmutableSet.copyOf(combinedDeps);
} }
@Override
public Key<F> getKey() { public Key<F> getKey() {
return factoryKey; return factoryKey;
} }
// Safe cast because values are typed to AssistedData, which is an AssistedMethod, and // Safe cast because values are typed to AssistedData, which is an AssistedMethod, and
// the collection is immutable. // the collection is immutable.
@Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Collection<AssistedMethod> getAssistedMethods() { public Collection<AssistedMethod> getAssistedMethods() {
return (Collection<AssistedMethod>) (Collection<?>) assistDataByMethod.values(); return (Collection<AssistedMethod>) (Collection<?>) assistDataByMethod.values();
} }
@Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T, V> V acceptExtensionVisitor(BindingTargetVisitor<T, V> visitor, public <T, V> V acceptExtensionVisitor(
ProviderInstanceBinding<? extends T> binding) { BindingTargetVisitor<T, V> visitor, ProviderInstanceBinding<? extends T> binding) {
if (visitor instanceof AssistedInjectTargetVisitor) { if (visitor instanceof AssistedInjectTargetVisitor) {
return ((AssistedInjectTargetVisitor<T, V>) visitor).visit((AssistedInjectBinding<T>) this); return ((AssistedInjectTargetVisitor<T, V>) visitor).visit((AssistedInjectBinding<T>) this);
} }
@ -365,7 +494,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) { private void validateFactoryReturnType(Errors errors, Class<?> returnType, Class<?> factoryType) {
if (Modifier.isPublic(factoryType.getModifiers()) if (Modifier.isPublic(factoryType.getModifiers())
&& !Modifier.isPublic(returnType.getModifiers())) { && !Modifier.isPublic(returnType.getModifiers())) {
errors.addMessage("%s is public, but has a method that returns a non-public type: %s. " errors.addMessage(
"%s is public, but has a method that returns a non-public type: %s. "
+ "Due to limitations with java.lang.reflect.Proxy, this is not allowed. " + "Due to limitations with java.lang.reflect.Proxy, this is not allowed. "
+ "Please either make the factory non-public or the return type public.", + "Please either make the factory non-public or the return type public.",
factoryType, returnType); factoryType, returnType);
@ -379,8 +509,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
private boolean isTypeNotSpecified(TypeLiteral<?> typeLiteral, ConfigurationException ce) { private boolean isTypeNotSpecified(TypeLiteral<?> typeLiteral, ConfigurationException ce) {
Collection<Message> messages = ce.getErrorMessages(); Collection<Message> messages = ce.getErrorMessages();
if (messages.size() == 1) { if (messages.size() == 1) {
Message msg = Iterables.getOnlyElement( Message msg =
new Errors().keyNotFullySpecified(typeLiteral).getMessages()); Iterables.getOnlyElement(new Errors().keyNotFullySpecified(typeLiteral).getMessages());
return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage()); return msg.getMessage().equals(Iterables.getOnlyElement(messages).getMessage());
} else { } else {
return false; return false;
@ -390,9 +520,9 @@ final class FactoryProvider2<F> implements InvocationHandler,
/** /**
* Finds a constructor suitable for the method. If the implementation contained any constructors * Finds a constructor suitable for the method. If the implementation contained any constructors
* marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly * marked with {@link AssistedInject}, this requires all {@link Assisted} parameters to exactly
* match the parameters (in any order) listed in the method. Otherwise, if no * match the parameters (in any order) listed in the method. Otherwise, if no {@link
* {@link AssistedInject} constructors exist, this will default to looking for an * AssistedInject} constructors exist, this will default to looking for an {@literal @}{@link
* {@literal @}{@link Inject} constructor. * Inject} constructor.
*/ */
private <T> InjectionPoint findMatchingConstructorInjectionPoint( private <T> InjectionPoint findMatchingConstructorInjectionPoint(
Method method, Key<?> returnType, TypeLiteral<T> implementation, List<Key<?>> paramList) Method method, Key<?> returnType, TypeLiteral<T> implementation, List<Key<?>> paramList)
@ -428,8 +558,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
anyAssistedInjectConstructors = true; anyAssistedInjectConstructors = true;
if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) { if (constructorHasMatchingParams(implementation, constructor, paramList, errors)) {
if (matchingConstructor != null) { if (matchingConstructor != null) {
errors errors.addMessage(
.addMessage(
"%s has more than one constructor annotated with @AssistedInject" "%s has more than one constructor annotated with @AssistedInject"
+ " that matches the parameters in method %s. Unable to create " + " that matches the parameters in method %s. Unable to create "
+ "AssistedInject factory.", + "AssistedInject factory.",
@ -443,7 +572,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
} }
if (!anyAssistedInjectConstructors) { if (!anyAssistedInjectConstructors) {
// If none existed, use @Inject. // If none existed, use @Inject or a no-arg constructor.
try { try {
return InjectionPoint.forConstructorOf(implementation); return InjectionPoint.forConstructorOf(implementation);
} catch (ConfigurationException e) { } catch (ConfigurationException e) {
@ -455,7 +584,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
if (matchingConstructor != null) { if (matchingConstructor != null) {
// safe because we got the constructor from this implementation. // safe because we got the constructor from this implementation.
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
InjectionPoint ip = InjectionPoint.forConstructor( InjectionPoint ip =
InjectionPoint.forConstructor(
(Constructor<? super T>) matchingConstructor, implementation); (Constructor<? super T>) matchingConstructor, implementation);
return ip; return ip;
} else { } else {
@ -469,21 +599,19 @@ final class FactoryProvider2<F> implements InvocationHandler,
} }
/** /**
* Matching logic for constructors annotated with AssistedInject. * Matching logic for constructors annotated with AssistedInject. This returns true if and only if
* This returns true if and only if all @Assisted parameters in the * all @Assisted parameters in the constructor exactly match (in any order) all @Assisted
* constructor exactly match (in any order) all @Assisted parameters * parameters the method's parameter.
* the method's parameter.
*/ */
private boolean constructorHasMatchingParams(TypeLiteral<?> type, private boolean constructorHasMatchingParams(
Constructor<?> constructor, List<Key<?>> paramList, Errors errors) TypeLiteral<?> type, Constructor<?> constructor, List<Key<?>> paramList, Errors errors)
throws ErrorsException { throws ErrorsException {
List<TypeLiteral<?>> params = type.getParameterTypes(constructor); List<TypeLiteral<?>> params = type.getParameterTypes(constructor);
Annotation[][] paramAnnotations = constructor.getParameterAnnotations(); Annotation[][] paramAnnotations = constructor.getParameterAnnotations();
int p = 0; int p = 0;
List<Key<?>> constructorKeys = Lists.newArrayList(); List<Key<?>> constructorKeys = Lists.newArrayList();
for (TypeLiteral<?> param : params) { for (TypeLiteral<?> param : params) {
Key<?> paramKey = Annotations.getKey(param, constructor, paramAnnotations[p++], Key<?> paramKey = Annotations.getKey(param, constructor, paramAnnotations[p++], errors);
errors);
constructorKeys.add(paramKey); constructorKeys.add(paramKey);
} }
// Require that every key exist in the constructor to match up exactly. // Require that every key exist in the constructor to match up exactly.
@ -503,10 +631,9 @@ final class FactoryProvider2<F> implements InvocationHandler,
return true; return true;
} }
/** /** Calculates all dependencies required by the implementation and constructor. */
* Calculates all dependencies required by the implementation and constructor. private Set<Dependency<?>> getDependencies(
*/ InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
private Set<Dependency<?>> getDependencies(InjectionPoint ctorPoint, TypeLiteral<?> implementation) {
ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder(); ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
builder.addAll(ctorPoint.getDependencies()); builder.addAll(ctorPoint.getDependencies());
if (!implementation.getRawType().isInterface()) { if (!implementation.getRawType().isInterface()) {
@ -517,9 +644,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
return builder.build(); return builder.build();
} }
/** /** Return all non-assisted dependencies. */
* Return all non-assisted dependencies.
*/
private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) { private Set<Dependency<?>> removeAssistedDeps(Set<Dependency<?>> deps) {
ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder(); ImmutableSet.Builder<Dependency<?>> builder = ImmutableSet.builder();
for (Dependency<?> dep : deps) { for (Dependency<?> dep : deps) {
@ -537,8 +662,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
* the assisted bindings are immediately provided. This looks for hints that the values may be * the assisted bindings are immediately provided. This looks for hints that the values may be
* lazily retrieved, by looking for injections of Injector or a Provider for the assisted values. * lazily retrieved, by looking for injections of Injector or a Provider for the assisted values.
*/ */
private boolean isValidForOptimizedAssistedInject(Set<Dependency<?>> dependencies, private boolean isValidForOptimizedAssistedInject(
Class<?> implementation, TypeLiteral<?> factoryType) { Set<Dependency<?>> dependencies, Class<?> implementation, TypeLiteral<?> factoryType) {
Set<Dependency<?>> badDeps = null; // optimization: create lazily Set<Dependency<?>> badDeps = null; // optimization: create lazily
for (Dependency<?> dep : dependencies) { for (Dependency<?> dep : dependencies) {
if (isInjectorOrAssistedProvider(dep)) { if (isInjectorOrAssistedProvider(dep)) {
@ -549,7 +674,9 @@ final class FactoryProvider2<F> implements InvocationHandler,
} }
} }
if (badDeps != null && !badDeps.isEmpty()) { if (badDeps != null && !badDeps.isEmpty()) {
logger.log(Level.WARNING, "AssistedInject factory {0} will be slow " logger.log(
Level.WARNING,
"AssistedInject factory {0} will be slow "
+ "because {1} has assisted Provider dependencies or injects the Injector. " + "because {1} has assisted Provider dependencies or injects the Injector. "
+ "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!) "
@ -561,33 +688,43 @@ final class FactoryProvider2<F> implements InvocationHandler,
} }
/** /**
* Returns true if the dependency is for {@link Injector} or if the dependency * Returns true if the dependency is for {@link Injector} or if the dependency is a {@link
* is a {@link Provider} for a parameter that is {@literal @}{@link Assisted}. * Provider} for a parameter that is {@literal @}{@link Assisted}.
*/ */
private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) { private boolean isInjectorOrAssistedProvider(Dependency<?> dependency) {
Class<?> annotationType = dependency.getKey().getAnnotationType(); Class<?> annotationType = dependency.getKey().getAnnotationType();
if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted.. if (annotationType != null && annotationType.equals(Assisted.class)) { // If it's assisted..
if (dependency.getKey().getTypeLiteral().getRawType().equals(Provider.class)) { // And a Provider... if (dependency
.getKey()
.getTypeLiteral()
.getRawType()
.equals(Provider.class)) { // And a Provider...
return true; return true;
} }
} else if (dependency.getKey().getTypeLiteral().getRawType().equals(Injector.class)) { // If it's the Injector... } else if (dependency
.getKey()
.getTypeLiteral()
.getRawType()
.equals(Injector.class)) { // If it's the Injector...
return true; return true;
} }
return false; return false;
} }
/** /**
* Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation. * Returns a key similar to {@code key}, but with an {@literal @}Assisted binding annotation. This
* This fails if another binding annotation is clobbered in the process. If the key already has * fails if another binding annotation is clobbered in the process. If the key already has the
* the {@literal @}Assisted annotation, it is returned as-is to preserve any String value. * {@literal @}Assisted annotation, it is returned as-is to preserve any String value.
*/ */
private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException { private <T> Key<T> assistKey(Method method, Key<T> key, Errors errors) throws ErrorsException {
if (key.getAnnotationType() == null) { if (key.getAnnotationType() == null) {
return Key.get(key.getTypeLiteral(), DEFAULT_ANNOTATION); return key.withAnnotation(DEFAULT_ANNOTATION);
} else if (key.getAnnotationType() == Assisted.class) { } else if (key.getAnnotationType() == Assisted.class) {
return key; return key;
} else { } else {
errors.withSource(method).addMessage( errors
.withSource(method)
.addMessage(
"Only @Assisted is allowed for factory parameters, but found @%s", "Only @Assisted is allowed for factory parameters, but found @%s",
key.getAnnotationType()); key.getAnnotationType());
throw errors.toException(); throw errors.toException();
@ -595,14 +732,17 @@ final class FactoryProvider2<F> implements InvocationHandler,
} }
/** /**
* At injector-creation time, we initialize the invocation handler. At this time we make sure * At injector-creation time, we initialize the invocation handler. At this time we make sure all
* all factory methods will be able to build the target types. * factory methods will be able to build the target types.
*/ */
@Inject @Inject
@Toolable @Toolable
void initialize(Injector injector) { void initialize(Injector injector) {
if (this.injector != null) { if (this.injector != null) {
throw new ConfigurationException(ImmutableList.of(new Message(FactoryProvider2.class, throw new ConfigurationException(
ImmutableList.of(
new Message(
FactoryProvider2.class,
"Factories.create() factories may only be used in one Injector!"))); "Factories.create() factories may only be used in one Injector!")));
} }
@ -618,7 +758,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
} else { } else {
args = null; // won't be used -- instead will bind to data.providers. args = null; // won't be used -- instead will bind to data.providers.
} }
getBindingFromNewInjector(method, args, data); // throws if the binding isn't properly configured getBindingFromNewInjector(
method, args, data); // throws if the binding isn't properly configured
} }
} }
@ -627,7 +768,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
*/ */
public Binding<?> getBindingFromNewInjector( public Binding<?> getBindingFromNewInjector(
final Method method, final Object[] args, final AssistData data) { final Method method, final Object[] args, final AssistData data) {
checkState(injector != null, checkState(
injector != null,
"Factories.create() factories cannot be used until they're initialized by Guice."); "Factories.create() factories cannot be used until they're initialized by Guice.");
final Key<?> returnType = data.returnType; final Key<?> returnType = data.returnType;
@ -635,17 +777,21 @@ final class FactoryProvider2<F> implements InvocationHandler,
// We ignore any pre-existing binding annotation. // We ignore any pre-existing binding annotation.
final Key<?> returnKey = Key.get(returnType.getTypeLiteral(), RETURN_ANNOTATION); final Key<?> returnKey = Key.get(returnType.getTypeLiteral(), RETURN_ANNOTATION);
Module assistedModule = new AbstractModule() { Module assistedModule =
new AbstractModule() {
@Override @Override
@SuppressWarnings({ @SuppressWarnings({
"unchecked", "rawtypes"}) // raw keys are necessary for the args array and return value "unchecked",
"rawtypes"
}) // raw keys are necessary for the args array and return value
protected void configure() { protected void configure() {
Binder binder = binder().withSource(method); Binder binder = binder().withSource(method);
int p = 0; int p = 0;
if (!data.optimized) { if (!data.optimized) {
for (Key<?> paramKey : data.paramTypes) { for (Key<?> paramKey : data.paramTypes) {
// Wrap in a Provider to cover null, and to prevent Guice from injecting the parameter // Wrap in a Provider to cover null, and to prevent Guice from injecting the
// parameter
binder.bind((Key) paramKey).toProvider(Providers.of(args[p++])); binder.bind((Key) paramKey).toProvider(Providers.of(args[p++]));
} }
} else { } else {
@ -660,7 +806,8 @@ final class FactoryProvider2<F> implements InvocationHandler,
// but if it isn't, we'll end up throwing a fairly good error // but if it isn't, we'll end up throwing a fairly good error
// message for the user. // message for the user.
if (constructor != null) { if (constructor != null) {
binder.bind(returnKey) binder
.bind(returnKey)
.toConstructor(constructor, (TypeLiteral) data.implementationType) .toConstructor(constructor, (TypeLiteral) data.implementationType)
.in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type .in(Scopes.NO_SCOPE); // make sure we erase any scope on the implementation type
} }
@ -680,6 +827,7 @@ final class FactoryProvider2<F> implements InvocationHandler,
* When a factory method is invoked, we create a child injector that binds all parameters, then * When a factory method is invoked, we create a child injector that binds all parameters, then
* use that to get an instance of the return type. * use that to get an instance of the return type.
*/ */
@Override
public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable { public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
// If we setup a method handle earlier for this method, call it. // If we setup a method handle earlier for this method, call it.
// This is necessary for default methods that java8 creates, so we // This is necessary for default methods that java8 creates, so we
@ -748,93 +896,19 @@ final class FactoryProvider2<F> implements InvocationHandler,
return factoryKey.equals(other.factoryKey) && Objects.equal(collector, other.collector); return factoryKey.equals(other.factoryKey) && Objects.equal(collector, other.collector);
} }
/** /** Returns true if {@code thrown} can be thrown by {@code invoked} without wrapping. */
* All the data necessary to perform an assisted inject. static boolean canRethrow(Method invoked, Throwable thrown) {
*/ if (thrown instanceof Error || thrown instanceof RuntimeException) {
private static class AssistData implements AssistedMethod { return true;
/**
* the constructor the implementation is constructed with.
*/
final Constructor<?> constructor;
/**
* the return type in the factory method that the constructor is bound to.
*/
final Key<?> returnType;
/**
* the parameters in the factory method associated with this data.
*/
final ImmutableList<Key<?>> paramTypes;
/**
* the type of the implementation constructed
*/
final TypeLiteral<?> implementationType;
/**
* All non-assisted dependencies required by this method.
*/
final Set<Dependency<?>> dependencies;
/**
* The factory method associated with this data
*/
final Method factoryMethod;
/**
* true if {@link #isValidForOptimizedAssistedInject} returned true.
*/
final boolean optimized;
/**
* the list of optimized providers, empty if not optimized.
*/
final List<ThreadLocalProvider> providers;
/**
* used to perform optimized factory creations.
*/
volatile Binding<?> cachedBinding; // TODO: volatile necessary?
AssistData(Constructor<?> constructor, Key<?> returnType, ImmutableList<Key<?>> paramTypes,
TypeLiteral<?> implementationType, Method factoryMethod,
Set<Dependency<?>> dependencies,
boolean optimized, List<ThreadLocalProvider> providers) {
this.constructor = constructor;
this.returnType = returnType;
this.paramTypes = paramTypes;
this.implementationType = implementationType;
this.factoryMethod = factoryMethod;
this.dependencies = dependencies;
this.optimized = optimized;
this.providers = providers;
} }
@Override for (Class<?> declared : invoked.getExceptionTypes()) {
public String toString() { if (declared.isInstance(thrown)) {
return Objects.toStringHelper(getClass()) return true;
.add("ctor", constructor) }
.add("return type", returnType)
.add("param type", paramTypes)
.add("implementation type", implementationType)
.add("dependencies", dependencies)
.add("factory method", factoryMethod)
.add("optimized", optimized)
.add("providers", providers)
.add("cached binding", cachedBinding)
.toString();
} }
public Set<Dependency<?>> getDependencies() { return false;
return dependencies;
}
public Method getFactoryMethod() {
return factoryMethod;
}
public Constructor<?> getImplementationConstructor() {
return constructor;
}
public TypeLiteral<?> getImplementationType() {
return implementationType;
}
} }
// not <T> because we'll never know and this is easier than suppressing warnings. // not <T> because we'll never know and this is easier than suppressing warnings.
@ -847,86 +921,106 @@ final class FactoryProvider2<F> implements InvocationHandler,
} }
} }
/** private static MethodHandle superMethodHandle(
* Wrapper around MethodHandles/MethodHandle, so we can compile+run on java6. Method method, Object proxy, MethodHandles.Lookup userLookups)
*/ throws ReflectiveOperationException {
private static class MethodHandleWrapper { MethodHandles.Lookup lookup = userLookups == null ? MethodHandles.lookup() : userLookups;
static final int ALL_MODES = Modifier.PRIVATE MethodHandle handle = SUPER_METHOD_LOOKUP.get().superMethodHandle(method, lookup);
| Modifier.STATIC /* package */ return handle != null ? handle.bindTo(proxy) : null;
| Modifier.PUBLIC }
| Modifier.PROTECTED;
static final Method unreflectSpecial; // begin by trying unreflectSpecial to find super method handles; this should work on Java14+
static final Method bindTo; private static final AtomicReference<SuperMethodLookup> SUPER_METHOD_LOOKUP =
static final Method invokeWithArguments; new AtomicReference<>(SuperMethodLookup.UNREFLECT_SPECIAL);
static final Constructor<?> lookupCxtor;
static final boolean valid;
static { private static enum SuperMethodLookup {
Method unreflectSpecialTmp = null; UNREFLECT_SPECIAL {
Method bindToTmp = null; @Override
Method invokeWithArgumentsTmp = null; MethodHandle superMethodHandle(Method method, MethodHandles.Lookup lookup)
boolean validTmp = false; throws ReflectiveOperationException {
Constructor<?> lookupCxtorTmp = null;
try { try {
Class<?> lookupClass = Class.forName("java.lang.invoke.MethodHandles$Lookup"); return lookup.unreflectSpecial(method, method.getDeclaringClass());
unreflectSpecialTmp = lookupClass.getMethod("unreflectSpecial", Method.class, Class.class); } catch (ReflectiveOperationException e) {
Class<?> methodHandleClass = Class.forName("java.lang.invoke.MethodHandle"); // fall back to findSpecial which should work on Java9+; use that for future lookups
bindToTmp = methodHandleClass.getMethod("bindTo", Object.class); SUPER_METHOD_LOOKUP.compareAndSet(this, FIND_SPECIAL);
invokeWithArgumentsTmp = methodHandleClass.getMethod("invokeWithArguments", Object[].class); return SUPER_METHOD_LOOKUP.get().superMethodHandle(method, lookup);
lookupCxtorTmp = lookupClass.getDeclaredConstructor(Class.class, int.class);
lookupCxtorTmp.setAccessible(true);
validTmp = true;
} catch (Exception invalid) {
// Ignore the exception, store the values & exit early in create(..) if invalid.
} }
// Store refs to later.
valid = validTmp;
unreflectSpecial = unreflectSpecialTmp;
bindTo = bindToTmp;
invokeWithArguments = invokeWithArgumentsTmp;
lookupCxtor = lookupCxtorTmp;
}
final Object handle;
MethodHandleWrapper(Object handle) {
this.handle = handle;
}
static MethodHandleWrapper create(Method method, Object proxy) {
if (!valid) {
return null;
} }
},
FIND_SPECIAL {
@Override
MethodHandle superMethodHandle(Method method, MethodHandles.Lookup lookup)
throws ReflectiveOperationException {
try { try {
Class<?> declaringClass = method.getDeclaringClass(); Class<?> declaringClass = method.getDeclaringClass();
// Note: this isn't a public API, but we need to use it in order to call default methods. // use findSpecial to workaround https://bugs.openjdk.java.net/browse/JDK-8209005
Object lookup = lookupCxtor.newInstance(declaringClass, ALL_MODES); return lookup.findSpecial(
method.setAccessible(true); declaringClass,
// These are part of the public API, but we use reflection since we run on java6 method.getName(),
// and they were introduced in java7. MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
lookup = unreflectSpecial.invoke(lookup, method, declaringClass); declaringClass);
Object handle = bindTo.invoke(lookup, proxy); } catch (ReflectiveOperationException e) {
return new MethodHandleWrapper(handle); if (!allowLookupReflection) {
} catch (InvocationTargetException ite) { throw e;
return null; }
} catch (IllegalAccessException iae) { // fall back to private Lookup which should work on Java8; use that for future lookups
return null; SUPER_METHOD_LOOKUP.compareAndSet(this, PRIVATE_LOOKUP);
} catch (InstantiationException ie) { return SUPER_METHOD_LOOKUP.get().superMethodHandle(method, lookup);
return null;
} }
} }
},
Object invokeWithArguments(Object[] args) throws Exception { PRIVATE_LOOKUP {
// We must cast the args to an object so the Object[] is the first param,
// as opposed to each individual varargs param.
return invokeWithArguments.invoke(handle, (Object) args);
}
@Override @Override
public String toString() { MethodHandle superMethodHandle(Method method, MethodHandles.Lookup unused)
return handle.toString(); throws ReflectiveOperationException {
return PrivateLookup.superMethodHandle(method);
}
};
abstract MethodHandle superMethodHandle(Method method, MethodHandles.Lookup lookup)
throws ReflectiveOperationException;
};
// Note: this isn't a public API, but we need to use it in order to call default methods on (or
// with) non-public types. If it doesn't exist, the code falls back to a less precise check.
static class PrivateLookup {
PrivateLookup() {}
private static final int ALL_MODES =
Modifier.PRIVATE | Modifier.STATIC /* package */ | Modifier.PUBLIC | Modifier.PROTECTED;
private static final Constructor<MethodHandles.Lookup> privateLookupCxtor =
findPrivateLookupCxtor();
private static Constructor<MethodHandles.Lookup> findPrivateLookupCxtor() {
try {
Constructor<MethodHandles.Lookup> cxtor;
try {
cxtor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
} catch (NoSuchMethodException ignored) {
cxtor =
MethodHandles.Lookup.class.getDeclaredConstructor(
Class.class, Class.class, int.class);
}
cxtor.setAccessible(true);
return cxtor;
} catch (ReflectiveOperationException | SecurityException e) {
return null;
}
}
static MethodHandle superMethodHandle(Method method) throws ReflectiveOperationException {
if (privateLookupCxtor == null) {
return null; // fall back to assistDataBuilder workaround
}
Class<?> declaringClass = method.getDeclaringClass();
MethodHandles.Lookup lookup;
if (privateLookupCxtor.getParameterCount() == 2) {
lookup = privateLookupCxtor.newInstance(declaringClass, ALL_MODES);
} else {
lookup = privateLookupCxtor.newInstance(declaringClass, null, ALL_MODES);
}
return lookup.unreflectSpecial(method, declaringClass);
} }
} }
} }

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

@ -14,17 +14,16 @@ import com.google.inject.Stage;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.spi.DefaultBindingTargetVisitor; import com.google.inject.spi.DefaultBindingTargetVisitor;
import java.util.Set;
/** /**
* Guarantees that processing of Binding elements happens in a sane way. * Guarantees that processing of Binding elements happens in a sane way.
*/ */
abstract class AbstractBindingProcessor extends AbstractProcessor { 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 list 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 ImmutableSet<Class<?>> FORBIDDEN_TYPES =
ImmutableSet.<Class<?>>of(
AbstractModule.class, AbstractModule.class,
Binder.class, Binder.class,
Binding.class, Binding.class,
@ -37,11 +36,11 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
Stage.class, Stage.class,
TypeLiteral.class); TypeLiteral.class);
protected final ProcessedBindingData bindingData; protected final ProcessedBindingData processedBindingData;
AbstractBindingProcessor(Errors errors, ProcessedBindingData bindingData) { AbstractBindingProcessor(Errors errors, ProcessedBindingData processedBindingData) {
super(errors); super(errors);
this.bindingData = bindingData; this.processedBindingData = processedBindingData;
} }
protected <T> UntargettedBindingImpl<T> invalidBinding( protected <T> UntargettedBindingImpl<T> invalidBinding(
@ -61,10 +60,10 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
BindingImpl<?> original = injector.getExistingBinding(key); BindingImpl<?> original = injector.getExistingBinding(key);
if (original != null) { if (original != null) {
// If it failed because of an explicit duplicate binding... // If it failed because of an explicit duplicate binding...
if (injector.state.getExplicitBinding(key) != null) { if (injector.getBindingData().getExplicitBinding(key) != null) {
try { try {
if (!isOkayDuplicate(original, binding, injector.state)) { if (!isOkayDuplicate(original, binding, injector.getBindingData())) {
errors.bindingAlreadySet(key, original.getSource()); errors.bindingAlreadySet(binding, original);
return; return;
} }
} catch (Throwable t) { } catch (Throwable t) {
@ -80,27 +79,34 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
} }
// prevent the parent from creating a JIT binding for this key // prevent the parent from creating a JIT binding for this key
injector.state.parent().blacklist(key, injector.state, binding.getSource()); injector
injector.state.putBinding(key, binding); .getJitBindingData()
.banKeyInParent(key, injector.getBindingData(), binding.getSource());
injector.getBindingData().putBinding(key, binding);
} }
/** /**
* We tolerate duplicate bindings if one exposes the other or if the two bindings * We tolerate duplicate bindings if one exposes the other or if the two bindings are considered
* are considered duplicates. * duplicates (see {@link Bindings#areDuplicates(BindingImpl, BindingImpl)}.
* *
* @param original the binding in the parent injector (candidate for an exposing binding) * @param original the binding in the parent injector (candidate for an exposing binding)
* @param binding the binding to check (candidate for the exposed binding) * @param binding the binding to check (candidate for the exposed binding)
*/ */
private boolean isOkayDuplicate(BindingImpl<?> original, BindingImpl<?> binding, State state) { private static boolean isOkayDuplicate(
BindingImpl<?> original, BindingImpl<?> binding, InjectorBindingData bindingData) {
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 {
original = (BindingImpl<?>) state.getExplicitBindingsThisLevel().get(binding.getKey()); original = (BindingImpl<?>) bindingData.getExplicitBindingsThisLevel().get(binding.getKey());
// If no original at this level, the original was on a parent, and we don't // If no original at this level, the original was on a parent, and we don't
// allow deduplication between parents & children. // allow deduplication between parents & children.
return original != null && original.equals(binding); if (original == null) {
return false;
} else {
return original.equals(binding);
}
} }
} }
@ -110,8 +116,8 @@ abstract class AbstractBindingProcessor extends AbstractProcessor {
} }
/** /**
* Processor for visiting bindings. Each overriden method that wants to * Processor for visiting bindings. Each overriden method that wants to actually process the
* actually process the binding should call prepareBinding first. * binding should call prepareBinding first.
*/ */
abstract class Processor<T, V> extends DefaultBindingTargetVisitor<T, V> { abstract class Processor<T, V> extends DefaultBindingTargetVisitor<T, V> {
final Object source; final Object source;
@ -131,16 +137,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.
*/
protected void scheduleInitialization(BindingImpl<?> binding) {
processedBindingData.addUninitializedBinding(() -> initializeBinding(binding));
}
/**
* Schedule initialization for this binding to occur after all other static initialization of
* bindings.
*/
protected void scheduleDelayedInitialization(BindingImpl<?> binding) {
processedBindingData.addDelayedUninitializedBinding(() -> initializeBinding(binding));
}
private void initializeBinding(BindingImpl<?> binding) {
try { try {
binding.getInjector().initializeBinding(binding, errors.withSource(source)); binding.getInjector().initializeBinding(binding, errors.withSource(source));
} catch (ErrorsException e) { } catch (ErrorsException e) {
errors.merge(e.getErrors()); errors.merge(e.getErrors());
} }
} }
});
}
} }
} }

View file

@ -1,6 +1,7 @@
package com.google.inject.internal; package com.google.inject.internal;
import com.google.common.base.Function; import static java.lang.annotation.RetentionPolicy.RUNTIME;
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;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
@ -16,8 +17,6 @@ import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes; import com.google.inject.internal.util.Classes;
import com.google.inject.name.Named; import com.google.inject.name.Named;
import com.google.inject.name.Names; import com.google.inject.name.Names;
import javax.inject.Qualifier;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@ -28,37 +27,16 @@ import java.lang.reflect.Proxy;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import javax.inject.Qualifier;
/** /**
* Annotation utilities. * Annotation utilities.
* *
* @author crazybob@google.com (Bob Lee)
*/ */
public class Annotations { public class Annotations {
private static final MapJoiner JOINER = Joiner.on(", ").withKeyValueSeparator("="); /** Returns {@code true} if the given annotation type has no attributes. */
private static final Function<Object, String> DEEP_TO_STRING_FN = new Function<Object, String>() {
@Override
public String apply(Object arg) {
String s = Arrays.deepToString(new Object[]{arg});
return s.substring(1, s.length() - 1); // cut off brackets
}
};
private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
CacheBuilder.newBuilder().weakKeys().build(
new CacheLoader<Class<? extends Annotation>, Annotation>() {
@Override
public Annotation load(Class<? extends Annotation> input) {
return generateAnnotationImpl(input);
}
});
private static final AnnotationChecker scopeChecker = new AnnotationChecker(
Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
private static final AnnotationChecker bindingAnnotationChecker = new AnnotationChecker(
Arrays.asList(BindingAnnotation.class, Qualifier.class));
/**
* Returns {@code true} if the given annotation type has no attributes.
*/
public static boolean isMarker(Class<? extends Annotation> annotationType) { public static boolean isMarker(Class<? extends Annotation> annotationType) {
return annotationType.getDeclaredMethods().length == 0; return annotationType.getDeclaredMethods().length == 0;
} }
@ -74,11 +52,22 @@ public class Annotations {
return hasMethods; return hasMethods;
} }
private static final LoadingCache<Class<? extends Annotation>, Annotation> cache =
CacheBuilder.newBuilder()
.weakKeys()
.build(
new CacheLoader<Class<? extends Annotation>, Annotation>() {
@Override
public Annotation load(Class<? extends Annotation> input) {
return generateAnnotationImpl(input);
}
});
/** /**
* Generates an Annotation for the annotation class. Requires that the annotation is all * Generates an Annotation for the annotation class. Requires that the annotation is all
* optionals. * optionals.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked") // Safe because generateAnnotationImpl returns T for Class<T>
public static <T extends Annotation> T generateAnnotation(Class<T> annotationType) { public static <T extends Annotation> T generateAnnotation(Class<T> annotationType) {
Preconditions.checkState( Preconditions.checkState(
isAllDefaultMethods(annotationType), "%s is not all default methods", annotationType); isAllDefaultMethods(annotationType), "%s is not all default methods", annotationType);
@ -87,7 +76,8 @@ public class Annotations {
private static <T extends Annotation> T generateAnnotationImpl(final Class<T> annotationType) { private static <T extends Annotation> T generateAnnotationImpl(final Class<T> annotationType) {
final Map<String, Object> members = resolveMembers(annotationType); final Map<String, Object> members = resolveMembers(annotationType);
return annotationType.cast(Proxy.newProxyInstance( return annotationType.cast(
Proxy.newProxyInstance(
annotationType.getClassLoader(), annotationType.getClassLoader(),
new Class<?>[] {annotationType}, new Class<?>[] {annotationType},
new InvocationHandler() { new InvocationHandler() {
@ -118,11 +108,10 @@ public class Annotations {
return result.build(); return result.build();
} }
/** /** Implements {@link Annotation#equals}. */
* Implements {@link Annotation#equals}. private static boolean annotationEquals(
*/ Class<? extends Annotation> type, Map<String, Object> members, Object other)
private static boolean annotationEquals(Class<? extends Annotation> type, throws Exception {
Map<String, Object> members, Object other) throws Exception {
if (!type.isInstance(other)) { if (!type.isInstance(other)) {
return false; return false;
} }
@ -136,11 +125,9 @@ public class Annotations {
return true; return true;
} }
/** /** Implements {@link Annotation#hashCode}. */
* Implements {@link Annotation#hashCode}. private static int annotationHashCode(
*/ Class<? extends Annotation> type, Map<String, Object> members) throws Exception {
private static int annotationHashCode(Class<? extends Annotation> type,
Map<String, Object> members) throws Exception {
int result = 0; int result = 0;
for (Method method : type.getDeclaredMethods()) { for (Method method : type.getDeclaredMethods()) {
String name = method.getName(); String name = method.getName();
@ -150,36 +137,38 @@ public class Annotations {
return result; return result;
} }
/** private static final MapJoiner JOINER = Joiner.on(", ").withKeyValueSeparator("=");
* Implements {@link Annotation#toString}.
*/ /** Implements {@link Annotation#toString}. */
private static String annotationToString(Class<? extends Annotation> type, private static String annotationToString(
Map<String, Object> members) throws Exception { Class<? extends Annotation> type, Map<String, Object> members) throws Exception {
StringBuilder sb = new StringBuilder().append("@").append(type.getName()).append("("); StringBuilder sb = new StringBuilder().append("@").append(type.getName()).append("(");
JOINER.appendTo(sb, Maps.transformValues(members, DEEP_TO_STRING_FN)); JOINER.appendTo(
sb,
Maps.transformValues(
members,
arg -> {
String s = Arrays.deepToString(new Object[] {arg});
return s.substring(1, s.length() - 1); // cut off brackets
}));
return sb.append(")").toString(); return sb.append(")").toString();
} }
/** /** Returns true if the given annotation is retained at runtime. */
* Returns true if the given annotation is retained at runtime.
*/
public static boolean isRetainedAtRuntime(Class<? extends Annotation> annotationType) { public static boolean isRetainedAtRuntime(Class<? extends Annotation> annotationType) {
Retention retention = annotationType.getAnnotation(Retention.class); Retention retention = annotationType.getAnnotation(Retention.class);
return retention != null && retention.value() == RetentionPolicy.RUNTIME; return retention != null && retention.value() == RetentionPolicy.RUNTIME;
} }
/** /** Returns the scope annotation on {@code type}, or null if none is specified. */
* Returns the scope annotation on {@code type}, or null if none is specified.
*/
public static Class<? extends Annotation> findScopeAnnotation( public static Class<? extends Annotation> findScopeAnnotation(
Errors errors, Class<?> implementation) { Errors errors, Class<?> implementation) {
return findScopeAnnotation(errors, implementation.getAnnotations()); return findScopeAnnotation(errors, implementation.getAnnotations());
} }
/** /** Returns the scoping annotation, or null if there isn't one. */
* Returns the scoping annotation, or null if there isn't one. public static Class<? extends Annotation> findScopeAnnotation(
*/ Errors errors, Annotation[] annotations) {
public static Class<? extends Annotation> findScopeAnnotation(Errors errors, Annotation[] annotations) {
Class<? extends Annotation> found = null; Class<? extends Annotation> found = null;
for (Annotation annotation : annotations) { for (Annotation annotation : annotations) {
@ -207,13 +196,115 @@ public class Annotations {
return false; return false;
} }
private static class AnnotationToStringConfig {
final boolean quote;
final boolean includeMemberName;
AnnotationToStringConfig(boolean quote, boolean includeMemberName) {
this.quote = quote;
this.includeMemberName = includeMemberName;
}
}
private static final AnnotationToStringConfig ANNOTATION_TO_STRING_CONFIG =
determineAnnotationToStringConfig();
/**
* Returns {@code value}, quoted if annotation implementations quote their member values. In Java
* 9, annotations quote their string members.
*/
public static String memberValueString(String value) {
return ANNOTATION_TO_STRING_CONFIG.quote ? "\"" + value + "\"" : value;
}
/**
* Returns string representation of the annotation memeber.
*
* <p>The value of the member is prefixed with `memberName=` unless the runtime omits the member
* name. The value of the member is quoted if annotation implementations quote their member values
* and the value type is String.
*
* <p>In Java 9, annotations quote their string members and in Java 15, the member name is
* omitted.
*/
public static String memberValueString(String memberName, Object value) {
StringBuilder sb = new StringBuilder();
boolean quote = ANNOTATION_TO_STRING_CONFIG.quote;
boolean includeMemberName = ANNOTATION_TO_STRING_CONFIG.includeMemberName;
if (includeMemberName) {
sb.append(memberName).append('=');
}
if (quote && (value instanceof String)) {
sb.append('"').append(value).append('"');
} else {
sb.append(value);
}
return sb.toString();
}
@Retention(RUNTIME)
private @interface TestAnnotation {
String value();
}
@TestAnnotation("determineAnnotationToStringConfig")
private static AnnotationToStringConfig determineAnnotationToStringConfig() {
try {
String annotation =
Annotations.class
.getDeclaredMethod("determineAnnotationToStringConfig")
.getAnnotation(TestAnnotation.class)
.toString();
boolean quote = annotation.contains("\"determineAnnotationToStringConfig\"");
boolean includeMemberName = annotation.contains("value=");
return new AnnotationToStringConfig(quote, includeMemberName);
} catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
}
/** Checks for the presence of annotations. Caches results because Android doesn't. */
static class AnnotationChecker {
private final Collection<Class<? extends Annotation>> annotationTypes;
/** Returns true if the given class has one of the desired annotations. */
private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations =
new CacheLoader<Class<? extends Annotation>, Boolean>() {
@Override
public Boolean load(Class<? extends Annotation> annotationType) {
for (Annotation annotation : annotationType.getAnnotations()) {
if (annotationTypes.contains(annotation.annotationType())) {
return true;
}
}
return false;
}
};
final LoadingCache<Class<? extends Annotation>, Boolean> cache =
CacheBuilder.newBuilder().weakKeys().build(hasAnnotations);
/** Constructs a new checker that looks for annotations of the given types. */
AnnotationChecker(Collection<Class<? extends Annotation>> annotationTypes) {
this.annotationTypes = annotationTypes;
}
/** Returns true if the given type has one of the desired annotations. */
boolean hasAnnotations(Class<? extends Annotation> annotated) {
return cache.getUnchecked(annotated);
}
}
private static final AnnotationChecker scopeChecker =
new AnnotationChecker(Arrays.asList(ScopeAnnotation.class, javax.inject.Scope.class));
public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) { public static boolean isScopeAnnotation(Class<? extends Annotation> annotationType) {
return scopeChecker.hasAnnotations(annotationType); return scopeChecker.hasAnnotations(annotationType);
} }
/** /**
* Adds an error if there is a misplaced annotations on {@code type}. Scoping * Adds an error if there is a misplaced annotations on {@code type}. Scoping annotations are not
* annotations are not allowed on abstract classes or interfaces. * allowed on abstract classes or interfaces.
*/ */
public static void checkForMisplacedScopeAnnotations( public static void checkForMisplacedScopeAnnotations(
Class<?> type, Object source, Errors errors) { Class<?> type, Object source, Errors errors) {
@ -229,20 +320,20 @@ public class Annotations {
} }
} }
/** // NOTE: getKey/findBindingAnnotation are used by Gin which is abandoned. So changing this API
* Gets a key for the given type, member and annotations. // will prevent Gin users from upgrading Guice version.
*/
public static Key<?> getKey(TypeLiteral<?> type, Member member, Annotation[] annotations, /** Gets a key for the given type, member and annotations. */
Errors errors) throws ErrorsException { public static Key<?> getKey(
TypeLiteral<?> type, Member member, Annotation[] annotations, Errors errors)
throws ErrorsException {
int numErrorsBefore = errors.size(); int numErrorsBefore = errors.size();
Annotation found = findBindingAnnotation(errors, member, annotations); Annotation found = findBindingAnnotation(errors, member, annotations);
errors.throwIfNewErrors(numErrorsBefore); errors.throwIfNewErrors(numErrorsBefore);
return found == null ? Key.get(type) : Key.get(type, found); return found == null ? Key.get(type) : Key.get(type, found);
} }
/** /** 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;
@ -261,9 +352,10 @@ public class Annotations {
return found; return found;
} }
/** private static final AnnotationChecker bindingAnnotationChecker =
* Returns true if annotations of the specified type are binding annotations. new AnnotationChecker(Arrays.asList(BindingAnnotation.class, Qualifier.class));
*/
/** Returns true if annotations of the specified type are binding annotations. */
public static boolean isBindingAnnotation(Class<? extends Annotation> annotationType) { public static boolean isBindingAnnotation(Class<? extends Annotation> annotationType) {
return bindingAnnotationChecker.hasAnnotations(annotationType); return bindingAnnotationChecker.hasAnnotations(annotationType);
} }
@ -294,41 +386,20 @@ public class Annotations {
} }
/** /**
* Checks for the presence of annotations. Caches results because Android doesn't. * 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.
*/ */
static class AnnotationChecker { public static String nameOf(Key<?> key) {
private final Collection<Class<? extends Annotation>> annotationTypes; Annotation annotation = key.getAnnotation();
Class<? extends Annotation> annotationType = key.getAnnotationType();
/** if (annotation != null && !isMarker(annotationType)) {
* Returns true if the given class has one of the desired annotations. return key.getAnnotation().toString();
*/ } else if (key.getAnnotationType() != null) {
private CacheLoader<Class<? extends Annotation>, Boolean> hasAnnotations = return "@" + key.getAnnotationType().getName();
new CacheLoader<Class<? extends Annotation>, Boolean>() { } else {
public Boolean load(Class<? extends Annotation> annotationType) { return "";
for (Annotation annotation : annotationType.getAnnotations()) {
if (annotationTypes.contains(annotation.annotationType())) {
return true;
}
}
return false;
}
};
final LoadingCache<Class<? extends Annotation>, Boolean> cache = CacheBuilder.newBuilder().weakKeys()
.build(hasAnnotations);
/**
* Constructs a new checker that looks for annotations of the given types.
*/
AnnotationChecker(Collection<Class<? extends Annotation>> annotationTypes) {
this.annotationTypes = annotationTypes;
}
/**
* Returns true if the given type has one of the desired annotations.
*/
boolean hasAnnotations(Class<? extends Annotation> annotated) {
return cache.getUnchecked(annotated);
} }
} }
} }

View file

@ -0,0 +1,52 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableList;
import com.google.inject.Binding;
import com.google.inject.spi.ErrorDetail;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.stream.Collectors;
/** Error reported by Guice when a key is bound at multiple places the injector. */
final class BindingAlreadySetError extends InternalErrorDetail<BindingAlreadySetError> {
private final Binding<?> binding;
private final Binding<?> original;
BindingAlreadySetError(Binding<?> binding, Binding<?> original, List<Object> sources) {
super(
ErrorId.BINDING_ALREADY_SET,
String.format("%s was bound multiple times.", Messages.convert(binding.getKey())),
sources,
null);
this.binding = binding;
this.original = original;
}
@Override
public boolean isMergeable(ErrorDetail<?> otherError) {
return otherError instanceof BindingAlreadySetError
&& ((BindingAlreadySetError) otherError).binding.getKey().equals(binding.getKey());
}
@Override
public void formatDetail(List<ErrorDetail<?>> mergeableErrors, Formatter formatter) {
List<List<Object>> sourcesList = new ArrayList<>();
sourcesList.add(ImmutableList.of(original.getSource()));
sourcesList.add(ImmutableList.of(binding.getSource()));
sourcesList.addAll(
mergeableErrors.stream()
.map(e -> ((BindingAlreadySetError) e).binding.getSource())
.map(ImmutableList::of)
.collect(Collectors.toList()));
formatter.format("%n%s%n", Messages.bold("Bound at:"));
for (int i = 0; i < sourcesList.size(); i++) {
ErrorFormatter.formatSources(i + 1, sourcesList.get(i), formatter);
}
}
@Override
public BindingAlreadySetError withSources(List<Object> newSources) {
return new BindingAlreadySetError(binding, original, newSources);
}
}

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

@ -15,7 +15,6 @@ import com.google.inject.spi.ProviderBinding;
import com.google.inject.spi.ProviderInstanceBinding; import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderKeyBinding; import com.google.inject.spi.ProviderKeyBinding;
import com.google.inject.spi.UntargettedBinding; import com.google.inject.spi.UntargettedBinding;
import java.util.Set; import java.util.Set;
/** /**
@ -25,8 +24,9 @@ final class BindingProcessor extends AbstractBindingProcessor {
private final Initializer initializer; private final Initializer initializer;
BindingProcessor(Errors errors, Initializer initializer, ProcessedBindingData bindingData) { BindingProcessor(
super(errors, bindingData); Errors errors, Initializer initializer, ProcessedBindingData processedBindingData) {
super(errors, processedBindingData);
this.initializer = initializer; this.initializer = initializer;
} }
@ -49,13 +49,22 @@ final class BindingProcessor extends AbstractBindingProcessor {
return true; return true;
} }
return command.acceptTargetVisitor(new Processor<T, Boolean>((BindingImpl<T>) command) { return command.acceptTargetVisitor(
new Processor<T, Boolean>((BindingImpl<T>) command) {
@Override @Override
public Boolean visit(ConstructorBinding<? extends T> binding) { public Boolean visit(ConstructorBinding<? extends T> binding) {
prepareBinding(); prepareBinding();
try { try {
ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key, ConstructorBindingImpl<T> onInjector =
binding.getConstructor(), source, scoping, errors, false, false); ConstructorBindingImpl.create(
injector,
key,
binding.getConstructor(),
source,
scoping,
errors,
false,
false);
scheduleInitialization(onInjector); scheduleInitialization(onInjector);
putBinding(onInjector); putBinding(onInjector);
} catch (ErrorsException e) { } catch (ErrorsException e) {
@ -72,13 +81,15 @@ 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(
instance)); new InstanceBindingImpl<T>(
injector, key, source, scopedFactory, injectionPoints, instance));
return true; return true;
} }
@ -86,35 +97,54 @@ 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.<javax.inject.Provider<? extends T>>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 =
initializable, source, new InternalFactoryToInitializableAdapter<T>(
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<T>(injector, key, source, scopedFactory, scoping, putBinding(
provider, injectionPoints)); new ProviderInstanceBindingImpl<T>(
injector, key, source, scopedFactory, scoping, provider, injectionPoints));
return true; return true;
} }
@Override @Override
public Boolean visit(ProviderKeyBinding<? extends T> binding) { public Boolean visit(ProviderKeyBinding<? extends T> binding) {
prepareBinding(); prepareBinding();
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 =
injector, providerKey, source, new BoundProviderFactory<T>(
injector,
providerKey,
source,
injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding)); injector.provisionListenerStore.get((ProviderKeyBinding<T>) binding));
bindingData.addCreationListener(boundProviderFactory); processedBindingData.addCreationListener(boundProviderFactory);
InternalFactory<? extends T> scopedFactory = Scoping.scope( InternalFactory<? extends T> scopedFactory =
key, injector, (InternalFactory<? extends T>) boundProviderFactory, source, scoping); Scoping.scope(
putBinding(new LinkedProviderBindingImpl<T>( key,
injector,
(InternalFactory<? extends T>) boundProviderFactory,
source,
scoping);
putBinding(
new LinkedProviderBindingImpl<T>(
injector, key, source, scopedFactory, scoping, providerKey)); injector, key, source, scopedFactory, scoping, providerKey));
return true; return true;
} }
@ -124,18 +154,44 @@ final class BindingProcessor extends AbstractBindingProcessor {
prepareBinding(); prepareBinding();
Key<? extends T> linkedKey = binding.getLinkedKey(); Key<? extends T> linkedKey = binding.getLinkedKey();
if (key.equals(linkedKey)) { if (key.equals(linkedKey)) {
errors.recursiveBinding(); // TODO: b/168656899 check for transitive recursive binding
errors.recursiveBinding(key, linkedKey);
} }
FactoryProxy<T> factory = new FactoryProxy<T>(injector, key, linkedKey, source); FactoryProxy<T> factory = new FactoryProxy<>(injector, key, linkedKey, source);
bindingData.addCreationListener(factory); processedBindingData.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<T>(injector, key, source, scopedFactory, scoping, linkedKey)); new LinkedBindingImpl<T>(injector, key, source, scopedFactory, scoping, linkedKey));
return true; return true;
} }
/** Handle ProviderMethods specially. */
private Boolean visitInternalProviderInstanceBindingFactory(
InternalProviderInstanceBindingImpl.Factory<T> provider) {
InternalProviderInstanceBindingImpl<T> binding =
new InternalProviderInstanceBindingImpl<T>(
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;
}
@Override @Override
public Boolean visit(UntargettedBinding<? extends T> untargetted) { public Boolean visit(UntargettedBinding<? extends T> untargetted) {
return false; return false;
@ -172,9 +228,14 @@ 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); processedBindingData.addCreationListener(exposedKeyFactory);
putBinding(new ExposedBindingImpl<T>( putBinding(
injector, privateElements.getExposedSource(key), key, exposedKeyFactory, privateElements)); new ExposedBindingImpl<T>(
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

@ -0,0 +1,85 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.inject.Key;
import com.google.inject.spi.ErrorDetail;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.stream.Collectors;
/**
* Error reported by Guice when a key is already bound in one or more child injectors or private
* modules.
*/
final class ChildBindingAlreadySetError extends InternalErrorDetail<ChildBindingAlreadySetError> {
private final Key<?> key;
private final ImmutableList<Object> existingSources;
ChildBindingAlreadySetError(Key<?> key, Iterable<Object> existingSoruces, List<Object> sources) {
super(
ErrorId.CHILD_BINDING_ALREADY_SET,
String.format(
"Unable to create binding for %s because it was already configured on one or more"
+ " child injectors or private modules.",
Messages.convert(key)),
sources,
null);
this.key = key;
// Can't use ImmutableList.toImmutableList here because of b/156759807.
this.existingSources =
ImmutableList.copyOf(
Streams.stream(existingSoruces)
.map(source -> source == null ? "" : source)
.collect(Collectors.toList()));
}
@Override
public boolean isMergeable(ErrorDetail<?> otherError) {
return otherError instanceof ChildBindingAlreadySetError
&& ((ChildBindingAlreadySetError) otherError).key.equals(this.key);
}
@Override
public void formatDetail(List<ErrorDetail<?>> mergeableErrors, Formatter formatter) {
formatter.format("%n%s%n", Messages.bold("Bound at:"));
int index = 1;
for (Object source : existingSources) {
formatter.format("%-2s: ", index++);
if (source.equals("")) {
formatter.format("as a just-in-time binding%n");
} else {
new SourceFormatter(source, formatter, /* omitPreposition= */ true).format();
}
}
List<List<Object>> sourcesList = new ArrayList<>();
sourcesList.add(getSources());
mergeableErrors.forEach(error -> sourcesList.add(error.getSources()));
List<List<Object>> filteredSources =
sourcesList.stream()
.map(this::trimSource)
.filter(list -> !list.isEmpty())
.collect(Collectors.toList());
if (!filteredSources.isEmpty()) {
formatter.format("%n%s%n", Messages.bold("Requested by:"));
for (int i = 0; i < sourcesList.size(); i++) {
ErrorFormatter.formatSources(i + 1, sourcesList.get(i), formatter);
}
}
// TODO(b/151482394): Detect if the key was bound in any PrivateModule and suggest exposing the
// key in those cases.
}
@Override
public ChildBindingAlreadySetError withSources(List<Object> newSources) {
return new ChildBindingAlreadySetError(key, existingSources, newSources);
}
/** Omit the key itself in the source list since the information is redundant. */
private List<Object> trimSource(List<Object> sources) {
return sources.stream().filter(source -> !source.equals(this.key)).collect(Collectors.toList());
}
}

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

@ -1,11 +1,15 @@
package com.google.inject.internal; package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkState;
import static com.google.inject.internal.Annotations.findScopeAnnotation;
import static com.google.inject.internal.GuiceInternal.GUICE_INTERNAL;
import static com.google.inject.spi.Elements.withTrustedSource;
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.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.inject.Binder; import com.google.inject.Binder;
import com.google.inject.ConfigurationException; import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Classes; import com.google.inject.internal.util.Classes;
@ -13,59 +17,70 @@ import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.ConstructorBinding; import com.google.inject.spi.ConstructorBinding;
import com.google.inject.spi.Dependency; import com.google.inject.spi.Dependency;
import com.google.inject.spi.InjectionPoint; import com.google.inject.spi.InjectionPoint;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Set; import java.util.Set;
import static com.google.common.base.Preconditions.checkState;
import static com.google.inject.internal.Annotations.findScopeAnnotation;
final class ConstructorBindingImpl<T> extends BindingImpl<T> 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(
InternalFactory<? extends T> scopedFactory, Scoping scoping, Factory<T> factory, InjectorImpl injector,
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;
this.constructorInjectionPoint = constructorInjectionPoint; this.constructorInjectionPoint = constructorInjectionPoint;
} }
public ConstructorBindingImpl(Key<T> key, Object source, Scoping scoping, public ConstructorBindingImpl(
InjectionPoint constructorInjectionPoint, Set<InjectionPoint> injectionPoints) { Key<T> key,
Object source,
Scoping scoping,
InjectionPoint constructorInjectionPoint,
Set<InjectionPoint> injectionPoints) {
super(source, key, scoping); super(source, key, scoping);
this.factory = new Factory<T>(false, key); this.factory = new Factory<>(false, key);
ConstructionProxy<T> constructionProxy ConstructionProxy<T> constructionProxy =
= new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create(); new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
this.constructorInjectionPoint = constructorInjectionPoint; this.constructorInjectionPoint = constructorInjectionPoint;
factory.constructorInjector = new ConstructorInjector<T>( factory.constructorInjector =
injectionPoints, constructionProxy, null, null); new ConstructorInjector<T>(injectionPoints, constructionProxy, null, null);
} }
/** /**
* @param constructorInjector the constructor to use, or {@code null} to use the default. * @param constructorInjector the constructor to use, or {@code null} to use the default.
* @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should * @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should only
* only succeed if retrieved from a linked binding * succeed if retrieved from a linked binding
*/ */
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key, static <T> ConstructorBindingImpl<T> create(
InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors, InjectorImpl injector,
boolean failIfNotLinked, boolean failIfNotExplicit) Key<T> key,
InjectionPoint constructorInjector,
Object source,
Scoping scoping,
Errors errors,
boolean failIfNotLinked,
boolean atInjectRequired)
throws ErrorsException { throws ErrorsException {
int numErrors = errors.size(); int numErrors = errors.size();
@SuppressWarnings("unchecked") // constructorBinding guarantees type is consistent Class<?> rawType =
Class<? super T> rawType = constructorInjector == null constructorInjector == null
? key.getTypeLiteral().getRawType() ? key.getTypeLiteral().getRawType()
: (Class) constructorInjector.getDeclaringType().getRawType(); : constructorInjector.getDeclaringType().getRawType();
// 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.
@ -78,10 +93,8 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
// Find a constructor annotated @Inject // Find a constructor annotated @Inject
if (constructorInjector == null) { if (constructorInjector == null) {
try { try {
constructorInjector = InjectionPoint.forConstructorOf(key.getTypeLiteral()); constructorInjector =
if (failIfNotExplicit && !hasAtInject((Constructor) constructorInjector.getMember())) { InjectionPoint.forConstructorOf(key.getTypeLiteral(), atInjectRequired);
errors.atInjectRequired(rawType);
}
} catch (ConfigurationException e) { } catch (ConfigurationException e) {
throw errors.merge(e.getErrorMessages()).toException(); throw errors.merge(e.getErrorMessages()).toException();
} }
@ -92,47 +105,36 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
Class<?> annotatedType = constructorInjector.getMember().getDeclaringClass(); Class<?> annotatedType = constructorInjector.getMember().getDeclaringClass();
Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, annotatedType); Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, annotatedType);
if (scopeAnnotation != null) { if (scopeAnnotation != null) {
scoping = Scoping.makeInjectable(Scoping.forAnnotation(scopeAnnotation), scoping =
injector, errors.withSource(rawType)); Scoping.makeInjectable(
Scoping.forAnnotation(scopeAnnotation), injector, errors.withSource(rawType));
} }
} }
errors.throwIfNewErrors(numErrors); errors.throwIfNewErrors(numErrors);
Factory<T> factoryFactory = new Factory<T>(failIfNotLinked, key); Factory<T> factoryFactory = new Factory<>(failIfNotLinked, key);
InternalFactory<? extends T> scopedFactory InternalFactory<? extends T> scopedFactory =
= Scoping.scope(key, injector, factoryFactory, source, scoping); Scoping.scope(key, injector, factoryFactory, source, scoping);
return new ConstructorBindingImpl<T>( return new ConstructorBindingImpl<T>(
injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector); injector, key, source, scopedFactory, scoping, factoryFactory, constructorInjector);
} }
/** @Override
* Returns true if the inject annotation is on the constructor.
*/
private static boolean hasAtInject(Constructor cxtor) {
return cxtor.isAnnotationPresent(Inject.class)
|| cxtor.isAnnotationPresent(javax.inject.Inject.class);
}
@SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type @SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException { public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
factory.constructorInjector = factory.constructorInjector =
(ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors); (ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
factory.provisionCallback = factory.provisionCallback = injector.provisionListenerStore.get(this);
injector.provisionListenerStore.get(this);
} }
/** /** True if this binding has been initialized and is ready for use. */
* True if this binding has been initialized and is ready for use.
*/
boolean isInitialized() { boolean isInitialized() {
return factory.constructorInjector != null; return factory.constructorInjector != null;
} }
/** /** Returns an injection point that can be used to clean up the constructor store. */
* Returns an injection point that can be used to clean up the constructor store.
*/
InjectionPoint getInternalConstructor() { InjectionPoint getInternalConstructor() {
if (factory.constructorInjector != null) { if (factory.constructorInjector != null) {
return factory.constructorInjector.getConstructionProxy().getInjectionPoint(); return factory.constructorInjector.getConstructionProxy().getInjectionPoint();
@ -141,44 +143,48 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
} }
} }
/** /** Returns a set of dependencies that can be iterated over to clean up stray JIT bindings. */
* Returns a set of dependencies that can be iterated over to clean up stray JIT bindings.
*/
Set<Dependency<?>> getInternalDependencies() { Set<Dependency<?>> getInternalDependencies() {
ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder(); ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
if (factory.constructorInjector == null) { if (factory.constructorInjector == null) {
builder.add(constructorInjectionPoint); builder.add(constructorInjectionPoint);
// If the below throws, it's OK -- we just ignore those dependencies, because no one
// could have used them anyway.
try { try {
builder.addAll(InjectionPoint.forInstanceMethodsAndFields(constructorInjectionPoint.getDeclaringType())); builder.addAll(
InjectionPoint.forInstanceMethodsAndFields(
constructorInjectionPoint.getDeclaringType()));
} catch (ConfigurationException ignored) { } catch (ConfigurationException ignored) {
// This is OK -- we just ignore those dependencies, because no one could have used them
// anyway.
} }
} else { } else {
builder.add(getConstructor()) builder.add(getConstructor()).addAll(getInjectableMembers());
.addAll(getInjectableMembers());
} }
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())
.addAll(getInjectableMembers()) .addAll(getInjectableMembers())
.build()); .build());
@ -196,11 +202,17 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint); null, key, getSource(), factory, getScoping(), factory, constructorInjectionPoint);
} }
@Override
@SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree @SuppressWarnings("unchecked") // the raw constructor member and declaring type always agree
public void applyTo(Binder binder) { public void applyTo(Binder binder) {
InjectionPoint constructor = getConstructor(); InjectionPoint constructor = getConstructor();
getScoping().applyTo(binder.withSource(getSource()).bind(getKey()).toConstructor( getScoping()
(Constructor) getConstructor().getMember(), (TypeLiteral) constructor.getDeclaringType())); .applyTo(
withTrustedSource(GUICE_INTERNAL, binder, getSource())
.bind(getKey())
.toConstructor(
(Constructor) getConstructor().getMember(),
(TypeLiteral) constructor.getDeclaringType()));
} }
@Override @Override
@ -240,19 +252,22 @@ final class ConstructorBindingImpl<T> extends BindingImpl<T>
this.key = key; this.key = key;
} }
@Override
@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) {
throw new IllegalStateException("Constructor not ready");
}
if (failIfNotLinked && !linked) { if (!linked && failIfNotLinked) {
throw errors.jitDisabled(key).toException(); 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) {
if (context.getInjectorOptions().disableCircularProxies) {
throw InternalProvisionException.circularDependenciesDisabled(
dependency.getKey().getTypeLiteral().getRawType());
} else {
return t; 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

@ -0,0 +1,127 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableMultimap;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ErrorDetail;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Error reported by Guice when duplicate elements are found in a {@link Multibinder} that does not
* permit duplicates.
*/
final class DuplicateElementError<T> extends InternalErrorDetail<DuplicateElementError<T>> {
private final Key<Set<T>> setKey;
private final ImmutableMultimap<T, Element<T>> elements;
DuplicateElementError(
Key<Set<T>> setKey, List<Binding<T>> bindings, T[] values, List<Object> sources) {
this(setKey, indexElements(bindings, values), sources);
}
private DuplicateElementError(
Key<Set<T>> setKey, ImmutableMultimap<T, Element<T>> elements, List<Object> sources) {
super(
ErrorId.DUPLICATE_ELEMENT,
String.format("Duplicate elements found in Multibinder %s.", Messages.convert(setKey)),
sources,
null);
this.setKey = setKey;
this.elements = elements;
}
@Override
protected void formatDetail(List<ErrorDetail<?>> others, Formatter formatter) {
formatter.format("%n%s%n", Messages.bold("Duplicates:"));
int duplicateIndex = 1;
for (Map.Entry<T, Collection<Element<T>>> entry : elements.asMap().entrySet()) {
formatter.format("%-2s: ", duplicateIndex++);
if (entry.getValue().size() > 1) {
Set<String> valuesAsString =
entry.getValue().stream()
.map(element -> element.value.toString())
.collect(Collectors.toSet());
if (valuesAsString.size() == 1) {
// String representation of the duplicates elements are the same, so only print out one.
formatter.format("Element: %s%n", Messages.redBold(valuesAsString.iterator().next()));
formatter.format(" Bound at:%n");
int index = 1;
for (Element<T> element : entry.getValue()) {
formatter.format(" %-2s: ", index++);
formatElement(element, formatter);
}
} else {
// Print out all elements as string when there are different string representations of the
// elements. To keep the logic simple, same strings are not grouped together unless all
// elements have the same string represnetation. This means some strings may be printed
// out multiple times.
// There is no indentation for the first duplicate element.
boolean indent = false;
for (Element<T> element : entry.getValue()) {
if (indent) {
formatter.format(" ");
} else {
indent = true;
}
formatter.format("Element: %s%n", Messages.redBold(element.value.toString()));
formatter.format(" Bound at: ");
formatElement(element, formatter);
}
}
}
}
formatter.format("%n%s%n", Messages.bold("Multibinder declared at:"));
// Multibinder source includes the key of the set. Filter it out since it's not useful in the
// printed error stack.
List<Object> filteredSource =
getSources().stream()
.filter(
source -> {
if (source instanceof Dependency) {
return !((Dependency<?>) source).getKey().equals(setKey);
}
return true;
})
.collect(Collectors.toList());
ErrorFormatter.formatSources(filteredSource, formatter);
}
private void formatElement(Element<T> element, Formatter formatter) {
Object source = element.binding.getSource();
new SourceFormatter(
source,
formatter,
/** omitPreposition= */
true)
.format();
}
@Override
public DuplicateElementError<T> withSources(List<Object> newSources) {
return new DuplicateElementError<>(setKey, elements, newSources);
}
static <T> ImmutableMultimap<T, Element<T>> indexElements(List<Binding<T>> bindings, T[] values) {
ImmutableMultimap.Builder<T, Element<T>> map = ImmutableMultimap.builder();
for (int i = 0; i < values.length; i++) {
map.put(values[i], new Element<T>(values[i], bindings.get(i)));
}
return map.build();
}
static class Element<T> {
T value;
Binding<T> binding;
Element(T value, Binding<T> binding) {
this.value = value;
this.binding = binding;
}
}
}

View file

@ -0,0 +1,70 @@
package com.google.inject.internal;
import com.google.common.collect.Multimap;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.spi.ErrorDetail;
import java.util.Collection;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Error reported by Guice when a duplicate key is found in a {@link MapBinder} that does not permit
* duplicates.
*/
final class DuplicateMapKeyError<K, V> extends InternalErrorDetail<DuplicateMapKeyError<K, V>> {
private final Key<Map<K, V>> mapKey;
private final Multimap<K, Binding<V>> duplicates;
DuplicateMapKeyError(
Key<Map<K, V>> mapKey, Multimap<K, Binding<V>> duplicates, List<Object> sources) {
super(ErrorId.DUPLICATE_MAP_KEY, getDuplicateKeysMessage(mapKey, duplicates), sources, null);
this.mapKey = mapKey;
this.duplicates = duplicates;
}
@Override
protected final void formatDetail(List<ErrorDetail<?>> others, Formatter formatter) {
formatter.format("%n%s%n", Messages.bold("Duplicates:"));
for (Map.Entry<K, Collection<Binding<V>>> entry : duplicates.asMap().entrySet()) {
formatter.format(" Key: \"%s\"%n", Messages.redBold(entry.getKey().toString()));
formatter.format(" Bound at:%n");
int index = 1;
for (Binding<V> binding : entry.getValue()) {
formatter.format(" %-2s: ", index++);
new SourceFormatter(
binding.getSource(),
formatter,
/** omitPreposition= */
true)
.format();
}
formatter.format("%n");
}
formatter.format("%s%n", Messages.bold("MapBinder declared at:"));
ErrorFormatter.formatSources(getSources(), formatter);
}
@Override
public DuplicateMapKeyError<K, V> withSources(List<Object> newSources) {
return new DuplicateMapKeyError<>(mapKey, duplicates, newSources);
}
private static <K, V> String getDuplicateKeysMessage(
Key<Map<K, V>> mapKey, Multimap<K, Binding<V>> duplicates) {
Set<K> duplicateKeys = duplicates.keySet();
String mapBinderKey = Messages.convert(mapKey).toString();
String firstDuplicateKey = duplicateKeys.iterator().next().toString();
if (duplicateKeys.size() == 1) {
return String.format("Duplicate key \"%s\" found in %s.", firstDuplicateKey, mapBinderKey);
} else {
return String.format(
"\"%s\" and %s other duplicate keys found in %s.",
firstDuplicateKey, duplicateKeys.size() - 1, mapBinderKey);
}
}
}

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

@ -0,0 +1,33 @@
package com.google.inject.internal;
import java.util.Formatter;
import java.util.List;
/** Helper for formatting Guice errors. */
final class ErrorFormatter {
private ErrorFormatter() {}
/**
* Format a list of sources to the given {@code formatter}, prefixed by the give {@code index}.
*/
static void formatSources(int index, List<Object> sources, Formatter formatter) {
for (int i = 0; i < sources.size(); i++) {
Object source = sources.get(i);
if (i == 0) {
formatter.format("%-3s: ", index);
} else {
formatter.format(SourceFormatter.INDENT);
}
new SourceFormatter(source, formatter, i == 0).format();
}
}
/** Format a list of sources to the given {@code formatter}. */
static void formatSources(List<Object> sources, Formatter formatter) {
for (int i = 0; i < sources.size(); i++) {
Object source = sources.get(i);
formatter.format(" ");
new SourceFormatter(source, formatter, i == 0).format();
}
}
}

View file

@ -0,0 +1,65 @@
package com.google.inject.internal;
/** Enum used to identify a specific Guice error. */
public enum ErrorId {
AMBIGUOUS_TYPE_CONVERSION,
AOP_DISABLED,
AT_INJECT_REQUIRED,
AT_TARGET_IS_MISSING_PARAMETER,
BINDING_ALREADY_SET,
BINDING_TO_GUICE_TYPE,
BINDING_TO_PROVIDER,
CAN_NOT_PROXY_CLASS,
CHILD_BINDING_ALREADY_SET,
CIRCULAR_PROXY_DISABLED,
CONSTRUCTOR_NOT_DEFINED_BY_TYPE,
CONVERSION_TYPE_ERROR,
CONVERTER_RETURNED_NULL,
DUPLICATE_BINDING_ANNOTATIONS,
DUPLICATE_ELEMENT,
DUPLICATE_MAP_KEY,
DUPLICATE_SCOPES,
DUPLICATE_SCOPE_ANNOTATIONS,
ERROR_ENHANCING_CLASS,
ERROR_INJECTING_CONSTRUCTOR,
ERROR_INJECTING_METHOD,
ERROR_IN_CUSTOM_PROVIDER,
ERROR_IN_USER_CODE,
ERROR_IN_USER_INJECTOR,
ERROR_NOTIFYING_TYPE_LISTENER,
EXPOSED_BUT_NOT_BOUND,
INJECT_ABSTRACT_METHOD,
INJECT_FINAL_FIELD,
INJECT_INNER_CLASS,
INJECT_METHOD_WITH_TYPE_PARAMETER,
INJECT_RAW_MEMBERS_INJECTOR,
INJECT_RAW_PROVIDER,
INJECT_RAW_TYPE_LITERAL,
JIT_BINDING_ALREADY_SET,
JIT_DISABLED,
JIT_DISABLED_IN_PARENT,
KEY_NOT_FULLY_SPECIFIED,
MISPLACED_BINDING_ANNOTATION,
MISSING_CONSTANT_VALUES,
MISSING_CONSTRUCTOR,
MISSING_IMPLEMENTATION,
MISSING_RUNTIME_RETENTION,
MISSING_SCOPE_ANNOTATION,
NOT_A_SUBTYPE,
NULL_ELEMENT_IN_SET,
NULL_INJECTED_INTO_NON_NULLABLE,
NULL_VALUE_IN_MAP,
OPTIONAL_CONSTRUCTOR,
RECURSIVE_BINDING,
RECURSIVE_IMPLEMENTATION_TYPE,
RECURSIVE_PROVIDER_TYPE,
SCOPE_ANNOTATION_ON_ABSTRACT_TYPE,
SCOPE_NOT_FOUND,
STATIC_INJECTION_ON_INTERFACE,
SUBTYPE_NOT_PROVIDED,
TOO_MANY_CONSTRUCTORS,
VOID_PROVIDER_METHOD,
// All other uncommon type of errors
OTHER;
}

File diff suppressed because it is too large Load diff

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,9 +18,10 @@ 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.getBindingData().getExplicitBinding(key);
// validate that the child injector has its own factory. If the getInternalFactory() returns // validate that the child injector has its own factory. If the getInternalFactory() returns
// this, then that child injector doesn't have a factory (and getExplicitBinding has returned // this, then that child injector doesn't have a factory (and getExplicitBinding has returned
@ -33,8 +34,10 @@ 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) @Override
throws ErrorsException { public T get(InternalContext context, Dependency<?> dependency, boolean linked)
return delegate.getInternalFactory().get(errors, context, dependency, linked); throws InternalProvisionException {
// TODO(lukes): add a source to the thrown exception?
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;
@ -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

@ -0,0 +1,34 @@
package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.inject.spi.ErrorDetail;
import java.io.Serializable;
import java.util.Formatter;
import java.util.List;
/** Generic error message representing a Guice internal error. */
public final class GenericErrorDetail extends InternalErrorDetail<GenericErrorDetail>
implements Serializable {
public GenericErrorDetail(
ErrorId errorId, String message, List<Object> sources, Throwable cause) {
super(errorId, checkNotNull(message, "message"), sources, cause);
}
@Override
public void formatDetail(List<ErrorDetail<?>> mergeableErrors, Formatter formatter) {
Preconditions.checkArgument(mergeableErrors.isEmpty(), "Unexpected mergeable errors");
List<Object> dependencies = getSources();
for (Object source : Lists.reverse(dependencies)) {
formatter.format(" ");
new SourceFormatter(source, formatter, /* omitPreposition= */ false).format();
}
}
@Override
public GenericErrorDetail withSources(List<Object> newSources) {
return new GenericErrorDetail(errorId, getMessage(), newSources, getCause());
}
}

View file

@ -0,0 +1,14 @@
package com.google.inject.internal;
/**
* Class used for restricting APIs in other packages to only be used by this package.
*
* <p>Other packages can reference this class but only this package can reference an instance of it,
* so adding this class as a method param ensures that only this package can call it (provided null
* is disallowed).
*/
public final class GuiceInternal {
static final GuiceInternal GUICE_INTERNAL = new GuiceInternal();
private GuiceInternal() {}
}

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

@ -1,160 +0,0 @@
package com.google.inject.internal;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.Scope;
import com.google.inject.TypeLiteral;
import com.google.inject.spi.ModuleAnnotatedMethodScannerBinding;
import com.google.inject.spi.ProvisionListenerBinding;
import com.google.inject.spi.ScopeBinding;
import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.spi.TypeListenerBinding;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
final class InheritingState implements State {
private final State parent;
// 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<?>> explicitBindings
= Collections.unmodifiableMap(explicitBindingsMutable);
private final Map<Class<? extends Annotation>, ScopeBinding> scopes = Maps.newHashMap();
private final List<TypeConverterBinding> converters = Lists.newArrayList();
private final List<TypeListenerBinding> typeListenerBindings = Lists.newArrayList();
private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList();
private final List<ModuleAnnotatedMethodScannerBinding> scannerBindings = Lists.newArrayList();
private final WeakKeySet blacklistedKeys;
private final Object lock;
InheritingState(State parent) {
this.parent = checkNotNull(parent, "parent");
this.lock = (parent == State.NONE) ? this : parent.lock();
this.blacklistedKeys = new WeakKeySet(lock);
}
public State parent() {
return parent;
}
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
Binding<?> binding = explicitBindings.get(key);
return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key);
}
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
return explicitBindings;
}
public void putBinding(Key<?> key, BindingImpl<?> binding) {
explicitBindingsMutable.put(key, binding);
}
public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
ScopeBinding scopeBinding = scopes.get(annotationType);
return scopeBinding != null ? scopeBinding : parent.getScopeBinding(annotationType);
}
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
scopes.put(annotationType, scope);
}
public Iterable<TypeConverterBinding> getConvertersThisLevel() {
return converters;
}
public void addConverter(TypeConverterBinding typeConverterBinding) {
converters.add(typeConverterBinding);
}
public TypeConverterBinding getConverter(
String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
TypeConverterBinding matchingConverter = null;
for (State s = this; s != State.NONE; s = s.parent()) {
for (TypeConverterBinding converter : s.getConvertersThisLevel()) {
if (converter.getTypeMatcher().matches(type)) {
if (matchingConverter != null) {
errors.ambiguousTypeConversion(stringValue, source, type, matchingConverter, converter);
}
matchingConverter = converter;
}
}
}
return matchingConverter;
}
public void addTypeListener(TypeListenerBinding listenerBinding) {
typeListenerBindings.add(listenerBinding);
}
public List<TypeListenerBinding> getTypeListenerBindings() {
List<TypeListenerBinding> parentBindings = parent.getTypeListenerBindings();
List<TypeListenerBinding> result =
Lists.newArrayListWithCapacity(parentBindings.size() + typeListenerBindings.size());
result.addAll(parentBindings);
result.addAll(typeListenerBindings);
return result;
}
public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
provisionListenerBindings.add(listenerBinding);
}
public List<ProvisionListenerBinding> getProvisionListenerBindings() {
List<ProvisionListenerBinding> parentBindings = parent.getProvisionListenerBindings();
List<ProvisionListenerBinding> result =
Lists.newArrayListWithCapacity(parentBindings.size() + provisionListenerBindings.size());
result.addAll(parentBindings);
result.addAll(provisionListenerBindings);
return result;
}
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
scannerBindings.add(scanner);
}
public List<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
List<ModuleAnnotatedMethodScannerBinding> parentBindings = parent.getScannerBindings();
List<ModuleAnnotatedMethodScannerBinding> result =
Lists.newArrayListWithCapacity(parentBindings.size() + scannerBindings.size());
result.addAll(parentBindings);
result.addAll(scannerBindings);
return result;
}
public void blacklist(Key<?> key, State state, Object source) {
parent.blacklist(key, state, source);
blacklistedKeys.add(key, state, source);
}
public boolean isBlacklisted(Key<?> key) {
return blacklistedKeys.contains(key);
}
public Set<Object> getSourcesForBlacklistedKey(Key<?> key) {
return blacklistedKeys.getSources(key);
}
public Object lock() {
return lock;
}
public Map<Class<? extends Annotation>, Scope> getScopes() {
ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder();
for (Map.Entry<Class<? extends Annotation>, ScopeBinding> entry : scopes.entrySet()) {
builder.put(entry.getKey(), entry.getValue().getScope());
}
return builder.build();
}
}

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()) { private enum InjectableReferenceState {
throw new AssertionError("Failed to satisfy " + pendingInjection); NEW,
VALIDATED,
INJECTING,
READY
} }
ready.countDown(); private static class InjectableReference<T> implements Initializable<T> {
} private volatile InjectableReferenceState state = InjectableReferenceState.NEW;
private volatile MembersInjectorImpl<T> membersInjector = null;
private class InjectableReference<T> implements Initializable<T> {
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.
// Reasoning:
// get() is called either directly from injectAll(), holds no locks and can not create
// a cycle, or it is called through a singleton scope, which resolves deadlocks by itself.
// Before calling get() object has to be requested for injection.
// 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 { try {
ready.await(); // lock acquired, current thread owns this instance initialization
switch (state) {
case READY:
return instance; return instance;
} catch (InterruptedException e) { // When instance depends on itself in the same thread potential dead lock
// Give up, since we don't know if our injection is ready // is not detected. We have to prevent a stack overflow and we use
throw new RuntimeException(e); // an "injecting" stage to short-circuit a call.
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);
} }
}
// toInject needs injection, do it right away. we only do this once, even if it fails
if (pendingInjection.remove(instance) != null) {
// safe because we only insert a members injector for the appropriate instance
@SuppressWarnings("unchecked")
MembersInjectorImpl<T> membersInjector =
(MembersInjectorImpl<T>) pendingMembersInjectors.remove(instance);
Preconditions.checkState(membersInjector != null,
"No membersInjector available for instance: %s, from key: %s", instance, key);
// 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; return instance;
} finally {
// always release our creation lock, even on failures
lock.unlock();
}
} }
@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 (injector.options.stage != Stage.TOOL || memberInjector.getInjectionPoint().isToolable()) { if (!isStageTool || memberInjector.getInjectionPoint().isToolable()) {
memberInjector.inject(errors, context, null); try {
} memberInjector.inject(context, null);
} } catch (InternalProvisionException e) {
return null; errors.merge(e);
} }
}); }
} catch (ErrorsException e) { }
throw new AssertionError();
} }
} }
} }

View file

@ -0,0 +1,231 @@
package com.google.inject.internal;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Binding;
import com.google.inject.Key;
import com.google.inject.Scope;
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.ProviderLookup;
import com.google.inject.spi.ProvisionListenerBinding;
import com.google.inject.spi.ScopeBinding;
import com.google.inject.spi.StaticInjectionRequest;
import com.google.inject.spi.TypeConverterBinding;
import com.google.inject.spi.TypeListenerBinding;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* A container that stores an injector's binding data. This excludes JIT binding data, which is
* stored in {@link InjectorJitBindingData}.
*/
class InjectorBindingData {
// The parent injector's InjectorBindingData, if the parent injector exists.
private final Optional<InjectorBindingData> parent;
// 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<?>> explicitBindings =
Collections.unmodifiableMap(explicitBindingsMutable);
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<TypeListenerBinding> typeListenerBindings = Lists.newArrayList();
private final List<ProvisionListenerBinding> provisionListenerBindings = Lists.newArrayList();
private final List<ModuleAnnotatedMethodScannerBinding> scannerBindings = Lists.newArrayList();
// The injector's explicit bindings, indexed by the binding's type.
private final ListMultimap<TypeLiteral<?>, Binding<?>> indexedExplicitBindings =
ArrayListMultimap.create();
InjectorBindingData(Optional<InjectorBindingData> parent) {
this.parent = parent;
}
public Optional<InjectorBindingData> parent() {
return parent;
}
@SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
Binding<?> binding = explicitBindings.get(key);
if (binding == null && parent.isPresent()) {
return parent.get().getExplicitBinding(key);
}
return (BindingImpl<T>) binding;
}
public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
return explicitBindings;
}
public void putBinding(Key<?> key, BindingImpl<?> binding) {
explicitBindingsMutable.put(key, binding);
}
public void putProviderLookup(ProviderLookup<?> lookup) {
providerLookups.add(lookup);
}
public Set<ProviderLookup<?>> getProviderLookupsThisLevel() {
return providerLookups;
}
public void putStaticInjectionRequest(StaticInjectionRequest staticInjectionRequest) {
staticInjectionRequests.add(staticInjectionRequest);
}
public Set<StaticInjectionRequest> getStaticInjectionRequestsThisLevel() {
return staticInjectionRequests;
}
public void putInjectionRequest(InjectionRequest<?> injectionRequest) {
injectionRequests.add(injectionRequest);
}
public Set<InjectionRequest<?>> getInjectionRequestsThisLevel() {
return injectionRequests;
}
public void putMembersInjectorLookup(MembersInjectorLookup<?> membersInjectorLookup) {
membersInjectorLookups.add(membersInjectorLookup);
}
public Set<MembersInjectorLookup<?>> getMembersInjectorLookupsThisLevel() {
return membersInjectorLookups;
}
public ScopeBinding getScopeBinding(Class<? extends Annotation> annotationType) {
ScopeBinding scopeBinding = scopes.get(annotationType);
if (scopeBinding == null && parent.isPresent()) {
return parent.get().getScopeBinding(annotationType);
}
return scopeBinding;
}
public void putScopeBinding(Class<? extends Annotation> annotationType, ScopeBinding scope) {
scopes.put(annotationType, scope);
}
public Collection<ScopeBinding> getScopeBindingsThisLevel() {
return scopes.values();
}
public Iterable<TypeConverterBinding> getConvertersThisLevel() {
return converters;
}
public void addConverter(TypeConverterBinding typeConverterBinding) {
converters.add(typeConverterBinding);
}
public TypeConverterBinding getConverter(
String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
TypeConverterBinding matchingConverter = null;
InjectorBindingData b = this;
while (b != null) {
for (TypeConverterBinding converter : b.getConvertersThisLevel()) {
if (converter.getTypeMatcher().matches(type)) {
if (matchingConverter != null) {
errors.ambiguousTypeConversion(stringValue, source, type, matchingConverter, converter);
}
matchingConverter = converter;
}
}
b = b.parent().orElse(null);
}
return matchingConverter;
}
public void addTypeListener(TypeListenerBinding listenerBinding) {
typeListenerBindings.add(listenerBinding);
}
public ImmutableList<TypeListenerBinding> getTypeListenerBindings() {
if (parent.isPresent()) {
return new ImmutableList.Builder<TypeListenerBinding>()
.addAll(parent.get().getTypeListenerBindings())
.addAll(typeListenerBindings)
.build();
}
return ImmutableList.copyOf(typeListenerBindings);
}
public ImmutableList<TypeListenerBinding> getTypeListenerBindingsThisLevel() {
return ImmutableList.copyOf(typeListenerBindings);
}
public void addProvisionListener(ProvisionListenerBinding listenerBinding) {
provisionListenerBindings.add(listenerBinding);
}
public ImmutableList<ProvisionListenerBinding> getProvisionListenerBindings() {
if (parent.isPresent()) {
return new ImmutableList.Builder<ProvisionListenerBinding>()
.addAll(parent.get().getProvisionListenerBindings())
.addAll(provisionListenerBindings)
.build();
}
return ImmutableList.copyOf(provisionListenerBindings);
}
public ImmutableList<ProvisionListenerBinding> getProvisionListenerBindingsThisLevel() {
return ImmutableList.copyOf(provisionListenerBindings);
}
public void addScanner(ModuleAnnotatedMethodScannerBinding scanner) {
scannerBindings.add(scanner);
}
public ImmutableList<ModuleAnnotatedMethodScannerBinding> getScannerBindings() {
if (parent.isPresent()) {
return new ImmutableList.Builder<ModuleAnnotatedMethodScannerBinding>()
.addAll(parent.get().getScannerBindings())
.addAll(scannerBindings)
.build();
}
return ImmutableList.copyOf(scannerBindings);
}
public ImmutableList<ModuleAnnotatedMethodScannerBinding> getScannerBindingsThisLevel() {
return ImmutableList.copyOf(scannerBindings);
}
public Map<Class<? extends Annotation>, Scope> getScopes() {
ImmutableMap.Builder<Class<? extends Annotation>, Scope> builder = ImmutableMap.builder();
for (Map.Entry<Class<? extends Annotation>, ScopeBinding> entry : scopes.entrySet()) {
builder.put(entry.getKey(), entry.getValue().getScope());
}
return builder.build();
}
/**
* Once the injector's explicit bindings are finalized, this method is called to index all
* explicit bindings by their return type.
*/
void indexBindingsByType() {
for (Binding<?> binding : getExplicitBindingsThisLevel().values()) {
indexedExplicitBindings.put(binding.getKey().getTypeLiteral(), binding);
}
}
public ListMultimap<TypeLiteral<?>, Binding<?>> getIndexedExplicitBindings() {
return indexedExplicitBindings;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
package com.google.inject.internal;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Key;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* A container for most just-in-time (JIT) binding data corresponding to an Injector. It
* includes @Inject constructor bindings. It does not include {@link MembersInjectorStore} or {@link
* ProvisionListenerCallbackStore}, which are still stored in {@link InjectorImpl}.
*/
final class InjectorJitBindingData {
/** Just-in-time binding cache. Guarded by {@link #lock}. */
private final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
/**
* Cache of Keys that we were unable to create JIT bindings for, so we don't keep trying. Guarded
* by {@link #lock}.
*/
private final Set<Key<?>> failedJitBindings = Sets.newHashSet();
// The set of JIT binding keys that are banned for this particular injector, because a binding
// already exists in a child injector. Guarded by {@link #lock}.
private final WeakKeySet bannedKeys;
// The InjectorJitBindingData corresponding to the Injector's parent, if it exists.
private final Optional<InjectorJitBindingData> parent;
/**
* This lock is needed for threadsafe InjectorJitBindingData accesses. It corresponds to this
* InjectorJitBindingData's highest ancestor.
*/
private final Object lock;
InjectorJitBindingData(Optional<InjectorJitBindingData> parent) {
this.parent = parent;
this.lock = parent.isPresent() ? parent.get().lock() : this;
this.bannedKeys = new WeakKeySet(lock);
}
Map<Key<?>, BindingImpl<?>> getJitBindings() {
return Collections.unmodifiableMap(jitBindings);
}
BindingImpl<?> getJitBinding(Key<?> key) {
return jitBindings.get(key);
}
void putJitBinding(Key<?> key, BindingImpl<?> binding) {
jitBindings.put(key, binding);
}
void removeJitBinding(Key<?> key) {
jitBindings.remove(key);
}
boolean isFailedJitBinding(Key<?> key) {
return failedJitBindings.contains(key);
}
void addFailedJitBinding(Key<?> key) {
failedJitBindings.add(key);
}
/**
* Forbids the corresponding injector and its ancestors from creating a binding to {@code key}.
* Child injectors ban their bound keys on their parent injectors to prevent just-in-time bindings
* on the parent injector that would conflict, and pass along their InjectorBindingData to control
* the banned key's lifetime.
*/
void banKey(Key<?> key, InjectorBindingData injectorBindingData, Object source) {
banKeyInParent(key, injectorBindingData, source);
bannedKeys.add(key, injectorBindingData, source);
}
/**
* Similar to {@link #banKey(Key, InjectorBindingData, Object)} but we only begin banning the
* binding at the parent level. This is used to prevent JIT bindings in the parent injector from
* overriding explicit bindings declared in a child injector.
*/
void banKeyInParent(Key<?> key, InjectorBindingData injectorBindingData, Object source) {
if (parent.isPresent()) {
parent.get().banKey(key, injectorBindingData, source);
}
}
/**
* Returns true if {@code key} is forbidden from being bound in the injector corresponding to this
* data object. This indicates that one of the injector's children has bound the key.
*/
boolean isBannedKey(Key<?> key) {
return bannedKeys.contains(key);
}
/** Returns the source of a banned key. */
Set<Object> getSourcesForBannedKey(Key<?> key) {
return bannedKeys.getSources(key);
}
Object lock() {
return lock;
}
}

View file

@ -1,5 +1,9 @@
package com.google.inject.internal; package com.google.inject.internal;
import static com.google.common.base.Preconditions.checkState;
import static com.google.inject.Scopes.SINGLETON;
import static com.google.inject.internal.GuiceInternal.GUICE_INTERNAL;
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.inject.Binder; import com.google.inject.Binder;
@ -10,8 +14,9 @@ import com.google.inject.Provider;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.Stage; import com.google.inject.Stage;
import com.google.inject.internal.InjectorImpl.InjectorOptions; import com.google.inject.internal.InjectorImpl.InjectorOptions;
import com.google.inject.internal.util.ContinuousStopwatch;
import com.google.inject.internal.util.SourceProvider; import com.google.inject.internal.util.SourceProvider;
import com.google.inject.internal.util.Stopwatch; import com.google.inject.spi.BindingSourceRestriction;
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;
@ -20,64 +25,34 @@ 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;
import com.google.inject.spi.TypeListenerBinding; import com.google.inject.spi.TypeListenerBinding;
import java.util.List; import java.util.List;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkState; import java.util.logging.Logger;
import static com.google.inject.Scopes.SINGLETON;
/** /**
* A partially-initialized injector. See {@link InternalInjectorCreator}, which * InjectorShell is used by {@link InternalInjectorCreator} to recursively create a tree of
* uses this to build a tree of injectors in batch. * uninitialized {@link Injector}s. Each InjectorShell corresponds to either the top-level root
* injector, or a private child injector.
*
* <p>The root InjectorShell extracts elements from its list of modules and processes these elements
* to aggregate data that is used to populate its injector's fields. Child injectors are constructed
* similarly, but using {@link PrivateElements} instead of modules.
*
* <p>It is necessary to create the root and child injectors in a single batch because there can be
* bidirectional parent <-> child injector dependencies that require the entire tree of injectors to
* be initialized together in the {@link InternalInjectorCreator}.
*
*/ */
final class InjectorShell { 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;
} }
/**
* The Injector is a special case because we allow both parent and child injectors to both have
* a binding for that key.
*/
private static void bindInjector(InjectorImpl injector) {
Key<Injector> key = Key.get(Injector.class);
InjectorFactory injectorFactory = new InjectorFactory(injector);
injector.state.putBinding(key,
new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE,
injectorFactory, Scoping.UNSCOPED, injectorFactory,
ImmutableSet.<InjectionPoint>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) {
Key<Stage> key = Key.get(Stage.class);
InstanceBindingImpl<Stage> stageBinding = new InstanceBindingImpl<Stage>(
injector,
key,
SourceProvider.UNKNOWN_SOURCE,
new ConstantFactory<Stage>(Initializables.of(stage)),
ImmutableSet.<InjectionPoint>of(),
stage);
injector.state.putBinding(key, stageBinding);
}
InjectorImpl getInjector() { InjectorImpl getInjector() {
return injector; return injector;
} }
@ -90,18 +65,15 @@ final class InjectorShell {
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();
/** // lazily constructed fields
* lazily constructed private InjectorBindingData bindingData;
*/ private InjectorJitBindingData jitBindingData;
private State state;
private InjectorImpl parent; private InjectorImpl parent;
private InjectorOptions options; private InjectorOptions options;
private Stage stage; private Stage stage;
/** /** null unless this exists in a {@link Binder#newPrivateBinder private environment} */
* null unless this exists in a {@link Binder#newPrivateBinder private environment}
*/
private PrivateElementsImpl privateElements; private PrivateElementsImpl privateElements;
Builder stage(Stage stage) { Builder stage(Stage stage) {
@ -111,7 +83,8 @@ final class InjectorShell {
Builder parent(InjectorImpl parent) { Builder parent(InjectorImpl parent) {
this.parent = parent; this.parent = parent;
this.state = new InheritingState(parent.state); this.jitBindingData = new InjectorJitBindingData(Optional.of(parent.getJitBindingData()));
this.bindingData = new InjectorBindingData(Optional.of(parent.getBindingData()));
this.options = parent.options; this.options = parent.options;
this.stage = options.stage; this.stage = options.stage;
return this; return this;
@ -124,22 +97,24 @@ final class InjectorShell {
} }
void addModules(Iterable<? extends Module> modules) { void addModules(Iterable<? extends Module> modules) {
if (modules != null) {
for (Module module : modules) { for (Module module : modules) {
this.modules.add(module); this.modules.add(module);
} }
} }
}
Stage getStage() { Stage getStage() {
return options.stage; return options.stage;
} }
/** /** Synchronize on this before calling {@link #build}. */
* Synchronize on this before calling {@link #build}.
*/
Object lock() { Object lock() {
return getState().lock(); // Lazily initializes bindingData and jitBindingData, if they were not already
// initialized with a parent injector by {@link #parent(InjectorImpl)}.
if (bindingData == null) {
jitBindingData = new InjectorJitBindingData(Optional.empty());
bindingData = new InjectorBindingData(Optional.empty());
}
return jitBindingData.lock();
} }
/** /**
@ -149,27 +124,38 @@ final class InjectorShell {
*/ */
List<InjectorShell> build( List<InjectorShell> build(
Initializer initializer, Initializer initializer,
ProcessedBindingData bindingData, ProcessedBindingData processedBindingData,
Stopwatch stopwatch, ContinuousStopwatch stopwatch,
Errors errors) { Errors errors) {
checkState(stage != null, "Stage not initialized"); checkState(stage != null, "Stage not initialized");
checkState(privateElements == null || parent != null, "PrivateElements with no parent"); checkState(privateElements == null || parent != null, "PrivateElements with no parent");
checkState(state != null, "no state. Did you remember to lock() ?"); checkState(bindingData != null, "no binding data. Did you remember to lock() ?");
checkState(
(privateElements == null && elements.isEmpty()) || modules.isEmpty(),
"The shell is either built from modules (root) or from PrivateElements (children).");
// bind Singleton if this is a top-level injector // bind Singleton if this is a top-level injector
if (parent == null) { if (parent == null) {
modules.add(0, new RootModule()); modules.add(0, new RootModule());
} else { } else {
modules.add(0, new InheritedScannersModule(parent.state)); modules.add(0, new InheritedScannersModule(parent.getBindingData()));
} }
elements.addAll(Elements.getElements(stage, modules)); elements.addAll(Elements.getElements(stage, modules));
// Check binding source restrictions only for the root shell (note that the root shell
// can have a parent Injector, when Injector.createChildInjector is called). It isn't
// necessary to call this check on child PrivateElements shells because it walks the entire
// tree of elements, recurring on PrivateElements.
if (privateElements == null) {
elements.addAll(BindingSourceRestriction.check(GUICE_INTERNAL, elements));
}
// Look for injector-changing options // Look for injector-changing options
InjectorOptionsProcessor optionsProcessor = new InjectorOptionsProcessor(errors); InjectorOptionsProcessor optionsProcessor = new InjectorOptionsProcessor(errors);
optionsProcessor.process(null, elements); optionsProcessor.process(null, elements);
options = optionsProcessor.getOptions(stage, options); options = optionsProcessor.getOptions(stage, options);
InjectorImpl injector = new InjectorImpl(parent, state, options); InjectorImpl injector = new InjectorImpl(parent, bindingData, jitBindingData, options);
if (privateElements != null) { if (privateElements != null) {
privateElements.initInjector(injector); privateElements.initInjector(injector);
} }
@ -184,10 +170,11 @@ final class InjectorShell {
new MessageProcessor(errors).process(injector, elements); new MessageProcessor(errors).process(injector, elements);
new ListenerBindingProcessor(errors).process(injector, elements); new ListenerBindingProcessor(errors).process(injector, elements);
List<TypeListenerBinding> typeListenerBindings = injector.state.getTypeListenerBindings(); List<TypeListenerBinding> typeListenerBindings =
injector.getBindingData().getTypeListenerBindings();
injector.membersInjectorStore = new MembersInjectorStore(injector, typeListenerBindings); injector.membersInjectorStore = new MembersInjectorStore(injector, typeListenerBindings);
List<ProvisionListenerBinding> provisionListenerBindings = List<ProvisionListenerBinding> provisionListenerBindings =
injector.state.getProvisionListenerBindings(); injector.getBindingData().getProvisionListenerBindings();
injector.provisionListenerStore = injector.provisionListenerStore =
new ProvisionListenerCallbackStore(provisionListenerBindings); new ProvisionListenerCallbackStore(provisionListenerBindings);
stopwatch.resetAndLog("TypeListeners & ProvisionListener creation"); stopwatch.resetAndLog("TypeListeners & ProvisionListener creation");
@ -200,38 +187,53 @@ final class InjectorShell {
bindStage(injector, stage); bindStage(injector, stage);
bindInjector(injector); bindInjector(injector);
//bindLogger(injector); bindLogger(injector);
// Process all normal bindings, then UntargettedBindings. // Process all normal bindings, then UntargettedBindings.
// This is necessary because UntargettedBindings can create JIT bindings // This is necessary because UntargettedBindings can create JIT bindings
// and need all their other dependencies set up ahead of time. // and need all their other dependencies set up ahead of time.
new BindingProcessor(errors, initializer, bindingData).process(injector, elements); new BindingProcessor(errors, initializer, processedBindingData).process(injector, elements);
new UntargettedBindingProcessor(errors, bindingData).process(injector, elements); new UntargettedBindingProcessor(errors, processedBindingData).process(injector, elements);
stopwatch.resetAndLog("Binding creation"); stopwatch.resetAndLog("Binding creation");
new ModuleAnnotatedMethodScannerProcessor(errors).process(injector, elements); new ModuleAnnotatedMethodScannerProcessor(errors).process(injector, elements);
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);
processor.process(injector, elements); processor.process(injector, elements);
for (Builder builder : processor.getInjectorShellBuilders()) { for (Builder builder : processor.getInjectorShellBuilders()) {
injectorShells.addAll(builder.build(initializer, bindingData, stopwatch, errors)); injectorShells.addAll(builder.build(initializer, processedBindingData, stopwatch, errors));
} }
stopwatch.resetAndLog("Private environment creation"); stopwatch.resetAndLog("Private environment creation");
return injectorShells; return injectorShells;
} }
private State getState() {
if (state == null) {
state = new InheritingState(State.NONE);
}
return state;
} }
/**
* The Injector is a special case because we allow both parent and child injectors to both have a
* binding for that key.
*/
private static void bindInjector(InjectorImpl injector) {
Key<Injector> key = Key.get(Injector.class);
InjectorFactory injectorFactory = new InjectorFactory(injector);
injector
.getBindingData()
.putBinding(
key,
new ProviderInstanceBindingImpl<Injector>(
injector,
key,
SourceProvider.UNKNOWN_SOURCE,
injectorFactory,
Scoping.UNSCOPED,
injectorFactory,
ImmutableSet.<InjectionPoint>of()));
} }
private static class InjectorFactory implements InternalFactory<Injector>, Provider<Injector> { private static class InjectorFactory implements InternalFactory<Injector>, Provider<Injector> {
@ -241,38 +243,78 @@ 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;
} }
@Override
public String toString() { public String toString() {
return "Provider<Injector>"; return "Provider<Injector>";
} }
} }
/*private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> { /**
public Logger get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) { * 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
.getBindingData()
.putBinding(
key,
new ProviderInstanceBindingImpl<Logger>(
injector,
key,
SourceProvider.UNKNOWN_SOURCE,
loggerFactory,
Scoping.UNSCOPED,
loggerFactory,
ImmutableSet.<InjectionPoint>of()));
}
private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
@Override
public Logger get(InternalContext context, Dependency<?> dependency, boolean linked) {
InjectionPoint injectionPoint = dependency.getInjectionPoint(); InjectionPoint injectionPoint = dependency.getInjectionPoint();
return injectionPoint == null return injectionPoint == null
? Logger.getAnonymousLogger() ? Logger.getAnonymousLogger()
: Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); : Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
} }
@Override
public Logger get() { public Logger get() {
return Logger.getAnonymousLogger(); return Logger.getAnonymousLogger();
} }
@Override
public String toString() { public String toString() {
return "Provider<Logger>"; return "Provider<Logger>";
} }
}*/ }
private static void bindStage(InjectorImpl injector, Stage stage) {
Key<Stage> key = Key.get(Stage.class);
InstanceBindingImpl<Stage> stageBinding =
new InstanceBindingImpl<Stage>(
injector,
key,
SourceProvider.UNKNOWN_SOURCE,
new ConstantFactory<Stage>(Initializables.of(stage)),
ImmutableSet.<InjectionPoint>of(),
stage);
injector.getBindingData().putBinding(key, stageBinding);
}
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);
@ -281,14 +323,15 @@ final class InjectorShell {
} }
private static class InheritedScannersModule implements Module { private static class InheritedScannersModule implements Module {
private final State state; private final InjectorBindingData bindingData;
InheritedScannersModule(State state) { InheritedScannersModule(InjectorBindingData bindingData) {
this.state = state; this.bindingData = bindingData;
} }
@Override
public void configure(Binder binder) { public void configure(Binder binder) {
for (ModuleAnnotatedMethodScannerBinding binding : state.getScannerBindings()) { for (ModuleAnnotatedMethodScannerBinding binding : bindingData.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);

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