Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attempted fix for #47 #65

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions src/main/java/net/fabricmc/tinyremapper/AsmClassRemapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
package net.fabricmc.tinyremapper;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.lang.model.SourceVersion;
Expand Down Expand Up @@ -48,7 +50,6 @@
class AsmClassRemapper extends ClassRemapper {
public AsmClassRemapper(ClassVisitor cv, AsmRemapper remapper, boolean checkPackageAccess, boolean skipLocalMapping, boolean renameInvalidLocals) {
super(cv, remapper);

this.checkPackageAccess = checkPackageAccess;
this.skipLocalMapping = skipLocalMapping;
this.renameInvalidLocals = renameInvalidLocals;
Expand All @@ -72,7 +73,33 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str
methodNode = new MethodNode(api, access, name, descriptor, signature, exceptions);
}

return super.visitMethod(access, name, descriptor, signature, exceptions);
String remappedDescriptor = remapper.mapMethodDesc(descriptor);
Collection<String> names = ((AsmRemapper) remapper).mapMultiMethodName(className, name, descriptor);
MethodVisitor visitor;

if(names.size() == 1) {
visitor = this.cv.visitMethod(
access,
names.iterator().next(),
remappedDescriptor,
remapper.mapSignature(signature, false),
exceptions == null ? null : remapper.mapTypes(exceptions));
} else {
List<MethodVisitor> visitorList = new ArrayList<>();

for(String newName : names) {
visitorList.add(this.cv.visitMethod(
access,
newName,
remappedDescriptor,
remapper.mapSignature(signature, false),
exceptions == null ? null : remapper.mapTypes(exceptions)));
}

visitor = new MultiMethodVisitor(api, visitorList);
}

return visitor == null ? null : createMethodRemapper(visitor);
}

@Override
Expand Down
48 changes: 46 additions & 2 deletions src/main/java/net/fabricmc/tinyremapper/AsmRemapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@

package net.fabricmc.tinyremapper;

import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.objectweb.asm.commons.Remapper;
Expand Down Expand Up @@ -53,23 +60,60 @@ public String mapFieldName(String owner, String name, String desc) {
return remapper.extraRemapper != null ? remapper.extraRemapper.mapFieldName(owner, name, desc) : name;
}

public Collection<String> mapMultiMethodName(String owner, String name, String desc) {
ClassInstance cls = getClass(owner);
if (cls == null) return Collections.singletonList(name);
List<MemberInstance> instances = cls.multiResolve(MemberType.METHOD, MemberInstance.getMethodId(name, desc));
if(instances.isEmpty()) {
return Collections.singletonList(mapMember(null, owner, name, desc));
} else {
return new AbstractCollection<String>() {
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
Iterator<MemberInstance> members = instances.iterator();
@Override
public boolean hasNext() {
return members.hasNext();
}

@Override
public String next() {
return mapMember(members.next(), owner, name, desc);
}
};
}

@Override
public int size() {
return instances.size();
}
};
}
}

@Override
public String mapMethodName(String owner, String name, String desc) {
ClassInstance cls = getClass(owner);
if (cls == null) return name; // TODO: try to map these from just the mappings?, warn if actual class is missing

MemberInstance member = cls.resolve(MemberType.METHOD, MemberInstance.getMethodId(name, desc));
return mapMember(member, owner, name, desc);
}

protected String mapMember(MemberInstance member, String srcOwner, String srcName, String srcDesc) {
String newName;

if (member != null && (newName = member.getNewName()) != null) {
return newName;
}

assert (newName = remapper.methodMap.get(owner+"/"+MemberInstance.getMethodId(name, desc))) == null || newName.equals(name);
assert (newName = remapper.methodMap.get(srcOwner+"/"+MemberInstance.getMethodId(srcName, srcDesc))) == null || newName.equals(srcName);

return remapper.extraRemapper != null ? remapper.extraRemapper.mapMethodName(owner, name, desc) : name;
return remapper.extraRemapper != null ? remapper.extraRemapper.mapMethodName(srcOwner, srcName, srcDesc) : srcName;
}


