View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package mockit.asm.types;
7   
8   import static mockit.asm.jvmConstants.Opcodes.ACONST_NULL;
9   import static mockit.asm.jvmConstants.Opcodes.ALOAD;
10  
11  import edu.umd.cs.findbugs.annotations.NonNull;
12  
13  import org.checkerframework.checker.index.qual.NonNegative;
14  
15  public abstract class ReferenceType extends JavaType {
16      /**
17       * The internal name of this Java reference type.
18       */
19      @NonNull
20      final char[] typeDescChars;
21  
22      /**
23       * The offset of the internal name of this Java type in {@link #typeDescChars}.
24       */
25      @NonNegative
26      final int off;
27  
28      ReferenceType(@NonNull char[] typeDesc) {
29          super(typeDesc.length);
30          typeDescChars = typeDesc;
31          off = 0;
32      }
33  
34      ReferenceType(@NonNull char[] typeDesc, @NonNegative int off, @NonNegative int len) {
35          super(len);
36          typeDescChars = typeDesc;
37          this.off = off;
38      }
39  
40      /**
41       * Returns the Java type corresponding to the given type descriptor.
42       *
43       * @param typeDesc
44       *            a type descriptor.
45       */
46      @NonNull
47      public static ReferenceType createFromTypeDescriptor(@NonNull String typeDesc) {
48          return getReferenceType(typeDesc.toCharArray(), 0);
49      }
50  
51      /**
52       * Returns the Java type corresponding to the given type descriptor. For method descriptors, <code>buf</code> is
53       * supposed to contain nothing more than the descriptor itself.
54       *
55       * @param buf
56       *            a buffer containing a type descriptor.
57       * @param off
58       *            the offset of this descriptor in the previous buffer.
59       */
60      @NonNull
61      static ReferenceType getReferenceType(@NonNull char[] buf, @NonNegative int off) {
62          switch (buf[off]) {
63              case '[':
64                  return ArrayType.create(buf, off);
65              case 'L':
66                  return ObjectType.create(buf, off);
67              case '(':
68                  return new MethodType(buf, off, buf.length - off);
69              default:
70                  throw new IllegalArgumentException("Invalid type descriptor: " + new String(buf));
71          }
72      }
73  
74      /**
75       * Returns the object or array type corresponding to the given internal name.
76       */
77      @NonNull
78      public static ReferenceType createFromInternalName(@NonNull String internalName) {
79          char[] buf = internalName.toCharArray();
80          return buf[0] == '[' ? new ArrayType(buf) : new ObjectType(buf);
81      }
82  
83      static int findTypeNameLength(@NonNull char[] buf, @NonNegative int off, @NonNegative int len) {
84          len++;
85  
86          while (buf[off + len] != ';') {
87              len++;
88          }
89  
90          return len;
91      }
92  
93      static void getDescriptor(@NonNull StringBuilder buf, @NonNull Class<?> aClass) {
94          buf.append('L');
95  
96          String name = aClass.getName();
97          int len = name.length();
98  
99          for (int i = 0; i < len; i++) {
100             char c = name.charAt(i);
101             buf.append(c == '.' ? '/' : c);
102         }
103 
104         buf.append(';');
105     }
106 
107     @Override
108     void getDescriptor(@NonNull StringBuilder typeDesc) {
109         typeDesc.append(typeDescChars, off, len);
110     }
111 
112     /**
113      * Returns the internal name of the class corresponding to this object or array type. The internal name of a class
114      * is its fully qualified name (as returned by Class.getName(), where '.' are replaced by '/'. For an array type, it
115      * starts with "[" and ends with the type descriptor of the array element type.
116      *
117      * @return the internal name of the class corresponding to this object or array type.
118      */
119     @NonNull
120     public final String getInternalName() {
121         return new String(typeDescChars, off, len);
122     }
123 
124     @Override
125     public int getSize() {
126         return 1;
127     }
128 
129     @Override
130     public int getOpcode(int opcode) {
131         return opcode + 4;
132     }
133 
134     @Override
135     public final int getLoadOpcode() {
136         return ALOAD;
137     }
138 
139     @Override
140     public final int getConstOpcode() {
141         return ACONST_NULL;
142     }
143 
144     @Override
145     public final boolean equals(Object o) {
146         if (this == o) {
147             return true;
148         }
149 
150         if (!(o instanceof ReferenceType)) {
151             return false;
152         }
153 
154         ReferenceType t = (ReferenceType) o;
155 
156         if (getClass() != t.getClass() || len != t.len) {
157             return false;
158         }
159 
160         for (int i = off, j = t.off, end = i + len; i < end; i++, j++) {
161             if (typeDescChars[i] != t.typeDescChars[j]) {
162                 return false;
163             }
164         }
165 
166         return true;
167     }
168 
169     @Override
170     public final int hashCode() {
171         int hc = 13;
172 
173         for (int i = off, end = i + len; i < end; i++) {
174             // noinspection CharUsedInArithmeticContext
175             hc = 17 * (hc + typeDescChars[i]);
176         }
177 
178         return hc;
179     }
180 }