X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fcojen%2Fclassfile%2FMethodDesc.java;fp=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fcojen%2Fclassfile%2FMethodDesc.java;h=e9547699238991920ac5406956cad1e020b85e35;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;p=simantics%2Fplatform.git 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 index 000000000..e95476992 --- /dev/null +++ b/bundles/org.simantics.scl.compiler/src/org/cojen/classfile/MethodDesc.java @@ -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 The Java Virtual Machine Specification, 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 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 list = new ArrayList(); + + 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 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