2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.dx.rop.type;
19 import java.util.HashMap;
22 * Representation of a method decriptor. Instances of this class are
23 * generally interned and may be usefully compared with each other
26 public final class Prototype implements Comparable<Prototype> {
27 /** {@code non-null;} intern table mapping string descriptors to instances */
28 private static final HashMap<String, Prototype> internTable =
29 new HashMap<String, Prototype>(500);
31 /** {@code non-null;} method descriptor */
32 private final String descriptor;
34 /** {@code non-null;} return type */
35 private final Type returnType;
37 /** {@code non-null;} list of parameter types */
38 private final StdTypeList parameterTypes;
40 /** {@code null-ok;} list of parameter frame types, if calculated */
41 private StdTypeList parameterFrameTypes;
44 * Returns the unique instance corresponding to the
45 * given method descriptor. See vmspec-2 sec4.3.3 for details on the
46 * field descriptor syntax.
48 * @param descriptor {@code non-null;} the descriptor
49 * @return {@code non-null;} the corresponding instance
50 * @throws IllegalArgumentException thrown if the descriptor has
53 public static Prototype intern(String descriptor) {
54 if (descriptor == null) {
55 throw new NullPointerException("descriptor == null");
58 Prototype result = internTable.get(descriptor);
63 Type[] params = makeParameterArray(descriptor);
69 char c = descriptor.charAt(at);
75 // Skip array markers.
78 c = descriptor.charAt(at);
82 // It looks like the start of a class name; find the end.
83 int endAt = descriptor.indexOf(';', at);
85 throw new IllegalArgumentException("bad descriptor");
93 Type.intern(descriptor.substring(startAt, at));
97 Type returnType = Type.internReturnType(descriptor.substring(at));
98 StdTypeList parameterTypes = new StdTypeList(paramCount);
100 for (int i = 0; i < paramCount; i++) {
101 parameterTypes.set(i, params[i]);
104 result = new Prototype(descriptor, returnType, parameterTypes);
105 return putIntern(result);
109 * Helper for {@link #intern} which returns an empty array to
110 * populate with parsed parameter types, and which also ensures
111 * that there is a '(' at the start of the descriptor and a
112 * single ')' somewhere before the end.
114 * @param descriptor {@code non-null;} the descriptor string
115 * @return {@code non-null;} array large enough to hold all parsed parameter
116 * types, but which is likely actually larger than needed
118 private static Type[] makeParameterArray(String descriptor) {
119 int length = descriptor.length();
121 if (descriptor.charAt(0) != '(') {
122 throw new IllegalArgumentException("bad descriptor");
126 * This is a cheesy way to establish an upper bound on the
127 * number of parameters: Just count capital letters.
131 for (int i = 1; i < length; i++) {
132 char c = descriptor.charAt(i);
137 if ((c >= 'A') && (c <= 'Z')) {
142 if ((closeAt == 0) || (closeAt == (length - 1))) {
143 throw new IllegalArgumentException("bad descriptor");
146 if (descriptor.indexOf(')', closeAt + 1) != -1) {
147 throw new IllegalArgumentException("bad descriptor");
150 return new Type[maxParams];
154 * Interns an instance, adding to the descriptor as necessary based
155 * on the given definer, name, and flags. For example, an init
156 * method has an uninitialized object of type {@code definer}
157 * as its first argument.
159 * @param descriptor {@code non-null;} the descriptor string
160 * @param definer {@code non-null;} class the method is defined on
161 * @param isStatic whether this is a static method
162 * @param isInit whether this is an init method
163 * @return {@code non-null;} the interned instance
165 public static Prototype intern(String descriptor, Type definer,
166 boolean isStatic, boolean isInit) {
167 Prototype base = intern(descriptor);
174 definer = definer.asUninitialized(Integer.MAX_VALUE);
177 return base.withFirstParameter(definer);
181 * Interns an instance which consists of the given number of
182 * {@code int}s along with the given return type
184 * @param returnType {@code non-null;} the return type
185 * @param count {@code > 0;} the number of elements in the prototype
186 * @return {@code non-null;} the interned instance
188 public static Prototype internInts(Type returnType, int count) {
189 // Make the descriptor...
191 StringBuffer sb = new StringBuffer(100);
195 for (int i = 0; i < count; i++) {
200 sb.append(returnType.getDescriptor());
203 return intern(sb.toString());
207 * Constructs an instance. This is a private constructor; use one
208 * of the public static methods to get instances.
210 * @param descriptor {@code non-null;} the descriptor string
212 private Prototype(String descriptor, Type returnType,
213 StdTypeList parameterTypes) {
214 if (descriptor == null) {
215 throw new NullPointerException("descriptor == null");
218 if (returnType == null) {
219 throw new NullPointerException("returnType == null");
222 if (parameterTypes == null) {
223 throw new NullPointerException("parameterTypes == null");
226 this.descriptor = descriptor;
227 this.returnType = returnType;
228 this.parameterTypes = parameterTypes;
229 this.parameterFrameTypes = null;
234 public boolean equals(Object other) {
237 * Since externally-visible instances are interned, this
238 * check helps weed out some easy cases.
243 if (!(other instanceof Prototype)) {
247 return descriptor.equals(((Prototype) other).descriptor);
252 public int hashCode() {
253 return descriptor.hashCode();
257 public int compareTo(Prototype other) {
263 * The return type is the major order, and then args in order,
264 * and then the shorter list comes first (similar to string
268 int result = returnType.compareTo(other.returnType);
274 int thisSize = parameterTypes.size();
275 int otherSize = other.parameterTypes.size();
276 int size = Math.min(thisSize, otherSize);
278 for (int i = 0; i < size; i++) {
279 Type thisType = parameterTypes.get(i);
280 Type otherType = other.parameterTypes.get(i);
282 result = thisType.compareTo(otherType);
289 if (thisSize < otherSize) {
291 } else if (thisSize > otherSize) {
300 public String toString() {
305 * Gets the descriptor string.
307 * @return {@code non-null;} the descriptor
309 public String getDescriptor() {
314 * Gets the return type.
316 * @return {@code non-null;} the return type
318 public Type getReturnType() {
323 * Gets the list of parameter types.
325 * @return {@code non-null;} the list of parameter types
327 public StdTypeList getParameterTypes() {
328 return parameterTypes;
332 * Gets the list of frame types corresponding to the list of parameter
333 * types. The difference between the two lists (if any) is that all
334 * "intlike" types (see {@link Type#isIntlike}) are replaced by
337 * @return {@code non-null;} the list of parameter frame types
339 public StdTypeList getParameterFrameTypes() {
340 if (parameterFrameTypes == null) {
341 int sz = parameterTypes.size();
342 StdTypeList list = new StdTypeList(sz);
344 for (int i = 0; i < sz; i++) {
345 Type one = parameterTypes.get(i);
346 if (one.isIntlike()) {
352 parameterFrameTypes = any ? list : parameterTypes;
355 return parameterFrameTypes;
359 * Returns a new interned instance, which is the same as this instance,
360 * except that it has an additional parameter prepended to the original's
363 * @param param {@code non-null;} the new first parameter
364 * @return {@code non-null;} an appropriately-constructed instance
366 public Prototype withFirstParameter(Type param) {
367 String newDesc = "(" + param.getDescriptor() + descriptor.substring(1);
368 StdTypeList newParams = parameterTypes.withFirst(param);
370 newParams.setImmutable();
373 new Prototype(newDesc, returnType, newParams);
375 return putIntern(result);
379 * Puts the given instance in the intern table if it's not already
380 * there. If a conflicting value is already in the table, then leave it.
381 * Return the interned value.
383 * @param desc {@code non-null;} instance to make interned
384 * @return {@code non-null;} the actual interned object
386 private static Prototype putIntern(Prototype desc) {
387 synchronized (internTable) {
388 String descriptor = desc.getDescriptor();
389 Prototype already = internTable.get(descriptor);
390 if (already != null) {
393 internTable.put(descriptor, desc);