]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/cojen/classfile/MethodDesc.java
e9547699238991920ac5406956cad1e020b85e35
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / cojen / classfile / MethodDesc.java
1 /*
2  *  Copyright 2004-2010 Brian S O'Neill
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 org.cojen.classfile;
18
19 import java.io.Serializable;
20 import java.io.Externalizable;
21 import java.io.ObjectOutput;
22 import java.io.ObjectInput;
23 import java.io.IOException;
24 import java.io.ObjectStreamException;
25 import java.lang.reflect.Method;
26 import java.util.List;
27 import java.util.ArrayList;
28
29 import org.cojen.util.WeakCanonicalSet;
30
31 /**
32  * This class is used to build method descriptor strings as 
33  * defined in <i>The Java Virtual Machine Specification</i>, section 4.3.3.
34  * MethodDesc instances are canonicalized and therefore "==" comparable.
35  *
36  * @author Brian S O'Neill
37  */
38 public class MethodDesc extends Descriptor implements Serializable {
39     private static final TypeDesc[] EMPTY_PARAMS = new TypeDesc[0];
40
41     // MethodDesc and TypeDesc can share the same instance cache.
42     private final static WeakCanonicalSet<Descriptor> cInstances = TypeDesc.cInstances;
43
44     static MethodDesc intern(MethodDesc desc) {
45         return cInstances.put(desc);
46     }
47
48     /**
49      * Acquire a MethodDesc from a set of arguments.
50      * @param ret return type of method; null implies void
51      * @param params parameters to method; null implies none
52      */
53     public static MethodDesc forArguments(TypeDesc ret, TypeDesc[] params) {
54         if (ret == null) {
55             ret = TypeDesc.VOID;
56         }
57         if (params == null || params.length == 0) {
58             params = EMPTY_PARAMS;
59         }
60         return intern(new MethodDesc(ret, params));
61     }
62
63     /**
64      * Acquire a MethodDesc from a type descriptor. This syntax is described in
65      * section 4.3.3, Method Descriptors.
66      */
67     public static MethodDesc forDescriptor(String desc) 
68         throws IllegalArgumentException
69     {
70         try {
71             int cursor = 0;
72             char c;
73
74             if ((c = desc.charAt(cursor++)) != '(') {
75                 throw invalidDescriptor(desc);
76             }
77
78             StringBuffer buf = new StringBuffer();
79             List<TypeDesc> list = new ArrayList<TypeDesc>();
80
81             while ((c = desc.charAt(cursor++)) != ')') {
82                 switch (c) {
83                 case 'V':
84                 case 'I':
85                 case 'C':
86                 case 'Z':
87                 case 'D':
88                 case 'F':
89                 case 'J':
90                 case 'B':
91                 case 'S':
92                     buf.append(c);
93                     break;
94                 case '[':
95                     buf.append(c);
96                     continue;
97                 case 'L':
98                     while (true) {
99                         buf.append(c);
100                         if (c == ';') {
101                             break;
102                         }
103                         c = desc.charAt(cursor++);
104                     }
105                     break;
106                 default:
107                     throw invalidDescriptor(desc);
108                 }
109
110                 list.add(TypeDesc.forDescriptor(buf.toString()));
111                 buf.setLength(0);
112             }
113
114             TypeDesc ret = TypeDesc.forDescriptor(desc.substring(cursor));
115
116             TypeDesc[] tds = list.toArray(new TypeDesc[list.size()]);
117
118             return intern(new MethodDesc(desc, ret, tds));
119         } catch (NullPointerException e) {
120             throw invalidDescriptor(desc);
121         } catch (IndexOutOfBoundsException e) {
122             throw invalidDescriptor(desc);
123         }
124     }
125
126     public static MethodDesc forMethod(Method method) {
127         Class<?>[] paramClasses = method.getParameterTypes();
128         TypeDesc[] paramTypes;
129         if (paramClasses == null || paramClasses.length == 0) {
130             paramTypes = EMPTY_PARAMS;
131         } else {
132             paramTypes = new TypeDesc[paramClasses.length];
133             for (int i=paramClasses.length; --i>=0; ) {
134                 paramTypes[i] = TypeDesc.forClass(paramClasses[i]);
135             }
136         }
137         return forArguments(TypeDesc.forClass(method.getReturnType()), paramTypes);
138     }
139
140     private static IllegalArgumentException invalidDescriptor(String desc) {
141         return new IllegalArgumentException("Invalid descriptor: " + desc);
142     }
143
144     private transient final String mDescriptor;
145     private transient final TypeDesc mRetType;
146     private transient final TypeDesc[] mParams;
147     
148     private MethodDesc(TypeDesc ret, TypeDesc[] params) {
149         mDescriptor = generateDescriptor(ret, params);
150         mRetType = ret;
151         mParams = params;
152     }
153
154     private MethodDesc(String desc, TypeDesc ret, TypeDesc[] params) {
155         mDescriptor = desc;
156         mRetType = ret;
157         mParams = params;
158     }
159
160     /**
161      * Returns a method descriptor string, excluding generics.
162      */
163     public String getDescriptor() {
164         return mDescriptor;
165     }
166
167     /**
168      * Returns a method descriptor string, including any generics.
169      */
170     //public abstract String getGenericDescriptor();
171
172     /**
173      * Returns the described return type, which is TypeDesc.VOID if void.
174      */
175     public TypeDesc getReturnType() {
176         return mRetType;
177     }
178
179     public int getParameterCount() {
180         return mParams.length;
181     }
182
183     public TypeDesc[] getParameterTypes() {
184         TypeDesc[] params = mParams;
185         return (params != EMPTY_PARAMS) ? (TypeDesc[])params.clone() : params;
186     }
187
188     /**
189      * Returns this in Java method signature syntax.
190      *
191      * @param name method name
192      */
193     public String toMethodSignature(String name) {
194         return toMethodSignature(name, false);
195     }
196
197     /**
198      * Returns this in Java method signature syntax.
199      *
200      * @param name method name
201      * @param varargs request that the last argument, if it is an array, to
202      * be formatted in varargs syntax.
203      */
204     public String toMethodSignature(String name, boolean varargs) {
205         StringBuffer buf = new StringBuffer();
206         buf.append(mRetType.getFullName());
207         buf.append(' ');
208         buf.append(name);
209         buf.append('(');
210
211         TypeDesc[] params = mParams;
212         for (int i=0; i<params.length; i++) {
213             if (i > 0) {
214                 buf.append(", ");
215             }
216             TypeDesc param = params[i];
217             if (varargs && param.isArray() && i == (params.length - 1)) {
218                 buf.append(param.getComponentType().getFullName());
219                 buf.append("...");
220             } else {
221                 buf.append(param.getFullName());
222             }
223         }
224
225         return buf.append(')').toString();
226     }
227
228     public String toString() {
229         // TODO: Return generic descriptor
230         return mDescriptor;
231     }
232
233     public int hashCode() {
234         return mDescriptor.hashCode();
235     }
236
237     public boolean equals(Object other) {
238         if (this == other) {
239             return true;
240         }
241         if (other instanceof MethodDesc) {
242             return ((MethodDesc)other).mDescriptor.equals(mDescriptor);
243         }
244         return false;
245     }
246
247     Object writeReplace() throws ObjectStreamException {
248         return new External(mDescriptor);
249     }
250
251     private static String generateDescriptor(TypeDesc ret, TypeDesc[] params) {
252         int length = ret.getDescriptor().length() + 2;
253         int paramsLength = params.length;
254         for (int i=paramsLength; --i >=0; ) {
255             length += params[i].getDescriptor().length();
256         }
257         char[] buf = new char[length];
258         buf[0] = '(';
259         int index = 1;
260         String paramDesc;
261         for (int i=0; i<paramsLength; i++) {
262             paramDesc = params[i].getDescriptor();
263             int paramDescLength = paramDesc.length();
264             paramDesc.getChars(0, paramDescLength, buf, index);
265             index += paramDescLength;
266         }
267         buf[index++] = ')';
268         paramDesc = ret.getDescriptor();
269         paramDesc.getChars(0, paramDesc.length(), buf, index);
270         return new String(buf);
271     }
272
273     private static class External implements Externalizable {
274         private String mDescriptor;
275
276         public External() {
277         }
278
279         public External(String desc) {
280             mDescriptor = desc;
281         }
282
283         public void writeExternal(ObjectOutput out) throws IOException {
284             out.writeUTF(mDescriptor);
285         }
286
287         public void readExternal(ObjectInput in) throws IOException {
288             mDescriptor = in.readUTF();
289         }
290
291         public Object readResolve() throws ObjectStreamException {
292             return forDescriptor(mDescriptor);
293         }
294     }
295 }