fix remaining issues, improve speed
This commit is contained in:
parent
80a9fc3fdc
commit
67c253f2f0
|
@ -26,7 +26,7 @@ dependencies {
|
||||||
implementation("com.google.code.gson:gson:2.10.1")
|
implementation("com.google.code.gson:gson:2.10.1")
|
||||||
implementation("org.vineflower:vineflower:1.10.1")
|
implementation("org.vineflower:vineflower:1.10.1")
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
implementation("com.electronwill.night-config:toml:3.7.1")
|
implementation("com.electronwill.night-config:toml:3.7.2")
|
||||||
implementation("com.google.jimfs:jimfs:1.3.0")
|
implementation("com.google.jimfs:jimfs:1.3.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,15 @@ import org.gradle.api.Project
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
import org.objectweb.asm.*
|
import org.objectweb.asm.*
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.*
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.attribute.BasicFileAttributes
|
import java.nio.file.Path
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.ConcurrentSkipListSet
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
import java.util.stream.Stream
|
||||||
import kotlin.io.path.bufferedReader
|
import kotlin.io.path.bufferedReader
|
||||||
|
import kotlin.io.path.exists
|
||||||
import kotlin.io.path.readBytes
|
import kotlin.io.path.readBytes
|
||||||
import kotlin.io.path.readLines
|
|
||||||
import kotlin.io.path.writeBytes
|
import kotlin.io.path.writeBytes
|
||||||
|
|
||||||
object AccessWidener {
|
object AccessWidener {
|
||||||
|
@ -21,7 +24,6 @@ object AccessWidener {
|
||||||
|
|
||||||
private var awHash = ""
|
private var awHash = ""
|
||||||
private val HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate()
|
private val HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate()
|
||||||
private val COMMENT = Pattern.compile("^#.*").asMatchPredicate()
|
|
||||||
private val SEPARATOR = "[\\t ]+".toRegex()
|
private val SEPARATOR = "[\\t ]+".toRegex()
|
||||||
|
|
||||||
private fun getAWFile(project: Project): Path? {
|
private fun getAWFile(project: Project): Path? {
|
||||||
|
@ -58,37 +60,39 @@ object AccessWidener {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readAW(project: Project): List<String> {
|
private fun readAW(project: Project): Stream<String> {
|
||||||
val awFile = getAWFile(project)
|
val awFile = getAWFile(project)
|
||||||
println("Found accesswidener in project: $awFile")
|
println("Found accesswidener in project: $awFile")
|
||||||
return awFile?.readLines(StandardCharsets.UTF_8)?.map { it.replace("transitive-", "") }?.toList()
|
return awFile?.bufferedReader(StandardCharsets.UTF_8)?.lines()?.map { it.replace("transitive-", "") }
|
||||||
?: listOf()
|
?: Stream.empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readTransitiveAW(path: Path): List<String> {
|
private fun readTransitiveAW(path: Path): Stream<String> {
|
||||||
return path.bufferedReader(StandardCharsets.UTF_8).lines().filter { it.startsWith("transitive-") }.toList()
|
return path.bufferedReader(StandardCharsets.UTF_8).lines().filter { it.startsWith("transitive-") }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseAW(entries: List<String>): List<Entry> {
|
private fun readAllAWs(project: Project): Stream<Entry> {
|
||||||
return entries.asSequence().filter { it.isNotBlank() }
|
return Stream.concat(
|
||||||
.filter { !COMMENT.test(it) }.filter { !HEADER.test(it) }.distinct()
|
findDependencyAWs(project).parallelStream().flatMap { i -> readTransitiveAW(i) },
|
||||||
.map { it.split(SEPARATOR) }.filter { it.isNotEmpty() } .map { Entry(it) }.toList()
|
readAW(project)
|
||||||
}
|
)
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
private fun readAllAWs(project: Project): List<Entry> {
|
.map { if (it.contains("#")) it.split("#")[0] else it }.filter { !HEADER.test(it) }
|
||||||
return findDependencyAWs(project).flatMap { readTransitiveAW(it) }.plus(readAW(project))
|
.filter { it.isNotBlank() }
|
||||||
.let { parseAW(it) }
|
.unordered().distinct()
|
||||||
|
.map { it.split(SEPARATOR) }.filter { it.isNotEmpty() }.map { Entry(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun apply(project: Project, input: Path) {
|
fun apply(project: Project, input: Path) {
|
||||||
val startTime = System.currentTimeMillis()
|
val startTime = System.currentTimeMillis()
|
||||||
val entries: List<Entry> = readAllAWs(project)
|
val classNames: MutableSet<String> = ConcurrentSkipListSet()
|
||||||
|
val classMap: MutableMap<String, Entry> = ConcurrentHashMap()
|
||||||
val classMap: MutableMap<String, Entry> = mutableMapOf()
|
val methods: MutableMap<String, MutableMap<String, Entry>> = ConcurrentHashMap()
|
||||||
val methods: MutableMap<String, MutableMap<String, Entry>> = mutableMapOf()
|
val fields: MutableMap<String, MutableMap<String, Entry>> = ConcurrentHashMap()
|
||||||
val fields: MutableMap<String, MutableMap<String, Entry>> = mutableMapOf()
|
val mutations: MutableMap<String, MutableMap<String, Entry>> = ConcurrentHashMap()
|
||||||
val mutations: MutableMap<String, MutableMap<String, Entry>> = mutableMapOf()
|
val entries = readAllAWs(project)
|
||||||
entries.forEach { e ->
|
entries.forEach { e ->
|
||||||
|
classNames.add(e.className)
|
||||||
if ("class" == e.targetType) {
|
if ("class" == e.targetType) {
|
||||||
if (e.type == AccessType.MUTABLE) {
|
if (e.type == AccessType.MUTABLE) {
|
||||||
throw IllegalArgumentException("aw format error: classes can not have a 'mutable' modifier (at: $e)")
|
throw IllegalArgumentException("aw format error: classes can not have a 'mutable' modifier (at: $e)")
|
||||||
|
@ -105,7 +109,7 @@ object AccessWidener {
|
||||||
if (e.type == AccessType.MUTABLE) {
|
if (e.type == AccessType.MUTABLE) {
|
||||||
throw IllegalArgumentException("aw format error: methods can not have a 'mutable' modifier (at: $e)")
|
throw IllegalArgumentException("aw format error: methods can not have a 'mutable' modifier (at: $e)")
|
||||||
}
|
}
|
||||||
val map = methods.computeIfAbsent(e.className) { mutableMapOf() }
|
val map = methods.computeIfAbsent(e.className) { ConcurrentHashMap() }
|
||||||
val id = e.name + e.descriptor
|
val id = e.name + e.descriptor
|
||||||
if (!map.containsKey(id)) {
|
if (!map.containsKey(id)) {
|
||||||
map[id] = e
|
map[id] = e
|
||||||
|
@ -119,13 +123,11 @@ object AccessWidener {
|
||||||
if (e.type == AccessType.EXTENDABLE) {
|
if (e.type == AccessType.EXTENDABLE) {
|
||||||
throw IllegalArgumentException("aw format error: fields can not have a 'extendable' modifier (at: $e)")
|
throw IllegalArgumentException("aw format error: fields can not have a 'extendable' modifier (at: $e)")
|
||||||
}
|
}
|
||||||
val map = fields.computeIfAbsent(e.className) { mutableMapOf() }
|
val map = fields.computeIfAbsent(e.className) { ConcurrentHashMap() }
|
||||||
val id = e.name + e.descriptor
|
val id = e.name + e.descriptor
|
||||||
if (e.type == AccessType.MUTABLE) {
|
if (e.type == AccessType.MUTABLE) {
|
||||||
mutations.computeIfAbsent(e.className) { mutableMapOf() }.putIfAbsent(id, e)
|
mutations.computeIfAbsent(e.className) { ConcurrentHashMap() }.putIfAbsent(id, e)
|
||||||
return
|
} else {
|
||||||
}
|
|
||||||
|
|
||||||
if (!map.containsKey(id)) {
|
if (!map.containsKey(id)) {
|
||||||
map[id] = e
|
map[id] = e
|
||||||
} else {
|
} else {
|
||||||
|
@ -136,11 +138,28 @@ object AccessWidener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FileSystems.newFileSystem(input).use {
|
FileSystems.newFileSystem(input).use {
|
||||||
Files.walkFileTree(it.getPath("/"), AWFileVisitor(classMap, fields, methods, mutations))
|
classNames.parallelStream().forEach { className ->
|
||||||
|
val file = it.getPath("/$className.class")
|
||||||
|
if (file.exists()) {
|
||||||
|
val reader = ClassReader(file.readBytes())
|
||||||
|
val writer = ClassWriter(0)
|
||||||
|
val mapper = AWClassVisitor(writer, classMap, fields, methods, mutations, className)
|
||||||
|
|
||||||
|
reader.accept(mapper, 0)
|
||||||
|
file.writeBytes(writer.toByteArray())
|
||||||
}
|
}
|
||||||
println("Finished widening ${classMap.size} classes, ${methods.size} methods and ${fields.size+mutations.size} fields in ${"%2f".format(System.currentTimeMillis()-startTime)}")
|
}
|
||||||
|
}
|
||||||
|
println(
|
||||||
|
"Applied AccessWideners in ${
|
||||||
|
"%.2fs".format(
|
||||||
|
(System.currentTimeMillis() - startTime) / 1000f
|
||||||
|
)
|
||||||
|
}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +190,8 @@ enum class AccessType(val id: String, val access: Int) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun of(name: String): AccessType {
|
fun of(name: String): AccessType {
|
||||||
return entries.first { a -> a.id == name }
|
return entries.firstOrNull { a -> a.id == name }
|
||||||
|
?: throw IllegalArgumentException("Access type '$name' is not known. Known types: ${entries.map { it.id }} (and transitive variants)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,31 +215,6 @@ private enum class Access(val index: Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AWFileVisitor(
|
|
||||||
private val classMap: Map<String, Entry>,
|
|
||||||
private val fields: Map<String, Map<String, Entry>>,
|
|
||||||
private val methods: Map<String, Map<String, Entry>>,
|
|
||||||
private val mutations: Map<String, Map<String, Entry>>,
|
|
||||||
) : SimpleFileVisitor<Path>() {
|
|
||||||
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
|
||||||
if (file.fileName.toString().endsWith(".class")) {
|
|
||||||
val className = file.toString().substring(1, file.toString().length - 6)
|
|
||||||
|
|
||||||
if (classMap.containsKey(className) || fields.containsKey(className) || methods.containsKey(className) || mutations.containsKey(className)) {
|
|
||||||
|
|
||||||
val reader = ClassReader(file.readBytes())
|
|
||||||
val writer = ClassWriter(0)
|
|
||||||
val mapper = AWClassVisitor(writer, classMap, fields, methods, mutations, className)
|
|
||||||
|
|
||||||
reader.accept(mapper, 0)
|
|
||||||
file.writeBytes(writer.toByteArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return FileVisitResult.CONTINUE
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class AWClassVisitor(
|
class AWClassVisitor(
|
||||||
visitor: ClassVisitor,
|
visitor: ClassVisitor,
|
||||||
private val classMap: Map<String, Entry>,
|
private val classMap: Map<String, Entry>,
|
||||||
|
@ -239,14 +234,15 @@ class AWClassVisitor(
|
||||||
var access = acc
|
var access = acc
|
||||||
val e = classMap[className]
|
val e = classMap[className]
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
access = access and ((Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv())
|
access = access and (Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv()
|
||||||
access = access.or(e.type.access)
|
access = access or e.type.access
|
||||||
} else if (fields.containsKey(className) || methods.containsKey(className) || mutations.containsKey(
|
}
|
||||||
|
if (fields.containsKey(className) || methods.containsKey(className) || mutations.containsKey(
|
||||||
className
|
className
|
||||||
)
|
)
|
||||||
) { // make all classes with modifications public as well
|
) { // make all classes with modifications public as well
|
||||||
access = access and ((Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv())
|
access = access and (Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv()
|
||||||
access = access.or(Opcodes.ACC_PUBLIC)
|
access = access or Opcodes.ACC_PUBLIC
|
||||||
}
|
}
|
||||||
super.visit(version, access, name, signature, superName, interfaces)
|
super.visit(version, access, name, signature, superName, interfaces)
|
||||||
}
|
}
|
||||||
|
@ -263,15 +259,16 @@ class AWClassVisitor(
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
val e = map[name + descriptor]
|
val e = map[name + descriptor]
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
access = access and ((Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv())// remove all access modifiers
|
access =
|
||||||
access = access.or(e.type.access)// re-add the new one
|
access and (Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv() // remove all access modifiers
|
||||||
|
access = access or e.type.access // re-add the new one
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map = mutations[className]
|
map = mutations[className]
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
val e = map[name + descriptor]
|
val e = map[name + descriptor]
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
access = access.or(Opcodes.ACC_FINAL.inv()) // always AccessType.MUTABLE
|
access = access and Opcodes.ACC_FINAL.inv() // always AccessType.MUTABLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visitField(access, name, descriptor, signature, value)
|
return super.visitField(access, name, descriptor, signature, value)
|
||||||
|
@ -289,8 +286,9 @@ class AWClassVisitor(
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
val e = map[name + descriptor]
|
val e = map[name + descriptor]
|
||||||
if (e != null) {
|
if (e != null) {
|
||||||
access = access and ((Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv())// remove all access modifiers
|
access =
|
||||||
access = access.or(e.type.access)// re-add the new one
|
access and ((Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv())// remove all access modifiers
|
||||||
|
access = access or (e.type.access)// re-add the new one
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visitMethod(access, name, descriptor, signature, exceptions)
|
return super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||||
|
|
|
@ -28,8 +28,8 @@ fun Project.minecraft(
|
||||||
val remappedJar = clientJar.resolveSibling("client-$version-remapped.jar")
|
val remappedJar = clientJar.resolveSibling("client-$version-remapped.jar")
|
||||||
PhytotelmaPlugin.remappedGameJarPath = remappedJar
|
PhytotelmaPlugin.remappedGameJarPath = remappedJar
|
||||||
println("Time to setup Minecraft!")
|
println("Time to setup Minecraft!")
|
||||||
var applyAW = false
|
val applyAW = AccessWidener.needsUpdate(this)
|
||||||
if (remappedJar.notExists() || AccessWidener.needsUpdate(this)) {
|
if (remappedJar.notExists() || applyAW) {
|
||||||
println("Remapping the game...")
|
println("Remapping the game...")
|
||||||
val data = ProguardParser.read(MojmapProvider.get(version, clientJar.resolveSibling("client-$version.txt")).orElseThrow()).reverse()
|
val data = ProguardParser.read(MojmapProvider.get(version, clientJar.resolveSibling("client-$version.txt")).orElseThrow()).reverse()
|
||||||
val paramMappings = ParchmentProvider.getParchment(
|
val paramMappings = ParchmentProvider.getParchment(
|
||||||
|
@ -37,7 +37,6 @@ fun Project.minecraft(
|
||||||
PhytotelmaPlugin.nonsenseCacheDir.resolve("org/parchmentmc/parchment/$version/$parchmentVersion")
|
PhytotelmaPlugin.nonsenseCacheDir.resolve("org/parchmentmc/parchment/$version/$parchmentVersion")
|
||||||
)
|
)
|
||||||
NonsenseRemapper.remap(data, clientJar, remappedJar, true, paramMappings)
|
NonsenseRemapper.remap(data, clientJar, remappedJar, true, paramMappings)
|
||||||
applyAW = true
|
|
||||||
}
|
}
|
||||||
println("Adding dependencies...")
|
println("Adding dependencies...")
|
||||||
dependencies.add("implementation","net.minecrell:terminalconsoleappender:1.2.0")
|
dependencies.add("implementation","net.minecrell:terminalconsoleappender:1.2.0")
|
||||||
|
|
Loading…
Reference in a new issue