broken
This commit is contained in:
parent
2c4b1e1409
commit
0ae3814cf5
9 changed files with 410 additions and 219 deletions
|
@ -30,7 +30,7 @@ tasks.withType<KotlinCompile> {
|
|||
}
|
||||
|
||||
gradlePlugin {
|
||||
// we handle publications ourselves
|
||||
// we handle publications manually
|
||||
isAutomatedPublishing = false
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,10 @@ dependencies {
|
|||
shade("net.fabricmc:lorenz-tiny:3.0.0")
|
||||
|
||||
shade("io.sigpipe:jbsdiff:1.0")
|
||||
|
||||
val jgraphtVersion = "1.5.0"
|
||||
shade("org.jgrapht:jgrapht-core:$jgraphtVersion")
|
||||
shade("org.jgrapht:jgrapht-io:$jgraphtVersion")
|
||||
}
|
||||
|
||||
ktlint {
|
||||
|
@ -124,13 +128,17 @@ tasks.shadowJar {
|
|||
"io.sigpipe",
|
||||
"me.jamiemansfield",
|
||||
"net.fabricmc",
|
||||
"org.antlr",
|
||||
"org.apache.commons.codec",
|
||||
"org.apache.commons.compress",
|
||||
"org.apache.commons.logging",
|
||||
"org.apache.commons.text",
|
||||
"org.apache.felix",
|
||||
"org.apache.http",
|
||||
"org.cadixdev",
|
||||
"org.eclipse",
|
||||
"org.jgrapht",
|
||||
"org.jheaps",
|
||||
"org.objectweb",
|
||||
"org.osgi",
|
||||
"org.tukaani"
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -152,7 +152,7 @@ class Paperweight : Plugin<Project> {
|
|||
val initialTasks = createInitialTasks()
|
||||
val generalTasks = createGeneralTasks()
|
||||
val vanillaTasks = createVanillaTasks(initialTasks, generalTasks)
|
||||
val spigotTasks = createSpigotTasks(initialTasks, generalTasks, vanillaTasks)
|
||||
val spigotTasks = createSpigotTasks(generalTasks, vanillaTasks)
|
||||
|
||||
val applyMergedAt by tasks.registering<ApplyAccessTransform> {
|
||||
inputJar.set(vanillaTasks.fixJar.flatMap { it.outputJar })
|
||||
|
@ -238,6 +238,7 @@ class Paperweight : Plugin<Project> {
|
|||
)
|
||||
|
||||
data class VanillaTasks(
|
||||
val inspectVanillaJar: TaskProvider<InspectVanillaJar>,
|
||||
val generateMappings: TaskProvider<GenerateMappings>,
|
||||
val fixJar: TaskProvider<FixJar>,
|
||||
val downloadMcLibraries: TaskProvider<DownloadMcLibraries>
|
||||
|
@ -336,11 +337,29 @@ class Paperweight : Plugin<Project> {
|
|||
val cache: File = layout.cache
|
||||
val downloadService = download
|
||||
|
||||
val downloadMcLibraries by tasks.registering<DownloadMcLibraries> {
|
||||
mcLibrariesFile.set(initialTasks.setupMcLibraries.flatMap { it.outputFile })
|
||||
mcRepo.set(Constants.MC_LIBRARY_URL)
|
||||
outputDir.set(cache.resolve(Constants.MINECRAFT_JARS_PATH))
|
||||
sourcesOutputDir.set(cache.resolve(Constants.MINECRAFT_SOURCES_PATH))
|
||||
|
||||
downloader.set(downloadService)
|
||||
}
|
||||
|
||||
val inspectVanillaJar by tasks.registering<InspectVanillaJar> {
|
||||
inputJar.set(generalTasks.downloadServerJar.flatMap { it.outputJar })
|
||||
librariesDir.set(downloadMcLibraries.flatMap { it.outputDir })
|
||||
mcLibraries.set(initialTasks.setupMcLibraries.flatMap { it.outputFile })
|
||||
|
||||
serverLibraries.set(cache.resolve(Constants.SERVER_LIBRARIES))
|
||||
}
|
||||
|
||||
val generateMappings by tasks.registering<GenerateMappings> {
|
||||
vanillaJar.set(generalTasks.filterVanillaJar.flatMap { it.outputJar })
|
||||
|
||||
vanillaMappings.set(initialTasks.downloadMappings.flatMap { it.outputFile })
|
||||
paramMappings.fileProvider(configurations.named(Constants.PARAM_MAPPINGS_CONFIG).map { it.singleFile })
|
||||
methodOverrides.set(inspectVanillaJar.flatMap { it.methodOverrides })
|
||||
|
||||
outputMappings.set(cache.resolve(Constants.MOJANG_YARN_MAPPINGS))
|
||||
}
|
||||
|
@ -358,25 +377,16 @@ class Paperweight : Plugin<Project> {
|
|||
vanillaJar.set(generalTasks.downloadServerJar.flatMap { it.outputJar })
|
||||
}
|
||||
|
||||
val downloadMcLibraries by tasks.registering<DownloadMcLibraries> {
|
||||
mcLibrariesFile.set(initialTasks.setupMcLibraries.flatMap { it.outputFile })
|
||||
mcRepo.set(Constants.MC_LIBRARY_URL)
|
||||
outputDir.set(cache.resolve(Constants.MINECRAFT_JARS_PATH))
|
||||
sourcesOutputDir.set(cache.resolve(Constants.MINECRAFT_SOURCES_PATH))
|
||||
|
||||
downloader.set(downloadService)
|
||||
}
|
||||
|
||||
return VanillaTasks(generateMappings, fixJar, downloadMcLibraries)
|
||||
return VanillaTasks(inspectVanillaJar, generateMappings, fixJar, downloadMcLibraries)
|
||||
}
|
||||
|
||||
private fun Project.createSpigotTasks(initialTasks: InitialTasks, generalTasks: GeneralTasks, vanillaTasks: VanillaTasks): SpigotTasks {
|
||||
private fun Project.createSpigotTasks(generalTasks: GeneralTasks, vanillaTasks: VanillaTasks): SpigotTasks {
|
||||
val cache: File = layout.cache
|
||||
val extension: PaperweightExtension = ext
|
||||
val downloadService = download
|
||||
|
||||
val (buildDataInfo, downloadServerJar, filterVanillaJar) = generalTasks
|
||||
val (generateMappings, _, _) = vanillaTasks
|
||||
val (inspectVanillaJar, generateMappings, _, _) = vanillaTasks
|
||||
|
||||
val addAdditionalSpigotMappings by tasks.registering<AddAdditionalSpigotMappings> {
|
||||
classSrg.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.classMappings }))
|
||||
|
@ -385,14 +395,6 @@ class Paperweight : Plugin<Project> {
|
|||
additionalMemberEntriesSrg.set(extension.paper.additionalSpigotMemberMappings)
|
||||
}
|
||||
|
||||
val inspectVanillaJar by tasks.registering<InspectVanillaJar> {
|
||||
inputJar.set(downloadServerJar.flatMap { it.outputJar })
|
||||
librariesDir.set(vanillaTasks.downloadMcLibraries.flatMap { it.outputDir })
|
||||
mcLibraries.set(initialTasks.setupMcLibraries.flatMap { it.outputFile })
|
||||
|
||||
serverLibraries.set(cache.resolve(Constants.SERVER_LIBRARIES))
|
||||
}
|
||||
|
||||
val generateSpigotMappings by tasks.registering<GenerateSpigotMappings> {
|
||||
classMappings.set(addAdditionalSpigotMappings.flatMap { it.outputClassSrg })
|
||||
memberMappings.set(addAdditionalSpigotMappings.flatMap { it.outputMemberSrg })
|
||||
|
@ -401,6 +403,7 @@ class Paperweight : Plugin<Project> {
|
|||
loggerFields.set(inspectVanillaJar.flatMap { it.loggerFile })
|
||||
paramIndexes.set(inspectVanillaJar.flatMap { it.paramIndexes })
|
||||
syntheticMethods.set(inspectVanillaJar.flatMap { it.syntheticMethods })
|
||||
methodOverrides.set(inspectVanillaJar.flatMap { it.methodOverrides })
|
||||
|
||||
sourceMappings.set(generateMappings.flatMap { it.outputMappings })
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.AsmUtil
|
||||
import io.papermc.paperweight.util.ClassNodeCache
|
||||
import io.papermc.paperweight.util.SyntheticUtil
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.file
|
||||
|
@ -33,7 +34,6 @@ import org.gradle.api.file.RegularFileProperty
|
|||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Type
|
||||
|
@ -75,8 +75,7 @@ abstract class FixJar : BaseTask(), AsmUtil {
|
|||
}
|
||||
|
||||
try {
|
||||
val node =
|
||||
classNodeCache.findClass(entry.name) ?: error("No ClassNode found for known entry")
|
||||
val node = classNodeCache.findClass(entry.name) ?: error("No ClassNode found for known entry")
|
||||
|
||||
ParameterAnnotationFixer(node).visitNode()
|
||||
OverrideAnnotationAdder(node, classNodeCache).visitNode()
|
||||
|
@ -230,49 +229,3 @@ class OverrideAnnotationAdder(private val node: ClassNode, private val classNode
|
|||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class ClassNodeCache(private val jarFile: JarFile, private val fallbackJar: JarFile) {
|
||||
|
||||
private val classNodeMap = hashMapOf<String, ClassNode?>()
|
||||
|
||||
fun findClass(name: String): ClassNode? {
|
||||
return classNodeMap.computeIfAbsent(normalize(name)) { fileName ->
|
||||
val classData = findClassData(fileName) ?: return@computeIfAbsent null
|
||||
val classReader = ClassReader(classData)
|
||||
val node = ClassNode(Opcodes.ASM9)
|
||||
classReader.accept(node, 0)
|
||||
return@computeIfAbsent node
|
||||
}
|
||||
}
|
||||
|
||||
private fun findClassData(className: String): ByteArray? {
|
||||
val entry = ZipEntry(className)
|
||||
return (
|
||||
jarFile.getInputStream(entry) // remapped class
|
||||
?: fallbackJar.getInputStream(entry) // library class
|
||||
?: ClassLoader.getSystemResourceAsStream(className)
|
||||
)?.use { it.readBytes() } // JDK class
|
||||
}
|
||||
|
||||
private fun normalize(name: String): String {
|
||||
var workingName = name
|
||||
if (workingName.endsWith(".class")) {
|
||||
workingName = workingName.substring(0, workingName.length - 6)
|
||||
}
|
||||
|
||||
var startIndex = 0
|
||||
var endIndex = workingName.length
|
||||
if (workingName.startsWith('L')) {
|
||||
startIndex = 1
|
||||
}
|
||||
if (workingName.endsWith(';')) {
|
||||
endIndex--
|
||||
}
|
||||
|
||||
return workingName.substring(startIndex, endIndex).replace('.', '/') + ".class"
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
classNodeMap.clear()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import io.papermc.paperweight.util.file
|
|||
import io.papermc.paperweight.util.path
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.util.zip.ZipFile
|
||||
import javax.inject.Inject
|
||||
import org.cadixdev.atlas.Atlas
|
||||
import org.cadixdev.bombe.asm.jar.JarEntryRemappingTransformer
|
||||
|
@ -67,6 +68,8 @@ abstract class GenerateMappings : DefaultTask() {
|
|||
abstract val vanillaMappings: RegularFileProperty
|
||||
@get:InputFile
|
||||
abstract val paramMappings: RegularFileProperty
|
||||
@get:InputFile
|
||||
abstract val methodOverrides: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputMappings: RegularFileProperty
|
||||
|
@ -93,11 +96,11 @@ abstract class GenerateMappings : DefaultTask() {
|
|||
.build()
|
||||
).merge()
|
||||
|
||||
ensureParentExists(outputMappings)
|
||||
|
||||
// Fill out any missing inheritance info in the mappings
|
||||
val tempMappingsFile = Files.createTempFile("mappings", "tiny")
|
||||
val tempMappingsOutputFile = Files.createTempFile("mappings-out", "tiny")
|
||||
|
||||
val filledMerged = try {
|
||||
try {
|
||||
MappingFormats.TINY.write(merged, tempMappingsFile, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
|
@ -107,21 +110,65 @@ abstract class GenerateMappings : DefaultTask() {
|
|||
queue.submit(AtlasAction::class) {
|
||||
inputJar.set(vanillaJar.file)
|
||||
mappingsFile.set(tempMappingsFile.toFile())
|
||||
outputMappingsFile.set(tempMappingsOutputFile.toFile())
|
||||
outputMappingsFile.set(outputMappings.file)
|
||||
}
|
||||
|
||||
queue.await()
|
||||
|
||||
MappingFormats.TINY.read(tempMappingsOutputFile, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
} finally {
|
||||
Files.deleteIfExists(tempMappingsFile)
|
||||
Files.deleteIfExists(tempMappingsOutputFile)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private fun copyOverridenParamMappings(mappings: MappingSet) {
|
||||
val methods = hashMapOf<String, String>()
|
||||
methodOverrides.file.reader(Charsets.UTF_8).useLines { lines ->
|
||||
lines.map { line ->
|
||||
val (method, superMethod) = line.split("|")
|
||||
methods[method] = superMethod
|
||||
}
|
||||
}
|
||||
|
||||
ensureParentExists(outputMappings)
|
||||
MappingFormats.TINY.write(filledMerged, outputMappings.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
for (classMapping in mappings.topLevelClassMappings) {
|
||||
copyOverridenParamMappings(mappings, classMapping, methods)
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyOverridenParamMappings(mappingSet: MappingSet, classMapping: ClassMapping<*, *>, methods: Map<String, String>) {
|
||||
methodLoop@for (mapping in classMapping.methodMappings) {
|
||||
if (mapping.parameterMappings.isNotEmpty()) {
|
||||
continue
|
||||
}
|
||||
|
||||
var baseClassName = classMapping.obfuscatedName
|
||||
var baseMethod = mapping.obfuscatedName
|
||||
var baseDesc = mapping.obfuscatedDescriptor
|
||||
|
||||
while (true) {
|
||||
val superMethod = methods["$baseClassName,$baseMethod,$baseDesc"] ?: continue@methodLoop
|
||||
val (className, methodName, methodDesc) = superMethod.split(",")
|
||||
|
||||
val superMethodMapping = mappingSet.getClassMapping(className).orNull?.getMethodMapping(methodName, methodDesc)?.orNull
|
||||
if (superMethodMapping != null) {
|
||||
superMethodMapping.parameterMappings.forEach { it.copy(mapping) }
|
||||
break
|
||||
}
|
||||
|
||||
// prevent possible infinite loops
|
||||
if (baseClassName == className) {
|
||||
break
|
||||
}
|
||||
|
||||
baseClassName = className
|
||||
baseMethod = methodName
|
||||
baseDesc = methodDesc
|
||||
}
|
||||
}
|
||||
|
||||
classMapping.innerClassMappings.forEach { copyOverridenParamMappings(mappingSet, it, methods) }
|
||||
}
|
||||
*/
|
||||
|
||||
abstract class AtlasAction : WorkAction<AtlasParameters> {
|
||||
override fun execute() {
|
||||
val mappings = MappingFormats.TINY.read(parameters.mappingsFile.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
|
|
|
@ -26,12 +26,12 @@ import io.papermc.paperweight.util.Constants.DEOBF_NAMESPACE
|
|||
import io.papermc.paperweight.util.Constants.OBF_NAMESPACE
|
||||
import io.papermc.paperweight.util.Constants.SPIGOT_NAMESPACE
|
||||
import io.papermc.paperweight.util.MappingFormats
|
||||
import io.papermc.paperweight.util.MethodRef
|
||||
import io.papermc.paperweight.util.emptyMergeResult
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.orNull
|
||||
import io.papermc.paperweight.util.parentClass
|
||||
import io.papermc.paperweight.util.readOverrides
|
||||
import io.papermc.paperweight.util.path
|
||||
import org.cadixdev.bombe.type.signature.MethodSignature
|
||||
import org.cadixdev.lorenz.MappingSet
|
||||
import org.cadixdev.lorenz.merge.MappingSetMerger
|
||||
import org.cadixdev.lorenz.merge.MappingSetMergerHandler
|
||||
|
@ -48,6 +48,8 @@ import org.gradle.api.file.RegularFileProperty
|
|||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jgrapht.graph.SimpleDirectedGraph
|
||||
import org.jgrapht.traverse.DepthFirstIterator
|
||||
|
||||
abstract class GenerateSpigotMappings : DefaultTask() {
|
||||
|
||||
|
@ -64,6 +66,8 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
abstract val paramIndexes: RegularFileProperty
|
||||
@get:InputFile
|
||||
abstract val syntheticMethods: RegularFileProperty
|
||||
@get:InputFile
|
||||
abstract val methodOverrides: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val sourceMappings: RegularFileProperty
|
||||
|
@ -88,6 +92,7 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
|
||||
val sourceMappings = MappingFormats.TINY.read(sourceMappings.path, OBF_NAMESPACE, DEOBF_NAMESPACE)
|
||||
|
||||
/*
|
||||
val synths = newSynths()
|
||||
syntheticMethods.file.useLines { lines ->
|
||||
for (line in lines) {
|
||||
|
@ -96,12 +101,15 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
.computeIfAbsent(desc) { hashMapOf() }[baseName] = synthName
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
val overrides = readOverrides(methodOverrides)
|
||||
|
||||
val notchToSpigotSet = MappingSetMerger.create(
|
||||
mergedMappingSet,
|
||||
sourceMappings,
|
||||
MergeConfig.builder()
|
||||
.withMergeHandler(SpigotMappingsMergerHandler(newPackage, synths))
|
||||
.withMergeHandler(SpigotMappingsMergerHandler(newPackage, overrides))
|
||||
.build()
|
||||
).merge()
|
||||
|
||||
|
@ -144,7 +152,7 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
params: Map<String, Map<String, Map<Int, Int>>>
|
||||
) {
|
||||
for (mapping in from.fieldMappings) {
|
||||
to.createFieldMapping(mapping.signature, mapping.deobfuscatedName)
|
||||
mapping.copy(to)
|
||||
}
|
||||
for (mapping in from.innerClassMappings) {
|
||||
val newMapping = to.createInnerClassMapping(mapping.obfuscatedName, mapping.deobfuscatedName)
|
||||
|
@ -162,7 +170,7 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
|
||||
val methodMap = classMap[mapping.signature.toJvmsIdentifier()] ?: continue
|
||||
for (paramMapping in paramMappings) {
|
||||
val i = methodMap[paramMapping.index] ?: paramMapping.index
|
||||
val i = methodMap[paramMapping.index] ?: continue
|
||||
newMapping.createParameterMapping(i, paramMapping.deobfuscatedName)
|
||||
}
|
||||
}
|
||||
|
@ -203,12 +211,14 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
typealias Synths = Map<String, Map<String, Map<String, String>>>
|
||||
fun newSynths() = hashMapOf<String, MutableMap<String, MutableMap<String, String>>>()
|
||||
*/
|
||||
|
||||
class SpigotMappingsMergerHandler(
|
||||
private val newPackage: String,
|
||||
private val synths: Synths
|
||||
private val methodOverrides: SimpleDirectedGraph<MethodRef, *>
|
||||
) : MappingSetMergerHandler {
|
||||
|
||||
override fun mergeTopLevelClassMappings(
|
||||
|
@ -338,6 +348,24 @@ class SpigotMappingsMergerHandler(
|
|||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<MethodMapping?> {
|
||||
val ref = MethodRef.from(left.parent.obfuscatedName, left.signature)
|
||||
val newMapping = if (methodOverrides.containsVertex(ref)) {
|
||||
val methodRef = DepthFirstIterator(methodOverrides, ref).asSequence().firstOrNull() ?: ref
|
||||
target.getOrCreateMethodMapping(methodRef.methodName, methodRef.methodDesc).also {
|
||||
it.deobfuscatedName = left.deobfuscatedName
|
||||
}
|
||||
} else {
|
||||
target.getOrCreateMethodMapping(left.signature).also {
|
||||
it.deobfuscatedName = left.deobfuscatedName
|
||||
}
|
||||
}
|
||||
|
||||
return MergeResult(newMapping)
|
||||
/*
|
||||
val newMapping = target.getOrCreateMethodMapping(left.signature).also {
|
||||
it.deobfuscatedName = left.deobfuscatedName
|
||||
}
|
||||
|
||||
// Check if Spigot calls this mapping something else
|
||||
val synthMethods = synths[left.parent.fullObfuscatedName]?.get(left.obfuscatedDescriptor)
|
||||
val newName = synthMethods?.get(left.obfuscatedName)
|
||||
|
@ -350,11 +378,21 @@ class SpigotMappingsMergerHandler(
|
|||
it.deobfuscatedName = left.deobfuscatedName
|
||||
}
|
||||
} else {
|
||||
// Can't find the name spigot uses, just use the original mapping
|
||||
// Can't find the name spigot uses, check the hierarchy
|
||||
|
||||
val methodRef = MethodRef(newMapping.parent.obfuscatedName, newMapping.obfuscatedName, newMapping.descriptor.toString())
|
||||
|
||||
target.getOrCreateMethodMapping(left.signature).also {
|
||||
it.deobfuscatedName = newName
|
||||
}
|
||||
}
|
||||
|
||||
val methodRef = MethodRef(newMapping.parent.obfuscatedName, newMapping.obfuscatedName, newMapping.descriptor.toString())
|
||||
if (methodOverrides.containsVertex(methodRef)) {
|
||||
for ((superClass, superName, superDesc) in BreadthFirstIterator(methodOverrides, methodRef)) {
|
||||
|
||||
}
|
||||
}
|
||||
MergeResult(newMapping)
|
||||
} else {
|
||||
// normal mapping
|
||||
|
@ -363,12 +401,28 @@ class SpigotMappingsMergerHandler(
|
|||
}
|
||||
return MergeResult(newMapping)
|
||||
}
|
||||
*/
|
||||
}
|
||||
override fun addLeftMethodMapping(
|
||||
left: MethodMapping,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<MethodMapping?> {
|
||||
val ref = MethodRef.from(left.parent.obfuscatedName, left.signature)
|
||||
val newMapping = if (methodOverrides.containsVertex(ref)) {
|
||||
val methodRef = DepthFirstIterator(methodOverrides, ref).asSequence().firstOrNull() ?: ref
|
||||
target.getOrCreateMethodMapping(methodRef.methodName, methodRef.methodDesc).also {
|
||||
it.deobfuscatedName = left.deobfuscatedName
|
||||
}
|
||||
} else {
|
||||
target.getOrCreateMethodMapping(left.signature).also {
|
||||
it.deobfuscatedName = left.deobfuscatedName
|
||||
}
|
||||
}
|
||||
|
||||
return MergeResult(newMapping)
|
||||
|
||||
/*
|
||||
// Check if Spigot maps this from a synthetic method name
|
||||
var obfName: String? = null
|
||||
val synthMethods = synths[left.parent.fullObfuscatedName]?.get(left.obfuscatedDescriptor)
|
||||
|
@ -390,6 +444,7 @@ class SpigotMappingsMergerHandler(
|
|||
val newMapping = target.getOrCreateMethodMapping(obfName, left.descriptor)
|
||||
newMapping.deobfuscatedName = left.deobfuscatedName
|
||||
return MergeResult(newMapping)
|
||||
*/
|
||||
}
|
||||
|
||||
override fun addLeftFieldMapping(left: FieldMapping, target: ClassMapping<*, *>, context: MergeContext): FieldMapping? {
|
||||
|
@ -411,6 +466,20 @@ class SpigotMappingsMergerHandler(
|
|||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<MethodMapping?> {
|
||||
val ref = MethodRef.from(right.parent.obfuscatedName, right.signature)
|
||||
if (methodOverrides.containsVertex(ref)) {
|
||||
val methodRef = DepthFirstIterator(methodOverrides, ref).asSequence().firstOrNull() ?: ref
|
||||
val originalMapping = context.left.getClassMapping(methodRef.className).orNull
|
||||
?.getMethodMapping(methodRef.methodName, methodRef.methodDesc)?.orNull ?: return emptyMergeResult()
|
||||
val newMapping = target.getOrCreateMethodMapping(right.signature).also {
|
||||
it.deobfuscatedName = originalMapping.deobfuscatedName
|
||||
}
|
||||
return MergeResult(newMapping)
|
||||
} else {
|
||||
return emptyMergeResult()
|
||||
}
|
||||
|
||||
/*
|
||||
// Check if spigot changes this method automatically
|
||||
val synthMethods = synths[right.parentClass.fullObfuscatedName]?.get(right.obfuscatedDescriptor)
|
||||
val newName = synthMethods?.get(right.obfuscatedName) ?: return emptyMergeResult()
|
||||
|
@ -424,6 +493,7 @@ class SpigotMappingsMergerHandler(
|
|||
newMapping.deobfuscatedName = newName
|
||||
}
|
||||
return MergeResult(newMapping)
|
||||
*/
|
||||
}
|
||||
|
||||
private fun prependPackage(name: String): String {
|
||||
|
|
|
@ -23,11 +23,16 @@
|
|||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.AsmUtil
|
||||
import io.papermc.paperweight.util.ClassNodeCache
|
||||
import io.papermc.paperweight.util.MavenArtifact
|
||||
import io.papermc.paperweight.util.MethodRef
|
||||
import io.papermc.paperweight.util.SyntheticUtil
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.isLibraryJar
|
||||
import io.papermc.paperweight.util.writeOverrides
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.jar.JarFile
|
||||
import java.util.zip.ZipFile
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
|
@ -35,13 +40,15 @@ import org.gradle.api.tasks.InputDirectory
|
|||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.FieldVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.jgrapht.graph.DefaultEdge
|
||||
import org.jgrapht.graph.SimpleDirectedGraph
|
||||
import org.jgrapht.nio.Attribute
|
||||
import org.jgrapht.nio.DefaultAttribute
|
||||
import org.jgrapht.nio.dot.DOTExporter
|
||||
import org.jgrapht.nio.dot.DOTImporter
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Type
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
abstract class InspectVanillaJar : BaseTask() {
|
||||
|
||||
|
@ -59,12 +66,15 @@ abstract class InspectVanillaJar : BaseTask() {
|
|||
@get:OutputFile
|
||||
abstract val syntheticMethods: RegularFileProperty
|
||||
@get:OutputFile
|
||||
abstract val methodOverrides: RegularFileProperty
|
||||
@get:OutputFile
|
||||
abstract val serverLibraries: RegularFileProperty
|
||||
|
||||
override fun init() {
|
||||
loggerFile.convention(defaultOutput("$name-loggerFields", "txt"))
|
||||
paramIndexes.convention(defaultOutput("$name-paramIndexes", "txt"))
|
||||
syntheticMethods.convention(defaultOutput("$name-syntheticMethods", "txt"))
|
||||
methodOverrides.convention(defaultOutput("$name-methodOverrides", "json"))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
|
@ -72,28 +82,32 @@ abstract class InspectVanillaJar : BaseTask() {
|
|||
val loggers = mutableListOf<LoggerFields.Data>()
|
||||
val params = mutableListOf<ParamIndexes.Data>()
|
||||
val synthMethods = mutableListOf<SyntheticMethods.Data>()
|
||||
val overrides = SimpleDirectedGraph<MethodRef, DefaultEdge>(DefaultEdge::class.java)
|
||||
|
||||
var visitor: ClassVisitor
|
||||
visitor = LoggerFields.Visitor(null, loggers)
|
||||
visitor = ParamIndexes.Visitor(visitor, params)
|
||||
visitor = SyntheticMethods.Visitor(visitor, synthMethods)
|
||||
JarFile(inputJar.file).use { jarFile ->
|
||||
val classNodeCache = ClassNodeCache(jarFile)
|
||||
|
||||
archives.zipTree(inputJar.file).matching {
|
||||
include("/*.class")
|
||||
include("/net/minecraft/**/*.class")
|
||||
}.forEach { file ->
|
||||
if (file.isDirectory) {
|
||||
return@forEach
|
||||
for (entry in jarFile.entries()) {
|
||||
if (!entry.name.endsWith(".class")) {
|
||||
continue
|
||||
}
|
||||
if (entry.name.count { it == '/' } > 0 && !entry.name.startsWith("net/minecraft/")) {
|
||||
continue
|
||||
}
|
||||
|
||||
val node = classNodeCache.findClass(entry.name) ?: error("No ClassNode found for known entry")
|
||||
|
||||
LoggerFields.Visitor.visit(node, loggers)
|
||||
ParamIndexes.Visitor.visit(node, params)
|
||||
SyntheticMethods.Visitor.visit(node, synthMethods)
|
||||
MethodOverrides.Visitor.visit(node, classNodeCache, overrides)
|
||||
}
|
||||
val classData = file.readBytes()
|
||||
|
||||
ClassReader(classData).accept(visitor, 0)
|
||||
}
|
||||
|
||||
val serverLibs = checkLibraries()
|
||||
|
||||
loggerFile.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
||||
loggers.sort()
|
||||
loggers.sort()
|
||||
loggerFile.file.bufferedWriter().use { writer ->
|
||||
for (loggerField in loggers) {
|
||||
writer.append(loggerField.className)
|
||||
writer.append(' ')
|
||||
|
@ -102,8 +116,8 @@ abstract class InspectVanillaJar : BaseTask() {
|
|||
}
|
||||
}
|
||||
|
||||
paramIndexes.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
||||
params.sort()
|
||||
params.sort()
|
||||
paramIndexes.file.bufferedWriter().use { writer ->
|
||||
for (methodData in params) {
|
||||
writer.append(methodData.className)
|
||||
writer.append(' ')
|
||||
|
@ -120,8 +134,8 @@ abstract class InspectVanillaJar : BaseTask() {
|
|||
}
|
||||
}
|
||||
|
||||
syntheticMethods.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
||||
synthMethods.sort()
|
||||
synthMethods.sort()
|
||||
syntheticMethods.file.bufferedWriter().use { writer ->
|
||||
for ((className, desc, synthName, baseName) in synthMethods) {
|
||||
writer.append(className)
|
||||
writer.append(' ')
|
||||
|
@ -134,8 +148,11 @@ abstract class InspectVanillaJar : BaseTask() {
|
|||
}
|
||||
}
|
||||
|
||||
serverLibraries.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
||||
serverLibs.map { it.toString() }.sorted().forEach { artifact ->
|
||||
writeOverrides(overrides, methodOverrides)
|
||||
|
||||
val libs = serverLibs.map { it.toString() }.sorted()
|
||||
serverLibraries.file.bufferedWriter().use { writer ->
|
||||
libs.forEach { artifact ->
|
||||
writer.appendln(artifact)
|
||||
}
|
||||
}
|
||||
|
@ -173,50 +190,22 @@ abstract class InspectVanillaJar : BaseTask() {
|
|||
}
|
||||
}
|
||||
|
||||
abstract class BaseClassVisitor(classVisitor: ClassVisitor?) : ClassVisitor(Opcodes.ASM8, classVisitor), AsmUtil {
|
||||
protected var currentClass: String? = null
|
||||
|
||||
override fun visit(
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String,
|
||||
signature: String?,
|
||||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
this.currentClass = name
|
||||
super.visit(version, access, name, signature, superName, interfaces)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SpecialSource2 automatically maps all Logger fields to the name LOGGER, without needing mappings defined, so we need
|
||||
* to make a note of all of those fields
|
||||
*/
|
||||
object LoggerFields {
|
||||
class Visitor(
|
||||
classVisitor: ClassVisitor?,
|
||||
private val fields: MutableList<Data>
|
||||
) : BaseClassVisitor(classVisitor) {
|
||||
object Visitor : AsmUtil {
|
||||
|
||||
override fun visitField(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
value: Any?
|
||||
): FieldVisitor? {
|
||||
val ret = super.visitField(access, name, descriptor, signature, value)
|
||||
val className = currentClass ?: return ret
|
||||
|
||||
if (Opcodes.ACC_STATIC !in access || Opcodes.ACC_FINAL !in access) {
|
||||
return ret
|
||||
fun visit(node: ClassNode, fields: MutableList<Data>) {
|
||||
for (field in node.fields) {
|
||||
if (Opcodes.ACC_STATIC !in field.access || Opcodes.ACC_FINAL !in field.access) {
|
||||
continue
|
||||
}
|
||||
if (field.desc == "Lorg/apache/logging/log4j/Logger;") {
|
||||
fields += Data(node.name, field.name)
|
||||
}
|
||||
}
|
||||
if (descriptor != "Lorg/apache/logging/log4j/Logger;") {
|
||||
return ret
|
||||
}
|
||||
fields += Data(className, name)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,45 +224,33 @@ object LoggerFields {
|
|||
* actual bytecode to translate the LVT-based indexes back to 0-based param indexes.
|
||||
*/
|
||||
object ParamIndexes {
|
||||
class Visitor(
|
||||
classVisitor: ClassVisitor?,
|
||||
private val methods: MutableList<Data>
|
||||
) : BaseClassVisitor(classVisitor) {
|
||||
object Visitor : AsmUtil {
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor? {
|
||||
val ret = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
val className = currentClass ?: return ret
|
||||
fun visit(node: ClassNode, methods: MutableList<Data>) {
|
||||
for (method in node.methods) {
|
||||
val isStatic = Opcodes.ACC_STATIC in method.access
|
||||
var currentIndex = if (isStatic) 0 else 1
|
||||
|
||||
val isStatic = access and Opcodes.ACC_STATIC != 0
|
||||
var currentIndex = if (isStatic) 0 else 1
|
||||
val types = Type.getArgumentTypes(method.desc)
|
||||
if (types.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
|
||||
val types = Type.getArgumentTypes(descriptor)
|
||||
if (types.isEmpty()) {
|
||||
return ret
|
||||
}
|
||||
val params = ArrayList<ParamTarget>(types.size)
|
||||
val data = Data(node.name, method.name, method.desc, params)
|
||||
methods += data
|
||||
|
||||
val params = ArrayList<ParamTarget>(types.size)
|
||||
val data = Data(className, name, descriptor, params)
|
||||
methods += data
|
||||
|
||||
for (i in types.indices) {
|
||||
params += ParamTarget(currentIndex, i)
|
||||
currentIndex++
|
||||
|
||||
// Figure out if we should skip the next index
|
||||
val type = types[i]
|
||||
if (type === Type.LONG_TYPE || type === Type.DOUBLE_TYPE) {
|
||||
for (i in types.indices) {
|
||||
params += ParamTarget(currentIndex, i)
|
||||
currentIndex++
|
||||
|
||||
// Figure out if we should skip the next index
|
||||
val type = types[i]
|
||||
if (type === Type.LONG_TYPE || type === Type.DOUBLE_TYPE) {
|
||||
currentIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,45 +280,20 @@ object ParamIndexes {
|
|||
* can handle it in our generated mappings.
|
||||
*/
|
||||
object SyntheticMethods {
|
||||
class Visitor(
|
||||
classVisitor: ClassVisitor?,
|
||||
private val methods: MutableList<Data>
|
||||
) : BaseClassVisitor(classVisitor) {
|
||||
object Visitor : AsmUtil {
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor? {
|
||||
val ret = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
val className = currentClass ?: return ret
|
||||
fun visit(node: ClassNode, methods: MutableList<Data>) {
|
||||
for (method in node.methods) {
|
||||
if (Opcodes.ACC_SYNTHETIC !in method.access || Opcodes.ACC_BRIDGE in method.access || method.name.contains('$')) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (Opcodes.ACC_SYNTHETIC !in access || Opcodes.ACC_BRIDGE in access || name.contains('$')) {
|
||||
return ret
|
||||
}
|
||||
val (baseName, baseDesc) = SyntheticUtil.findBaseMethod(method, node.name)
|
||||
|
||||
return SynthMethodVisitor(access, name, descriptor, signature, exceptions, className, methods)
|
||||
}
|
||||
}
|
||||
|
||||
private class SynthMethodVisitor(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?,
|
||||
private val className: String,
|
||||
private val methods: MutableList<Data>
|
||||
) : MethodNode(Opcodes.ASM9, access, name, descriptor, signature, exceptions) {
|
||||
|
||||
override fun visitEnd() {
|
||||
val (baseName, baseDesc) = SyntheticUtil.findBaseMethod(this, className)
|
||||
|
||||
if (baseName != name || baseDesc != desc) {
|
||||
// Add this method as a synthetic for baseName
|
||||
methods += Data(className, baseDesc, name, baseName)
|
||||
if (baseName != method.name || baseDesc != method.desc) {
|
||||
// Add this method as a synthetic for baseName
|
||||
methods += Data(node.name, baseDesc, method.name, baseName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -362,3 +314,68 @@ object SyntheticMethods {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* There's no direct marker in bytecode to know if a
|
||||
*/
|
||||
object MethodOverrides {
|
||||
object Visitor : AsmUtil {
|
||||
|
||||
fun visit(
|
||||
node: ClassNode,
|
||||
classNodeCache: ClassNodeCache,
|
||||
methodOverrides: SimpleDirectedGraph<MethodRef, DefaultEdge>
|
||||
) {
|
||||
val superMethods = collectSuperMethods(node, classNodeCache)
|
||||
|
||||
val disqualifiedMethods = Opcodes.ACC_STATIC or Opcodes.ACC_PRIVATE
|
||||
for (method in node.methods) {
|
||||
if (method.access in disqualifiedMethods) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (method.name == "<init>" || method.name == "<clinit>") {
|
||||
continue
|
||||
}
|
||||
val (name, desc) = SyntheticUtil.findBaseMethod(method, node.name)
|
||||
|
||||
superMethods[method.name + method.desc]?.let { className ->
|
||||
val targetMethod = node.methods.firstOrNull { it.name == name && it.desc == desc } ?: method
|
||||
|
||||
val methodRef = MethodRef(node.name, method.name, method.desc)
|
||||
val targetMethodRef = MethodRef(className, targetMethod.name, targetMethod.desc)
|
||||
methodOverrides.addVertex(methodRef)
|
||||
methodOverrides.addVertex(targetMethodRef)
|
||||
methodOverrides.addEdge(methodRef, targetMethodRef)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectSuperMethods(node: ClassNode, classNodeCache: ClassNodeCache): Map<String, String> {
|
||||
fun collectSuperMethods(node: ClassNode, superMethods: HashMap<String, String>) {
|
||||
val supers = listOfNotNull(node.superName, *node.interfaces.toTypedArray())
|
||||
if (supers.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val disqualifiedMethods = Opcodes.ACC_STATIC or Opcodes.ACC_PRIVATE
|
||||
val superNodes = supers.mapNotNull { classNodeCache.findClass(it) }
|
||||
for (superNode in superNodes) {
|
||||
superNode.methods.asSequence()
|
||||
.filter { method -> method.access !in disqualifiedMethods }
|
||||
.filter { method -> method.name != "<init>" && method.name != "<clinit>" }
|
||||
.map { method -> method.name + method.desc to superNode.name }
|
||||
.toMap(superMethods)
|
||||
}
|
||||
|
||||
for (superNode in superNodes) {
|
||||
collectSuperMethods(superNode, superMethods)
|
||||
}
|
||||
}
|
||||
|
||||
val result = hashMapOf<String, String>()
|
||||
collectSuperMethods(node, result)
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
53
src/main/kotlin/util/ClassNodeCache.kt
Normal file
53
src/main/kotlin/util/ClassNodeCache.kt
Normal file
|
@ -0,0 +1,53 @@
|
|||
package io.papermc.paperweight.util
|
||||
|
||||
import java.util.jar.JarFile
|
||||
import java.util.zip.ZipEntry
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
class ClassNodeCache(private val jarFile: JarFile, private val fallbackJar: JarFile? = null) {
|
||||
|
||||
private val classNodeMap = hashMapOf<String, ClassNode?>()
|
||||
|
||||
fun findClass(name: String): ClassNode? {
|
||||
return classNodeMap.computeIfAbsent(normalize(name)) { fileName ->
|
||||
val classData = findClassData(fileName) ?: return@computeIfAbsent null
|
||||
val classReader = ClassReader(classData)
|
||||
val node = ClassNode(Opcodes.ASM9)
|
||||
classReader.accept(node, 0)
|
||||
return@computeIfAbsent node
|
||||
}
|
||||
}
|
||||
|
||||
private fun findClassData(className: String): ByteArray? {
|
||||
val entry = ZipEntry(className)
|
||||
return (
|
||||
jarFile.getInputStream(entry) // remapped class
|
||||
?: fallbackJar?.getInputStream(entry) // library class
|
||||
?: ClassLoader.getSystemResourceAsStream(className) // JDK class
|
||||
)?.use { it.readBytes() }
|
||||
}
|
||||
|
||||
private fun normalize(name: String): String {
|
||||
var workingName = name
|
||||
if (workingName.endsWith(".class")) {
|
||||
workingName = workingName.substring(0, workingName.length - 6)
|
||||
}
|
||||
|
||||
var startIndex = 0
|
||||
var endIndex = workingName.length
|
||||
if (workingName.startsWith('L')) {
|
||||
startIndex = 1
|
||||
}
|
||||
if (workingName.endsWith(';')) {
|
||||
endIndex--
|
||||
}
|
||||
|
||||
return workingName.substring(startIndex, endIndex).replace('.', '/') + ".class"
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
classNodeMap.clear()
|
||||
}
|
||||
}
|
40
src/main/kotlin/util/Methodref.kt
Normal file
40
src/main/kotlin/util/Methodref.kt
Normal file
|
@ -0,0 +1,40 @@
|
|||
package io.papermc.paperweight.util
|
||||
|
||||
import org.cadixdev.bombe.type.signature.MethodSignature
|
||||
import org.jgrapht.Graph
|
||||
import org.jgrapht.graph.DefaultEdge
|
||||
import org.jgrapht.graph.SimpleDirectedGraph
|
||||
import org.jgrapht.nio.dot.DOTImporter
|
||||
import org.jgrapht.nio.gml.GmlExporter
|
||||
import org.jgrapht.nio.gml.GmlImporter
|
||||
import org.jgrapht.nio.json.JSONExporter
|
||||
import org.jgrapht.nio.json.JSONImporter
|
||||
|
||||
data class MethodRef(val className: String, val methodName: String, val methodDesc: String) {
|
||||
companion object {
|
||||
fun from(className: String, sig: MethodSignature): MethodRef {
|
||||
return MethodRef(className, sig.name, sig.descriptor.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun writeOverrides(graph: Graph<MethodRef, DefaultEdge>, file: Any) {
|
||||
val exporter = JSONExporter<MethodRef, DefaultEdge> { ref -> "${ref.className} ${ref.methodName} ${ref.methodDesc}" }
|
||||
file.convertToFile().bufferedWriter().use { writer ->
|
||||
exporter.exportGraph(graph, writer)
|
||||
}
|
||||
}
|
||||
|
||||
fun readOverrides(file: Any): SimpleDirectedGraph<MethodRef, *> {
|
||||
val importer = JSONImporter<MethodRef, DefaultEdge>()
|
||||
importer.setVertexFactory {
|
||||
val (className, methodName, methodDesc) = it.split(" ")
|
||||
MethodRef(className, methodName, methodDesc)
|
||||
}
|
||||
val overrides = SimpleDirectedGraph<MethodRef, DefaultEdge>(DefaultEdge::class.java)
|
||||
file.convertToFile().bufferedReader().use { reader ->
|
||||
importer.importGraph(overrides, reader)
|
||||
}
|
||||
|
||||
return overrides
|
||||
}
|
Loading…
Reference in a new issue