Skip to content

Commit

Permalink
Add custom mapping parsers
Browse files Browse the repository at this point in the history
  • Loading branch information
skyrising committed Jul 23, 2021
1 parent 7dba98c commit 9ed4a53
Show file tree
Hide file tree
Showing 15 changed files with 764 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[*.tiny]
indent_style = tab
1 change: 1 addition & 0 deletions src/main/kotlin/de/skyrising/guardian/gen/json.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package de.skyrising.guardian.gen

import com.google.gson.*
import com.google.gson.reflect.TypeToken
import de.skyrising.guardian.gen.mappings.MappingProvider
import java.io.Reader
import java.lang.reflect.Type
import java.net.URI
Expand Down
37 changes: 37 additions & 0 deletions src/main/kotlin/de/skyrising/guardian/gen/mappings/formats.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package de.skyrising.guardian.gen.mappings

import java.io.BufferedReader
import java.util.stream.Stream

interface MappingsParser {
fun parse(reader: BufferedReader): MappingTree
}

interface MappingsWriter {
fun write(mappings: MappingTree, writer: Appendable)
}

interface MappingsFormat : MappingsParser, MappingsWriter

abstract class LineBasedMappingFormat<T> : MappingsFormat {
fun parse(initialState: T?, reader: BufferedReader): MappingTree {
var state: T? = initialState
reader.lineSequence().forEach {
state = readLine(state, it)
}
return decodeState(state!!)
}

override fun parse(reader: BufferedReader) = parse(null, reader)

override fun write(mappings: MappingTree, writer: Appendable) {
getLines(mappings).forEach { writer.appendln(it) }
}

abstract fun readLine(state: T?, line: String): T
abstract fun decodeState(state: T): MappingTree
abstract fun getLines(tree: MappingTree): Stream<String>
}

inline fun String.toDotted() = this.replace('/', '.')
inline fun String.toSlashed() = this.replace('.', '/')
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package de.skyrising.guardian.gen
package de.skyrising.guardian.gen.mappings

import com.google.gson.JsonArray
import cuchaz.enigma.ProgressListener
import cuchaz.enigma.translation.mapping.EntryMapping
import cuchaz.enigma.translation.mapping.serde.MappingFormat
import cuchaz.enigma.translation.mapping.tree.EntryTree
import cuchaz.enigma.translation.mapping.tree.HashEntryTree
import de.skyrising.guardian.gen.*
import java.net.URI
import java.nio.file.FileSystem
import java.nio.file.Files
Expand All @@ -14,7 +15,6 @@ import java.util.concurrent.CompletableFuture

val JARS_MAPPED_DIR: Path = JARS_DIR.resolve("mapped")


