package org.openrs2.deob.processor import com.fasterxml.jackson.databind.ObjectMapper import com.google.inject.Guice import com.google.inject.Key import com.sun.source.util.Trees import org.openrs2.asm.MemberRef import org.openrs2.asm.toInternalClassName import org.openrs2.deob.annotation.OriginalArg import org.openrs2.deob.annotation.OriginalClass import org.openrs2.deob.annotation.OriginalMember import org.openrs2.deob.util.map.Field import org.openrs2.deob.util.map.Method import org.openrs2.deob.util.map.NameMap import org.openrs2.inject.CloseableInjector import org.openrs2.util.io.useAtomicBufferedWriter import org.openrs2.yaml.Yaml import java.lang.reflect.Proxy import java.nio.file.Files import java.nio.file.Path import java.util.TreeMap import javax.annotation.processing.AbstractProcessor import javax.annotation.processing.ProcessingEnvironment import javax.annotation.processing.RoundEnvironment import javax.annotation.processing.SupportedAnnotationTypes import javax.annotation.processing.SupportedOptions import javax.annotation.processing.SupportedSourceVersion import javax.lang.model.SourceVersion import javax.lang.model.element.ExecutableElement import javax.lang.model.element.TypeElement import javax.lang.model.element.VariableElement @SupportedAnnotationTypes( "org.openrs2.deob.annotation.OriginalArg", "org.openrs2.deob.annotation.OriginalClass", "org.openrs2.deob.annotation.OriginalMember", "org.openrs2.deob.annotation.Pc" ) @SupportedSourceVersion(SourceVersion.RELEASE_11) @SupportedOptions( "map" ) public class NameMapProcessor : AbstractProcessor() { private val injector = CloseableInjector(Guice.createInjector(DeobfuscatorProcessorModule)) private val map = NameMap() private val mapper = injector.getInstance(Key.get(ObjectMapper::class.java, Yaml::class.java)) private lateinit var trees: Trees private lateinit var localScanner: LocalVariableScanner override fun init(env: ProcessingEnvironment) { super.init(env) trees = Trees.instance(unwrap(env)) localScanner = LocalVariableScanner(trees) } // see https://youtrack.jetbrains.com/issue/IDEA-256707 private fun unwrap(env: ProcessingEnvironment): ProcessingEnvironment { if (!Proxy.isProxyClass(env.javaClass)) { return env } val invocationHandler = Proxy.getInvocationHandler(env) val field = invocationHandler.javaClass.getDeclaredField("val\$delegateTo") field.isAccessible = true return field.get(invocationHandler) as ProcessingEnvironment } private fun getPath(): Path? { val map = processingEnv.options["map"] ?: return null return Path.of(map) } override fun process(annotations: Set, env: RoundEnvironment): Boolean { val mapPath = getPath() ?: return true for (element in env.getElementsAnnotatedWith(OriginalClass::class.java)) { check(element is TypeElement) val originalClass = element.getAnnotation(OriginalClass::class.java)!! map.classes[originalClass.value] = element.qualifiedName.toString().toInternalClassName() } for (element in env.getElementsAnnotatedWith(OriginalMember::class.java)) { val path = trees.getPath(element) val owner = trees.getScope(path).enclosingClass.qualifiedName.toString().toInternalClassName() val name = element.simpleName.toString() val originalMember = element.getAnnotation(OriginalMember::class.java)!! val ref = MemberRef(originalMember.owner, originalMember.name, originalMember.descriptor) when (element) { is VariableElement -> map.fields[ref] = Field(owner, name) is ExecutableElement -> { val arguments = element.parameters.map { parameter -> val originalArg = parameter.getAnnotation(OriginalArg::class.java)!! Pair(originalArg.value, parameter.simpleName.toString()) }.toMap(LinkedHashMap()) val locals = TreeMap() localScanner.scan(path, locals) map.methods[ref] = Method(owner, name, arguments, locals) } else -> error("Unexpected element type") } } if (env.processingOver()) { Files.createDirectories(mapPath.parent) val combinedMap: NameMap if (Files.exists(mapPath)) { combinedMap = Files.newBufferedReader(mapPath).use { reader -> mapper.readValue(reader, NameMap::class.java) } combinedMap.add(map) } else { combinedMap = map } mapPath.useAtomicBufferedWriter { writer -> mapper.writeValue(writer, combinedMap) } injector.close() } return true } }