@@ -10,6 +10,8 @@ import com.github.jengelman.gradle.plugins.shadow.util.noOpDelegate
1010import java.io.File
1111import java.lang.classfile.Attributes
1212import java.lang.classfile.ClassFile
13+ import java.lang.classfile.instruction.InvokeInstruction
14+ import java.lang.classfile.instruction.TypeCheckInstruction
1315import java.nio.file.Path
1416import kotlin.io.path.copyTo
1517import kotlin.io.path.createParentDirectories
@@ -150,6 +152,76 @@ class BytecodeRemappingTest {
150152 assertThat(methodDescriptors).contains(" (BL$relocatedFixtureBase ;)L$relocatedFixtureBase ;" )
151153 }
152154
155+ @Test
156+ fun stringConstantIsRelocated () {
157+ val result = fixtureSubjectDetails.remapClass(relocators)
158+
159+ val classModel = ClassFile .of().parse(result)
160+ // Find the constant string in the bytecode.
161+ val stringConstants =
162+ classModel.constantPool().mapNotNull { entry ->
163+ if (entry is java.lang.classfile.constantpool.StringEntry ) entry.stringValue() else null
164+ }
165+ assertThat(stringConstants).contains(" com.example.relocated.BytecodeRemappingTest\$ FixtureBase" )
166+ }
167+
168+ @Test
169+ fun interfaceIsRelocated () {
170+ val result = fixtureSubjectDetails.remapClass(relocators)
171+
172+ val classModel = ClassFile .of().parse(result)
173+ val interfaces = classModel.interfaces().map { it.asInternalName() }
174+ assertThat(interfaces)
175+ .contains($$" com/example/relocated/BytecodeRemappingTest$FixtureInterface " )
176+ }
177+
178+ @Test
179+ fun signatureIsRelocated () {
180+ val result = fixtureSubjectDetails.remapClass(relocators)
181+
182+ val classModel = ClassFile .of().parse(result)
183+ val method = classModel.methods().first { it.methodName().stringValue() == " methodWithGeneric" }
184+ val signatureAttr = method.findAttribute(Attributes .signature())
185+ assertThat(signatureAttr.isPresent).isTrue()
186+ val sig = signatureAttr.get().signature().stringValue()
187+ assertThat(sig).contains(" L$relocatedFixtureBase ;" )
188+ }
189+
190+ @Test
191+ fun localVariableIsRelocated () {
192+ val result = fixtureSubjectDetails.remapClass(relocators)
193+
194+ val classModel = ClassFile .of().parse(result)
195+ val method = classModel.methods().first { it.methodName().stringValue() == " method" }
196+ val code = method.code().get()
197+ val lvt = code.findAttribute(Attributes .localVariableTable())
198+ assertThat(lvt.isPresent).isTrue()
199+ val descriptors = lvt.get().localVariables().map { it.type().stringValue() }
200+ assertThat(descriptors).contains(" L$relocatedFixtureBase ;" )
201+ }
202+
203+ @Test
204+ fun instructionIsRelocated () {
205+ val result = fixtureSubjectDetails.remapClass(relocators)
206+
207+ val classModel = ClassFile .of().parse(result)
208+ val method =
209+ classModel.methods().first { it.methodName().stringValue() == " methodWithCheckCast" }
210+ val code = method.code().get()
211+
212+ val hasRelocatedCheckCast =
213+ code.elementStream().anyMatch { element ->
214+ element is TypeCheckInstruction && element.type().asInternalName() == relocatedFixtureBase
215+ }
216+ assertThat(hasRelocatedCheckCast).isTrue()
217+
218+ val hasRelocatedInvoke =
219+ code.elementStream().anyMatch { element ->
220+ element is InvokeInstruction && element.owner().asInternalName() == relocatedFixtureBase
221+ }
222+ assertThat(hasRelocatedInvoke).isTrue()
223+ }
224+
153225 private fun KClass <* >.toFileCopyDetails () =
154226 object : FileCopyDetails by noOpDelegate() {
155227 private val _path = java.name.replace(' .' , ' /' ) + " .class"
@@ -172,21 +244,32 @@ class BytecodeRemappingTest {
172244
173245 @Retention(AnnotationRetention .RUNTIME )
174246 @Target(AnnotationTarget .CLASS )
175- private annotation class FixtureAnnotation
247+ annotation class FixtureAnnotation
176248
177- private open class FixtureBase
249+ interface FixtureInterface
250+
251+ open class FixtureBase
178252
179253 @Suppress(" unused" ) // Used by parsing bytecode.
180254 @FixtureAnnotation
181- private class FixtureSubject : FixtureBase () {
255+ class FixtureSubject : FixtureBase (), FixtureInterface {
182256 val field: FixtureBase = FixtureBase ()
183257 val arrayField: Array <FixtureBase > = emptyArray()
184258 val array2dField: Array <Array <FixtureBase >> = emptyArray()
259+ val stringConstant: String =
260+ $$" com.github.jengelman.gradle.plugins.shadow.internal.BytecodeRemappingTest$FixtureBase "
185261
186262 fun method (arg : FixtureBase ): FixtureBase = arg
187263
188264 fun methodMultiArgs (a : FixtureBase , b : FixtureBase ): FixtureBase = a
189265
190266 fun methodWithPrimitivePlusClass (b : Byte , arg : FixtureBase ): FixtureBase = arg
267+
268+ fun methodWithCheckCast (arg : Any ): FixtureBase {
269+ (arg as FixtureBase ).toString()
270+ return arg
271+ }
272+
273+ fun methodWithGeneric (list : List <FixtureBase >): FixtureBase = list[0 ]
191274 }
192275}
0 commit comments