public String mapMethodNamePrefixDesc(String owner, String name, String descPrefix) {
ClassInstance cls = getClass(owner);
if (cls == null) return name;
Expand Down
49 changes: 39 additions & 10 deletions src/main/java/net/fabricmc/tinyremapper/ClassInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@

import java.nio.file.Path;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -218,15 +220,16 @@ void propagate(TinyRemapper remapper, MemberType type, String originatingCls, St
* different branches of the hierarchy tree that were not visited before may access it.
*/

if (dir == Direction.ANY || dir == Direction.UP || isVirtual && member != null && (member.access & (Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE)) == 0) {
boolean trueVirtual = isVirtual && member != null && (member.access & (Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE)) == 0;
if (dir == Direction.ANY || dir == Direction.UP || trueVirtual) {
for (ClassInstance node : parents) {
if (visitedUp.add(node)) {
node.propagate(remapper, type, originatingCls, idSrc, nameDst, Direction.UP, isVirtual, false, visitedUp, visitedDown);
node.propagate(remapper, type, originatingCls, idSrc, nameDst, Direction.UP, true, false, visitedUp, visitedDown);
}
}
}

if (dir == Direction.ANY || dir == Direction.DOWN || isVirtual && member != null && (member.access & (Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE)) == 0) {
if (dir == Direction.ANY || dir == Direction.DOWN || trueVirtual) {
for (ClassInstance node : children) {
if (visitedDown.add(node)) {
node.propagate(remapper, type, originatingCls, idSrc, nameDst, Direction.DOWN, isVirtual, false, visitedUp, visitedDown);
Expand All @@ -236,6 +239,16 @@ void propagate(TinyRemapper remapper, MemberType type, String originatingCls, St
}

public MemberInstance resolve(MemberType type, String id) {
return resolve(type, id, null);
}

public List<MemberInstance> multiResolve(MemberType type, String id) {
List<MemberInstance> created = new ArrayList<>();
this.resolve(type, id, created);
return created;
}

public MemberInstance resolve(MemberType type, String id, List<MemberInstance> multiResolve) {
MemberInstance member = getMember(type, id);
if (member != null) return member;

Expand All @@ -244,7 +257,7 @@ public MemberInstance resolve(MemberType type, String id) {

if (member == null) {
// compute
member = resolve0(type, id);
member = resolve0(type, id, multiResolve);
assert member != null;

// put in cache
Expand All @@ -255,7 +268,7 @@ public MemberInstance resolve(MemberType type, String id) {
return member != nullMember ? member : null;
}

private MemberInstance resolve0(MemberType type, String id) {
private MemberInstance resolve0(MemberType type, String id, List<MemberInstance> multiResolve) {
boolean isField = type == MemberType.FIELD;
Set<ClassInstance> visited = Collections.newSetFromMap(new IdentityHashMap<>());
Deque<ClassInstance> queue = new ArrayDeque<>();
Expand All @@ -274,9 +287,15 @@ private MemberInstance resolve0(MemberType type, String id) {
for (ClassInstance parent : cls.parents) {
if (parent.isInterface() == isField && visited.add(parent)) {
MemberInstance ret = parent.getMember(type, id);
if (ret != null) return ret;

queue.addLast(parent);
if (ret != null) {
if(multiResolve == null) {
return ret;
} else {
multiResolve.add(ret);
}
} else {
queue.addLast(parent);
}
}
}
} while ((cls = queue.pollLast()) != null);
Expand Down Expand Up @@ -310,7 +329,12 @@ private MemberInstance resolve0(MemberType type, String id) {
if (!isField && (parentMember.access & (Opcodes.ACC_ABSTRACT)) != 0) {
secondaryMatch = parentMember;
} else {
return parentMember;
if(multiResolve == null) {
return parentMember;
} else {
multiResolve.add(parentMember);
continue;
}
}
}
}
Expand All @@ -321,7 +345,12 @@ private MemberInstance resolve0(MemberType type, String id) {
} while (!isField && (cls = queue.pollFirst()) != null);
} while ((context = queue.pollFirst()) != null); // overall-recursion for fields

return secondaryMatch != null ? secondaryMatch : nullMember;
if(multiResolve == null) {
return secondaryMatch != null ? secondaryMatch : nullMember;
} else {
multiResolve.add(secondaryMatch);
return nullMember;
}
}

public MemberInstance resolvePartial(MemberType type, String name, String descPrefix) {
Expand Down
128 changes: 128 additions & 0 deletions src/main/java/net/fabricmc/tinyremapper/MetaInfFixer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package net.fabricmc.tinyremapper;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Iterator;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

public class MetaInfFixer implements OutputConsumerPath.ResourceRemapper {
public static final MetaInfFixer INSTANCE = new MetaInfFixer();

protected MetaInfFixer() {}

@Override
public boolean canTransform(TinyRemapper remapper, Path relativePath) {
return shouldStripForFixMeta(relativePath) ||
relativePath.getFileName().toString().equals("MANIFEST.MF") ||
(remapper != null && relativePath.getNameCount() == 3 && relativePath.getName(1).toString().equals("services"));
}

@Override
public void transform(Path destinationDirectory, Path relativePath, InputStream input, TinyRemapper remapper) throws IOException {
String fileName = relativePath.getFileName().toString();
if (fileName.equals("MANIFEST.MF")) {
Manifest manifest = new Manifest(input);
fixManifest(manifest, remapper);
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(destinationDirectory.resolve(relativePath)))) {
manifest.write(os);
}
} else if (remapper != null && relativePath.getNameCount() == 3 && relativePath.getName(1).toString().equals("services")) {
fileName = mapFullyQualifiedClassName(fileName, remapper);
Path newFile = destinationDirectory.resolve(relativePath).getParent().resolve(fileName);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(input));
BufferedWriter writer = Files.newBufferedWriter(newFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.WRITE)) {
fixServiceDecl(reader, writer, remapper);
}
}
}

private static boolean shouldStripForFixMeta(Path file) {
if (file.getNameCount() != 2) return false; // not directly inside META-INF dir

assert file.getName(0).toString().equals("META-INF");

String fileName = file.getFileName().toString();

// https://docs.oracle.com/en/java/javase/12/docs/specs/jar/jar.html#signed-jar-file
return fileName.endsWith(".SF")
|| fileName.endsWith(".DSA")
|| fileName.endsWith(".RSA")
|| fileName.startsWith("SIG-");
}

private static String mapFullyQualifiedClassName(String name, TinyRemapper remapper) {
assert name.indexOf('/') < 0;

return remapper.mapClass(name.replace('.', '/')).replace('/', '.');
}

private static void fixManifest(Manifest manifest, TinyRemapper remapper) {
Attributes mainAttrs = manifest.getMainAttributes();

if (remapper != null) {
String val = mainAttrs.getValue(Attributes.Name.MAIN_CLASS);
if (val != null) mainAttrs.put(Attributes.Name.MAIN_CLASS, mapFullyQualifiedClassName(val, remapper));

val = mainAttrs.getValue("Launcher-Agent-Class");
if (val != null) mainAttrs.put("Launcher-Agent-Class", mapFullyQualifiedClassName(val, remapper));
}

mainAttrs.remove(Attributes.Name.SIGNATURE_VERSION);

for (Iterator<Attributes> it = manifest.getEntries().values().iterator(); it.hasNext(); ) {
Attributes attrs = it.next();

for (Iterator<Object> it2 = attrs.keySet().iterator(); it2.hasNext(); ) {
Attributes.Name attrName = (Attributes.Name) it2.next();
String name = attrName.toString();

if (name.endsWith("-Digest") || name.contains("-Digest-") || name.equals("Magic")) {
it2.remove();
}
}

if (attrs.isEmpty()) it.remove();
}
}

private static void fixServiceDecl(BufferedReader reader, BufferedWriter writer, TinyRemapper remapper) throws IOException {
String line;

while ((line = reader.readLine()) != null) {
int end = line.indexOf('#');
if (end < 0) end = line.length();

// trim start+end to skip ' ' and '\t'

int start = 0;
char c;

while (start < end && ((c = line.charAt(start)) == ' ' || c == '\t')) {
start++;
}

while (end > start && ((c = line.charAt(end - 1)) == ' ' || c == '\t')) {
end--;
}

if (start == end) {
writer.write(line);
} else {
writer.write(line, 0, start);
writer.write(mapFullyQualifiedClassName(line.substring(start, end), remapper));
writer.write(line, end, line.length() - end);
}

writer.newLine();
}
}
}
19 changes: 19 additions & 0 deletions src/main/java/net/fabricmc/tinyremapper/MetaInfRemover.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package net.fabricmc.tinyremapper;

import java.io.InputStream;
import java.nio.file.Path;

public class MetaInfRemover implements OutputConsumerPath.ResourceRemapper {
public static final MetaInfRemover INSTANCE = new MetaInfRemover();

protected MetaInfRemover() {}

@Override
public boolean canTransform(TinyRemapper remapper, Path relativePath) {
return relativePath.startsWith("META-INF") && relativePath.getNameCount() != 2;
}

@Override
public void transform(Path destinationDirectory, Path relativePath, InputStream input, TinyRemapper remapper) {
}
}
Loading