11package com.github.jengelman.gradle.plugins.shadow.internal
22
33import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator
4- import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass
54import com.github.jengelman.gradle.plugins.shadow.relocation.relocatePath
6- import java.util.regex.Pattern
5+ import java.util.zip.ZipException
6+ import org.apache.tools.zip.UnixStat
7+ import org.apache.tools.zip.ZipOutputStream
8+ import org.gradle.api.GradleException
9+ import org.gradle.api.file.FileCopyDetails
10+ import org.gradle.api.logging.Logger
11+ import org.vafer.jdeb.shaded.objectweb.asm.ClassReader
12+ import org.vafer.jdeb.shaded.objectweb.asm.ClassWriter
713import org.vafer.jdeb.shaded.objectweb.asm.Opcodes
14+ import org.vafer.jdeb.shaded.objectweb.asm.commons.ClassRemapper
815import org.vafer.jdeb.shaded.objectweb.asm.commons.Remapper
916
17+ /* *
18+ * Applies remapping to the given class with the specified relocation path. The remapped class is
19+ * then written to the zip file.
20+ */
21+ internal fun FileCopyDetails.remapClass (
22+ relocators : Set <Relocator >,
23+ zipOutStr : ZipOutputStream ,
24+ preserveFileTimestamps : Boolean ,
25+ lastModified : Long ,
26+ logger : Logger ,
27+ ) =
28+ file.readBytes().let { bytes ->
29+ var modified = false
30+ val remapper = RelocatorRemapper (relocators) { modified = true }
31+
32+ // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool.
33+ // Copying the original constant pool should be avoided because it would keep references to the
34+ // original class names. This is not a problem at runtime (because these entries in the constant
35+ // pool are never used), but confuses some tools such as Felix's maven-bundle-plugin that use
36+ // the constant pool to determine the dependencies of a class.
37+ val cw = ClassWriter (0 )
38+ val cr = ClassReader (bytes)
39+ val cv = ClassRemapper (cw, remapper)
40+
41+ try {
42+ cr.accept(cv, ClassReader .EXPAND_FRAMES )
43+ } catch (t: Throwable ) {
44+ throw GradleException (" Error in ASM processing class $path " , t)
45+ }
46+
47+ // If we didn't need to change anything, keep the original bytes as-is.
48+ val newBytes = if (modified) cw.toByteArray() else bytes
49+
50+ // Temporarily remove the multi-release prefix.
51+ val multiReleasePrefix = " ^META-INF/versions/\\ d+/" .toRegex().find(path)?.value.orEmpty()
52+ val newPath = path.replace(multiReleasePrefix, " " )
53+ val relocatedPath = multiReleasePrefix + relocators.relocatePath(newPath)
54+ try {
55+ val entry =
56+ zipEntry(relocatedPath, preserveFileTimestamps, lastModified) {
57+ unixMode = UnixStat .FILE_FLAG or permissions.toUnixNumeric()
58+ }
59+ // Now we put it back on so the class file is written out with the right extension.
60+ zipOutStr.putNextEntry(entry)
61+ zipOutStr.write(newBytes)
62+ zipOutStr.closeEntry()
63+ } catch (_: ZipException ) {
64+ logger.warn(" We have a duplicate $relocatedPath in source project" )
65+ }
66+ }
67+
1068/* *
1169 * Modified from
1270 * [org.apache.maven.plugins.shade.DefaultShader.RelocatorRemapper](https://github.com/apache/maven-shade-plugin/blob/83c123d1f9c5f6927af2aca12ee322b5168a7c63/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java#L689-L772).
@@ -15,58 +73,19 @@ import org.vafer.jdeb.shaded.objectweb.asm.commons.Remapper
1573 *
1674 * @author John Engelman
1775 */
18- internal class RelocatorRemapper (
76+ private class RelocatorRemapper (
1977 private val relocators : Set <Relocator >,
20- private val onModified : () -> Unit = {} ,
78+ private val onModified : () -> Unit ,
2179) : Remapper(Opcodes .ASM9 ) {
2280
2381 override fun mapValue (value : Any ): Any {
2482 return if (value is String ) {
25- mapName(value, mapLiterals = true )
83+ relocators. mapName(name = value, mapLiterals = true , onModified = onModified )
2684 } else {
2785 super .mapValue(value)
2886 }
2987 }
3088
31- override fun map (internalName : String ): String = mapName(internalName)
32-
33- private fun mapName (name : String , mapLiterals : Boolean = false): String {
34- // Maybe a list of types.
35- val newName = name.split(' ;' ).joinToString(" ;" ) { mapNameImpl(it, mapLiterals) }
36-
37- if (newName != name) {
38- onModified()
39- }
40- return newName
41- }
42-
43- private fun mapNameImpl (name : String , mapLiterals : Boolean ): String {
44- var newName = name
45- var prefix = " "
46- var suffix = " "
47-
48- val matcher = classPattern.matcher(newName)
49- if (matcher.matches()) {
50- prefix = matcher.group(1 ) + " L"
51- suffix = " "
52- newName = matcher.group(2 )
53- }
54-
55- for (relocator in relocators) {
56- if (mapLiterals && relocator.skipStringConstants) {
57- return name
58- } else if (relocator.canRelocateClass(newName)) {
59- return prefix + relocator.relocateClass(newName) + suffix
60- } else if (relocator.canRelocatePath(newName)) {
61- return prefix + relocator.relocatePath(newName) + suffix
62- }
63- }
64-
65- return name
66- }
67-
68- private companion object {
69- /* * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html */
70- val classPattern: Pattern = Pattern .compile(" ([\\ [()BCDFIJSZ]*)?L([^;]+);?" )
71- }
89+ override fun map (internalName : String ): String =
90+ relocators.mapName(name = internalName, onModified = onModified)
7291}
0 commit comments