Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main/kotlin/org/moe/tools/classvalidator/ClassModifier.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.moe.tools.classvalidator

import org.objectweb.asm.ClassVisitor

/**
* A [ClassVisitor] that could modify a class
*/
abstract class ClassModifier(api: Int, classVisitor: ClassVisitor?) : ClassVisitor(api, classVisitor) {

var modified: Boolean = false
private set

fun markAsModified() {
modified = true
}
}
21 changes: 16 additions & 5 deletions src/main/kotlin/org/moe/tools/classvalidator/ClassSaver.kt
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
package org.moe.tools.classvalidator

import org.moe.common.utils.prepareDir
import org.objectweb.asm.ClassReader
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path

internal class ClassSaver(
private val outputDir: Path
) {
fun save(bytecode: ByteArray?) {

private val classNameBytecodeMap = mutableMapOf<String, ByteArray>()
fun add(bytecode: ByteArray?) {
if (bytecode == null) {
return
}

val cr = ClassReader(bytecode)
val outputFile = outputDir.resolve(cr.className + ".class")
outputFile.parent.prepareDir()
Files.write(outputFile, bytecode)
classNameBytecodeMap[cr.className + ".class"] = bytecode
}

fun save() {
// In jar replacement
val fileSystem = FileSystems.newFileSystem(outputDir, null)
fileSystem.use {
classNameBytecodeMap.forEach { (fileName, bytecode) ->
val outputFile = it.getPath(fileName)
Files.write(outputFile, bytecode)
}
}
}
}
52 changes: 31 additions & 21 deletions src/main/kotlin/org/moe/tools/classvalidator/ClassValidator.kt
Original file line number Diff line number Diff line change
@@ -1,37 +1,49 @@
package org.moe.tools.classvalidator

import org.moe.common.utils.classAndJarInputIterator
import org.moe.common.utils.classpathIterator
import org.moe.tools.classvalidator.natj.AddMissingAnnotations
import org.moe.tools.classvalidator.natj.AddMissingNatJRegister
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
import java.io.File
import java.nio.file.Path

object ClassValidator {
fun process(
inputFiles: Set<File>,
outputDir: Path,
classpath: Set<File>,
inputFiles: Set<File>,
classpath: Set<File>,
) {
ContextClassLoaderHolder(
ChildFirstClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray())
).use {
val classSaver = ClassSaver(outputDir.resolve(OUTPUT_CLASSES))

inputFiles.classAndJarInputIterator { _, inputStream ->
val cr = ClassReader(inputStream)

val byteCode = processClass(cr) { next ->
next
.let(::AddMissingAnnotations)
.let(::AddMissingNatJRegister)
val classSavers = mutableListOf<ClassSaver>()
ChildFirstClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray()).use { loader ->
ContextClassLoaderHolder(loader).use {
inputFiles.forEach { jar ->
val classSaver = ClassSaver(jar.absoluteFile.toPath())
jar.classpathIterator({ _, inputStream ->
val cr = ClassReader(inputStream)

val chain = mutableListOf<ClassVisitor>()
fun ClassVisitor.chain(nextBuilder: (ClassVisitor) -> ClassVisitor): ClassVisitor {
val next = nextBuilder(this)
chain.add(next)
return next
}

val byteCode = processClass(cr) { next ->
next
.chain(::AddMissingAnnotations)
.chain(::AddMissingNatJRegister)
}

// Only save modified class
if (chain.any { it is ClassModifier && it.modified }) {
classSaver.add(byteCode)
}
}, { it.endsWith(".class") })
classSavers.add(classSaver)
}

classSaver.save(byteCode)
}
}
classSavers.forEach { it.save() }
}

private inline fun processClass(reader: ClassReader, chain: (ClassVisitor) -> ClassVisitor): ByteArray {
Expand All @@ -47,6 +59,4 @@ object ClassValidator {

return writer.toByteArray()
}

const val OUTPUT_CLASSES = "classes"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.moe.tools.classvalidator.natj

import org.moe.tools.classvalidator.getParentImplementation
import org.moe.tools.classvalidator.toClass
import org.moe.tools.classvalidator.ClassModifier
import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
Expand All @@ -11,7 +12,7 @@ import java.lang.reflect.AnnotatedElement

class AddMissingAnnotations(
next: ClassVisitor?
) : ClassVisitor(Opcodes.ASM5, next) {
) : ClassModifier(Opcodes.ASM5, next) {
private var skip: Boolean = false
private var superName: String? = null
private var interfaces: Array<out String>? = null
Expand Down Expand Up @@ -130,6 +131,7 @@ class AddMissingAnnotations(
}
}
}
markAsModified()
}

private fun convertClassToType(value: Any): Any {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.moe.tools.classvalidator.natj

import org.moe.tools.classvalidator.ClassModifier
import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
Expand All @@ -13,7 +14,7 @@ import org.objectweb.asm.tree.MethodNode
*/
class AddMissingNatJRegister(
next: ClassVisitor?
) : ClassVisitor(Opcodes.ASM5, next) {
) : ClassModifier(Opcodes.ASM5, next) {

private var skip: Boolean = false
private var visit: Boolean = false
Expand Down Expand Up @@ -89,6 +90,7 @@ class AddMissingNatJRegister(
NatJRuntime.NATJ_OWNER, NatJRuntime.NATJ_REGISTER_NAME,
NatJRuntime.NATJ_REGISTER_DESC, false)
CLI.accept(mv)
markAsModified()
println("Injected NatJ.register() into $name")
}
NATJREG_LEAVE_ALONE -> {
Expand All @@ -107,6 +109,7 @@ class AddMissingNatJRegister(
mv.visitInsn(Opcodes.RETURN)
mv.visitMaxs(-1, -1)
mv.visitEnd()
markAsModified()
println("Injected NatJ.register() into $name")
}

Expand Down