]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/cojen/classfile/TypeDesc.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / cojen / classfile / TypeDesc.java
diff --git a/bundles/org.simantics.scl.compiler/src/org/cojen/classfile/TypeDesc.java b/bundles/org.simantics.scl.compiler/src/org/cojen/classfile/TypeDesc.java
new file mode 100644 (file)
index 0000000..084eb3d
--- /dev/null
@@ -0,0 +1,921 @@
+/*
+ *  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.ref.SoftReference;
+import java.lang.reflect.Array;
+import java.util.Collections;
+import java.util.Map;
+
+import org.cojen.util.SoftValuedHashMap;
+import org.cojen.util.WeakCanonicalSet;
+import org.cojen.util.WeakIdentityMap;
+
+/**
+ * This class is used to build field and return type descriptor strings as 
+ * defined in <i>The Java Virtual Machine Specification</i>, section 4.3.2.
+ * TypeDesc instances are canonicalized and therefore "==" comparable.
+ *
+ * @author Brian S O'Neill
+ */
+@SuppressWarnings("rawtypes")
+public abstract class TypeDesc extends Descriptor implements Serializable {
+    /**
+     * Type code returned from getTypeCode, which can be used with the
+     * newarray instruction.
+     */
+    public final static int
+        OBJECT_CODE = 0,
+        VOID_CODE = 1,
+        BOOLEAN_CODE = 4,
+        CHAR_CODE = 5,
+        FLOAT_CODE = 6,
+        DOUBLE_CODE = 7,
+        BYTE_CODE = 8,
+        SHORT_CODE = 9,
+        INT_CODE = 10,
+        LONG_CODE = 11;
+
+    /** Primitive type void */
+    public final static TypeDesc VOID;
+    /** Primitive type boolean */
+    public final static TypeDesc BOOLEAN;
+    /** Primitive type char */
+    public final static TypeDesc CHAR;
+    /** Primitive type byte */
+    public final static TypeDesc BYTE;
+    /** Primitive type short */
+    public final static TypeDesc SHORT;
+    /** Primitive type int */
+    public final static TypeDesc INT;
+    /** Primitive type long */
+    public final static TypeDesc LONG;
+    /** Primitive type float */
+    public final static TypeDesc FLOAT;
+    /** Primitive type double */
+    public final static TypeDesc DOUBLE;
+
+    /** Object type java.lang.Object, provided for convenience */
+    public final static TypeDesc OBJECT;
+    /** Object type java.lang.String, provided for convenience */
+    public final static TypeDesc STRING;
+
+    // Pool of all shared instances. Ensures identity comparison works.
+    final static WeakCanonicalSet<Descriptor> cInstances;
+
+    // Cache that maps Classes to TypeDescs.
+    private final static Map<Class, TypeDesc> cClassesToInstances;
+
+    // Cache that maps String names to TypeDescs.
+    private final static Map<String, TypeDesc> cNamesToInstances;
+
+    // Cache that maps String descriptors to TypeDescs.
+    private final static Map<String, TypeDesc> cDescriptorsToInstances;
+
+    static {
+        cInstances = new WeakCanonicalSet<Descriptor>();
+
+        cClassesToInstances = Collections.synchronizedMap(new WeakIdentityMap<Class, TypeDesc>());
+        cNamesToInstances = Collections.synchronizedMap(new SoftValuedHashMap<String, TypeDesc>());
+        cDescriptorsToInstances = Collections.synchronizedMap
+            (new SoftValuedHashMap<String, TypeDesc>());
+
+        VOID = intern(new PrimitiveType("V", VOID_CODE));
+        BOOLEAN = intern(new PrimitiveType("Z", BOOLEAN_CODE));
+        CHAR = intern(new PrimitiveType("C", CHAR_CODE));
+        BYTE = intern(new PrimitiveType("B", BYTE_CODE));
+        SHORT = intern(new PrimitiveType("S", SHORT_CODE));
+        INT = intern(new PrimitiveType("I", INT_CODE));
+        LONG = intern(new PrimitiveType("J", LONG_CODE));
+        FLOAT = intern(new PrimitiveType("F", FLOAT_CODE));
+        DOUBLE = intern(new PrimitiveType("D", DOUBLE_CODE));
+
+        OBJECT = forClass("java.lang.Object");
+        STRING = forClass("java.lang.String");
+    }
+
+    static TypeDesc intern(TypeDesc type) {
+        return cInstances.put(type);
+    }
+
+    /**
+     * Acquire a TypeDesc from any class, including primitives and arrays.
+     */
+    public static TypeDesc forClass(final Class clazz) {
+        if (clazz == null) {
+            return null;
+        }
+
+        TypeDesc type = (TypeDesc)cClassesToInstances.get(clazz);
+
+        if (type == null || type.toClass() != clazz) {
+            if (clazz.isArray()) {
+                type = forClass(clazz.getComponentType()).toArrayType();
+            } else if (clazz.isPrimitive()) {
+                if (clazz == int.class) {
+                    type = INT;
+                } else if (clazz == boolean.class) {
+                    type = BOOLEAN;
+                } else if (clazz == char.class) {
+                    type = CHAR;
+                } else if (clazz == byte.class) {
+                    type = BYTE;
+                } else if (clazz == long.class) {
+                    type = LONG;
+                } else if (clazz == float.class) {
+                    type = FLOAT;
+                } else if (clazz == double.class) {
+                    type = DOUBLE;
+                } else if (clazz == short.class) {
+                    type = SHORT;
+                } else if (clazz == void.class) {
+                    type = VOID;
+                }
+            } else {
+                String name = clazz.getName();
+                type = intern(new ObjectType(generateDescriptor(name), name));
+            }
+        
+            if (type.toClass() != clazz) {
+                type = new ObjectType(type.getDescriptor(), clazz.getName());
+                ((ObjectType) type).setClass(clazz);
+            }
+
+            synchronized (cClassesToInstances) {
+                if (cClassesToInstances.containsKey(clazz)) {
+                    type = (TypeDesc)cClassesToInstances.get(clazz);
+                } else {
+                    cClassesToInstances.put(clazz, type);
+                }
+            }
+        }
+
+        return type;
+    }
+
+    /**
+     * Acquire a TypeDesc from any class name, including primitives and arrays.
+     * Primitive and array syntax matches Java declarations.
+     */
+    public static TypeDesc forClass(final String name) throws IllegalArgumentException {
+        if (name.length() < 1) {
+            throw invalidName(name);
+        }
+
+        // TODO: Support generics in name.
+
+        TypeDesc type = (TypeDesc)cNamesToInstances.get(name);
+        if (type != null) {
+            return type;
+        }
+
+        int index1 = name.lastIndexOf('[');
+        int index2 = name.lastIndexOf(']');
+        if (index2 >= 0) {
+            if (index2 + 1 != name.length() || index1 + 1 != index2) {
+                throw invalidName(name);
+            }
+            try {
+                type = forClass(name.substring(0, index1)).toArrayType();
+            } catch (IllegalArgumentException e) {
+                throw invalidName(name);
+            }
+        } else if (index1 >= 0) {
+            throw invalidName(name);
+        } else {
+            setType: {
+                switch (name.charAt(0)) {
+                case 'v':
+                    if (name.equals("void")) {
+                        type = VOID;
+                        break setType;
+                    }
+                    break;
+                case 'b':
+                    if (name.equals("boolean")) {
+                        type = BOOLEAN;
+                        break setType;
+                    } else if (name.equals("byte")) {
+                        type =  BYTE;
+                        break setType;
+                    }
+                    break;
+                case 'c':
+                    if (name.equals("char")) {
+                        type = CHAR;
+                        break setType;
+                    }
+                    break;
+                case 's':
+                    if (name.equals("short")) {
+                        type = SHORT;
+                        break setType;
+                    }
+                    break;
+                case 'i':
+                    if (name.equals("int")) {
+                        type = INT;
+                        break setType;
+                    }
+                    break;
+                case 'l':
+                    if (name.equals("long")) {
+                        type = LONG;
+                        break setType;
+                    }
+                    break;
+                case 'f':
+                    if (name.equals("float")) {
+                        type = FLOAT;
+                        break setType;
+                    }
+                    break;
+                case 'd':
+                    if (name.equals("double")) {
+                        type = DOUBLE;
+                        break setType;
+                    }
+                    break;
+                }
+
+                String desc = generateDescriptor(name);
+                if (name.indexOf('/') < 0) {
+                    type = new ObjectType(desc, name);
+                } else {
+                    type = new ObjectType(desc, name.replace('/', '.'));
+                }
+                type = intern(type);
+            }
+        }
+
+        cNamesToInstances.put(name, type);
+        return type;
+    }
+
+    private static IllegalArgumentException invalidName(String name) {
+        return new IllegalArgumentException("Invalid name: " + name);
+    }
+
+    /**
+     * Acquire a TypeDesc from a type descriptor. This syntax is described in
+     * section 4.3.2, Field Descriptors.
+     */
+    public static TypeDesc forDescriptor(final String desc) throws IllegalArgumentException {
+        TypeDesc type = (TypeDesc)cDescriptorsToInstances.get(desc);
+        if (type != null) {
+            return type;
+        }
+
+        // TODO: Support generics in descriptor.
+
+        String rootDesc = desc;
+        int cursor = 0;
+        int dim = 0;
+        try {
+            char c;
+            while ((c = rootDesc.charAt(cursor++)) == '[') {
+                dim++;
+            }
+
+            switch (c) {
+            case 'V':
+                type = VOID;
+                break;
+            case 'Z':
+                type = BOOLEAN;
+                break;
+            case 'C':
+                type = CHAR;
+                break;
+            case 'B':
+                type = BYTE;
+                break;
+            case 'S':
+                type = SHORT;
+                break;
+            case 'I':
+                type = INT;
+                break;
+            case 'J':
+                type = LONG;
+                break;
+            case 'F':
+                type = FLOAT;
+                break;
+            case 'D':
+                type = DOUBLE;
+                break;
+            case 'L':
+                if (dim > 0) {
+                    rootDesc = rootDesc.substring(dim);
+                    cursor = 1;
+                }
+                StringBuffer name = new StringBuffer(rootDesc.length() - 2);
+                while ((c = rootDesc.charAt(cursor++)) != ';') {
+                    if (c == '/') {
+                        c = '.';
+                    }
+                    name.append(c);
+                }
+                type = intern(new ObjectType(rootDesc, name.toString()));
+                break;
+            default:
+                throw invalidDescriptor(desc);
+            }
+        } catch (NullPointerException e) {
+            throw invalidDescriptor(desc);
+        } catch (IndexOutOfBoundsException e) {
+            throw invalidDescriptor(desc);
+        }
+
+        if (cursor != rootDesc.length()) {
+            throw invalidDescriptor(desc);
+        }
+
+        while (--dim >= 0) {
+            type = type.toArrayType();
+        }
+
+        cDescriptorsToInstances.put(desc, type);
+        return type;
+    }
+
+    private static IllegalArgumentException invalidDescriptor(String desc) {
+        return new IllegalArgumentException("Invalid descriptor: " + desc);
+    }
+
+    private static String generateDescriptor(String classname) {
+        int length = classname.length();
+        char[] buf = new char[length + 2];
+        buf[0] = 'L';
+        classname.getChars(0, length, buf, 1);
+        int i;
+        for (i=1; i<=length; i++) {
+            char c = buf[i];
+            if (c == '.') {
+                buf[i] = '/';
+            }
+        }
+        buf[i] = ';';
+        return new String(buf);
+    }
+
+    transient final String mDescriptor;
+
+    TypeDesc(String desc) {
+        mDescriptor = desc;
+    }
+
+    /**
+     * Returns a type descriptor string, excluding generics.
+     */
+    public final String getDescriptor() {
+        return mDescriptor;
+    }
+
+    /**
+     * Returns a type descriptor string, including any generics.
+     */
+    //public abstract String getGenericDescriptor();
+
+    /**
+     * Returns the class name for this descriptor. If the type is primitive,
+     * then the Java primitive type name is returned. If the type is an array,
+     * only the root component type name is returned.
+     */
+    public abstract String getRootName();
+
+    /**
+     * Returns the class name for this descriptor. If the type is primitive,
+     * then the Java primitive type name is returned. If the type is an array,
+     * "[]" is append at the end of the name for each dimension.
+     */
+    public abstract String getFullName();
+
+    // TODO
+    //public abstract String getGenericRootName();
+
+    // TODO
+    //public abstract String getGenericFullName();
+
+    /**
+     * Returns a type code for operating on primitive types in switches. If
+     * not primitive, OBJECT_CODE is returned.
+     */
+    public abstract int getTypeCode();
+
+    /**
+     * Returns true if this is a primitive type.
+     */
+    public abstract boolean isPrimitive();
+
+    /**
+     * Returns true if this is a primitive long or double type.
+     */
+    public abstract boolean isDoubleWord();
+
+    /**
+     * Returns true if this is an array type.
+     */
+    public abstract boolean isArray();
+
+    /**
+     * Returns the number of dimensions this array type has. If not an array,
+     * zero is returned.
+     */
+    public abstract int getDimensions();
+
+    /**
+     * Returns the component type of this array type. If not an array, null is
+     * returned.
+     */
+    public abstract TypeDesc getComponentType();
+
+    /**
+     * Returns the root component type of this array type. If not an array,
+     * null is returned.
+     */
+    public abstract TypeDesc getRootComponentType();
+
+    /**
+     * Convertes this type to an array type. If already an array, another
+     * dimension is added.
+     */
+    public abstract TypeDesc toArrayType();
+
+    /**
+     * Returns the object peer of this primitive type. For int, the object peer
+     * is java.lang.Integer. If this type is an object type, it is simply 
+     * returned.
+     */
+    public abstract TypeDesc toObjectType();
+
+    /**
+     * Returns the primitive peer of this object type, if one exists. For
+     * java.lang.Integer, the primitive peer is int. If this type is a
+     * primitive type, it is simply returned. Arrays have no primitive peer,
+     * and so null is returned instead.
+     */
+    public abstract TypeDesc toPrimitiveType();
+
+    /**
+     * Returns this type as a class. If the class isn't found, null is
+     * returned.
+     */
+    public abstract Class toClass();
+
+    /**
+     * Returns this type as a class. If the class isn't found, null is
+     * returned.
+     * @param loader optional ClassLoader to load class from
+     */
+    public abstract Class toClass(ClassLoader loader);
+
+    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 TypeDesc) {
+            return ((TypeDesc)other).mDescriptor.equals(mDescriptor);
+        }
+        return false;
+    }
+
+    Object writeReplace() throws ObjectStreamException {
+        return new External(mDescriptor);
+    }
+
+    private static class PrimitiveType extends TypeDesc {
+        private transient final int mCode;
+        private transient TypeDesc mArrayType;
+        private transient TypeDesc mObjectType;
+        
+        PrimitiveType(String desc, int code) {
+            super(desc);
+            mCode = code;
+        }
+
+        public String getRootName() {
+            switch (mCode) {
+            default:
+            case VOID_CODE:
+                return "void";
+            case BOOLEAN_CODE:
+                return "boolean";
+            case CHAR_CODE:
+                return "char";
+            case BYTE_CODE:
+                return "byte";
+            case SHORT_CODE:
+                return "short";
+            case INT_CODE:
+                return "int";
+            case LONG_CODE:
+                return "long";
+            case FLOAT_CODE:
+                return "float";
+            case DOUBLE_CODE:
+                return "double";
+            }
+        }
+        
+        public String getFullName() {
+            return getRootName();
+        }
+
+        public int getTypeCode() {
+            return mCode;
+        }
+        
+        public boolean isPrimitive() {
+            return true;
+        }
+        
+        public boolean isDoubleWord() {
+            return mCode == DOUBLE_CODE || mCode == LONG_CODE;
+        }
+        
+        public boolean isArray() {
+            return false;
+        }
+        
+        public int getDimensions() {
+            return 0;
+        }
+        
+        public TypeDesc getComponentType() {
+            return null;
+        }
+        
+        public TypeDesc getRootComponentType() {
+            return null;
+        }
+        
+        public TypeDesc toArrayType() {
+            if (mArrayType == null) {
+                char[] buf = new char[2];
+                buf[0] = '[';
+                buf[1] = mDescriptor.charAt(0);
+                mArrayType = intern(new ArrayType(new String(buf), this));
+            }
+            return mArrayType;
+        }
+        
+        public TypeDesc toObjectType() {
+            if (mObjectType == null) {
+                switch (mCode) {
+                default:
+                case VOID_CODE:
+                    mObjectType = forClass("java.lang.Void");
+                    break;
+                case BOOLEAN_CODE:
+                    mObjectType = forClass("java.lang.Boolean");
+                    break;
+                case CHAR_CODE:
+                    mObjectType = forClass("java.lang.Character");
+                    break;
+                case BYTE_CODE:
+                    mObjectType = forClass("java.lang.Byte");
+                    break;
+                case SHORT_CODE:
+                    mObjectType = forClass("java.lang.Short");
+                    break;
+                case INT_CODE:
+                    mObjectType = forClass("java.lang.Integer");
+                    break;
+                case LONG_CODE:
+                    mObjectType = forClass("java.lang.Long");
+                    break;
+                case FLOAT_CODE:
+                    mObjectType = forClass("java.lang.Float");
+                    break;
+                case DOUBLE_CODE:
+                    mObjectType = forClass("java.lang.Double");
+                    break;
+                }
+            }
+            return mObjectType;
+        }
+        
+        public TypeDesc toPrimitiveType() {
+            return this;
+        }
+
+        public Class toClass() {
+            switch (mCode) {
+            default:
+            case VOID_CODE:
+                return void.class;
+            case BOOLEAN_CODE:
+                return boolean.class;
+            case CHAR_CODE:
+                return char.class;
+            case BYTE_CODE:
+                return byte.class;
+            case SHORT_CODE:
+                return short.class;
+            case INT_CODE:
+                return int.class;
+            case LONG_CODE:
+                return long.class;
+            case FLOAT_CODE:
+                return float.class;
+            case DOUBLE_CODE:
+                return double.class;
+            }
+        }
+
+        public Class toClass(ClassLoader loader) {
+            return toClass();
+        }
+    }
+
+    private static class ObjectType extends TypeDesc {
+        private transient final String mName;
+        private transient TypeDesc mArrayType;
+        private transient TypeDesc mPrimitiveType;
+
+        // Since cClassesToInstances may reference this instance, softly
+        // reference back to class to allow it to be garbage collected.
+        private transient SoftReference<Class> mClassRef;
+
+        ObjectType(String desc, String name) {
+            super(desc);
+            mName = name;
+        }
+
+        public String getRootName() {
+            return mName;
+        }
+
+        public String getFullName() {
+            return mName;
+        }
+
+        public int getTypeCode() {
+            return OBJECT_CODE;
+        }
+
+        public boolean isPrimitive() {
+            return false;
+        }
+        
+        public boolean isDoubleWord() {
+            return false;
+        }
+        
+        public boolean isArray() {
+            return false;
+        }
+        
+        public int getDimensions() {
+            return 0;
+        }
+        
+        public TypeDesc getComponentType() {
+            return null;
+        }
+        
+        public TypeDesc getRootComponentType() {
+            return null;
+        }
+        
+        public TypeDesc toArrayType() {
+            if (mArrayType == null) {
+                int length = mDescriptor.length();
+                char[] buf = new char[length + 1];
+                buf[0] = '[';
+                mDescriptor.getChars(0, length, buf, 1);
+                mArrayType = intern(new ArrayType(new String(buf), this));
+            }
+            return mArrayType;
+        }
+        
+        public TypeDesc toObjectType() {
+            return this;
+        }
+        
+        public TypeDesc toPrimitiveType() {
+            if (mPrimitiveType == null) {
+                String name = mName;
+                if (name.startsWith("java.lang.") && name.length() > 10) {
+                    switch (name.charAt(10)) {
+                    case 'V':
+                        if (name.equals("java.lang.Void")) {
+                            mPrimitiveType = VOID;
+                        }
+                        break;
+                    case 'B':
+                        if (name.equals("java.lang.Boolean")) {
+                            mPrimitiveType = BOOLEAN;
+                        } else if (name.equals("java.lang.Byte")) {
+                            mPrimitiveType = BYTE;
+                        }
+                        break;
+                    case 'C':
+                        if (name.equals("java.lang.Character")) {
+                            mPrimitiveType = CHAR;
+                        }
+                        break;
+                    case 'S':
+                        if (name.equals("java.lang.Short")) {
+                            mPrimitiveType = SHORT;
+                        }
+                        break;
+                    case 'I':
+                        if (name.equals("java.lang.Integer")) {
+                            mPrimitiveType = INT;
+                        }
+                        break;
+                    case 'L':
+                        if (name.equals("java.lang.Long")) {
+                            mPrimitiveType = LONG;
+                        }
+                        break;
+                    case 'F':
+                        if (name.equals("java.lang.Float")) {
+                            mPrimitiveType = FLOAT;
+                        }
+                        break;
+                    case 'D':
+                        if (name.equals("java.lang.Double")) {
+                            mPrimitiveType = DOUBLE;
+                        }
+                        break;
+                    }
+                }
+            }
+
+            return mPrimitiveType;
+        }
+
+        public final synchronized Class toClass() {
+            Class clazz;
+            if (mClassRef != null) {
+                clazz = mClassRef.get();
+                if (clazz != null) {
+                    return clazz;
+                }
+            }
+            clazz = toClass(null);
+            mClassRef = new SoftReference<Class>(clazz);
+            return clazz;
+        }
+
+        public Class toClass(ClassLoader loader) {
+            TypeDesc type = toPrimitiveType();
+            if (type != null) {
+                switch (type.getTypeCode()) {
+                default:
+                case VOID_CODE:
+                    return Void.class;
+                case BOOLEAN_CODE:
+                    return Boolean.class;
+                case CHAR_CODE:
+                    return Character.class;
+                case FLOAT_CODE:
+                    return Float.class;
+                case DOUBLE_CODE:
+                    return Double.class;
+                case BYTE_CODE:
+                    return Byte.class;
+                case SHORT_CODE:
+                    return Short.class;
+                case INT_CODE:
+                    return Integer.class;
+                case LONG_CODE:
+                    return Long.class;
+                }
+            }
+
+            try {
+                if (loader == null) {
+                    return Class.forName(mName);
+                } else {
+                    return loader.loadClass(mName);
+                }
+            } catch (ClassNotFoundException e) {
+                return null;
+            }
+        }
+
+        void setClass(Class clazz) {
+            mClassRef = new SoftReference<Class>(clazz);
+        }
+    }
+
+    private static class ArrayType extends ObjectType {
+        private transient final TypeDesc mComponent;
+        private transient final String mFullName;
+
+        ArrayType(String desc, TypeDesc component) {
+            super(desc, component.getRootName());
+            mComponent = component;
+            mFullName = component.getFullName().concat("[]");
+        }
+
+        public String getFullName() {
+            return mFullName;
+        }
+
+        public boolean isArray() {
+            return true;
+        }
+        
+        public int getDimensions() {
+            return mComponent.getDimensions() + 1;
+        }
+        
+        public TypeDesc getComponentType() {
+            return mComponent;
+        }
+        
+        public TypeDesc getRootComponentType() {
+            TypeDesc type = mComponent;
+            while (type.isArray()) {
+                type = type.getComponentType();
+            }
+            return type;
+        }
+        
+        public TypeDesc toPrimitiveType() {
+            return null;
+        }
+
+        public Class toClass(ClassLoader loader) {
+            if (loader == null) {
+                return arrayClass(getRootComponentType().toClass());
+            } else {
+                return arrayClass(getRootComponentType().toClass(loader));
+            }
+        }
+
+        private Class arrayClass(Class clazz) {
+            if (clazz == null) {
+                return null;
+            }
+            int dim = getDimensions();
+            try {
+                if (dim == 1) {
+                    return Array.newInstance(clazz, 0).getClass();
+                } else {
+                    return Array.newInstance(clazz, new int[dim]).getClass();
+                }
+            } catch (IllegalArgumentException e) {
+                return null;
+            }
+        }
+    }
+
+    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);
+        }
+    }
+}