interface MappingProvider {
val name: String
val format: MappingFormat
Expand Down
144 changes: 144 additions & 0 deletions src/main/kotlin/de/skyrising/guardian/gen/mappings/proguard.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package de.skyrising.guardian.gen.mappings

import jdk.internal.org.objectweb.asm.Type
import java.util.stream.Stream

object ProguardMappings : LineBasedMappingFormat<Pair<MappingTree, ClassMapping?>>() {
override fun readLine(state: Pair<MappingTree, ClassMapping?>?, line: String): Pair<MappingTree, ClassMapping?> {
val mappings = state?.first ?: MappingTree(arrayOf("named", "official"))
var currentClass = state?.second
when(line[0]) {
'#' -> {}
' ' -> parseMember(currentClass, line)
else -> {
val cls = parseClass(line)
currentClass = cls
mappings.classes.add(cls)
}
}
return Pair(mappings, currentClass)
}

override fun decodeState(state: Pair<MappingTree, ClassMapping?>) = state.first

override fun getLines(tree: MappingTree): Stream<String> = tree.classes.stream().flatMap { cls ->
Stream.of(
Stream.of("${cls[0].toDotted()} -> ${cls[1].toDotted()}:"),
cls.fields.stream().map { f ->
" ${getTypeName(f.defaultName.type)} ${f[0]} -> ${f[1]}"
},
cls.methods.stream().map { m ->
val lineFrom = if (m is ProguardMethodMapping) m.lineFrom else 0
val lineTo = if (m is ProguardMethodMapping) m.lineTo else 0
val type = Type.getMethodType(m.defaultName.type)
val sb = StringBuilder(" ")
sb.append(lineFrom).append(':').append(lineTo).append(':')
sb.append(getTypeName(type.returnType.descriptor)).append(' ')
sb.append(m[0]).append('(')
var first = true
for (arg in type.argumentTypes) {
if (first) first = false
else sb.append(',')
sb.append(getTypeName(arg.descriptor))
}
sb.append(") -> ").append(m[1])
sb.toString()
}
).flatMap { it }
}

private fun parseClass(line: String): ClassMapping {
val arrow = line.indexOf(" -> ")
if (arrow < 0 || line[line.lastIndex] != ':') throw IllegalArgumentException("Expected '->' and ':' for class mapping")
val from = line.substring(0, arrow).toSlashed()
val to = line.substring(arrow + 4, line.lastIndex).toSlashed()
return ClassMapping(arrayOf(from, to))
}

private fun parseMember(currentClass: ClassMapping?, line: String) {
if (currentClass == null) throw IllegalStateException("Cannot parse class member without a class")
if (!line.startsWith(" ")) throw IllegalArgumentException("Expected line to start with ' '")
when (line[4]) {
in '0' .. '9' -> parseMethod(currentClass, line)
in 'a' .. 'z', in 'A' .. 'Z' -> parseField(currentClass, line)
else -> throw IllegalArgumentException("Expected method or field mapping, got '$line'")
}
}

private fun parseField(currentClass: ClassMapping, line: String) {
val space = line.indexOf(' ', 4)
val arrow = line.indexOf(" -> ", space + 1)
if (space < 0 || arrow < 0) throw IllegalArgumentException("Invalid field mapping '$line'")
val fieldType = getTypeDescriptor(line.substring(4, space))
val from = line.substring(space + 1, arrow)
val to = line.substring(arrow + 4)
currentClass.fields.add(FieldMappingImpl(MemberDescriptor(from, fieldType), arrayOf(from, to)))
}

private fun parseMethod(currentClass: ClassMapping, line: String) {
val colon1 = line.indexOf(':', 4)
val colon2 = line.indexOf(':', colon1 + 1)
val space = line.indexOf(' ', colon2 + 1)
val openParen = line.indexOf('(', space + 1)
val closeParenArrow = line.indexOf(") -> ", openParen + 1)
if (colon1 < 0 || colon2 < 0 || space < 0 || openParen < 0 || closeParenArrow < 0) {
throw IllegalArgumentException("Invalid method mapping '$line'")
}
val lineFrom = line.substring(4, colon1).toInt()
val lineTo = line.substring(colon1 + 1, colon2).toInt()
val from = line.substring(space + 1, openParen)
val desc = StringBuilder("(")
var i = openParen + 1
while (i < closeParenArrow) {
var argEnd = line.indexOf(',', i)
if (argEnd < 0) argEnd = closeParenArrow
desc.append(getTypeDescriptor(line.substring(i, argEnd)))
i = argEnd + 1
}
desc.append(")").append(getTypeDescriptor(line.substring(colon2 + 1, space)))
val to = line.substring(closeParenArrow + 5)
currentClass.methods.add(ProguardMethodMapping(lineFrom, lineTo, MemberDescriptor(from, desc.toString()), arrayOf(from, to)))
}

private fun getTypeDescriptor(type: String): String = when {
type.endsWith("[]") -> "[" + getTypeDescriptor(type.substring(0, type.length - 2))
type in PRIMITIVE_DESCRIPTORS -> PRIMITIVE_DESCRIPTORS[type]!!
else -> "L" + type.toSlashed() + ";"
}

private fun getTypeName(type: String): String = when {
type.startsWith("[") -> getTypeName(type.substring(1)) + "[]"
type in PRIMITIVE_NAMES -> PRIMITIVE_NAMES[type]!!
type.startsWith("L") -> type.substring(1, type.length - 1).toDotted()
else -> throw IllegalArgumentException(type)
}

private val PRIMITIVE_DESCRIPTORS = mapOf(
"byte" to "B",
"char" to "C",
"double" to "D",
"float" to "F",
"int" to "I",
"long" to "J",
"short" to "S",
"void" to "V",
"boolean" to "Z"
)

private val PRIMITIVE_NAMES = mapOf(
"B" to "byte",
"C" to "char",
"D" to "double",
"F" to "float",
"I" to "int",
"J" to "long",
"S" to "short",
"V" to "void",
"Z" to "boolean"
)
}

class ProguardMethodMapping(val lineFrom: Int, val lineTo: Int, defaultName: MemberDescriptor, names: Array<String>)
: ArrayMemberMapping(defaultName, names), MethodMapping {
override fun invert(size: Int, index: Int, tree: MappingTree): ProguardMethodMapping = ProguardMethodMapping(lineFrom, lineTo, invertDefaultName(index, tree), invertNames(size, index))
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.skyrising.guardian.gen
package de.skyrising.guardian.gen.mappings

import cuchaz.enigma.translation.mapping.EntryMapping
import cuchaz.enigma.translation.mapping.tree.EntryTree
Expand All @@ -7,6 +7,7 @@ import cuchaz.enigma.translation.representation.TypeDescriptor
import cuchaz.enigma.translation.representation.entry.ClassEntry
import cuchaz.enigma.translation.representation.entry.FieldEntry
import cuchaz.enigma.translation.representation.entry.MethodEntry
import de.skyrising.guardian.gen.*
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes
Expand Down
Loading

0 comments on commit 9ed4a53

Please sign in to comment.