]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/cojen/classfile/MethodDesc.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / cojen / classfile / MethodDesc.java
diff --git a/bundles/org.simantics.scl.compiler/src/org/cojen/classfile/MethodDesc.java b/bundles/org.simantics.scl.compiler/src/org/cojen/classfile/MethodDesc.java
new file mode 100644 (file)
index 0000000..e954769
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ *  Copyright 2004-2010 Brian S O'Neill
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.cojen.classfile;
+
+import java.io.Serializable;
+import java.io.Externalizable;
+import java.io.ObjectOutput;
+import java.io.ObjectInput;
+import java.io.IOException;
+import java.io.ObjectStreamException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.cojen.util.WeakCanonicalSet;
+
+/**
+ * This class is used to build method descriptor strings as 
+ * defined in <i>The Java Virtual Machine Specification</i>, section 4.3.3.
+ * MethodDesc instances are canonicalized and therefore "==" comparable.
+ *
+ * @author Brian S O'Neill
+ */
+public class MethodDesc extends Descriptor implements Serializable {
+    private static final TypeDesc[] EMPTY_PARAMS = new TypeDesc[0];
+
+    // MethodDesc and TypeDesc can share the same instance cache.
+    private final static WeakCanonicalSet<Descriptor> cInstances = TypeDesc.cInstances;
+
+    static MethodDesc intern(MethodDesc desc) {
+        return cInstances.put(desc);
+    }
+
+    /**
+     * Acquire a MethodDesc from a set of arguments.
+     * @param ret return type of method; null implies void
+     * @param params parameters to method; null implies none
+     */
+    public static MethodDesc forArguments(TypeDesc ret, TypeDesc[] params) {
+        if (ret == null) {
+            ret = TypeDesc.VOID;
+        }
+        if (params == null || params.length == 0) {
+            params = EMPTY_PARAMS;
+        }
+        return intern(new MethodDesc(ret, params));
+    }
+
+    /**
+     * Acquire a MethodDesc from a type descriptor. This syntax is described in
+     * section 4.3.3, Method Descriptors.
+     */
+    public static MethodDesc forDescriptor(String desc) 
+        throws IllegalArgumentException
+    {
+        try {
+            int cursor = 0;
+            char c;
+
+            if ((c = desc.charAt(cursor++)) != '(') {
+                throw invalidDescriptor(desc);
+            }
+
+            StringBuffer buf = new StringBuffer();
+            List<TypeDesc> list = new ArrayList<TypeDesc>();
+
+            while ((c = desc.charAt(cursor++)) != ')') {
+                switch (c) {
+                case 'V':
+                case 'I':
+                case 'C':
+                case 'Z':
+                case 'D':
+                case 'F':
+                case 'J':
+                case 'B':
+                case 'S':
+                    buf.append(c);
+                    break;
+                case '[':
+                    buf.append(c);
+                    continue;
+                case 'L':
+                    while (true) {
+                        buf.append(c);
+                        if (c == ';') {
+                            break;
+                        }
+                        c = desc.charAt(cursor++);
+                    }
+                    break;
+                default:
+                    throw invalidDescriptor(desc);
+                }
+
+                list.add(TypeDesc.forDescriptor(buf.toString()));
+                buf.setLength(0);
+            }
+
+            TypeDesc ret = TypeDesc.forDescriptor(desc.substring(cursor));
+
+            TypeDesc[] tds = list.toArray(new TypeDesc[list.size()]);
+
+            return intern(new MethodDesc(desc, ret, tds));
+        } catch (NullPointerException e) {
+            throw invalidDescriptor(desc);
+        } catch (IndexOutOfBoundsException e) {
+            throw invalidDescriptor(desc);
+        }
+    }
+
+    public static MethodDesc forMethod(Method method) {
+        Class<?>[] paramClasses = method.getParameterTypes();
+        TypeDesc[] paramTypes;
+        if (paramClasses == null || paramClasses.length == 0) {
+            paramTypes = EMPTY_PARAMS;
+        } else {
+            paramTypes = new TypeDesc[paramClasses.length];
+            for (int i=paramClasses.length; --i>=0; ) {
+                paramTypes[i] = TypeDesc.forClass(paramClasses[i]);
+            }
+        }
+        return forArguments(TypeDesc.forClass(method.getReturnType()), paramTypes);
+    }
+
+    private static IllegalArgumentException invalidDescriptor(String desc) {
+        return new IllegalArgumentException("Invalid descriptor: " + desc);
+    }
+
+    private transient final String mDescriptor;
+    private transient final TypeDesc mRetType;
+    private transient final TypeDesc[] mParams;
+    
+    private MethodDesc(TypeDesc ret, TypeDesc[] params) {
+        mDescriptor = generateDescriptor(ret, params);
+        mRetType = ret;
+        mParams = params;
+    }
+
+    private MethodDesc(String desc, TypeDesc ret, TypeDesc[] params) {
+        mDescriptor = desc;
+        mRetType = ret;
+        mParams = params;
+    }
+
+    /**
+     * Returns a method descriptor string, excluding generics.
+     */
+    public String getDescriptor() {
+        return mDescriptor;
+    }
+
+    /**
+     * Returns a method descriptor string, including any generics.
+     */
+    //public abstract String getGenericDescriptor();
+
+    /**
+     * Returns the described return type, which is TypeDesc.VOID if void.
+     */
+    public TypeDesc getReturnType() {
+        return mRetType;
+    }
+
+    public int getParameterCount() {
+        return mParams.length;
+    }
+
+    public TypeDesc[] getParameterTypes() {
+        TypeDesc[] params = mParams;
+        return (params != EMPTY_PARAMS) ? (TypeDesc[])params.clone() : params;
+    }
+
+    /**
+     * Returns this in Java method signature syntax.
+     *
+     * @param name method name
+     */
+    public String toMethodSignature(String name) {
+        return toMethodSignature(name, false);
+    }
+
+    /**
+     * Returns this in Java method signature syntax.
+     *
+     * @param name method name
+     * @param varargs request that the last argument, if it is an array, to
+     * be formatted in varargs syntax.
+     */
+    public String toMethodSignature(String name, boolean varargs) {
+        StringBuffer buf = new StringBuffer();
+        buf.append(mRetType.getFullName());
+        buf.append(' ');
+        buf.append(name);
+        buf.append('(');
+
+        TypeDesc[] params = mParams;
+        for (int i=0; i<params.length; i++) {
+            if (i > 0) {
+                buf.append(", ");
+            }
+            TypeDesc param = params[i];
+            if (varargs && param.isArray() && i == (params.length - 1)) {
+                buf.append(param.getComponentType().getFullName());
+                buf.append("...");
+            } else {
+                buf.append(param.getFullName());
+            }
+        }
+
+        return buf.append(')').toString();
+    }
+
+    public String toString() {
+        // TODO: Return generic descriptor
+        return mDescriptor;
+    }
+
+    public int hashCode() {
+        return mDescriptor.hashCode();
+    }
+
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (other instanceof MethodDesc) {
+            return ((MethodDesc)other).mDescriptor.equals(mDescriptor);
+        }
+        return false;
+    }
+
+    Object writeReplace() throws ObjectStreamException {
+        return new External(mDescriptor);
+    }
+
+    private static String generateDescriptor(TypeDesc ret, TypeDesc[] params) {
+        int length = ret.getDescriptor().length() + 2;
+        int paramsLength = params.length;
+        for (int i=paramsLength; --i >=0; ) {
+            length += params[i].getDescriptor().length();
+        }
+        char[] buf = new char[length];
+        buf[0] = '(';
+        int index = 1;
+        String paramDesc;
+        for (int i=0; i<paramsLength; i++) {
+            paramDesc = params[i].getDescriptor();
+            int paramDescLength = paramDesc.length();
+            paramDesc.getChars(0, paramDescLength, buf, index);
+            index += paramDescLength;
+        }
+        buf[index++] = ')';
+        paramDesc = ret.getDescriptor();
+        paramDesc.getChars(0, paramDesc.length(), buf, index);
+        return new String(buf);
+    }
+
+    private static class External implements Externalizable {
+        private String mDescriptor;
+
+        public External() {
+        }
+
+        public External(String desc) {
+            mDescriptor = desc;
+        }
+
+        public void writeExternal(ObjectOutput out) throws IOException {
+            out.writeUTF(mDescriptor);
+        }
+
+        public void readExternal(ObjectInput in) throws IOException {
+            mDescriptor = in.readUTF();
+        }
+
+        public Object readResolve() throws ObjectStreamException {
+            return forDescriptor(mDescriptor);
+        }
+    }
+}