OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / dalvik / dx / src / com / android / dx / rop / type / Prototype.java
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.dx.rop.type;
18
19 import java.util.HashMap;
20
21 /**
22  * Representation of a method decriptor. Instances of this class are
23  * generally interned and may be usefully compared with each other
24  * using {@code ==}.
25  */
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);
30
31     /** {@code non-null;} method descriptor */
32     private final String descriptor;
33
34     /** {@code non-null;} return type */
35     private final Type returnType;
36
37     /** {@code non-null;} list of parameter types */
38     private final StdTypeList parameterTypes;
39
40     /** {@code null-ok;} list of parameter frame types, if calculated */
41     private StdTypeList parameterFrameTypes;
42
43     /**
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.
47      *
48      * @param descriptor {@code non-null;} the descriptor
49      * @return {@code non-null;} the corresponding instance
50      * @throws IllegalArgumentException thrown if the descriptor has
51      * invalid syntax
52      */
53     public static Prototype intern(String descriptor) {
54         if (descriptor == null) {
55             throw new NullPointerException("descriptor == null");
56         }
57
58         Prototype result = internTable.get(descriptor);
59         if (result != null) {
60             return result;
61         }
62
63         Type[] params = makeParameterArray(descriptor);
64         int paramCount = 0;
65         int at = 1;
66
67         for (;;) {
68             int startAt = at;
69             char c = descriptor.charAt(at);
70             if (c == ')') {
71                 at++;
72                 break;
73             }
74
75             // Skip array markers.
76             while (c == '[') {
77                 at++;
78                 c = descriptor.charAt(at);
79             }
80
81             if (c == 'L') {
82                 // It looks like the start of a class name; find the end.
83                 int endAt = descriptor.indexOf(';', at);
84                 if (endAt == -1) {
85                     throw new IllegalArgumentException("bad descriptor");
86                 }
87                 at = endAt + 1;
88             } else {
89                 at++;
90             }
91
92             params[paramCount] =
93                 Type.intern(descriptor.substring(startAt, at));
94             paramCount++;
95         }
96
97         Type returnType = Type.internReturnType(descriptor.substring(at));
98         StdTypeList parameterTypes = new StdTypeList(paramCount);
99
100         for (int i = 0; i < paramCount; i++) {
101             parameterTypes.set(i, params[i]);
102         }
103
104         result = new Prototype(descriptor, returnType, parameterTypes);
105         return putIntern(result);
106     }
107
108     /**
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.
113      *
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
117      */
118     private static Type[] makeParameterArray(String descriptor) {
119         int length = descriptor.length();
120
121         if (descriptor.charAt(0) != '(') {
122             throw new IllegalArgumentException("bad descriptor");
123         }
124
125         /*
126          * This is a cheesy way to establish an upper bound on the
127          * number of parameters: Just count capital letters.
128          */
129         int closeAt = 0;
130         int maxParams = 0;
131         for (int i = 1; i < length; i++) {
132             char c = descriptor.charAt(i);
133             if (c == ')') {
134                 closeAt = i;
135                 break;
136             }
137             if ((c >= 'A') && (c <= 'Z')) {
138                 maxParams++;
139             }
140         }
141
142         if ((closeAt == 0) || (closeAt == (length - 1))) {
143             throw new IllegalArgumentException("bad descriptor");
144         }
145
146         if (descriptor.indexOf(')', closeAt + 1) != -1) {
147             throw new IllegalArgumentException("bad descriptor");
148         }
149
150         return new Type[maxParams];
151     }
152
153     /**
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.
158      *
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
164      */
165     public static Prototype intern(String descriptor, Type definer,
166             boolean isStatic, boolean isInit) {
167         Prototype base = intern(descriptor);
168
169         if (isStatic) {
170             return base;
171         }
172
173         if (isInit) {
174             definer = definer.asUninitialized(Integer.MAX_VALUE);
175         }
176
177         return base.withFirstParameter(definer);
178     }
179
180     /**
181      * Interns an instance which consists of the given number of
182      * {@code int}s along with the given return type
183      *
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
187      */
188     public static Prototype internInts(Type returnType, int count) {
189         // Make the descriptor...
190
191         StringBuffer sb = new StringBuffer(100);
192
193         sb.append('(');
194
195         for (int i = 0; i < count; i++) {
196             sb.append('I');
197         }
198
199         sb.append(')');
200         sb.append(returnType.getDescriptor());
201
202         // ...and intern it.
203         return intern(sb.toString());
204     }
205
206     /**
207      * Constructs an instance. This is a private constructor; use one
208      * of the public static methods to get instances.
209      *
210      * @param descriptor {@code non-null;} the descriptor string
211      */
212     private Prototype(String descriptor, Type returnType,
213             StdTypeList parameterTypes) {
214         if (descriptor == null) {
215             throw new NullPointerException("descriptor == null");
216         }
217
218         if (returnType == null) {
219             throw new NullPointerException("returnType == null");
220         }
221
222         if (parameterTypes == null) {
223             throw new NullPointerException("parameterTypes == null");
224         }
225
226         this.descriptor = descriptor;
227         this.returnType = returnType;
228         this.parameterTypes = parameterTypes;
229         this.parameterFrameTypes = null;
230     }
231
232     /** {@inheritDoc} */
233     @Override
234     public boolean equals(Object other) {
235         if (this == other) {
236             /*
237              * Since externally-visible instances are interned, this
238              * check helps weed out some easy cases.
239              */
240             return true;
241         }
242
243         if (!(other instanceof Prototype)) {
244             return false;
245         }
246
247         return descriptor.equals(((Prototype) other).descriptor);
248     }
249
250     /** {@inheritDoc} */
251     @Override
252     public int hashCode() {
253         return descriptor.hashCode();
254     }
255
256     /** {@inheritDoc} */
257     public int compareTo(Prototype other) {
258         if (this == other) {
259             return 0;
260         }
261
262         /*
263          * The return type is the major order, and then args in order,
264          * and then the shorter list comes first (similar to string
265          * sorting).
266          */
267
268         int result = returnType.compareTo(other.returnType);
269
270         if (result != 0) {
271             return result;
272         }
273
274         int thisSize = parameterTypes.size();
275         int otherSize = other.parameterTypes.size();
276         int size = Math.min(thisSize, otherSize);
277
278         for (int i = 0; i < size; i++) {
279             Type thisType = parameterTypes.get(i);
280             Type otherType = other.parameterTypes.get(i);
281
282             result = thisType.compareTo(otherType);
283
284             if (result != 0) {
285                 return result;
286             }
287         }
288
289         if (thisSize < otherSize) {
290             return -1;
291         } else if (thisSize > otherSize) {
292             return 1;
293         } else {
294             return 0;
295         }
296     }
297
298     /** {@inheritDoc} */
299     @Override
300     public String toString() {
301         return descriptor;
302     }
303
304     /**
305      * Gets the descriptor string.
306      *
307      * @return {@code non-null;} the descriptor
308      */
309     public String getDescriptor() {
310         return descriptor;
311     }
312
313     /**
314      * Gets the return type.
315      *
316      * @return {@code non-null;} the return type
317      */
318     public Type getReturnType() {
319         return returnType;
320     }
321
322     /**
323      * Gets the list of parameter types.
324      *
325      * @return {@code non-null;} the list of parameter types
326      */
327     public StdTypeList getParameterTypes() {
328         return parameterTypes;
329     }
330
331     /**
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
335      * {@link Type#INT}.
336      *
337      * @return {@code non-null;} the list of parameter frame types
338      */
339     public StdTypeList getParameterFrameTypes() {
340         if (parameterFrameTypes == null) {
341             int sz = parameterTypes.size();
342             StdTypeList list = new StdTypeList(sz);
343             boolean any = false;
344             for (int i = 0; i < sz; i++) {
345                 Type one = parameterTypes.get(i);
346                 if (one.isIntlike()) {
347                     any = true;
348                     one = Type.INT;
349                 }
350                 list.set(i, one);
351             }
352             parameterFrameTypes = any ? list : parameterTypes;
353         }
354
355         return parameterFrameTypes;
356     }
357
358     /**
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
361      * argument list.
362      *
363      * @param param {@code non-null;} the new first parameter
364      * @return {@code non-null;} an appropriately-constructed instance
365      */
366     public Prototype withFirstParameter(Type param) {
367         String newDesc = "(" + param.getDescriptor() + descriptor.substring(1);
368         StdTypeList newParams = parameterTypes.withFirst(param);
369
370         newParams.setImmutable();
371
372         Prototype result =
373             new Prototype(newDesc, returnType, newParams);
374
375         return putIntern(result);
376     }
377
378     /**
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.
382      *
383      * @param desc {@code non-null;} instance to make interned
384      * @return {@code non-null;} the actual interned object
385      */
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) {
391                 return already;
392             }
393             internTable.put(descriptor, desc);
394             return desc;
395         }
396     }
397 }