📄 proxy.java
字号:
return data; } } // class ProxyData /** * Does all the work of building a class. By making this a nested class, * this code is not loaded in memory if the VM has a native * implementation instead. * * @author Eric Blake (ebb9@email.byu.edu) */ private static final class ClassFactory { /** Constants for assisting the compilation */ private static final byte FIELD = 1; private static final byte METHOD = 2; private static final byte INTERFACE = 3; private static final String CTOR_SIG = "(Ljava/lang/reflect/InvocationHandler;)V"; private static final String INVOKE_SIG = "(Ljava/lang/Object;" + "Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"; /** Bytecodes for insertion in the class definition byte[] */ private static final char ACONST_NULL = 1; private static final char ICONST_0 = 3; private static final char BIPUSH = 16; private static final char SIPUSH = 17; private static final char ILOAD = 21; private static final char ILOAD_0 = 26; private static final char ALOAD_0 = 42; private static final char ALOAD_1 = 43; private static final char AALOAD = 50; private static final char AASTORE = 83; private static final char DUP = 89; private static final char DUP_X1 = 90; private static final char SWAP = 95; private static final char IRETURN = 172; private static final char LRETURN = 173; private static final char FRETURN = 174; private static final char DRETURN = 175; private static final char ARETURN = 176; private static final char RETURN = 177; private static final char GETSTATIC = 178; private static final char GETFIELD = 180; private static final char INVOKEVIRTUAL = 182; private static final char INVOKESPECIAL = 183; private static final char INVOKEINTERFACE = 185; private static final char NEW = 187; private static final char ANEWARRAY = 189; private static final char ATHROW = 191; private static final char CHECKCAST = 192; // Implementation note: we use StringBuffers to hold the byte data, since // they automatically grow. However, we only use the low 8 bits of // every char in the array, so we are using twice the necessary memory // for the ease StringBuffer provides. /** The constant pool. */ private final StringBuffer pool = new StringBuffer(); /** The rest of the class data. */ private final StringBuffer stream = new StringBuffer(); /** Map of strings to byte sequences, to minimize size of pool. */ private final Map poolEntries = new HashMap(); /** The VM name of this proxy class. */ private final String qualName; /** * The Method objects the proxy class refers to when calling the * invocation handler. */ private final Method[] methods; /** * Initializes the buffers with the bytecode contents for a proxy class. * * @param data the remainder of the class data * @throws IllegalArgumentException if anything else goes wrong this * late in the game; as far as I can tell, this will only happen * if the constant pool overflows, which is possible even when * the user doesn't exceed the 65535 interface limit */ ClassFactory(ProxyData data) { methods = data.methods; // magic = 0xcafebabe // minor_version = 0 // major_version = 46 // constant_pool_count: place-holder for now pool.append("\u00ca\u00fe\u00ba\u00be\0\0\0\56\0\0"); // constant_pool[], filled in as we go // access_flags putU2(Modifier.SUPER | Modifier.FINAL | Modifier.PUBLIC); // this_class qualName = (data.pack + "$Proxy" + data.id); putU2(classInfo(TypeSignature.getEncodingOfClass(qualName, false))); // super_class putU2(classInfo("java/lang/reflect/Proxy")); // interfaces_count putU2(data.interfaces.length); // interfaces[] for (int i = 0; i < data.interfaces.length; i++) putU2(classInfo(data.interfaces[i])); // Recall that Proxy classes serialize specially, so we do not need // to worry about a <clinit> method for this field. Instead, we // just assign it by reflection after the class is successfully loaded. // fields_count - private static Method[] m; putU2(1); // fields[] // m.access_flags putU2(Modifier.PRIVATE | Modifier.STATIC); // m.name_index putU2(utf8Info("m")); // m.descriptor_index putU2(utf8Info("[Ljava/lang/reflect/Method;")); // m.attributes_count putU2(0); // m.attributes[] // methods_count - # handler methods, plus <init> putU2(methods.length + 1); // methods[] // <init>.access_flags putU2(Modifier.PUBLIC); // <init>.name_index putU2(utf8Info("<init>")); // <init>.descriptor_index putU2(utf8Info(CTOR_SIG)); // <init>.attributes_count - only Code is needed putU2(1); // <init>.Code.attribute_name_index putU2(utf8Info("Code")); // <init>.Code.attribute_length = 18 // <init>.Code.info: // $Proxynn(InvocationHandler h) { super(h); } // <init>.Code.max_stack = 2 // <init>.Code.max_locals = 2 // <init>.Code.code_length = 6 // <init>.Code.code[] stream.append("\0\0\0\22\0\2\0\2\0\0\0\6" + ALOAD_0 + ALOAD_1 + INVOKESPECIAL); putU2(refInfo(METHOD, "java/lang/reflect/Proxy", "<init>", CTOR_SIG)); // <init>.Code.exception_table_length = 0 // <init>.Code.exception_table[] // <init>.Code.attributes_count = 0 // <init>.Code.attributes[] stream.append(RETURN + "\0\0\0\0"); for (int i = methods.length - 1; i >= 0; i--) emitMethod(i, data.exceptions[i]); // attributes_count putU2(0); // attributes[] - empty; omit SourceFile attribute // XXX should we mark this with a Synthetic attribute? } /** * Produce the bytecode for a single method. * * @param i the index of the method we are building * @param e the exceptions possible for the method */ private void emitMethod(int i, Class[] e) { // First, we precalculate the method length and other information. Method m = methods[i]; Class[] paramtypes = m.getParameterTypes(); int wrap_overhead = 0; // max words taken by wrapped primitive int param_count = 1; // 1 for this int code_length = 16; // aload_0, getfield, aload_0, getstatic, const, // aaload, const/aconst_null, invokeinterface if (i > 5) { if (i > Byte.MAX_VALUE) code_length += 2; // sipush else code_length++; // bipush } if (paramtypes.length > 0) { code_length += 3; // anewarray if (paramtypes.length > Byte.MAX_VALUE) code_length += 2; // sipush else if (paramtypes.length > 5) code_length++; // bipush for (int j = 0; j < paramtypes.length; j++) { code_length += 4; // dup, const, load, store Class type = paramtypes[j]; if (j > 5) { if (j > Byte.MAX_VALUE) code_length += 2; // sipush else code_length++; // bipush } if (param_count >= 4) code_length++; // 2-byte load param_count++; if (type.isPrimitive()) { code_length += 7; // new, dup, invokespecial if (type == long.class || type == double.class) { wrap_overhead = 3; param_count++; } else if (wrap_overhead < 2) wrap_overhead = 2; } } } int end_pc = code_length; Class ret_type = m.getReturnType(); if (ret_type == void.class) code_length++; // return else if (ret_type.isPrimitive()) code_length += 7; // cast, invokevirtual, return else code_length += 4; // cast, return int exception_count = 0; boolean throws_throwable = false; for (int j = 0; j < e.length; j++) if (e[j] == Throwable.class) { throws_throwable = true; break; } if (! throws_throwable) { exception_count = e.length + 3; // Throwable, Error, RuntimeException code_length += 9; // new, dup_x1, swap, invokespecial, athrow } int handler_pc = code_length - 1; StringBuffer signature = new StringBuffer("("); for (int j = 0; j < paramtypes.length; j++) signature.append(TypeSignature.getEncodingOfClass(paramtypes[j])); signature.append(")").append(TypeSignature.getEncodingOfClass(ret_type)); // Now we have enough information to emit the method. // handler.access_flags putU2(Modifier.PUBLIC | Modifier.FINAL); // handler.name_index putU2(utf8Info(m.getName())); // handler.descriptor_index putU2(utf8Info(signature.toString())); // handler.attributes_count - Code is necessary, Exceptions possible putU2(e.length > 0 ? 2 : 1); // handler.Code.info: // type name(args) { // try { // return (type) h.invoke(this, methods[i], new Object[] {args}); // } catch (<declared Exceptions> e) { // throw e; // } catch (Throwable t) { // throw new UndeclaredThrowableException(t); // } // } // Special cases: // if arg_n is primitive, wrap it // if method throws Throwable, try-catch is not needed // if method returns void, return statement not needed // if method returns primitive, unwrap it // save space by sharing code for all the declared handlers // handler.Code.attribute_name_index putU2(utf8Info("Code")); // handler.Code.attribute_length putU4(12 + code_length + 8 * exception_count); // handler.Code.max_stack putU2(param_count == 1 ? 4 : 7 + wrap_overhead); // handler.Code.max_locals putU2(param_count); // handler.Code.code_length putU4(code_length); // handler.Code.code[] putU1(ALOAD_0); putU1(GETFIELD); putU2(refInfo(FIELD, "java/lang/reflect/Proxy", "h", "Ljava/lang/reflect/InvocationHandler;")); putU1(ALOAD_0); putU1(GETSTATIC); putU2(refInfo(FIELD, TypeSignature.getEncodingOfClass(qualName, false), "m", "[Ljava/lang/reflect/Method;")); putConst(i); putU1(AALOAD); if (paramtypes.length > 0) { putConst(paramtypes.length); putU1(ANEWARRAY); putU2(classInfo("java/lang/Object")); param_count = 1; for (int j = 0; j < paramtypes.length; j++, param_count++) { putU1(DUP); putConst(j); if (paramtypes[j].isPrimitive()) { putU1(NEW); putU2(classInfo(wrapper(paramtypes[j]))); putU1(DUP); } putLoad(param_count, paramtypes[j]); if (paramtypes[j].isPrimitive()) { putU1(INVOKESPECIAL); putU2(refInfo(METHOD, wrapper(paramtypes[j]), "<init>", '(' + (TypeSignature .getEncodingOfClass(paramtypes[j]) + ")V"))); if (paramtypes[j] == long.class || paramtypes[j] == double.class) param_count++; } putU1(AASTORE); } } else putU1(ACONST_NULL); putU1(INVOKEINTERFACE); putU2(refInfo(INTERFACE, "java/lang/reflect/InvocationHandler", "invoke", INVOKE_SIG)); putU1(4); // InvocationHandler, this, Method, Object[] putU1(0); if (ret_type == void.class) putU1(RETURN); else if (ret_type.isPrimitive()) { putU1(CHECKCAST); putU2(classInfo(wrapper(ret_type))); putU1(INVOKEVIRTUAL); putU2(refInfo(METHOD, wrapper(ret_type), ret_type.getName() + "Value", "()" + TypeSignature.getEncodingOfClass(ret_type))); if (ret_type == long.class) putU1(LRETURN); else if (ret_type == float.class) putU1(FRETURN); else if (ret_type == double.class) putU1(DRETURN); else putU1(IRETURN); } else { putU1(CHECKCAST); putU2(classInfo(ret_type)); putU1(ARETURN); } if (! throws_throwable) { putU1(NEW); putU2(classInfo("java/lang/reflect/UndeclaredThrowableException")); putU1(DUP_X1); putU1(SWAP); putU1(INVOKESPECIAL); putU2(refInfo(METHOD, "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V")); putU1(ATHROW); } // handler.Code.exception_table_length putU2(exception_count); // handler.Code.exception_table[] if (! throws_throwable) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -