aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/ftbsc/lll/processor/tools/containers/MethodContainer.java
blob: 688912910d46288392d144dc90a5227b52abbe4f (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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package ftbsc.lll.processor.tools.containers;

import ftbsc.lll.exceptions.AmbiguousDefinitionException;
import ftbsc.lll.exceptions.TargetNotFoundException;
import ftbsc.lll.processor.annotations.Find;
import ftbsc.lll.processor.annotations.Patch;
import ftbsc.lll.processor.annotations.Target;
import ftbsc.lll.processor.tools.obfuscation.ObfuscationMapper;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;

import static ftbsc.lll.processor.tools.ASTUtils.*;

/**
 * Container for information about a method.
 * Used internally for efficiency reasons.
 * @since 0.5.0
 */
public class MethodContainer {
   /**
    * The name of the method.
    */
   public final String name;

   /**
    * The descriptor of the method.
    */
   public final String descriptor;

   /**
    * The obfuscated name of the method.
    * If the mapper passed is null, then this will be identical to {@link #name}.
    */
   public final String nameObf;

   /**
    * The obfuscated descriptor of the field.
    * If the mapper passed is null, then this will be identical to {@link #descriptor}.
    */
   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, ProcessingEnvironment, ObfuscationMapper)}.
    * @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 isntead (see {@link Target#bridge()} for more info)
    * @param env the {@link ProcessingEnvironment} to perform the operation in
    * @param mapper the {@link ObfuscationMapper} to be used, may be null
    */
   private MethodContainer(
      ClassContainer parent, String name, String descriptor, boolean strict,
      boolean bridge, ProcessingEnvironment env, ObfuscationMapper mapper) {
      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;
         this.name = name;
         this.descriptor = descriptor;
      } else {
         ExecutableElement tmp = (ExecutableElement) findMember(
            parent, name, descriptor, descriptor != null && strict,false, env
         );
         this.elem = bridge ? findOverloadedMethod((TypeElement) this.parent.elem, tmp, env) : tmp;
         this.name = this.elem.getSimpleName().toString();
         this.descriptor = descriptorFromExecutableElement(this.elem, env);
      }
      this.descriptorObf = mapper == null ? this.descriptor : mapper.obfuscateMethodDescriptor(this.descriptor);
      this.nameObf = findMemberName(parent.fqn, this.name, this.descriptor, mapper);
   }

   /**
    * 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 env the {@link ProcessingEnvironment} to perform the operation in
    * @param mapper the {@link ObfuscationMapper} to be used, may be null
    * @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, ProcessingEnvironment env, ObfuscationMapper mapper) {
      //the parent always has a @Patch annotation
      Patch patchAnn = stub.getEnclosingElement().getAnnotation(Patch.class);
      ClassContainer parent = ClassContainer.findOrFallback(
         ClassContainer.from(patchAnn, Patch::value, patchAnn.className(), env, mapper),
         f, env, mapper
      );

      String name = !t.methodName().equals("")
         ?  t.methodName() //name was specified in target
         : stub.getSimpleName().toString();
      String descriptor = t.strict()
         ? descriptorFromExecutableElement(stub, env)
         : null;

      return new MethodContainer(parent, name, descriptor, t.strict(), t.bridge(), env, mapper);
   }
}