2009 lines · 77.6 KB · Java
RawDownload
                                        // u4 magic;            dout.writeInt(0xCAFEBABE);                                        // u2 minor_version;            dout.writeShort(CLASSFILE_MINOR_VERSION);                                        // u2 major_version;            dout.writeShort(CLASSFILE_MAJOR_VERSION);             cp.write(dout);             // (write constant pool)                                         // u2 access_flags;            dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);                                        // u2 this_class;            dout.writeShort(cp.getClass(dotToSlash(className)));                                        // u2 super_class;            dout.writeShort(cp.getClass(superclassName));                                         // u2 interfaces_count;            dout.writeShort(interfaces.length);                                        // u2 interfaces[interfaces_count];            for (int i = 0; i < interfaces.length; i++) {                dout.writeShort(cp.getClass(                    dotToSlash(interfaces[i].getName())));            }                                         // u2 fields_count;            dout.writeShort(fields.size());                                        // field_info fields[fields_count];            for (FieldInfo f : fields) {                f.write(dout);            }                                         // u2 methods_count;            dout.writeShort(methods.size());                                        // method_info methods[methods_count];            for (MethodInfo m : methods) {                m.write(dout);            }                                          // u2 attributes_count;            dout.writeShort(0); // (no ClassFile attributes for proxy classes)         } catch (IOException e) {            throw new InternalError("unexpected I/O Exception");        }         return bout.toByteArray();    }     /**     * Add another method to be proxied, either by creating a new     * ProxyMethod object or augmenting an old one for a duplicate     * method.     *     * "fromClass" indicates the proxy interface that the method was     * found through, which may be different from (a subinterface of)     * the method's "declaring class".  Note that the first Method     * object passed for a given name and descriptor identifies the     * Method object (and thus the declaring class) that will be     * passed to the invocation handler's "invoke" method for a given     * set of duplicate methods.     */    private void addProxyMethod(Method m, Class fromClass) {        String name = m.getName();        Class[] parameterTypes = m.getParameterTypes();        Class returnType = m.getReturnType();        Class[] exceptionTypes = m.getExceptionTypes();         String sig = name + getParameterDescriptors(parameterTypes);        List<ProxyMethod> sigmethods = proxyMethods.get(sig);        if (sigmethods != null) {            for (ProxyMethod pm : sigmethods) {                if (returnType == pm.returnType) {                    /*                     * Found a match: reduce exception types to the                     * greatest set of exceptions that can thrown                     * compatibly with the throws clauses of both                     * overridden methods.                     */                    List<Class<?>> legalExceptions = new ArrayList<Class<?>>();                    collectCompatibleTypes(                        exceptionTypes, pm.exceptionTypes, legalExceptions);                    collectCompatibleTypes(                        pm.exceptionTypes, exceptionTypes, legalExceptions);                    pm.exceptionTypes = new Class[legalExceptions.size()];                    pm.exceptionTypes =                        legalExceptions.toArray(pm.exceptionTypes);                    return;                }            }        } else {            sigmethods = new ArrayList<ProxyMethod>(3);            proxyMethods.put(sig, sigmethods);        }        sigmethods.add(new ProxyMethod(name, parameterTypes, returnType,                                       exceptionTypes, fromClass));    }     /**     * For a given set of proxy methods with the same signature, check     * that their return types are compatible according to the Proxy     * specification.     *     * Specifically, if there is more than one such method, then all     * of the return types must be reference types, and there must be     * one return type that is assignable to each of the rest of them.     */    private static void checkReturnTypes(List<ProxyMethod> methods) {        /*         * If there is only one method with a given signature, there         * cannot be a conflict.  This is the only case in which a         * primitive (or void) return type is allowed.         */        if (methods.size() < 2) {            return;        }         /*         * List of return types that are not yet known to be         * assignable from ("covered" by) any of the others.         */        LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<Class<?>>();     nextNewReturnType:        for (ProxyMethod pm : methods) {            Class<?> newReturnType = pm.returnType;            if (newReturnType.isPrimitive()) {                throw new IllegalArgumentException(                    "methods with same signature " +                    getFriendlyMethodSignature(pm.methodName,                                               pm.parameterTypes) +                    " but incompatible return types: " +                    newReturnType.getName() + " and others");            }            boolean added = false;             /*             * Compare the new return type to the existing uncovered             * return types.             */            ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator();            while (liter.hasNext()) {                Class<?> uncoveredReturnType = liter.next();                 /*                 * If an existing uncovered return type is assignable                 * to this new one, then we can forget the new one.                 */                if (newReturnType.isAssignableFrom(uncoveredReturnType)) {                    assert !added;                    continue nextNewReturnType;                }                 /*                 * If the new return type is assignable to an existing                 * uncovered one, then should replace the existing one                 * with the new one (or just forget the existing one,                 * if the new one has already be put in the list).                 */                if (uncoveredReturnType.isAssignableFrom(newReturnType)) {                    // (we can assume that each return type is unique)                    if (!added) {                        liter.set(newReturnType);                        added = true;                    } else {                        liter.remove();                    }                }            }             /*             * If we got through the list of existing uncovered return             * types without an assignability relationship, then add             * the new return type to the list of uncovered ones.             */            if (!added) {                uncoveredReturnTypes.add(newReturnType);            }        }         /*         * We shouldn't end up with more than one return type that is         * not assignable from any of the others.         */        if (uncoveredReturnTypes.size() > 1) {            ProxyMethod pm = methods.get(0);            throw new IllegalArgumentException(                "methods with same signature " +                getFriendlyMethodSignature(pm.methodName, pm.parameterTypes) +                " but incompatible return types: " + uncoveredReturnTypes);        }    }     /**     * A FieldInfo object contains information about a particular field     * in the class being generated.  The class mirrors the data items of     * the "field_info" structure of the class file format (see JVMS 4.5).     */    private class FieldInfo {        public int accessFlags;        public String name;        public String descriptor;         public FieldInfo(String name, String descriptor, int accessFlags) {            this.name = name;            this.descriptor = descriptor;            this.accessFlags = accessFlags;             /*             * Make sure that constant pool indexes are reserved for the             * following items before starting to write the final class file.             */            cp.getUtf8(name);            cp.getUtf8(descriptor);        }         public void write(DataOutputStream out) throws IOException {            /*             * Write all the items of the "field_info" structure.             * See JVMS section 4.5.             */                                        // u2 access_flags;            out.writeShort(accessFlags);                                        // u2 name_index;            out.writeShort(cp.getUtf8(name));                                        // u2 descriptor_index;            out.writeShort(cp.getUtf8(descriptor));                                        // u2 attributes_count;            out.writeShort(0);  // (no field_info attributes for proxy classes)        }    }     /**     * An ExceptionTableEntry object holds values for the data items of     * an entry in the "exception_table" item of the "Code" attribute of     * "method_info" structures (see JVMS 4.7.3).     */    private static class ExceptionTableEntry {        public short startPc;        public short endPc;        public short handlerPc;        public short catchType;         public ExceptionTableEntry(short startPc, short endPc,                                   short handlerPc, short catchType)        {            this.startPc = startPc;            this.endPc = endPc;            this.handlerPc = handlerPc;            this.catchType = catchType;        }    };     /**     * A MethodInfo object contains information about a particular method     * in the class being generated.  This class mirrors the data items of     * the "method_info" structure of the class file format (see JVMS 4.6).     */    private class MethodInfo {        public int accessFlags;        public String name;        public String descriptor;        public short maxStack;        public short maxLocals;        public ByteArrayOutputStream code = new ByteArrayOutputStream();        public List<ExceptionTableEntry> exceptionTable =            new ArrayList<ExceptionTableEntry>();        public short[] declaredExceptions;         public MethodInfo(String name, String descriptor, int accessFlags) {            this.name = name;            this.descriptor = descriptor;            this.accessFlags = accessFlags;             /*             * Make sure that constant pool indexes are reserved for the             * following items before starting to write the final class file.             */            cp.getUtf8(name);            cp.getUtf8(descriptor);            cp.getUtf8("Code");            cp.getUtf8("Exceptions");        }         public void write(DataOutputStream out) throws IOException {            /*             * Write all the items of the "method_info" structure.             * See JVMS section 4.6.             */                                        // u2 access_flags;            out.writeShort(accessFlags);                                        // u2 name_index;            out.writeShort(cp.getUtf8(name));                                        // u2 descriptor_index;            out.writeShort(cp.getUtf8(descriptor));                                        // u2 attributes_count;            out.writeShort(2);  // (two method_info attributes:)             // Write "Code" attribute. See JVMS section 4.7.3.                                         // u2 attribute_name_index;            out.writeShort(cp.getUtf8("Code"));                                        // u4 attribute_length;            out.writeInt(12 + code.size() + 8 * exceptionTable.size());                                        // u2 max_stack;            out.writeShort(maxStack);                                        // u2 max_locals;            out.writeShort(maxLocals);                                        // u2 code_length;            out.writeInt(code.size());                                        // u1 code[code_length];            code.writeTo(out);                                        // u2 exception_table_length;            out.writeShort(exceptionTable.size());            for (ExceptionTableEntry e : exceptionTable) {                                        // u2 start_pc;                out.writeShort(e.startPc);                                        // u2 end_pc;                out.writeShort(e.endPc);                                        // u2 handler_pc;                out.writeShort(e.handlerPc);                                        // u2 catch_type;                out.writeShort(e.catchType);            }                                        // u2 attributes_count;            out.writeShort(0);             // write "Exceptions" attribute.  See JVMS section 4.7.4.                                         // u2 attribute_name_index;            out.writeShort(cp.getUtf8("Exceptions"));                                        // u4 attributes_length;            out.writeInt(2 + 2 * declaredExceptions.length);                                        // u2 number_of_exceptions;            out.writeShort(declaredExceptions.length);                        // u2 exception_index_table[number_of_exceptions];            for (int i = 0; i < declaredExceptions.length; i++) {                out.writeShort(declaredExceptions[i]);            }        }     }     /**     * A ProxyMethod object represents a proxy method in the proxy class     * being generated: a method whose implementation will encode and     * dispatch invocations to the proxy instance's invocation handler.     */    private class ProxyMethod {         public String methodName;        public Class[] parameterTypes;        public Class returnType;        public Class[] exceptionTypes;        public Class fromClass;        public String methodFieldName;         private ProxyMethod(String methodName, Class[] parameterTypes,                            Class returnType, Class[] exceptionTypes,                            Class fromClass)        {            this.methodName = methodName;            this.parameterTypes = parameterTypes;            this.returnType = returnType;            this.exceptionTypes = exceptionTypes;            this.fromClass = fromClass;            this.methodFieldName = "m" + proxyMethodCount++;        }         /**         * Return a MethodInfo object for this method, including generating         * the code and exception table entry.         */        private MethodInfo generateMethod() throws IOException {            String desc = getMethodDescriptor(parameterTypes, returnType);            MethodInfo minfo = new MethodInfo(methodName, desc,                ACC_PUBLIC | ACC_FINAL);             int[] parameterSlot = new int[parameterTypes.length];            int nextSlot = 1;            for (int i = 0; i < parameterSlot.length; i++) {                parameterSlot[i] = nextSlot;                nextSlot += getWordsPerType(parameterTypes[i]);            }            int localSlot0 = nextSlot;            short pc, tryBegin = 0, tryEnd;             DataOutputStream out = new DataOutputStream(minfo.code);             code_aload(0, out);             out.writeByte(opc_getfield);            out.writeShort(cp.getFieldRef(                superclassName,                handlerFieldName, "Ljava/lang/reflect/InvocationHandler;"));             code_aload(0, out);             out.writeByte(opc_getstatic);            out.writeShort(cp.getFieldRef(                dotToSlash(className),                methodFieldName, "Ljava/lang/reflect/Method;"));             if (parameterTypes.length > 0) {                 code_ipush(parameterTypes.length, out);                 out.writeByte(opc_anewarray);                out.writeShort(cp.getClass("java/lang/Object"));                 for (int i = 0; i < parameterTypes.length; i++) {                     out.writeByte(opc_dup);                     code_ipush(i, out);                     codeWrapArgument(parameterTypes[i], parameterSlot[i], out);                     out.writeByte(opc_aastore);                }            } else {                 out.writeByte(opc_aconst_null);            }             out.writeByte(opc_invokeinterface);            out.writeShort(cp.getInterfaceMethodRef(                "java/lang/reflect/InvocationHandler",                "invoke",                "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +                    "[Ljava/lang/Object;)Ljava/lang/Object;"));            out.writeByte(4);            out.writeByte(0);             if (returnType == void.class) {                 out.writeByte(opc_pop);                 out.writeByte(opc_return);             } else {                 codeUnwrapReturnValue(returnType, out);            }             tryEnd = pc = (short) minfo.code.size();             List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes);            if (catchList.size() > 0) {                 for (Class<?> ex : catchList) {                    minfo.exceptionTable.add(new ExceptionTableEntry(                        tryBegin, tryEnd, pc,                        cp.getClass(dotToSlash(ex.getName()))));                }                 out.writeByte(opc_athrow);                 pc = (short) minfo.code.size();                 minfo.exceptionTable.add(new ExceptionTableEntry(                    tryBegin, tryEnd, pc, cp.getClass("java/lang/Throwable")));                 code_astore(localSlot0, out);                 out.writeByte(opc_new);                out.writeShort(cp.getClass(                    "java/lang/reflect/UndeclaredThrowableException"));                 out.writeByte(opc_dup);                 code_aload(localSlot0, out);                 out.writeByte(opc_invokespecial);                 out.writeShort(cp.getMethodRef(                    "java/lang/reflect/UndeclaredThrowableException",                    "<init>", "(Ljava/lang/Throwable;)V"));                 out.writeByte(opc_athrow);            }             if (minfo.code.size() > 65535) {                throw new IllegalArgumentException("code size limit exceeded");            }             minfo.maxStack = 10;            minfo.maxLocals = (short) (localSlot0 + 1);            minfo.declaredExceptions = new short[exceptionTypes.length];            for (int i = 0; i < exceptionTypes.length; i++) {                minfo.declaredExceptions[i] = cp.getClass(                    dotToSlash(exceptionTypes[i].getName()));            }             return minfo;        }         /**         * Generate code for wrapping an argument of the given type         * whose value can be found at the specified local variable         * index, in order for it to be passed (as an Object) to the

Lines 501–1000 of 2009. Previous lines