Skip to content

Commit

Permalink
Update Signature attribute in ClassFile when an @androidentrypoint ha…
Browse files Browse the repository at this point in the history
…s parameterized types.

When an Android entry point has a superclass that uses parameterized types then the Class file will contain a Signature attribute that also has to be updated since the super_class is changed via Hilt's transform. The attribute is not checked by the JVM during linking, which is why it had not caused any issues before, but it does affected certain reflective APIs. See: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9

Fixes: #3094
RELNOTES=Fix an issue where Hilt transform was not correctly updating the Signature attribute of an @androidentrypoint whose superclass contained a type variable.
PiperOrigin-RevId: 414441547
  • Loading branch information
danysantiago authored and Dagger Team committed Dec 6, 2021
1 parent 79e1c74 commit 73809a0
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ class AndroidEntryPointClassVisitor(
newSuperclassName =
packageName + "/Hilt_" + className.replace("$", "_")
oldSuperclassName = superName ?: error { "Superclass of $name is null!" }
super.visit(version, access, name, signature, newSuperclassName, interfaces)
val newSignature = signature?.replace(oldSuperclassName, newSuperclassName)
super.visit(version, access, name, newSignature, newSuperclassName, interfaces)
}

override fun visitMethod(
Expand Down
54 changes: 54 additions & 0 deletions java/dagger/hilt/android/plugin/src/test/kotlin/TransformTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import java.io.DataInputStream
import java.io.FileInputStream
import javassist.bytecode.ByteArray
import javassist.bytecode.ClassFile
import javassist.bytecode.SignatureAttribute
import org.gradle.testkit.runner.TaskOutcome
import org.junit.Assert
import org.junit.Before
Expand Down Expand Up @@ -306,6 +307,59 @@ class TransformTest {
assertThat(assembleTask.outcome).isEqualTo(TaskOutcome.SUCCESS)
}

// Verifies the Signature attribute in the ClassFile is updated when the superclass uses type
// variables or parameterized types.
// See: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9
@Test
fun testTransform_genericSuperclass() {
gradleRunner.addDependencies(
"implementation 'androidx.appcompat:appcompat:1.1.0'",
"implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'",
"annotationProcessor 'com.google.dagger:hilt-compiler:LOCAL-SNAPSHOT'"
)

gradleRunner.addSrc(
srcPath = "minimal/BaseActivity.java",
srcContent =
"""
package minimal;
import androidx.appcompat.app.AppCompatActivity;
public abstract class BaseActivity<T> extends AppCompatActivity {
}
""".trimIndent()
)

gradleRunner.addSrc(
srcPath = "minimal/SimpleActivity.java",
srcContent =
"""
package minimal;
@dagger.hilt.android.AndroidEntryPoint
public class SimpleActivity extends BaseActivity<String> {
}
""".trimIndent()
)

val result = gradleRunner.build()
val assembleTask = result.getTask(":assembleDebug")
Assert.assertEquals(TaskOutcome.SUCCESS, assembleTask.outcome)

val transformedClass = result.getTransformedFile("minimal/SimpleActivity.class")
FileInputStream(transformedClass).use { fileInput ->
ClassFile(DataInputStream(fileInput)).let { classFile ->
Assert.assertEquals("minimal.Hilt_SimpleActivity", classFile.superclass)
val signatureAttr = classFile.getAttribute(SignatureAttribute.tag) as SignatureAttribute
Assert.assertEquals(
"Lminimal/Hilt_SimpleActivity<Ljava/lang/String;>;",
signatureAttr.signature
)
}
}
}

companion object {
const val TRANSFORM_TASK_NAME =
":transformDebugClassesWithAsm"
Expand Down

0 comments on commit 73809a0

Please sign in to comment.