mirror of
https://github.com/revanced/revanced-patcher.git
synced 2025-05-03 14:14:26 +02:00
fix: give ClassWriter a ClassReader for symtable
removed SafeClassWriter as it was unused
This commit is contained in:
parent
8161ce4fa6
commit
41749ba829
@ -1,6 +1,5 @@
|
|||||||
package app.revanced.patcher.util
|
package app.revanced.patcher.util
|
||||||
|
|
||||||
import app.revanced.patcher.writer.SafeClassWriter
|
|
||||||
import org.objectweb.asm.ClassReader
|
import org.objectweb.asm.ClassReader
|
||||||
import org.objectweb.asm.ClassWriter
|
import org.objectweb.asm.ClassWriter
|
||||||
import org.objectweb.asm.tree.ClassNode
|
import org.objectweb.asm.tree.ClassNode
|
||||||
@ -50,21 +49,21 @@ internal class Io(
|
|||||||
fun saveAsJar() {
|
fun saveAsJar() {
|
||||||
val jis = ZipInputStream(bufferedInputStream)
|
val jis = ZipInputStream(bufferedInputStream)
|
||||||
val jos = ZipOutputStream(output)
|
val jos = ZipOutputStream(output)
|
||||||
|
val classReaders = mutableMapOf<String, ClassReader>()
|
||||||
|
|
||||||
// first write all non .class zip entries from the original input stream to the output stream
|
// first write all non .class zip entries from the original input stream to the output stream
|
||||||
// we read it first to close the input stream as fast as possible
|
// we read it first to close the input stream as fast as possible
|
||||||
// TODO(oSumAtrIX): There is currently no way to remove non .class files.
|
// TODO(oSumAtrIX): There is currently no way to remove non .class files.
|
||||||
lateinit var zipEntry: ZipEntry
|
lateinit var zipEntry: ZipEntry
|
||||||
while (jis.nextEntry.also { if (it != null) zipEntry = it } != null) {
|
while (jis.nextEntry.also { if (it != null) zipEntry = it } != null) {
|
||||||
// skip all class files because we added them in the loop above
|
if (zipEntry.name.endsWith(".class")) {
|
||||||
// TODO(oSumAtrIX): Check for zipEntry.isDirectory
|
classReaders[zipEntry.name] = ClassReader(jis.readBytes())
|
||||||
if (zipEntry.name.endsWith(".class")) continue
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// create a new zipEntry and write the contents of the zipEntry to the output stream
|
// create a new zipEntry and write the contents of the zipEntry to the output stream and close it
|
||||||
jos.putNextEntry(ZipEntry(zipEntry))
|
jos.putNextEntry(ZipEntry(zipEntry))
|
||||||
jos.write(jis.readBytes())
|
jos.write(jis.readBytes())
|
||||||
|
|
||||||
// close the newly created zipEntry
|
|
||||||
jos.closeEntry()
|
jos.closeEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,10 +75,12 @@ internal class Io(
|
|||||||
// now write all the patched classes to the output stream
|
// now write all the patched classes to the output stream
|
||||||
for (patchedClass in classes) {
|
for (patchedClass in classes) {
|
||||||
// create a new entry of the patched class
|
// create a new entry of the patched class
|
||||||
jos.putNextEntry(JarEntry(patchedClass.name + ".class"))
|
val name = patchedClass.name + ".class"
|
||||||
|
jos.putNextEntry(JarEntry(name))
|
||||||
|
|
||||||
// parse the patched class to a byte array and write it to the output stream
|
// parse the patched class to a byte array and write it to the output stream
|
||||||
val cw: ClassWriter = SafeClassWriter(
|
val cw = ClassWriter(
|
||||||
|
classReaders[name]!!,
|
||||||
ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS
|
ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS
|
||||||
)
|
)
|
||||||
patchedClass.accept(cw)
|
patchedClass.accept(cw)
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
package app.revanced.patcher.writer
|
|
||||||
|
|
||||||
import org.objectweb.asm.ClassReader
|
|
||||||
import org.objectweb.asm.ClassWriter
|
|
||||||
import org.objectweb.asm.Opcodes
|
|
||||||
import java.io.IOException
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A ClassWriter that computes the common super class of two classes without
|
|
||||||
* actually loading them with a ClassLoader.
|
|
||||||
*
|
|
||||||
* @author Eric Bruneton
|
|
||||||
*/
|
|
||||||
// TODO(Sculas): should we add the ClassReader parameter back?
|
|
||||||
class SafeClassWriter(flags: Int) : ClassWriter(flags) {
|
|
||||||
override fun getCommonSuperClass(type1: String, type2: String): String {
|
|
||||||
try {
|
|
||||||
val info1 = typeInfo(type1)
|
|
||||||
val info2 = typeInfo(type2)
|
|
||||||
if (info1.access and Opcodes.ACC_INTERFACE != 0) {
|
|
||||||
return if (typeImplements(type2, info2, type1)) {
|
|
||||||
type1
|
|
||||||
} else {
|
|
||||||
"java/lang/Object"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (info2.access and Opcodes.ACC_INTERFACE != 0) {
|
|
||||||
return if (typeImplements(type1, info1, type2)) {
|
|
||||||
type2
|
|
||||||
} else {
|
|
||||||
"java/lang/Object"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val b1 = typeAncestors(type1, info1)
|
|
||||||
val b2 = typeAncestors(type2, info2)
|
|
||||||
var result = "java/lang/Object"
|
|
||||||
var end1 = b1.length
|
|
||||||
var end2 = b2.length
|
|
||||||
while (true) {
|
|
||||||
val start1 = b1.lastIndexOf(";", end1 - 1)
|
|
||||||
val start2 = b2.lastIndexOf(";", end2 - 1)
|
|
||||||
if (start1 != -1 && start2 != -1 && end1 - start1 == end2 - start2) {
|
|
||||||
val p1 = b1.substring(start1 + 1, end1)
|
|
||||||
val p2 = b2.substring(start2 + 1, end2)
|
|
||||||
if (p1 == p2) {
|
|
||||||
result = p1
|
|
||||||
end1 = start1
|
|
||||||
end2 = start2
|
|
||||||
} else {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: IOException) {
|
|
||||||
throw RuntimeException(e.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the internal names of the ancestor classes of the given type.
|
|
||||||
*
|
|
||||||
* @param _type
|
|
||||||
* the internal name of a class or interface.
|
|
||||||
* @param _info
|
|
||||||
* the ClassReader corresponding to 'type'.
|
|
||||||
* @return a StringBuilder containing the ancestor classes of 'type',
|
|
||||||
* separated by ';'. The returned string has the following format:
|
|
||||||
* ";type1;type2 ... ;typeN", where type1 is 'type', and typeN is a
|
|
||||||
* direct subclass of Object. If 'type' is Object, the returned
|
|
||||||
* string is empty.
|
|
||||||
* @throws IOException
|
|
||||||
* if the bytecode of 'type' or of some of its ancestor class
|
|
||||||
* cannot be loaded.
|
|
||||||
*/
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private fun typeAncestors(_type: String, _info: ClassReader): StringBuilder {
|
|
||||||
var type = _type
|
|
||||||
var info = _info
|
|
||||||
val b = StringBuilder()
|
|
||||||
while ("java/lang/Object" != type) {
|
|
||||||
b.append(';').append(type)
|
|
||||||
type = info.superName
|
|
||||||
info = typeInfo(type)
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the given type implements the given interface.
|
|
||||||
*
|
|
||||||
* @param _type
|
|
||||||
* the internal name of a class or interface.
|
|
||||||
* @param _info
|
|
||||||
* the ClassReader corresponding to 'type'.
|
|
||||||
* @param itf
|
|
||||||
* the internal name of a interface.
|
|
||||||
* @return true if 'type' implements directly or indirectly 'itf'
|
|
||||||
* @throws IOException
|
|
||||||
* if the bytecode of 'type' or of some of its ancestor class
|
|
||||||
* cannot be loaded.
|
|
||||||
*/
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private fun typeImplements(_type: String, _info: ClassReader, itf: String): Boolean {
|
|
||||||
var type = _type
|
|
||||||
var info = _info
|
|
||||||
while ("java/lang/Object" != type) {
|
|
||||||
info.interfaces.forEach {
|
|
||||||
if (it == itf) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info.interfaces.forEach {
|
|
||||||
if (typeImplements(it, typeInfo(it), itf)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type = info.superName
|
|
||||||
info = typeInfo(type)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a ClassReader corresponding to the given class or interface.
|
|
||||||
*
|
|
||||||
* @param type
|
|
||||||
* the internal name of a class or interface.
|
|
||||||
* @return the ClassReader corresponding to 'type'.
|
|
||||||
* @throws IOException
|
|
||||||
* if the bytecode of 'type' cannot be loaded.
|
|
||||||
*/
|
|
||||||
@Throws(IOException::class)
|
|
||||||
private fun typeInfo(type: String): ClassReader {
|
|
||||||
val input = ClassLoader.getSystemClassLoader().getResourceAsStream("$type.class")
|
|
||||||
?: throw IOException("Cannot create ClassReader for type $type")
|
|
||||||
return input.use(::ClassReader)
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,6 +7,6 @@ internal class ReaderTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `read jar containing multiple classes`() {
|
fun `read jar containing multiple classes`() {
|
||||||
val testData = PatcherTest::class.java.getResourceAsStream("/test2.jar")!!
|
val testData = PatcherTest::class.java.getResourceAsStream("/test2.jar")!!
|
||||||
Patcher(testData, ByteArrayOutputStream(), PatcherTest.testSignatures) // reusing test sigs from PatcherTest
|
Patcher(testData, ByteArrayOutputStream(), PatcherTest.testSignatures).save() // reusing test sigs from PatcherTest
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user