summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/analysis/index/ReferenceIndex.java
blob: ee28b3e7f0a86646b4e380e8108165f1d4f9878a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package cuchaz.enigma.analysis.index;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import cuchaz.enigma.analysis.EntryReference;
import cuchaz.enigma.translation.mapping.ResolutionStrategy;
import cuchaz.enigma.translation.representation.entry.*;

import java.util.Collection;
import java.util.Map;
import java.util.Optional;

public class ReferenceIndex implements JarIndexer {
	private Multimap<MethodEntry, MethodEntry> methodReferences = HashMultimap.create();

	private Multimap<MethodEntry, EntryReference<MethodEntry, MethodDefEntry>> referencesToMethods = HashMultimap.create();
	private Multimap<ClassEntry, EntryReference<ClassEntry, MethodDefEntry>> referencesToClasses = HashMultimap.create();
	private Multimap<FieldEntry, EntryReference<FieldEntry, MethodDefEntry>> referencesToFields = HashMultimap.create();

	@Override
	public void indexMethodReference(MethodDefEntry callerEntry, MethodEntry referencedEntry) {
		referencesToMethods.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry));
		methodReferences.put(callerEntry, referencedEntry);

		if (referencedEntry.isConstructor()) {
			ClassEntry referencedClass = referencedEntry.getParent();
			referencesToClasses.put(referencedClass, new EntryReference<>(referencedClass, referencedEntry.getName(), callerEntry));
		}
	}

	@Override
	public void indexFieldReference(MethodDefEntry callerEntry, FieldEntry referencedEntry) {
		referencesToFields.put(referencedEntry, new EntryReference<>(referencedEntry, referencedEntry.getName(), callerEntry));
	}

	@Override
	public void processIndex(JarIndex index) {
		methodReferences = remapReferences(index, methodReferences);
		referencesToMethods = remapReferencesTo(index, referencesToMethods);
		referencesToClasses = remapReferencesTo(index, referencesToClasses);
		referencesToFields = remapReferencesTo(index, referencesToFields);
	}

	private <K extends Entry<?>, V extends Entry<?>> Multimap<K, V> remapReferences(JarIndex index, Multimap<K, V> multimap) {
		Multimap<K, V> resolved = HashMultimap.create();
		for (Map.Entry<K, V> entry : multimap.entries()) {
			resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue()));
		}
		return resolved;
	}

	private <E extends Entry<?>, C extends Entry<?>> Multimap<E, EntryReference<E, C>> remapReferencesTo(JarIndex index, Multimap<E, EntryReference<E, C>> multimap) {
		Multimap<E, EntryReference<E, C>> resolved = HashMultimap.create();
		for (Map.Entry<E, EntryReference<E, C>> entry : multimap.entries()) {
			resolved.put(remap(index, entry.getKey()), remap(index, entry.getValue()));
		}
		return resolved;
	}

	private <E extends Entry<?>> E remap(JarIndex index, E entry) {
		E resolvedEntry = index.getEntryResolver().resolveFirstEntry(entry, ResolutionStrategy.RESOLVE_CLOSEST);

		Optional<E> remappedBridge = getRemappedBridge(index, resolvedEntry);
		return remappedBridge.orElse(resolvedEntry);
	}

	private <E extends Entry<?>, C extends Entry<?>> EntryReference<E, C> remap(JarIndex index, EntryReference<E, C> reference) {
		EntryReference<E, C> resolvedReference = index.getEntryResolver().resolveFirstReference(reference, ResolutionStrategy.RESOLVE_CLOSEST);

		getRemappedBridge(index, resolvedReference.entry).ifPresent(e -> resolvedReference.entry = e);
		getRemappedBridge(index, resolvedReference.context).ifPresent(e -> resolvedReference.context = e);

		return resolvedReference;
	}

	@SuppressWarnings("unchecked")
	private <E extends Entry<?>> Optional<E> getRemappedBridge(JarIndex index, E entry) {
		if (entry instanceof MethodEntry) {
			MethodEntry bridgeEntry = index.getBridgeMethodIndex().getBridgeFromAccessed((MethodEntry) entry);
			if (bridgeEntry != null) {
				return Optional.of((E) entry.withName(bridgeEntry.getName()));
			}
		}
		return Optional.empty();
	}

	public Collection<MethodEntry> getMethodsReferencedBy(MethodEntry entry) {
		return methodReferences.get(entry);
	}

	public Collection<EntryReference<FieldEntry, MethodDefEntry>> getReferencesToField(FieldEntry entry) {
		return referencesToFields.get(entry);
	}

	public Collection<EntryReference<ClassEntry, MethodDefEntry>> getReferencesToClass(ClassEntry entry) {
		return referencesToClasses.get(entry);
	}

	public Collection<EntryReference<MethodEntry, MethodDefEntry>> getReferencesToMethod(MethodEntry entry) {
		return referencesToMethods.get(entry);
	}
}