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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
package ftbsc.lll.processor.containers;
import ftbsc.lll.exceptions.AmbiguousDefinitionException;
import ftbsc.lll.exceptions.TargetNotFoundException;
import ftbsc.lll.mapper.data.ClassData;
import ftbsc.lll.mapper.utils.MappingUtils;
import ftbsc.lll.mapper.data.MethodData;
import ftbsc.lll.processor.annotations.Find;
import ftbsc.lll.processor.annotations.Patch;
import ftbsc.lll.processor.annotations.Target;
import ftbsc.lll.processor.ProcessorOptions;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import static ftbsc.lll.processor.utils.ASTUtils.*;
/**
* Container for information about a method.
* Used internally for efficiency reasons.
* @since 0.5.0
*/
public class MethodContainer {
/**
* The {@link MethodData} for the method represented by this container.
*/
public final MethodData data;
/**
* The obfuscated descriptor of the field.
* If the mapper passed is null, this will be identical to the one inside
* {@link #data}.
*/
public final String descriptorObf;
/**
* The {@link ClassContainer} representing the parent of this method.
*/
public final ClassContainer parent;
/**
* The {@link ExecutableElement} corresponding to the method.
* May only be null intentionally i.e. when the method is
* a child of an anonymous class.
*/
public final ExecutableElement elem;
/**
* Private constructor, called from
* {@link #from(ExecutableElement, Target, Find, ProcessorOptions)}.
* @param parent the {@link ClassContainer} representing the parent
* @param name the fully-qualified name of the target method
* @param descriptor the descriptor of the target method
* @param strict whether the matching should be strict (see {@link Target#strict()} for more info)
* @param bridge whether the "bridge" should be matched instead (see {@link Target#bridge()} for more info)
* @param options the {@link ProcessorOptions} to be used
*/
private MethodContainer(ClassContainer parent, String name, String descriptor, boolean strict, boolean bridge, ProcessorOptions options) {
this.parent = parent;
if(parent.elem == null) { // unverified
if(descriptor == null)
throw new AmbiguousDefinitionException("Cannot use name-based lookups for methods of unverifiable classes!");
this.elem = null;
} else {
ExecutableElement tmp = (ExecutableElement) findMember(
parent, name, descriptor, descriptor != null && strict,false, options.env
);
this.elem = bridge ? findSyntheticBridge(this.parent.elem, tmp, options.env) : tmp;
name = this.elem.getSimpleName().toString();
descriptor = descriptorFromExecutableElement(this.elem, options.env);
}
// some mapping formats omit methods if they are overriding a parent's method
// since there is no drawback but efficiency, let's use the top parent's name for that (when possible)
if(this.parent.elem != null) {
ExecutableElement top = findOverloadedMethod(this.parent.elem, this.elem, options.env);
ClassData topParentData = getClassData(
internalNameFromType(top.getEnclosingElement().asType(), options.env),
options.mapper
);
MethodData topData = getMethodData(topParentData.name, name, descriptor, options.mapper);
this.data = new MethodData(
parent.data,
topData.signature.name,
topData.nameMapped,
topData.signature.descriptor
);
} else this.data = getMethodData(parent.data.name, name, descriptor, options.mapper);
this.descriptorObf = options.mapper == null ? this.data.signature.descriptor
: MappingUtils.mapMethodDescriptor(this.data.signature.descriptor, options.mapper, false);
}
/**
* Builds the {@link MethodContainer} corresponding to a stub annotated with {@link Target}.
* @param stub the {@link ExecutableElement} for the stub
* @param t the {@link Target} annotation relevant to this case
* @param f the {@link Find} annotation containing fallback data, may be null
* @param options the {@link ProcessorOptions} to be used
* @return the {@link MethodContainer} corresponding to the method
* @throws AmbiguousDefinitionException if it finds more than one candidate
* @throws TargetNotFoundException if it finds no valid candidate
* @since 0.3.0
*/
public static MethodContainer from(ExecutableElement stub, Target t, Find f, ProcessorOptions options) {
//the parent always has a @Patch annotation
Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class);
ClassContainer parent = ClassContainer.findOrFallback(
ClassContainer.from((TypeElement) stub.getEnclosingElement(), options), patchAnn, f, options
);
String name = !t.methodName().isEmpty()
? t.methodName() //name was specified in target
: stub.getSimpleName().toString();
String descriptor = t.strict()
? descriptorFromExecutableElement(stub, options.env)
: null;
return new MethodContainer(parent, name, descriptor, t.strict(), t.bridge(), options);
}
}
|