mirror of https://github.com/FabricMC/yarn.git
Migrate name proposal code to filament
This commit is contained in:
parent
70d538edef
commit
24a83e4ae9
12
build.gradle
12
build.gradle
|
@ -9,7 +9,6 @@ buildscript {
|
|||
}
|
||||
dependencies {
|
||||
classpath "cuchaz:enigma-cli:${project.enigma_version}"
|
||||
classpath "net.fabricmc:name-proposal:${project.name_proposal_version}"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +70,6 @@ def unpickMetaFile = file("unpick-definitions/unpick.json")
|
|||
|
||||
dependencies {
|
||||
enigmaRuntime "cuchaz:enigma-swing:${project.enigma_version}"
|
||||
enigmaRuntime "net.fabricmc:name-proposal:${project.name_proposal_version}"
|
||||
enigmaRuntime "net.fabricmc:cfr:${project.cfr_version}"
|
||||
javadocClasspath "net.fabricmc:fabric-loader:${project.fabric_loader_version}"
|
||||
javadocClasspath "org.jetbrains:annotations:${project.jetbrains_annotations_version}"
|
||||
|
@ -106,8 +104,8 @@ import net.fabricmc.filament.task.mappingio.CompleteMappingsTask
|
|||
import net.fabricmc.filament.task.mappingio.ConvertMappingsTask
|
||||
import net.fabricmc.filament.task.mappingio.FormatMappingsTask
|
||||
import net.fabricmc.filament.task.mappingio.MergeMappingsTask
|
||||
import net.fabricmc.filament.nameproposal.MappingNameCompleter
|
||||
import net.fabricmc.mappingio.format.MappingFormat
|
||||
import net.fabricmc.nameproposal.MappingNameCompleter
|
||||
import org.gradle.work.DisableCachingByDefault
|
||||
import com.diffplug.spotless.LineEnding
|
||||
import groovy.xml.XmlSlurper
|
||||
|
@ -624,7 +622,13 @@ abstract class EnigmaTask extends JavaExec {
|
|||
abstract Property<File> getMappings()
|
||||
|
||||
EnigmaTask() {
|
||||
classpath = project.configurations.enigmaRuntime
|
||||
def filamentCodeSource = MappingPoetTask.class.getProtectionDomain().getCodeSource()
|
||||
def filamentJarFile = new File(filamentCodeSource.getLocation().getFile())
|
||||
def runtimeClasspath = project.files()
|
||||
runtimeClasspath.from(project.configurations.enigmaRuntime)
|
||||
runtimeClasspath.from(filamentJarFile)
|
||||
|
||||
classpath = runtimeClasspath
|
||||
mainClass.set('cuchaz.enigma.gui.Main')
|
||||
jvmArgs "-Xmx2048m"
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
filament_version=0.8.0
|
||||
filament_version=0.9.0
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
public class Constants {
|
||||
public static int ASM_VERSION = Opcodes.ASM9;
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.LdcInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.analysis.Analyzer;
|
||||
import org.objectweb.asm.tree.analysis.AnalyzerException;
|
||||
import org.objectweb.asm.tree.analysis.Frame;
|
||||
import org.objectweb.asm.tree.analysis.SourceInterpreter;
|
||||
import org.objectweb.asm.tree.analysis.SourceValue;
|
||||
|
||||
public class FieldNameFinder {
|
||||
public Map<MappingEntry, String> findNames(Iterable<byte[]> classes) throws Exception {
|
||||
Map<String, List<MethodNode>> methods = new HashMap<>();
|
||||
Map<String, Set<String>> enumFields = new HashMap<>();
|
||||
|
||||
for (byte[] data : classes) {
|
||||
ClassReader reader = new ClassReader(data);
|
||||
NameFinderVisitor vClass = new NameFinderVisitor(Constants.ASM_VERSION, enumFields, methods);
|
||||
reader.accept(vClass, ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
|
||||
return findNames(enumFields, methods);
|
||||
}
|
||||
|
||||
public Map<MappingEntry, String> findNames(Map<String, Set<String>> allEnumFields, Map<String, List<MethodNode>> classes) {
|
||||
Analyzer<SourceValue> analyzer = new Analyzer<>(new SourceInterpreter());
|
||||
Map<MappingEntry, String> fieldNames = new HashMap<>();
|
||||
Map<String, Set<String>> fieldNamesUsed = new HashMap<>();
|
||||
Map<String, Set<String>> fieldNamesDuplicate = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, List<MethodNode>> entry : classes.entrySet()) {
|
||||
String owner = entry.getKey();
|
||||
Set<String> enumFields = allEnumFields.getOrDefault(owner, Collections.emptySet());
|
||||
|
||||
for (MethodNode mn : entry.getValue()) {
|
||||
Frame<SourceValue>[] frames;
|
||||
|
||||
try {
|
||||
frames = analyzer.analyze(owner, mn);
|
||||
} catch (AnalyzerException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
InsnList instrs = mn.instructions;
|
||||
|
||||
for (int i = 1; i < instrs.size(); i++) {
|
||||
AbstractInsnNode instr1 = instrs.get(i - 1);
|
||||
AbstractInsnNode instr2 = instrs.get(i);
|
||||
String s = null;
|
||||
|
||||
if (instr2.getOpcode() == Opcodes.PUTSTATIC && ((FieldInsnNode) instr2).owner.equals(owner)
|
||||
&& (instr1 instanceof MethodInsnNode && ((MethodInsnNode) instr1).owner.equals(owner) || enumFields.contains(((FieldInsnNode) instr2).desc + ((FieldInsnNode) instr2).name))
|
||||
&& (instr1.getOpcode() == Opcodes.INVOKESTATIC || (instr1.getOpcode() == Opcodes.INVOKESPECIAL && "<init>".equals(((MethodInsnNode) instr1).name)))) {
|
||||
for (int j = 0; j < frames[i - 1].getStackSize(); j++) {
|
||||
SourceValue sv = frames[i - 1].getStack(j);
|
||||
|
||||
for (AbstractInsnNode ci : sv.insns) {
|
||||
if (ci instanceof LdcInsnNode && ((LdcInsnNode) ci).cst instanceof String) {
|
||||
//if (s == null || !s.equals(((LdcInsnNode) ci).cst)) {
|
||||
if (s == null) {
|
||||
s = (String) (((LdcInsnNode) ci).cst);
|
||||
// stringsFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s != null) {
|
||||
if (s.contains(":")) {
|
||||
s = s.substring(s.indexOf(':') + 1);
|
||||
}
|
||||
|
||||
if (s.contains("/")) {
|
||||
int separator = s.indexOf('/');
|
||||
String sFirst = s.substring(0, separator);
|
||||
String sLast;
|
||||
|
||||
if (s.contains(".") && s.indexOf('.') > separator) {
|
||||
sLast = s.substring(separator + 1, s.indexOf('.'));
|
||||
} else {
|
||||
sLast = s.substring(separator + 1);
|
||||
}
|
||||
|
||||
if (sFirst.endsWith("s")) {
|
||||
sFirst = sFirst.substring(0, sFirst.length() - 1);
|
||||
}
|
||||
|
||||
s = sLast + "_" + sFirst;
|
||||
}
|
||||
|
||||
String oldS = s;
|
||||
boolean hasAlpha = false;
|
||||
|
||||
for (int j = 0; j < s.length(); j++) {
|
||||
char c = s.charAt(j);
|
||||
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
|
||||
hasAlpha = true;
|
||||
}
|
||||
|
||||
if (!(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && !(c >= '0' && c <= '9') && !(c == '_')) {
|
||||
s = s.substring(0, j) + "_" + s.substring(j + 1);
|
||||
} else if (j > 0 && Character.isUpperCase(s.charAt(j)) && Character.isLowerCase(s.charAt(j - 1))) {
|
||||
s = s.substring(0, j) + "_" + s.substring(j, j + 1).toLowerCase(Locale.ROOT) + s.substring(j + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasAlpha) {
|
||||
s = s.toUpperCase(Locale.ROOT);
|
||||
|
||||
Set<String> usedNames = fieldNamesUsed.computeIfAbsent(((FieldInsnNode) instr2).owner, (a) -> new HashSet<>());
|
||||
Set<String> usedNamesDuplicate = fieldNamesDuplicate.computeIfAbsent(((FieldInsnNode) instr2).owner, (a) -> new HashSet<>());
|
||||
|
||||
if (!usedNamesDuplicate.contains(s)) {
|
||||
if (!usedNames.add(s)) {
|
||||
System.out.println("Warning: Duplicate key: " + s + " (" + oldS + ")!");
|
||||
usedNamesDuplicate.add(s);
|
||||
usedNames.remove(s);
|
||||
}
|
||||
}
|
||||
|
||||
if (usedNames.contains(s)) {
|
||||
fieldNames.put(new MappingEntry(((FieldInsnNode) instr2).owner, ((FieldInsnNode) instr2).name, ((FieldInsnNode) instr2).desc), s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fieldNames;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal;
|
||||
|
||||
public record MappingEntry(String owner, String name, String desc) {
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import net.fabricmc.mappingio.MappedElementKind;
|
||||
import net.fabricmc.mappingio.MappingReader;
|
||||
import net.fabricmc.mappingio.MappingWriter;
|
||||
import net.fabricmc.mappingio.format.MappingFormat;
|
||||
import net.fabricmc.mappingio.tree.MappingTree;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
public class MappingNameCompleter {
|
||||
// <intermediaryJar> <inputYarnMappings> <inputIntermediaryMappings> <outputYarnMappings>
|
||||
public static void main(String[] args) throws IOException {
|
||||
completeNames(Paths.get(args[0]), Paths.get(args[1]), Paths.get(args[2]), Paths.get(args[3]));
|
||||
}
|
||||
|
||||
public static void completeNames(Path intermediaryJar, Path inputYarnMappings, Path inputIntermediaryMappings, Path outputYarnMappings) throws IOException {
|
||||
NameFinder nameFinder = new NameFinder();
|
||||
|
||||
acceptJar(nameFinder, intermediaryJar);
|
||||
|
||||
// We need the full intermediary mappings on their own to lookup the record component's root fields/methods.
|
||||
nameFinder.acceptIntermediaryMappings(readMappings(inputIntermediaryMappings));
|
||||
|
||||
Map<MappingEntry, String> fieldNames = nameFinder.getFieldNames();
|
||||
Map<MappingEntry, String> methodNames = nameFinder.getMethodNames();
|
||||
Map<String, String> recordNames = nameFinder.getRecordNames();
|
||||
|
||||
System.out.printf("Found %d field names%n", fieldNames.size());
|
||||
System.out.printf("Found %d method names%n", methodNames.size());
|
||||
System.out.printf("Found %d record names%n", recordNames.size());
|
||||
|
||||
final MemoryMappingTree yarn = readMappings(inputYarnMappings);
|
||||
final int yarnIntermediaryNs = yarn.getNamespaceId("intermediary");
|
||||
final int yarnNamedNs = yarn.getNamespaceId("named");
|
||||
|
||||
for (Map.Entry<MappingEntry, String> entry : fieldNames.entrySet()) {
|
||||
MappingEntry mappingEntry = entry.getKey();
|
||||
|
||||
// Ensure there is a class mapping for this
|
||||
yarn.visitClass(mappingEntry.owner());
|
||||
|
||||
MemoryMappingTree.ClassMapping classMapping = yarn.getClass(mappingEntry.owner(), yarnIntermediaryNs);
|
||||
|
||||
yarn.visitField(mappingEntry.name(), mappingEntry.desc());
|
||||
MappingTree.FieldMapping fieldMapping = Objects.requireNonNull(classMapping.getField(mappingEntry.name(), mappingEntry.desc(), yarnIntermediaryNs), "Could not find field");
|
||||
String yarnFieldName = fieldMapping.getName(yarnNamedNs);
|
||||
|
||||
if (yarnFieldName == null || yarnFieldName.startsWith("field_") || yarnFieldName.startsWith("comp_")) {
|
||||
// Set a new dst name if it doesn't have one, or matches intermediary
|
||||
yarn.visitDstName(MappedElementKind.FIELD, yarnNamedNs, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<MappingEntry, String> entry : methodNames.entrySet()) {
|
||||
MappingEntry mappingEntry = entry.getKey();
|
||||
|
||||
// Ensure there is a class mapping for this
|
||||
yarn.visitClass(mappingEntry.owner());
|
||||
|
||||
MemoryMappingTree.ClassMapping classMapping = yarn.getClass(mappingEntry.owner(), yarnIntermediaryNs);
|
||||
|
||||
yarn.visitMethod(mappingEntry.name(), mappingEntry.desc());
|
||||
MappingTree.MethodMapping methodMapping = Objects.requireNonNull(classMapping.getMethod(mappingEntry.name(), mappingEntry.desc(), yarnIntermediaryNs), "Could not find method");
|
||||
String yarnFieldName = methodMapping.getName(yarnNamedNs);
|
||||
|
||||
if (yarnFieldName == null || yarnFieldName.startsWith("method_") || yarnFieldName.startsWith("comp_")) {
|
||||
// Set a new dst name if it doesn't have one, or matches intermediary
|
||||
yarn.visitDstName(MappedElementKind.METHOD, yarnNamedNs, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
inheritMappedNamesOfEnclosingClasses(yarn);
|
||||
|
||||
try (MappingWriter mappingWriter = MappingWriter.create(outputYarnMappings, MappingFormat.TINY_2_FILE)) {
|
||||
yarn.accept(mappingWriter);
|
||||
}
|
||||
}
|
||||
|
||||
private static void acceptJar(NameFinder nameFinder, Path jar) throws IOException {
|
||||
try (JarInputStream jarInputStream = new JarInputStream(Files.newInputStream(jar))) {
|
||||
JarEntry entry;
|
||||
|
||||
while ((entry = jarInputStream.getNextJarEntry()) != null) {
|
||||
if (!entry.getName().endsWith(".class")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ClassReader reader = new ClassReader(jarInputStream);
|
||||
ClassNode classNode = new ClassNode();
|
||||
reader.accept(classNode, 0);
|
||||
|
||||
nameFinder.accept(classNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MemoryMappingTree readMappings(Path path) throws IOException {
|
||||
MemoryMappingTree mappingTree = new MemoryMappingTree();
|
||||
MappingReader.read(path, mappingTree);
|
||||
return mappingTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based off loom: https://github.com/FabricMC/fabric-loom/commit/98d8f3767253a1a3308542c2e896cbf5f4382033.
|
||||
*/
|
||||
private static void inheritMappedNamesOfEnclosingClasses(MemoryMappingTree tree) {
|
||||
int namedIdx = tree.getNamespaceId("named");
|
||||
|
||||
// The tree does not have an index by intermediary names by default
|
||||
tree.setIndexByDstNames(true);
|
||||
|
||||
for (MappingTree.ClassMapping classEntry : tree.getClasses()) {
|
||||
String intermediaryName = Objects.requireNonNull(classEntry.getSrcName());
|
||||
String namedName = classEntry.getDstName(namedIdx);
|
||||
|
||||
// No named name, or intermediary name equals the named - and inner class.
|
||||
if ((namedName == null || intermediaryName.equals(namedName)) && intermediaryName.contains("$")) {
|
||||
String[] path = intermediaryName.split(Pattern.quote("$"));
|
||||
int parts = path.length;
|
||||
|
||||
for (int i = parts - 2; i >= 0; i--) {
|
||||
String currentPath = String.join("$", Arrays.copyOfRange(path, 0, i + 1));
|
||||
|
||||
String namedParentClass = tree.mapClassName(currentPath, namedIdx);
|
||||
|
||||
if (!namedParentClass.equals(currentPath)) {
|
||||
classEntry.setDstName(namedParentClass
|
||||
+ "$" + String.join("$", Arrays.copyOfRange(path, i + 1, path.length)),
|
||||
namedIdx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import net.fabricmc.mappingio.tree.MappingTree;
|
||||
import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
|
||||
public class NameFinder {
|
||||
// comp_x -> name
|
||||
private final Map<String, String> recordNames = new HashMap<>();
|
||||
private final Map<MappingEntry, String> recordFieldNames = new HashMap<>();
|
||||
private final Map<MappingEntry, String> recordMethodNames = new HashMap<>();
|
||||
|
||||
private final Map<String, Set<String>> enumFields = new HashMap<>();
|
||||
private final Map<String, List<MethodNode>> methods = new HashMap<>();
|
||||
|
||||
public void accept(ClassNode classNode) {
|
||||
classNode.accept(new NameFinderVisitor(Constants.ASM_VERSION, enumFields, methods));
|
||||
|
||||
if ("java/lang/Record".equals(classNode.superName)) {
|
||||
classNode.accept(new RecordComponentNameFinder(Constants.ASM_VERSION, recordNames));
|
||||
}
|
||||
}
|
||||
|
||||
public void acceptIntermediaryMappings(MemoryMappingTree mappingTree) {
|
||||
if (recordNames.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int intermediaryId = mappingTree.getNamespaceId("intermediary");
|
||||
|
||||
for (Map.Entry<String, String> entry : recordNames.entrySet()) {
|
||||
boolean foundMethod = false;
|
||||
boolean foundField = false;
|
||||
|
||||
for (MappingTree.ClassMapping classMapping : mappingTree.getClasses()) {
|
||||
for (MappingTree.FieldMapping fieldMapping : classMapping.getFields()) {
|
||||
if (fieldMapping.getName(intermediaryId).equals(entry.getKey())) {
|
||||
MappingEntry fieldEntry = new MappingEntry(classMapping.getName(intermediaryId), fieldMapping.getName(intermediaryId), fieldMapping.getDesc(intermediaryId));
|
||||
recordFieldNames.put(fieldEntry, entry.getValue());
|
||||
foundField = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (MappingTree.MethodMapping methodMapping : classMapping.getMethods()) {
|
||||
if (methodMapping.getName(intermediaryId).equals(entry.getKey())) {
|
||||
MappingEntry fieldEntry = new MappingEntry(classMapping.getName(intermediaryId), methodMapping.getName(intermediaryId), methodMapping.getDesc(intermediaryId));
|
||||
recordMethodNames.put(fieldEntry, entry.getValue());
|
||||
foundMethod = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundField) {
|
||||
System.err.println("Failed to find field for " + entry);
|
||||
}
|
||||
|
||||
if (!foundMethod) {
|
||||
System.err.println("Failed to find method for " + entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getRecordNames() {
|
||||
return recordNames;
|
||||
}
|
||||
|
||||
public Map<MappingEntry, String> getFieldNames() {
|
||||
Map<MappingEntry, String> fieldNames = new HashMap<>(new FieldNameFinder().findNames(enumFields, methods));
|
||||
fieldNames.putAll(recordFieldNames);
|
||||
return fieldNames;
|
||||
}
|
||||
|
||||
public Map<MappingEntry, String> getMethodNames() {
|
||||
return recordMethodNames;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
public class NameFinderVisitor extends ClassVisitor {
|
||||
private String owner;
|
||||
private final Map<String, Set<String>> allEnumFields;
|
||||
private final Map<String, List<MethodNode>> allMethods;
|
||||
|
||||
public NameFinderVisitor(int api, Map<String, Set<String>> allEnumFields, Map<String, List<MethodNode>> allMethods) {
|
||||
super(api);
|
||||
this.allMethods = allMethods;
|
||||
this.allEnumFields = allEnumFields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
this.owner = name;
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
||||
if ((access & Opcodes.ACC_ENUM) != 0) {
|
||||
if (!allEnumFields.computeIfAbsent(owner, s -> new HashSet<>()).add(descriptor + name)) {
|
||||
throw new IllegalArgumentException("Found two enum fields with the same name \"" + name + "\"!");
|
||||
}
|
||||
}
|
||||
|
||||
return super.visitField(access, name, descriptor, signature, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(
|
||||
final int access,
|
||||
final String name,
|
||||
final String descriptor,
|
||||
final String signature,
|
||||
final String[] exceptions) {
|
||||
if ("<clinit>".equals(name)) {
|
||||
MethodNode node = new MethodNode(api, access, name, descriptor, signature, exceptions);
|
||||
allMethods.computeIfAbsent(owner, s -> new ArrayList<>()).add(node);
|
||||
return node;
|
||||
} else {
|
||||
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
public final class RecordComponentNameFinder extends ClassVisitor {
|
||||
static final Handle OBJ_MTH_BOOTSTRAP = new Handle(
|
||||
Opcodes.H_INVOKESTATIC,
|
||||
"java/lang/runtime/ObjectMethods",
|
||||
"bootstrap",
|
||||
"""
|
||||
(Ljava/lang/invoke/MethodHandles$Lookup;\
|
||||
Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;\
|
||||
Ljava/lang/Class;Ljava/lang/String;\
|
||||
[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;""",
|
||||
false
|
||||
);
|
||||
static final Set<Component> OBJECT_METHODS = Set.of(
|
||||
new Component("equals", "(Ljava/lang/Object;)Z"),
|
||||
new Component("toString", "()Ljava/lang/String;"),
|
||||
new Component("hashCode", "()I")
|
||||
);
|
||||
// comp_x -> name
|
||||
private final Map<String, String> recordNames;
|
||||
private String name;
|
||||
|
||||
public RecordComponentNameFinder(int api, Map<String, String> recordNames) {
|
||||
super(api);
|
||||
this.recordNames = recordNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
var nameAndType = new Component(name, descriptor);
|
||||
|
||||
if (OBJECT_METHODS.contains(nameAndType)) {
|
||||
return new ObjectMethodVisitor(api, nameAndType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private class ObjectMethodVisitor extends MethodVisitor {
|
||||
private final String owner;
|
||||
private final Component id;
|
||||
|
||||
ObjectMethodVisitor(int api, Component id) {
|
||||
super(api);
|
||||
this.owner = Objects.requireNonNull(RecordComponentNameFinder.this.name);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) {
|
||||
super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments);
|
||||
|
||||
if (!bootstrapMethodHandle.equals(OBJ_MTH_BOOTSTRAP) || !id.checkIndy(this.owner, name, descriptor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert bootstrapMethodArguments[0] instanceof Type;
|
||||
String recordClassName = ((Type) bootstrapMethodArguments[0]).getInternalName();
|
||||
|
||||
if (!recordClassName.equals(owner)) {
|
||||
System.out.println("found mismatching object method bootstrap record class in caller class " + owner + "::" + id);
|
||||
}
|
||||
|
||||
assert bootstrapMethodArguments[1] instanceof String;
|
||||
String[] names = ((String) bootstrapMethodArguments[1]).split(";");
|
||||
assert names.length == bootstrapMethodArguments.length - 2;
|
||||
|
||||
for (int i = 2; i < bootstrapMethodArguments.length; i++) {
|
||||
if (bootstrapMethodArguments[i] instanceof Handle handle) {
|
||||
if (handle.getTag() == Opcodes.H_GETFIELD && handle.getOwner().equals(recordClassName)) {
|
||||
var argName = names[i - 2];
|
||||
put(recordNames, handle.getName(), argName);
|
||||
} else {
|
||||
// valid bytecode but we cannot guess
|
||||
System.out.println("found special constant pool method handle, cannot process: " + owner + "::" + id);
|
||||
}
|
||||
} else {
|
||||
// valid bytecode, may be condy bsm arg, can't process
|
||||
System.out.println("found special bootstrap method arg (maybe condy), cannot process: " + owner + "::" + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void put(Map<String, String> out, String key, String value) {
|
||||
var old = out.put(key, value);
|
||||
|
||||
if (old == null || old.equals(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Found conflicting name for component " + key + " in " + owner + "::"
|
||||
+ id + ", replaced " + old + " with " + value);
|
||||
}
|
||||
}
|
||||
|
||||
private record Component(String name, String desc) {
|
||||
boolean checkIndy(String owner, String name, String desc) {
|
||||
return this.name.equals(name) && ("(L" + owner + ";" + this.desc.substring(1)).equals(desc);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal.enigma;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import cuchaz.enigma.analysis.index.JarIndex;
|
||||
import cuchaz.enigma.api.service.JarIndexerService;
|
||||
import cuchaz.enigma.api.service.NameProposalService;
|
||||
import cuchaz.enigma.classprovider.ClassProvider;
|
||||
import cuchaz.enigma.translation.mapping.EntryRemapper;
|
||||
import cuchaz.enigma.translation.representation.entry.Entry;
|
||||
import cuchaz.enigma.translation.representation.entry.FieldEntry;
|
||||
import cuchaz.enigma.translation.representation.entry.MethodEntry;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import net.fabricmc.filament.nameproposal.MappingEntry;
|
||||
import net.fabricmc.filament.nameproposal.NameFinder;
|
||||
|
||||
public class EnigmaNameProposalService implements JarIndexerService, NameProposalService {
|
||||
private Map<String, String> recordNames;
|
||||
Map<MappingEntry, String> fieldNames;
|
||||
|
||||
@Override
|
||||
public void acceptJar(Set<String> classNames, ClassProvider classProvider, JarIndex jarIndex) {
|
||||
NameFinder nameFinder = new NameFinder();
|
||||
|
||||
for (String className : classNames) {
|
||||
ClassNode classNode = classProvider.get(className);
|
||||
nameFinder.accept(Objects.requireNonNull(classNode, "Failed to get ClassNode for " + className));
|
||||
}
|
||||
|
||||
recordNames = nameFinder.getRecordNames();
|
||||
fieldNames = nameFinder.getFieldNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> proposeName(Entry<?> obfEntry, EntryRemapper remapper) {
|
||||
Objects.requireNonNull(recordNames, "Cannot proposeName before indexing");
|
||||
|
||||
if (obfEntry instanceof FieldEntry fieldEntry) {
|
||||
if (fieldEntry.getName().startsWith("comp_")) {
|
||||
return Optional.ofNullable(recordNames.get(fieldEntry.getName()));
|
||||
}
|
||||
|
||||
return Optional.ofNullable(fieldNames.get(new MappingEntry(fieldEntry.getContainingClass().getFullName(), fieldEntry.getName(), fieldEntry.getDesc().toString())));
|
||||
} else if (obfEntry instanceof MethodEntry methodEntry) {
|
||||
if (methodEntry.getName().startsWith("comp_")) {
|
||||
return Optional.ofNullable(recordNames.get(methodEntry.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal.enigma;
|
||||
|
||||
import cuchaz.enigma.api.service.EnigmaServiceContext;
|
||||
import cuchaz.enigma.api.service.ObfuscationTestService;
|
||||
import cuchaz.enigma.translation.representation.entry.ClassEntry;
|
||||
import cuchaz.enigma.translation.representation.entry.Entry;
|
||||
import cuchaz.enigma.translation.representation.entry.FieldEntry;
|
||||
import cuchaz.enigma.translation.representation.entry.MethodEntry;
|
||||
|
||||
public class IntermediaryObfuscationTestService implements ObfuscationTestService {
|
||||
private final String prefix, classPrefix, classPackagePrefix, fieldPrefix, methodPrefix, componentPrefix;
|
||||
|
||||
public IntermediaryObfuscationTestService(EnigmaServiceContext<ObfuscationTestService> context) {
|
||||
this.prefix = context.getArgument("package").orElse("net/minecraft") + "/";
|
||||
this.classPrefix = context.getArgument("classPrefix").orElse("class_");
|
||||
this.fieldPrefix = context.getArgument("fieldPrefix").orElse("field_");
|
||||
this.methodPrefix = context.getArgument("methodPrefix").orElse("method_");
|
||||
this.componentPrefix = context.getArgument("componentPrefix").orElse("comp_");
|
||||
|
||||
this.classPackagePrefix = this.prefix + this.classPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean testDeobfuscated(Entry<?> entry) {
|
||||
if (entry instanceof ClassEntry) {
|
||||
ClassEntry ce = (ClassEntry) entry;
|
||||
String[] components = ce.getFullName().split("\\$");
|
||||
|
||||
// all obfuscated components are, at their outermost, class_
|
||||
String lastComponent = components[components.length - 1];
|
||||
|
||||
if (lastComponent.startsWith(this.classPrefix) || lastComponent.startsWith(this.classPackagePrefix)) {
|
||||
return false;
|
||||
}
|
||||
} else if (entry instanceof FieldEntry) {
|
||||
if (entry.getName().startsWith(this.fieldPrefix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.getName().startsWith(this.componentPrefix)) {
|
||||
return false;
|
||||
}
|
||||
} else if (entry instanceof MethodEntry) {
|
||||
if (entry.getName().startsWith(this.methodPrefix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.getName().startsWith(this.componentPrefix)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// unknown type
|
||||
return false;
|
||||
}
|
||||
|
||||
// known type, not obfuscated
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2021 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.filament.nameproposal.enigma;
|
||||
|
||||
import cuchaz.enigma.api.EnigmaPlugin;
|
||||
import cuchaz.enigma.api.EnigmaPluginContext;
|
||||
import cuchaz.enigma.api.service.JarIndexerService;
|
||||
import cuchaz.enigma.api.service.NameProposalService;
|
||||
import cuchaz.enigma.api.service.ObfuscationTestService;
|
||||
|
||||
public class NameProposalServiceEnigmaPlugin implements EnigmaPlugin {
|
||||
private static final String ID_PREFIX = "nameproposal:";
|
||||
|
||||
@Override
|
||||
public void init(EnigmaPluginContext ctx) {
|
||||
ctx.registerService(ID_PREFIX + "intermediary_obfuscation_test", ObfuscationTestService.TYPE, IntermediaryObfuscationTestService::new);
|
||||
|
||||
EnigmaNameProposalService service = new EnigmaNameProposalService();
|
||||
ctx.registerService(ID_PREFIX + "jar_indexer", JarIndexerService.TYPE, ctx1 -> service);
|
||||
ctx.registerService(ID_PREFIX + "name_proposal", NameProposalService.TYPE, ctx1 -> service);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
net.fabricmc.filament.nameproposal.enigma.NameProposalServiceEnigmaPlugin
|
|
@ -7,7 +7,6 @@ enigma_version=2.5.0
|
|||
unpick_version=2.3.0
|
||||
cfr_version=0.2.2
|
||||
vineflower_version=1.9.3
|
||||
name_proposal_version=0.2.0
|
||||
asm_version=9.6
|
||||
|
||||
# Javadoc generation/linking
|
||||
|
|
Loading…
Reference in New Issue