0001: /*
0002: * @(#)ProxyGenerator.java 1.12 06/10/10
0003: *
0004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: *
0026: */
0027:
0028: package sun.misc;
0029:
0030: import java.lang.reflect.*;
0031: import java.io.*;
0032: import java.util.*;
0033:
0034: import sun.tools.java.RuntimeConstants;
0035: import sun.security.action.GetBooleanAction;
0036:
0037: /**
0038: * ProxyGenerator contains the code to generate a dynamic proxy class
0039: * for the java.lang.reflect.Proxy API.
0040: *
0041: * The external interfaces to ProxyGenerator is the static
0042: * "generateProxyClass" method.
0043: *
0044: * @author Peter Jones
0045: * @version 1.3, 00/02/02
0046: * @since JDK1.3
0047: */
0048: public class ProxyGenerator {
0049: /*
0050: * In the comments below, "JVMS" refers to The Java Virtual Machine
0051: * Specification Second Edition and "JLS" refers to the original
0052: * version of The Java Language Specification, unless otherwise
0053: * specified.
0054: */
0055:
0056: /*
0057: * Note that this class imports sun.tools.java.RuntimeConstants and
0058: * references many final static primitive fields of that interface.
0059: * By JLS section 13.4.8, the compiler should inline all of these
0060: * references, so their presence should not require the loading of
0061: * RuntimeConstants at runtime when ProxyGenerator is linked. This
0062: * non-requirement is important because ProxyGenerator is intended
0063: * to be bundled with the JRE, but classes in the sun.tools
0064: * hierarchy, such as RuntimeConstants, are not.
0065: *
0066: * The Java compiler does add a CONSTANT_Class entry in the constant
0067: * pool of this class for "sun/tools/java/RuntimeConstants". The
0068: * evaluation of bugid 4162387 seems to imply that this is for the
0069: * compiler's implementation of the "-Xdepend" option. This
0070: * CONSTANT_Class entry may, however, confuse tools which use such
0071: * entries to compute runtime class dependencies or virtual machine
0072: * implementations which use them to effect eager class resolution.
0073: */
0074:
0075: /** name of the superclass of proxy classes */
0076: private final static String super className = "java/lang/reflect/Proxy";
0077:
0078: /** name of field for storing a proxy instance's invocation handler */
0079: private final static String handlerFieldName = "h";
0080:
0081: /** debugging flag for saving generated class files */
0082: private final static boolean saveGeneratedFiles = ((Boolean) java.security.AccessController
0083: .doPrivileged(new GetBooleanAction(
0084: "sun.misc.ProxyGenerator.saveGeneratedFiles")))
0085: .booleanValue();
0086:
0087: /**
0088: * Generate a proxy class given a name and a list of proxy interfaces.
0089: */
0090: public static byte[] generateProxyClass(final String name,
0091: Class[] interfaces) {
0092: ProxyGenerator gen = new ProxyGenerator(name, interfaces);
0093: final byte[] classFile = gen.generateClassFile();
0094:
0095: if (saveGeneratedFiles) {
0096: java.security.AccessController
0097: .doPrivileged(new java.security.PrivilegedAction() {
0098: public Object run() {
0099: try {
0100: FileOutputStream file = new FileOutputStream(
0101: dotToSlash(name) + ".class");
0102: file.write(classFile);
0103: file.close();
0104: return null;
0105: } catch (IOException e) {
0106: throw new InternalError(
0107: "I/O exception saving generated file: "
0108: + e);
0109: }
0110: }
0111: });
0112: }
0113:
0114: return classFile;
0115: }
0116:
0117: /* preloaded Method objects for methods in java.lang.Object */
0118: private static Method hashCodeMethod;
0119: private static Method equalsMethod;
0120: private static Method toStringMethod;
0121: static {
0122: try {
0123: hashCodeMethod = Object.class.getMethod("hashCode", null);
0124: equalsMethod = Object.class.getMethod("equals",
0125: new Class[] { Object.class });
0126: toStringMethod = Object.class.getMethod("toString", null);
0127: } catch (NoSuchMethodException e) {
0128: throw new NoSuchMethodError(e.getMessage());
0129: }
0130: }
0131:
0132: /** name of proxy class */
0133: private String className;
0134:
0135: /** proxy interfaces */
0136: private Class[] interfaces;
0137:
0138: /** constant pool of class being generated */
0139: private ConstantPool cp = new ConstantPool();
0140:
0141: /** FieldInfo struct for each field of generated class */
0142: private List fields = new ArrayList();
0143:
0144: /** MethodInfo struct for each method of generated class */
0145: private List methods = new ArrayList();
0146:
0147: /**
0148: * for each method to be proxied, maps method name and parameter
0149: * descriptor to ProxyMethod object
0150: */
0151: private Map proxyMethods = new HashMap(11);
0152:
0153: /**
0154: * Construct a ProxyGenerator to generate a proxy class with the
0155: * specified name and for the given interfaces.
0156: *
0157: * A ProxyGenerator object contains the state for the ongoing
0158: * generation of a particular proxy class.
0159: */
0160: private ProxyGenerator(String className, Class[] interfaces) {
0161: this .className = className;
0162: this .interfaces = interfaces;
0163: }
0164:
0165: /**
0166: * Generate a class file for the proxy class. This method drives the
0167: * class file generation process.
0168: */
0169: private byte[] generateClassFile() {
0170:
0171: /* ============================================================
0172: * Step 1: Assemble ProxyMethod objects for all methods to
0173: * generate proxy dispatching code for.
0174: */
0175:
0176: /*
0177: * Record that proxy methods are needed for the hashCode, equals,
0178: * and toString methods of java.lang.Object. This is done before
0179: * the methods from the proxy interfaces so that the methods from
0180: * java.lang.Object take precedence over duplicate methods in the
0181: * proxy interfaces.
0182: */
0183: addProxyMethod(hashCodeMethod, Object.class);
0184: addProxyMethod(equalsMethod, Object.class);
0185: addProxyMethod(toStringMethod, Object.class);
0186:
0187: /*
0188: * Now record all of the methods from the proxy interfaces, giving
0189: * earlier interfaces precedence over later ones with duplicate
0190: * methods.
0191: */
0192: for (int i = 0; i < interfaces.length; i++) {
0193: Method[] methods = interfaces[i].getMethods();
0194: for (int j = 0; j < methods.length; j++) {
0195: addProxyMethod(methods[j], interfaces[i]);
0196: }
0197: }
0198:
0199: /* ============================================================
0200: * Step 2: Assemble FieldInfo and MethodInfo structs for all of
0201: * fields and methods in the class we are generating.
0202: */
0203: try {
0204: // fields.add(new FieldInfo(
0205: // handlerFieldName, "Ljava/lang/reflect/InvocationHandler;",
0206: // RuntimeConstants.ACC_PRIVATE | RuntimeConstants.ACC_FINAL));
0207:
0208: methods.add(generateConstructor());
0209:
0210: for (Iterator iter = proxyMethods.values().iterator(); iter
0211: .hasNext();) {
0212: ProxyMethod pm = (ProxyMethod) iter.next();
0213:
0214: // add static field for method's Method object
0215: fields.add(new FieldInfo(pm.methodFieldName,
0216: "Ljava/lang/reflect/Method;",
0217: RuntimeConstants.ACC_PRIVATE
0218: | RuntimeConstants.ACC_STATIC));
0219:
0220: // generate code for proxy method and add it
0221: methods.add(pm.generateMethod());
0222: }
0223:
0224: methods.add(generateStaticInitializer());
0225:
0226: } catch (IOException e) {
0227: throw new InternalError("unexpected I/O Exception");
0228: }
0229:
0230: /* ============================================================
0231: * Step 3: Write the final class file.
0232: */
0233:
0234: /*
0235: * Make sure that constant pool indexes are reserved for the
0236: * following items before starting to write the final class file.
0237: */
0238: cp.getClass(dotToSlash(className));
0239: cp.getClass(super className);
0240: for (int i = 0; i < interfaces.length; i++) {
0241: cp.getClass(dotToSlash(interfaces[i].getName()));
0242: }
0243:
0244: /*
0245: * Disallow new constant pool additions beyond this point, since
0246: * we are about to write the final constant pool table.
0247: */
0248: cp.setReadOnly();
0249:
0250: ByteArrayOutputStream bout = new ByteArrayOutputStream();
0251: DataOutputStream dout = new DataOutputStream(bout);
0252:
0253: try {
0254: /*
0255: * Write all the items of the "ClassFile" structure.
0256: * See JVMS section 4.1.
0257: */
0258: // u4 magic;
0259: dout.writeInt(RuntimeConstants.JAVA_MAGIC);
0260: // u2 major_version;
0261: dout
0262: .writeShort(RuntimeConstants.JAVA_DEFAULT_MINOR_VERSION);
0263: // u2 minor_version;
0264: dout.writeShort(RuntimeConstants.JAVA_DEFAULT_VERSION);
0265:
0266: cp.write(dout); // (write constant pool)
0267:
0268: // u2 access_flags;
0269: dout.writeShort(RuntimeConstants.ACC_PUBLIC
0270: | RuntimeConstants.ACC_FINAL
0271: | RuntimeConstants.ACC_SUPER);
0272: // u2 this_class;
0273: dout.writeShort(cp.getClass(dotToSlash(className)));
0274: // u2 super_class;
0275: dout.writeShort(cp.getClass(super className));
0276:
0277: // u2 interfaces_count;
0278: dout.writeShort(interfaces.length);
0279: // u2 interfaces[interfaces_count];
0280: for (int i = 0; i < interfaces.length; i++) {
0281: dout.writeShort(cp.getClass(dotToSlash(interfaces[i]
0282: .getName())));
0283: }
0284:
0285: // u2 fields_count;
0286: dout.writeShort(fields.size());
0287: // field_info fields[fields_count];
0288: for (Iterator iter = fields.iterator(); iter.hasNext();) {
0289: FieldInfo f = (FieldInfo) iter.next();
0290: f.write(dout);
0291: }
0292:
0293: // u2 methods_count;
0294: dout.writeShort(methods.size());
0295: // method_info methods[methods_count];
0296: for (Iterator iter = methods.iterator(); iter.hasNext();) {
0297: MethodInfo m = (MethodInfo) iter.next();
0298: m.write(dout);
0299: }
0300:
0301: // u2 attributes_count;
0302: dout.writeShort(0); // (no ClassFile attributes for proxy classes)
0303:
0304: } catch (IOException e) {
0305: throw new InternalError("unexpected I/O Exception");
0306: }
0307:
0308: return bout.toByteArray();
0309: }
0310:
0311: /**
0312: * Add another method to be proxied, either by creating a new ProxyMethod
0313: * object or augmenting an old one for a duplicate method.
0314: *
0315: * "fromClass" indicates the proxy interface that the method was found
0316: * through, which may be different from (a subinterface of) the method's
0317: * "declaring class". Note that the first Method object passed for a
0318: * given name and parameter types identifies the Method object (and thus
0319: * the declaring class) that will be passed to the invocation handler's
0320: * "invoke" method for a given set of duplicate methods.
0321: */
0322: private void addProxyMethod(Method m, Class fromClass) {
0323: String name = m.getName();
0324: Class[] parameterTypes = m.getParameterTypes();
0325: Class returnType = m.getReturnType();
0326: Class[] exceptionTypes = m.getExceptionTypes();
0327:
0328: String key = name + getParameterDescriptors(parameterTypes);
0329: ProxyMethod pm = (ProxyMethod) proxyMethods.get(key);
0330: if (pm != null) {
0331: /*
0332: * If a proxy method with the same name and parameter types has
0333: * already been added, verify that it has the same return type...
0334: */
0335: if (returnType != pm.returnType) {
0336: throw new IllegalArgumentException(
0337: "methods with same name and parameter "
0338: + "signature but different return type in "
0339: + pm.fromClass + " and " + fromClass
0340: + ": " + key);
0341: }
0342: /*
0343: * ...and compute the greatest common set of exceptions that can
0344: * thrown by the proxy method compatibly with both inherited
0345: * methods.
0346: */
0347: List legalExceptions = new ArrayList();
0348: collectCompatibleTypes(exceptionTypes, pm.exceptionTypes,
0349: legalExceptions);
0350: collectCompatibleTypes(pm.exceptionTypes, exceptionTypes,
0351: legalExceptions);
0352: pm.exceptionTypes = new Class[legalExceptions.size()];
0353: pm.exceptionTypes = (Class[]) legalExceptions
0354: .toArray(pm.exceptionTypes);
0355: } else {
0356: pm = new ProxyMethod(name, parameterTypes, returnType,
0357: exceptionTypes, fromClass, "m"
0358: + proxyMethods.size());
0359: proxyMethods.put(key, pm);
0360: }
0361: }
0362:
0363: /**
0364: * A FieldInfo object contains information about a particular field
0365: * in the class being generated. The class mirrors the data items of
0366: * the "field_info" structure of the class file format (see JVMS 4.5).
0367: */
0368: private class FieldInfo {
0369: public int accessFlags;
0370: public String name;
0371: public String descriptor;
0372:
0373: public FieldInfo(String name, String descriptor, int accessFlags) {
0374: this .name = name;
0375: this .descriptor = descriptor;
0376: this .accessFlags = accessFlags;
0377:
0378: /*
0379: * Make sure that constant pool indexes are reserved for the
0380: * following items before starting to write the final class file.
0381: */
0382: cp.getUtf8(name);
0383: cp.getUtf8(descriptor);
0384: }
0385:
0386: public void write(DataOutputStream out) throws IOException {
0387: /*
0388: * Write all the items of the "field_info" structure.
0389: * See JVMS section 4.5.
0390: */
0391: // u2 access_flags;
0392: out.writeShort(accessFlags);
0393: // u2 name_index;
0394: out.writeShort(cp.getUtf8(name));
0395: // u2 descriptor_index;
0396: out.writeShort(cp.getUtf8(descriptor));
0397: // u2 attributes_count;
0398: out.writeShort(0); // (no field_info attributes for proxy classes)
0399: }
0400: }
0401:
0402: /**
0403: * An ExceptionTableEntry object holds values for the data items of
0404: * an entry in the "exception_table" item of the "Code" attribute of
0405: * "method_info" structures (see JVMS 4.7.3).
0406: */
0407: private static class ExceptionTableEntry {
0408: public short startPc;
0409: public short endPc;
0410: public short handlerPc;
0411: public short catchType;
0412:
0413: public ExceptionTableEntry(short startPc, short endPc,
0414: short handlerPc, short catchType) {
0415: this .startPc = startPc;
0416: this .endPc = endPc;
0417: this .handlerPc = handlerPc;
0418: this .catchType = catchType;
0419: }
0420: };
0421:
0422: /**
0423: * A MethodInfo object contains information about a particular method
0424: * in the class being generated. This class mirrors the data items of
0425: * the "method_info" structure of the class file format (see JVMS 4.6).
0426: */
0427: private class MethodInfo {
0428: public int accessFlags;
0429: public String name;
0430: public String descriptor;
0431: public short maxStack;
0432: public short maxLocals;
0433: public ByteArrayOutputStream code = new ByteArrayOutputStream();
0434: public List exceptionTable = new ArrayList();
0435: public short[] declaredExceptions;
0436:
0437: public MethodInfo(String name, String descriptor,
0438: int accessFlags) {
0439: this .name = name;
0440: this .descriptor = descriptor;
0441: this .accessFlags = accessFlags;
0442:
0443: /*
0444: * Make sure that constant pool indexes are reserved for the
0445: * following items before starting to write the final class file.
0446: */
0447: cp.getUtf8(name);
0448: cp.getUtf8(descriptor);
0449: cp.getUtf8("Code");
0450: cp.getUtf8("Exceptions");
0451: }
0452:
0453: public void write(DataOutputStream out) throws IOException {
0454: /*
0455: * Write all the items of the "method_info" structure.
0456: * See JVMS section 4.6.
0457: */
0458: // u2 access_flags;
0459: out.writeShort(accessFlags);
0460: // u2 name_index;
0461: out.writeShort(cp.getUtf8(name));
0462: // u2 descriptor_index;
0463: out.writeShort(cp.getUtf8(descriptor));
0464: // u2 attributes_count;
0465: out.writeShort(2); // (two method_info attributes:)
0466:
0467: // Write "Code" attribute. See JVMS section 4.7.3.
0468:
0469: // u2 attribute_name_index;
0470: out.writeShort(cp.getUtf8("Code"));
0471: // u4 attribute_length;
0472: out.writeInt(12 + code.size() + 8 * exceptionTable.size());
0473: // u2 max_stack;
0474: out.writeShort(maxStack);
0475: // u2 max_locals;
0476: out.writeShort(maxLocals);
0477: // u2 code_length;
0478: out.writeInt(code.size());
0479: // u1 code[code_length];
0480: code.writeTo(out);
0481: // u2 exception_table_length;
0482: out.writeShort(exceptionTable.size());
0483: for (Iterator iter = exceptionTable.iterator(); iter
0484: .hasNext();) {
0485: ExceptionTableEntry e = (ExceptionTableEntry) iter
0486: .next();
0487: // u2 start_pc;
0488: out.writeShort(e.startPc);
0489: // u2 end_pc;
0490: out.writeShort(e.endPc);
0491: // u2 handler_pc;
0492: out.writeShort(e.handlerPc);
0493: // u2 catch_type;
0494: out.writeShort(e.catchType);
0495: }
0496: // u2 attributes_count;
0497: out.writeShort(0);
0498:
0499: // write "Exceptions" attribute. See JVMS section 4.7.4.
0500:
0501: // u2 attribute_name_index;
0502: out.writeShort(cp.getUtf8("Exceptions"));
0503: // u4 attributes_length;
0504: out.writeInt(2 + 2 * declaredExceptions.length);
0505: // u2 number_of_exceptions;
0506: out.writeShort(declaredExceptions.length);
0507: // u2 exception_index_table[number_of_exceptions];
0508: for (int i = 0; i < declaredExceptions.length; i++) {
0509: out.writeShort(declaredExceptions[i]);
0510: }
0511: }
0512:
0513: }
0514:
0515: /**
0516: * A ProxyMethod object represents a proxy method in the proxy class
0517: * being generated: a method whose implementation will encode and
0518: * dispatch invocations to the proxy instance's invocation handler.
0519: */
0520: private class ProxyMethod {
0521:
0522: public String methodName;
0523: public Class[] parameterTypes;
0524: public Class returnType;
0525: public Class[] exceptionTypes;
0526: public Class fromClass;
0527: public String methodFieldName;
0528:
0529: private ProxyMethod(String methodName, Class[] parameterTypes,
0530: Class returnType, Class[] exceptionTypes,
0531: Class fromClass, String methodFieldName) {
0532: this .methodName = methodName;
0533: this .parameterTypes = parameterTypes;
0534: this .returnType = returnType;
0535: this .exceptionTypes = exceptionTypes;
0536: this .fromClass = fromClass;
0537: this .methodFieldName = methodFieldName;
0538: }
0539:
0540: /**
0541: * Return a MethodInfo object for this method, including generating
0542: * the code and exception table entry.
0543: */
0544: private MethodInfo generateMethod() throws IOException {
0545: String desc = getMethodDescriptor(parameterTypes,
0546: returnType);
0547: MethodInfo minfo = new MethodInfo(methodName, desc,
0548: RuntimeConstants.ACC_PUBLIC
0549: | RuntimeConstants.ACC_FINAL);
0550:
0551: int[] parameterSlot = new int[parameterTypes.length];
0552: int nextSlot = 1;
0553: for (int i = 0; i < parameterSlot.length; i++) {
0554: parameterSlot[i] = nextSlot;
0555: nextSlot += getWordsPerType(parameterTypes[i]);
0556: }
0557: int localSlot0 = nextSlot;
0558: short pc, tryBegin = 0, tryEnd;
0559:
0560: DataOutputStream out = new DataOutputStream(minfo.code);
0561:
0562: code_aload(0, out);
0563:
0564: out.writeByte(RuntimeConstants.opc_getfield);
0565: out.writeShort(cp.getFieldRef(
0566: // dotToSlash(className),
0567: super className, handlerFieldName,
0568: "Ljava/lang/reflect/InvocationHandler;"));
0569:
0570: code_aload(0, out);
0571:
0572: out.writeByte(RuntimeConstants.opc_getstatic);
0573: out.writeShort(cp.getFieldRef(dotToSlash(className),
0574: methodFieldName, "Ljava/lang/reflect/Method;"));
0575:
0576: if (parameterTypes.length > 0) {
0577:
0578: code_ipush(parameterTypes.length, out);
0579:
0580: out.writeByte(RuntimeConstants.opc_anewarray);
0581: out.writeShort(cp.getClass("java/lang/Object"));
0582:
0583: for (int i = 0; i < parameterTypes.length; i++) {
0584:
0585: out.writeByte(RuntimeConstants.opc_dup);
0586:
0587: code_ipush(i, out);
0588:
0589: codeWrapArgument(parameterTypes[i],
0590: parameterSlot[i], out);
0591:
0592: out.writeByte(RuntimeConstants.opc_aastore);
0593: }
0594: } else {
0595:
0596: out.writeByte(RuntimeConstants.opc_aconst_null);
0597: }
0598:
0599: out.writeByte(RuntimeConstants.opc_invokeinterface);
0600: out
0601: .writeShort(cp
0602: .getInterfaceMethodRef(
0603: "java/lang/reflect/InvocationHandler",
0604: "invoke",
0605: "(Ljava/lang/Object;Ljava/lang/reflect/Method;"
0606: + "[Ljava/lang/Object;)Ljava/lang/Object;"));
0607: out.writeByte(4);
0608: out.writeByte(0);
0609:
0610: if (returnType == void.class) {
0611:
0612: out.writeByte(RuntimeConstants.opc_pop);
0613:
0614: out.writeByte(RuntimeConstants.opc_return);
0615:
0616: } else {
0617:
0618: codeUnwrapReturnValue(returnType, out);
0619: }
0620:
0621: tryEnd = pc = (short) minfo.code.size();
0622:
0623: List catchList = computeUniqueCatchList(exceptionTypes);
0624: if (catchList.size() > 0) {
0625:
0626: for (Iterator iter = catchList.iterator(); iter
0627: .hasNext();) {
0628: Class ex = (Class) iter.next();
0629: minfo.exceptionTable
0630: .add(new ExceptionTableEntry(tryBegin,
0631: tryEnd, pc, cp
0632: .getClass(dotToSlash(ex
0633: .getName()))));
0634: }
0635:
0636: out.writeByte(RuntimeConstants.opc_athrow);
0637:
0638: pc = (short) minfo.code.size();
0639:
0640: minfo.exceptionTable.add(new ExceptionTableEntry(
0641: tryBegin, tryEnd, pc, cp
0642: .getClass("java/lang/Throwable")));
0643:
0644: code_astore(localSlot0, out);
0645:
0646: out.writeByte(RuntimeConstants.opc_new);
0647: out
0648: .writeShort(cp
0649: .getClass("java/lang/reflect/UndeclaredThrowableException"));
0650:
0651: out.writeByte(RuntimeConstants.opc_dup);
0652:
0653: code_aload(localSlot0, out);
0654:
0655: out.writeByte(RuntimeConstants.opc_invokespecial);
0656:
0657: out
0658: .writeShort(cp
0659: .getMethodRef(
0660: "java/lang/reflect/UndeclaredThrowableException",
0661: "<init>",
0662: "(Ljava/lang/Throwable;)V"));
0663:
0664: out.writeByte(RuntimeConstants.opc_athrow);
0665: }
0666:
0667: minfo.maxStack = 10;
0668: minfo.maxLocals = (short) (localSlot0 + 1);
0669: minfo.declaredExceptions = new short[exceptionTypes.length];
0670: for (int i = 0; i < exceptionTypes.length; i++) {
0671: minfo.declaredExceptions[i] = cp
0672: .getClass(dotToSlash(exceptionTypes[i]
0673: .getName()));
0674: }
0675:
0676: return minfo;
0677: }
0678:
0679: /**
0680: * Generate code for wrapping a parameter of the given type and whose
0681: * value can be found at the specified local variable index to be
0682: * passed to the invocation handler's "invoke" method (as an Object).
0683: * The code is written to the supplied stream.
0684: */
0685: private void codeWrapArgument(Class type, int slot,
0686: DataOutputStream out) throws IOException {
0687: if (type.isPrimitive()) {
0688: PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
0689:
0690: out.writeByte(RuntimeConstants.opc_new);
0691: out.writeShort(cp.getClass(prim.wrapperClassName));
0692:
0693: out.writeByte(RuntimeConstants.opc_dup);
0694:
0695: if (type == int.class || type == boolean.class
0696: || type == byte.class || type == char.class
0697: || type == short.class) {
0698: code_iload(slot, out);
0699: } else if (type == long.class) {
0700: code_lload(slot, out);
0701: } else if (type == float.class) {
0702: code_fload(slot, out);
0703: } else if (type == double.class) {
0704: code_dload(slot, out);
0705: } else {
0706: _assert(false);
0707: }
0708:
0709: out.writeByte(RuntimeConstants.opc_invokespecial);
0710: out.writeShort(cp.getMethodRef(prim.wrapperClassName,
0711: "<init>", prim.wrapperConstructorDesc));
0712:
0713: } else {
0714:
0715: code_aload(slot, out);
0716: }
0717: }
0718:
0719: /**
0720: * Generate code for unwrapping the return value of the given type
0721: * from the invocation handler's "invoke" method (of type Object) to
0722: * its correct type. The code is written to the supplied stream.
0723: */
0724: private void codeUnwrapReturnValue(Class type,
0725: DataOutputStream out) throws IOException {
0726: if (type.isPrimitive()) {
0727: PrimitiveTypeInfo prim = PrimitiveTypeInfo.get(type);
0728:
0729: out.writeByte(RuntimeConstants.opc_checkcast);
0730: out.writeShort(cp.getClass(prim.wrapperClassName));
0731:
0732: out.writeByte(RuntimeConstants.opc_invokevirtual);
0733: out.writeShort(cp.getMethodRef(prim.wrapperClassName,
0734: prim.unwrapMethodName, prim.unwrapMethodDesc));
0735:
0736: if (type == int.class || type == boolean.class
0737: || type == byte.class || type == char.class
0738: || type == short.class) {
0739: out.writeByte(RuntimeConstants.opc_ireturn);
0740: } else if (type == long.class) {
0741: out.writeByte(RuntimeConstants.opc_lreturn);
0742: } else if (type == float.class) {
0743: out.writeByte(RuntimeConstants.opc_freturn);
0744: } else if (type == double.class) {
0745: out.writeByte(RuntimeConstants.opc_dreturn);
0746: } else {
0747: _assert(false);
0748: }
0749:
0750: } else {
0751:
0752: out.writeByte(RuntimeConstants.opc_checkcast);
0753: out.writeShort(cp.getClass(dotToSlash(type.getName())));
0754:
0755: out.writeByte(RuntimeConstants.opc_areturn);
0756: }
0757: }
0758:
0759: /**
0760: * Generate code for initializing the static field that stores
0761: * the Method object for this proxy method. The code is written
0762: * to the supplied stream.
0763: */
0764: private void codeFieldInitialization(DataOutputStream out)
0765: throws IOException {
0766: codeClassForName(fromClass, out);
0767:
0768: code_ldc(cp.getString(methodName), out);
0769:
0770: code_ipush(parameterTypes.length, out);
0771:
0772: out.writeByte(RuntimeConstants.opc_anewarray);
0773: out.writeShort(cp.getClass("java/lang/Class"));
0774:
0775: for (int i = 0; i < parameterTypes.length; i++) {
0776:
0777: out.writeByte(RuntimeConstants.opc_dup);
0778:
0779: code_ipush(i, out);
0780:
0781: if (parameterTypes[i].isPrimitive()) {
0782: PrimitiveTypeInfo prim = PrimitiveTypeInfo
0783: .get(parameterTypes[i]);
0784:
0785: out.writeByte(RuntimeConstants.opc_getstatic);
0786: out.writeShort(cp.getFieldRef(
0787: prim.wrapperClassName, "TYPE",
0788: "Ljava/lang/Class;"));
0789:
0790: } else {
0791: codeClassForName(parameterTypes[i], out);
0792: }
0793:
0794: out.writeByte(RuntimeConstants.opc_aastore);
0795: }
0796:
0797: out.writeByte(RuntimeConstants.opc_invokevirtual);
0798: out.writeShort(cp.getMethodRef("java/lang/Class",
0799: "getMethod",
0800: "(Ljava/lang/String;[Ljava/lang/Class;)"
0801: + "Ljava/lang/reflect/Method;"));
0802:
0803: out.writeByte(RuntimeConstants.opc_putstatic);
0804: out.writeShort(cp.getFieldRef(dotToSlash(className),
0805: methodFieldName, "Ljava/lang/reflect/Method;"));
0806: }
0807: }
0808:
0809: /**
0810: * Generate the constructor method for the proxy class.
0811: */
0812: private MethodInfo generateConstructor() throws IOException {
0813: MethodInfo minfo = new MethodInfo("<init>",
0814: "(Ljava/lang/reflect/InvocationHandler;)V",
0815: RuntimeConstants.ACC_PUBLIC);
0816:
0817: DataOutputStream out = new DataOutputStream(minfo.code);
0818:
0819: code_aload(0, out);
0820:
0821: code_aload(1, out);
0822:
0823: out.writeByte(RuntimeConstants.opc_invokespecial);
0824: out.writeShort(cp.getMethodRef(super className, "<init>",
0825: "(Ljava/lang/reflect/InvocationHandler;)V"));
0826:
0827: // code_aload(0, out);
0828: //
0829: // code_aload(1, out);
0830: //
0831: // out.writeByte(RuntimeConstants.opc_putfield);
0832: // out.writeShort(cp.getFieldRef(
0833: // dotToSlash(className),
0834: // handlerFieldName, "Ljava/lang/reflect/InvocationHandler;"));
0835:
0836: out.writeByte(RuntimeConstants.opc_return);
0837:
0838: minfo.maxStack = 10;
0839: minfo.maxLocals = 2;
0840: minfo.declaredExceptions = new short[0];
0841:
0842: return minfo;
0843: }
0844:
0845: /**
0846: * Generate the static initializer method for the proxy class.
0847: */
0848: private MethodInfo generateStaticInitializer() throws IOException {
0849: MethodInfo minfo = new MethodInfo("<clinit>", "()V",
0850: RuntimeConstants.ACC_STATIC);
0851:
0852: int localSlot0 = 1;
0853: short pc, tryBegin = 0, tryEnd;
0854:
0855: DataOutputStream out = new DataOutputStream(minfo.code);
0856:
0857: for (Iterator iter = proxyMethods.values().iterator(); iter
0858: .hasNext();) {
0859: ProxyMethod pm = (ProxyMethod) iter.next();
0860: pm.codeFieldInitialization(out);
0861: }
0862:
0863: out.writeByte(RuntimeConstants.opc_return);
0864:
0865: tryEnd = pc = (short) minfo.code.size();
0866:
0867: minfo.exceptionTable.add(new ExceptionTableEntry(tryBegin,
0868: tryEnd, pc, cp
0869: .getClass("java/lang/NoSuchMethodException")));
0870:
0871: code_astore(localSlot0, out);
0872:
0873: out.writeByte(RuntimeConstants.opc_new);
0874: out.writeShort(cp.getClass("java/lang/NoSuchMethodError"));
0875:
0876: out.writeByte(RuntimeConstants.opc_dup);
0877:
0878: code_aload(localSlot0, out);
0879:
0880: out.writeByte(RuntimeConstants.opc_invokevirtual);
0881: out.writeShort(cp.getMethodRef("java/lang/Throwable",
0882: "getMessage", "()Ljava/lang/String;"));
0883:
0884: out.writeByte(RuntimeConstants.opc_invokespecial);
0885: out.writeShort(cp.getMethodRef("java/lang/NoSuchMethodError",
0886: "<init>", "(Ljava/lang/String;)V"));
0887:
0888: out.writeByte(RuntimeConstants.opc_athrow);
0889:
0890: pc = (short) minfo.code.size();
0891:
0892: minfo.exceptionTable.add(new ExceptionTableEntry(tryBegin,
0893: tryEnd, pc, cp
0894: .getClass("java/lang/ClassNotFoundException")));
0895:
0896: code_astore(localSlot0, out);
0897:
0898: out.writeByte(RuntimeConstants.opc_new);
0899: out.writeShort(cp.getClass("java/lang/NoClassDefFoundError"));
0900:
0901: out.writeByte(RuntimeConstants.opc_dup);
0902:
0903: code_aload(localSlot0, out);
0904:
0905: out.writeByte(RuntimeConstants.opc_invokevirtual);
0906: out.writeShort(cp.getMethodRef("java/lang/Throwable",
0907: "getMessage", "()Ljava/lang/String;"));
0908:
0909: out.writeByte(RuntimeConstants.opc_invokespecial);
0910: out.writeShort(cp.getMethodRef(
0911: "java/lang/NoClassDefFoundError", "<init>",
0912: "(Ljava/lang/String;)V"));
0913:
0914: out.writeByte(RuntimeConstants.opc_athrow);
0915:
0916: minfo.maxStack = 10;
0917: minfo.maxLocals = (short) (localSlot0 + 1);
0918: minfo.declaredExceptions = new short[0];
0919:
0920: return minfo;
0921: }
0922:
0923: /*
0924: * =============== Code Generation Utility Methods ===============
0925: */
0926:
0927: /*
0928: * The following methods generate code for the load or store operation
0929: * indicated by their name for the given local variable. The code is
0930: * written to the supplied stream.
0931: */
0932:
0933: private void code_iload(int lvar, DataOutputStream out)
0934: throws IOException {
0935: codeLocalLoadStore(lvar, RuntimeConstants.opc_iload,
0936: RuntimeConstants.opc_iload_0, out);
0937: }
0938:
0939: private void code_lload(int lvar, DataOutputStream out)
0940: throws IOException {
0941: codeLocalLoadStore(lvar, RuntimeConstants.opc_lload,
0942: RuntimeConstants.opc_lload_0, out);
0943: }
0944:
0945: private void code_fload(int lvar, DataOutputStream out)
0946: throws IOException {
0947: codeLocalLoadStore(lvar, RuntimeConstants.opc_fload,
0948: RuntimeConstants.opc_fload_0, out);
0949: }
0950:
0951: private void code_dload(int lvar, DataOutputStream out)
0952: throws IOException {
0953: codeLocalLoadStore(lvar, RuntimeConstants.opc_dload,
0954: RuntimeConstants.opc_dload_0, out);
0955: }
0956:
0957: private void code_aload(int lvar, DataOutputStream out)
0958: throws IOException {
0959: codeLocalLoadStore(lvar, RuntimeConstants.opc_aload,
0960: RuntimeConstants.opc_aload_0, out);
0961: }
0962:
0963: private void code_istore(int lvar, DataOutputStream out)
0964: throws IOException {
0965: codeLocalLoadStore(lvar, RuntimeConstants.opc_istore,
0966: RuntimeConstants.opc_istore_0, out);
0967: }
0968:
0969: /* 6220850
0970: private void code_lstore(int lvar, DataOutputStream out)
0971: throws IOException
0972: {
0973: codeLocalLoadStore(lvar,
0974: RuntimeConstants.opc_lstore, RuntimeConstants.opc_lstore_0, out);
0975: }
0976:
0977: private void code_fstore(int lvar, DataOutputStream out)
0978: throws IOException
0979: {
0980: codeLocalLoadStore(lvar,
0981: RuntimeConstants.opc_fstore, RuntimeConstants.opc_fstore_0, out);
0982: }
0983:
0984: private void code_dstore(int lvar, DataOutputStream out)
0985: throws IOException
0986: {
0987: codeLocalLoadStore(lvar,
0988: RuntimeConstants.opc_dstore, RuntimeConstants.opc_dstore_0, out);
0989: }
0990: 6220850 */
0991:
0992: private void code_astore(int lvar, DataOutputStream out)
0993: throws IOException {
0994: codeLocalLoadStore(lvar, RuntimeConstants.opc_astore,
0995: RuntimeConstants.opc_astore_0, out);
0996: }
0997:
0998: /**
0999: * Generate code for a load or store instruction for the given local
1000: * variable. The code is written to the supplied stream.
1001: *
1002: * "opcode" indicates the opcode form of the desired load or store
1003: * instruction that takes an explicit local variable index, and
1004: * "opcode_0" indicates the corresponding form of the instruction
1005: * with the implicit index 0.
1006: */
1007: private void codeLocalLoadStore(int lvar, int opcode, int opcode_0,
1008: DataOutputStream out) throws IOException {
1009: _assert(lvar >= 0 && lvar <= 0xFFFF);
1010: if (lvar <= 3) {
1011: out.writeByte(opcode_0 + lvar);
1012: } else if (lvar <= 0xFF) {
1013: out.writeByte(opcode);
1014: out.writeByte(lvar & 0xFF);
1015: } else {
1016: /*
1017: * Use the "wide" instruction modifier for local variable
1018: * indexes that do not fit into an unsigned byte.
1019: */
1020: out.writeByte(RuntimeConstants.opc_wide);
1021: out.writeByte(opcode);
1022: out.writeShort(lvar & 0xFFFF);
1023: }
1024: }
1025:
1026: /**
1027: * Generate code for an "ldc" instruction for the given constant pool
1028: * index (the "ldc_w" instruction is used if the index does not fit
1029: * into an unsigned byte). The code is written to the supplied stream.
1030: */
1031: private void code_ldc(int index, DataOutputStream out)
1032: throws IOException {
1033: _assert(index >= 0 && index <= 0xFFFF);
1034: if (index <= 0xFF) {
1035: out.writeByte(RuntimeConstants.opc_ldc);
1036: out.writeByte(index & 0xFF);
1037: } else {
1038: out.writeByte(RuntimeConstants.opc_ldc_w);
1039: out.writeShort(index & 0xFFFF);
1040: }
1041: }
1042:
1043: /**
1044: * Generate code to push a constant integer value on to the operand
1045: * stack, using the "iconst_<i>", "bipush", or "sipush" instructions
1046: * depending on the size of the value. The code is written to the
1047: * supplied stream.
1048: */
1049: private void code_ipush(int value, DataOutputStream out)
1050: throws IOException {
1051: if (value >= -1 && value <= 5) {
1052: out.writeByte(RuntimeConstants.opc_iconst_0 + value);
1053: } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
1054: out.writeByte(RuntimeConstants.opc_bipush);
1055: out.writeByte(value & 0xFF);
1056: } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
1057: out.writeByte(RuntimeConstants.opc_sipush);
1058: out.writeShort(value & 0xFFFF);
1059: } else {
1060: _assert(false);
1061: }
1062: }
1063:
1064: /**
1065: * Generate code to invoke the Class.forName with the name of the given
1066: * class to get its Class object at runtime. The code is written to
1067: * the supplied stream. Note that the code generated by this method
1068: * may caused the checked ClassNotFoundException to be thrown.
1069: */
1070: private void codeClassForName(Class cl, DataOutputStream out)
1071: throws IOException {
1072: code_ldc(cp.getString(cl.getName()), out);
1073:
1074: out.writeByte(RuntimeConstants.opc_invokestatic);
1075: out.writeShort(cp.getMethodRef("java/lang/Class", "forName",
1076: "(Ljava/lang/String;)Ljava/lang/Class;"));
1077: }
1078:
1079: /*
1080: * ==================== General Utility Methods ====================
1081: */
1082:
1083: /**
1084: * Assert that an assertion is true: throw InternalError if it is not.
1085: */
1086: private static void _assert(boolean assertion) {
1087: if (assertion != true) {
1088: throw new InternalError("assertion failure");
1089: }
1090: }
1091:
1092: /**
1093: * Convert a fully qualified class name that uses '.' as the package
1094: * separator, the external representation used by the Java language
1095: * and APIs, to a fully qualified class name that uses '/' as the
1096: * package separator, the representation used in the class file
1097: * format (see JVMS section 4.2).
1098: */
1099: private static String dotToSlash(String name) {
1100: return name.replace('.', '/');
1101: }
1102:
1103: /**
1104: * Return the "method descriptor" string for a method with the given
1105: * parameter types and return type. See JVMS section 4.3.3.
1106: */
1107: private static String getMethodDescriptor(Class[] parameterTypes,
1108: Class returnType) {
1109: return getParameterDescriptors(parameterTypes)
1110: + ((returnType == void.class) ? "V"
1111: : getFieldType(returnType));
1112: }
1113:
1114: /**
1115: * Return the list of "parameter descriptor" strings enclosed in
1116: * parentheses corresponding to the given parameter types (in other
1117: * words, a method descriptor without a return descriptor). This
1118: * string is useful for constructing string keys for methods without
1119: * regard to their return type.
1120: */
1121: private static String getParameterDescriptors(Class[] parameterTypes) {
1122: StringBuffer desc = new StringBuffer("(");
1123: for (int i = 0; i < parameterTypes.length; i++) {
1124: desc.append(getFieldType(parameterTypes[i]));
1125: }
1126: desc.append(')');
1127: return desc.toString();
1128: }
1129:
1130: /**
1131: * Return the "field type" string for the given type, appropriate for
1132: * a field descriptor, a parameter descriptor, or a return descriptor
1133: * other than "void". See JVMS section 4.3.2.
1134: */
1135: private static String getFieldType(Class type) {
1136: if (type.isPrimitive()) {
1137: return PrimitiveTypeInfo.get(type).baseTypeString;
1138: } else if (type.isArray()) {
1139: /*
1140: * According to JLS 20.3.2, the getName() method on Class does
1141: * return the VM type descriptor format for array classes (only);
1142: * using that should be quicker than the otherwise obvious code:
1143: *
1144: * return "[" + getTypeDescriptor(type.getComponentType());
1145: */
1146: return type.getName().replace('.', '/');
1147: } else {
1148: return "L" + dotToSlash(type.getName()) + ";";
1149: }
1150: }
1151:
1152: /**
1153: * Return the number of abstract "words", or consecutive local variable
1154: * indexes, required to contain a value of the given type. See JVMS
1155: * section 3.6.1.
1156: *
1157: * Note that the original version of the JVMS contained a definition of
1158: * this abstract notion of a "word" in section 3.4, but that definition
1159: * was removed for the second edition.
1160: */
1161: private static int getWordsPerType(Class type) {
1162: if (type == long.class || type == double.class) {
1163: return 2;
1164: } else {
1165: return 1;
1166: }
1167: }
1168:
1169: /**
1170: * Add to the given list all of the types in the "from" array that
1171: * are not already contained in the liast and are assignable to at
1172: * least one of the types in the "with" array.
1173: *
1174: * This method is useful for computing the greatest common set of
1175: * declared exceptions from duplicate methods inherited from
1176: * different interfaces.
1177: */
1178: private static void collectCompatibleTypes(Class[] from,
1179: Class[] with, List list) {
1180: for (int i = 0; i < from.length; i++) {
1181: if (!list.contains(from[i])) {
1182: for (int j = 0; j < with.length; j++) {
1183: if (with[j].isAssignableFrom(from[i])) {
1184: list.add(from[i]);
1185: break;
1186: }
1187: }
1188: }
1189: }
1190: }
1191:
1192: /**
1193: * Given the exceptions declared in the throws clause of a proxy method,
1194: * compute the exceptions that need to be caught from the invocation
1195: * handler's invoke method and rethrown intact in the method's
1196: * implementation before catching other Throwables and wrapping them
1197: * in UndeclaredThrowableExceptions.
1198: *
1199: * The exceptions to be caught are returned in a List object. Each
1200: * exception in the returned list is guaranteed to not be a subclass of
1201: * any of the other exceptions in the list, so the catch blocks for
1202: * these exceptions may be generated in any order relative to each other.
1203: *
1204: * Error and RuntimeException are each always contained by the returned
1205: * list (if none of their superclasses are contained), since those
1206: * unchecked exceptions should always be rethrown intact, and thus their
1207: * subclasses will never appear in the returned list.
1208: *
1209: * The returned List will be empty if java.lang.Throwable is in the
1210: * given list of declared exceptions, indicating that no exceptions
1211: * need to be caught.
1212: */
1213: private static List computeUniqueCatchList(Class[] exceptions) {
1214: List uniqueList = new ArrayList(); // unique exceptions to catch
1215:
1216: uniqueList.add(Error.class); // always catch/rethrow these
1217: uniqueList.add(RuntimeException.class);
1218:
1219: nextException: for (int i = 0; i < exceptions.length; i++) {
1220: Class ex = exceptions[i];
1221: if (ex.isAssignableFrom(Throwable.class)) {
1222: /*
1223: * If Throwable is declared to be thrown by the proxy method,
1224: * then no catch blocks are necessary, because the invoke
1225: * can, at most, throw Throwable anyway.
1226: */
1227: uniqueList.clear();
1228: break;
1229: } else if (!Throwable.class.isAssignableFrom(ex)) {
1230: /*
1231: * Ignore types that cannot be thrown by the invoke method.
1232: */
1233: continue;
1234: }
1235: /*
1236: * Compare this exception against the current list of
1237: * exceptions that need to be caught:
1238: */
1239: for (int j = 0; j < uniqueList.size();) {
1240: Class ex2 = (Class) uniqueList.get(j);
1241: if (ex2.isAssignableFrom(ex)) {
1242: /*
1243: * if a superclass of this exception is already on
1244: * the list to catch, then ignore this one and continue;
1245: */
1246: continue nextException;
1247: } else if (ex.isAssignableFrom(ex2)) {
1248: /*
1249: * if a subclass of this exception is on the list
1250: * to catch, then remove it;
1251: */
1252: uniqueList.remove(j);
1253: } else {
1254: j++; // else continue comparing.
1255: }
1256: }
1257: // This exception is unique (so far): add it to the list to catch.
1258: uniqueList.add(ex);
1259: }
1260: return uniqueList;
1261: }
1262:
1263: /**
1264: * A PrimitiveTypeInfo object contains assorted information about
1265: * a primitive type in its public fields. The struct for a particular
1266: * primitive type can be obtained using the static "get" method.
1267: */
1268: private static class PrimitiveTypeInfo {
1269:
1270: /** "base type" used in various descriptors (see JVMS section 4.3.2) */
1271: public String baseTypeString;
1272:
1273: /** name of corresponding wrapper class */
1274: public String wrapperClassName;
1275:
1276: /** method descriptor for wrapper class constructor */
1277: public String wrapperConstructorDesc;
1278:
1279: /** name of wrapper class method for retrieving primitive value */
1280: public String unwrapMethodName;
1281:
1282: /** descriptor of same method */
1283: public String unwrapMethodDesc;
1284:
1285: private static Map table = new HashMap(11);
1286: static {
1287: table.put(int.class, new PrimitiveTypeInfo("I",
1288: "java/lang/Integer", "(I)V", "intValue", "()I"));
1289: table
1290: .put(boolean.class, new PrimitiveTypeInfo("Z",
1291: "java/lang/Boolean", "(Z)V",
1292: "booleanValue", "()Z"));
1293: table.put(byte.class, new PrimitiveTypeInfo("B",
1294: "java/lang/Byte", "(B)V", "byteValue", "()B"));
1295: table.put(char.class, new PrimitiveTypeInfo("C",
1296: "java/lang/Character", "(C)V", "charValue", "()C"));
1297: table.put(short.class, new PrimitiveTypeInfo("S",
1298: "java/lang/Short", "(S)V", "shortValue", "()S"));
1299: table.put(long.class, new PrimitiveTypeInfo("J",
1300: "java/lang/Long", "(J)V", "longValue", "()J"));
1301: table.put(float.class, new PrimitiveTypeInfo("F",
1302: "java/lang/Float", "(F)V", "floatValue", "()F"));
1303: table.put(double.class, new PrimitiveTypeInfo("D",
1304: "java/lang/Double", "(D)V", "doubleValue", "()D"));
1305: }
1306:
1307: private PrimitiveTypeInfo(String baseTypeString,
1308: String wrapperClassName, String wrapperConstructorDesc,
1309: String unwrapMethodName, String unwrapMethodDesc) {
1310: this .baseTypeString = baseTypeString;
1311: this .wrapperClassName = wrapperClassName;
1312: this .wrapperConstructorDesc = wrapperConstructorDesc;
1313: this .unwrapMethodName = unwrapMethodName;
1314: this .unwrapMethodDesc = unwrapMethodDesc;
1315: }
1316:
1317: public static PrimitiveTypeInfo get(Class cl) {
1318: return (PrimitiveTypeInfo) table.get(cl);
1319: }
1320: }
1321:
1322: /**
1323: * A ConstantPool object represents the constant pool of a class file
1324: * being generated. This representation of a constant pool is designed
1325: * specifically for use by ProxyGenerator; in particular, it assumes
1326: * that constant pool entries will not need to be resorted (for example,
1327: * by their type, as the Java compiler does), so that the final index
1328: * value can be assigned and used when an entry is first created.
1329: *
1330: * Note that new entries cannot be created after the constant pool has
1331: * been written to a class file. To prevent such logic errors, a
1332: * ConstantPool instance can be marked "read only", so that further
1333: * attempts to add new entries will fail with a runtime exception.
1334: *
1335: * See JVMS section 4.4 for more information about the constant pool
1336: * of a class file.
1337: */
1338: private static class ConstantPool {
1339:
1340: /**
1341: * list of constant pool entries, in constant pool index order.
1342: *
1343: * This list is used when writing the constant pool to a stream
1344: * and for assigning the next index value. Note that element 0
1345: * of this list corresponds to constant pool index 1.
1346: */
1347: private List pool = new ArrayList(32);
1348:
1349: /**
1350: * maps constant pool data of all types to constant pool indexes.
1351: *
1352: * This map is used to look up the index of an existing entry for
1353: * values of all types.
1354: */
1355: private Map map = new HashMap(16);
1356:
1357: /** true if no new constant pool entries may be added */
1358: private boolean readOnly = false;
1359:
1360: /**
1361: * Get or assign the index for a CONSTANT_Utf8 entry.
1362: */
1363: public short getUtf8(String s) {
1364: if (s == null) {
1365: throw new NullPointerException();
1366: }
1367: return getValue(s);
1368: }
1369:
1370: /**
1371: * Get or assign the index for a CONSTANT_Integer entry.
1372: */
1373: public short getInteger(int i) {
1374: return getValue(new Integer(i));
1375: }
1376:
1377: /**
1378: * Get or assign the index for a CONSTANT_Float entry.
1379: */
1380: public short getFloat(float f) {
1381: return getValue(new Float(f));
1382: }
1383:
1384: /**
1385: * Get or assign the index for a CONSTANT_Long entry.
1386: */
1387: public short getLong(long l) {
1388: return getValue(new Long(l));
1389: }
1390:
1391: /**
1392: * Get or assign the index for a CONSTANT_Double entry.
1393: */
1394: public short getDouble(double d) {
1395: return getValue(new Double(d));
1396: }
1397:
1398: /**
1399: * Get or assign the index for a CONSTANT_Class entry.
1400: */
1401: public short getClass(String name) {
1402: short utf8Index = getUtf8(name);
1403: return getIndirect(new IndirectEntry(
1404: RuntimeConstants.CONSTANT_CLASS, utf8Index));
1405: }
1406:
1407: /**
1408: * Get or assign the index for a CONSTANT_String entry.
1409: */
1410: public short getString(String s) {
1411: short utf8Index = getUtf8(s);
1412: return getIndirect(new IndirectEntry(
1413: RuntimeConstants.CONSTANT_STRING, utf8Index));
1414: }
1415:
1416: /**
1417: * Get or assign the index for a CONSTANT_FieldRef entry.
1418: */
1419: public short getFieldRef(String className, String name,
1420: String descriptor) {
1421: short classIndex = getClass(className);
1422: short nameAndTypeIndex = getNameAndType(name, descriptor);
1423: return getIndirect(new IndirectEntry(
1424: RuntimeConstants.CONSTANT_FIELD, classIndex,
1425: nameAndTypeIndex));
1426: }
1427:
1428: /**
1429: * Get or assign the index for a CONSTANT_MethodRef entry.
1430: */
1431: public short getMethodRef(String className, String name,
1432: String descriptor) {
1433: short classIndex = getClass(className);
1434: short nameAndTypeIndex = getNameAndType(name, descriptor);
1435: return getIndirect(new IndirectEntry(
1436: RuntimeConstants.CONSTANT_METHOD, classIndex,
1437: nameAndTypeIndex));
1438: }
1439:
1440: /**
1441: * Get or assign the index for a CONSTANT_InterfaceMethodRef entry.
1442: */
1443: public short getInterfaceMethodRef(String className,
1444: String name, String descriptor) {
1445: short classIndex = getClass(className);
1446: short nameAndTypeIndex = getNameAndType(name, descriptor);
1447: return getIndirect(new IndirectEntry(
1448: RuntimeConstants.CONSTANT_INTERFACEMETHOD,
1449: classIndex, nameAndTypeIndex));
1450: }
1451:
1452: /**
1453: * Get or assign the index for a CONSTANT_NameAndType entry.
1454: */
1455: public short getNameAndType(String name, String descriptor) {
1456: short nameIndex = getUtf8(name);
1457: short descriptorIndex = getUtf8(descriptor);
1458: return getIndirect(new IndirectEntry(
1459: RuntimeConstants.CONSTANT_NAMEANDTYPE, nameIndex,
1460: descriptorIndex));
1461: }
1462:
1463: /**
1464: * Set this ConstantPool instance to be "read only".
1465: *
1466: * After this method has been called, further requests to get
1467: * an index for a non-existent entry will cause an InternalError
1468: * to be thrown instead of creating of the entry.
1469: */
1470: public void setReadOnly() {
1471: readOnly = true;
1472: }
1473:
1474: /**
1475: * Write this constant pool to a stream as part of
1476: * the class file format.
1477: *
1478: * This consists of writing the "constant_pool_count" and
1479: * "constant_pool[]" items of the "ClassFile" structure, as
1480: * described in JVMS section 4.1.
1481: */
1482: public void write(OutputStream out) throws IOException {
1483: DataOutputStream dataOut = new DataOutputStream(out);
1484:
1485: // constant_pool_count: number of entries plus one
1486: dataOut.writeShort(pool.size() + 1);
1487:
1488: for (Iterator iter = pool.iterator(); iter.hasNext();) {
1489: Entry e = (Entry) iter.next();
1490: e.write(dataOut);
1491: }
1492: }
1493:
1494: /**
1495: * Add a new constant pool entry and return its index.
1496: */
1497: private short addEntry(Entry entry) {
1498: pool.add(entry);
1499: return (short) pool.size();
1500: }
1501:
1502: /**
1503: * Get or assign the index for an entry of a type that contains
1504: * a direct value. The type of the given object determines the
1505: * type of the desired entry as follows:
1506: *
1507: * java.lang.String CONSTANT_Utf8
1508: * java.lang.Integer CONSTANT_Integer
1509: * java.lang.Float CONSTANT_Float
1510: * java.lang.Long CONSTANT_Long
1511: * java.lang.Double CONSTANT_DOUBLE
1512: */
1513: private short getValue(Object key) {
1514: Short index = (Short) map.get(key);
1515: if (index != null) {
1516: return index.shortValue();
1517: } else {
1518: if (readOnly) {
1519: throw new InternalError(
1520: "late constant pool addition: " + key);
1521: }
1522: short i = addEntry(new ValueEntry(key));
1523: map.put(key, new Short(i));
1524: return i;
1525: }
1526: }
1527:
1528: /**
1529: * Get or assign the index for an entry of a type that contains
1530: * references to other constant pool entries.
1531: */
1532: private short getIndirect(IndirectEntry e) {
1533: Short index = (Short) map.get(e);
1534: if (index != null) {
1535: return index.shortValue();
1536: } else {
1537: if (readOnly) {
1538: throw new InternalError(
1539: "late constant pool addition");
1540: }
1541: short i = addEntry(e);
1542: map.put(e, new Short(i));
1543: return i;
1544: }
1545: }
1546:
1547: /**
1548: * Entry is the abstact superclass of all constant pool entry types
1549: * that can be stored in the "pool" list; its purpose is to define a
1550: * common method for writing constant pool entries to a class file.
1551: */
1552: private static abstract class Entry {
1553: public abstract void write(DataOutputStream out)
1554: throws IOException;
1555: }
1556:
1557: /**
1558: * ValueEntry represents a constant pool entry of a type that
1559: * contains a direct value (see the comments for the "getValue"
1560: * method for a list of such types).
1561: *
1562: * ValueEntry objects are not used as keys for their entries in the
1563: * Map "map", so no useful hashCode or equals methods are defined.
1564: */
1565: private static class ValueEntry extends Entry {
1566: private Object value;
1567:
1568: public ValueEntry(Object value) {
1569: this .value = value;
1570: }
1571:
1572: public void write(DataOutputStream out) throws IOException {
1573: if (value instanceof String) {
1574: out.writeByte(RuntimeConstants.CONSTANT_UTF8);
1575: out.writeUTF((String) value);
1576: } else if (value instanceof Integer) {
1577: out.writeByte(RuntimeConstants.CONSTANT_INTEGER);
1578: out.writeInt(((Integer) value).intValue());
1579: } else if (value instanceof Float) {
1580: out.writeByte(RuntimeConstants.CONSTANT_FLOAT);
1581: out.writeFloat(((Float) value).floatValue());
1582: } else if (value instanceof Long) {
1583: out.writeByte(RuntimeConstants.CONSTANT_LONG);
1584: out.writeLong(((Long) value).longValue());
1585: } else if (value instanceof Double) {
1586: out.writeDouble(RuntimeConstants.CONSTANT_DOUBLE);
1587: out.writeDouble(((Double) value).doubleValue());
1588: } else {
1589: throw new InternalError("bogus value entry: "
1590: + value);
1591: }
1592: }
1593: }
1594:
1595: /**
1596: * IndirectEntry represents a constant pool entry of a type that
1597: * references other constant pool entries, i.e., the following types:
1598: *
1599: * CONSTANT_Class, CONSTANT_String, CONSTANT_Fieldref,
1600: * CONSTANT_Methodref, CONSTANT_InterfaceMethodref, and
1601: * CONSTANT_NameAndType.
1602: *
1603: * Each of these entry types contains either one or two indexes of
1604: * other constant pool entries.
1605: *
1606: * IndirectEntry objects are used as the keys for their entries in
1607: * the Map "map", so the hashCode and equals methods are overridden
1608: * to allow matching.
1609: */
1610: private static class IndirectEntry extends Entry {
1611: private int tag;
1612: private short index0;
1613: private short index1;
1614:
1615: /**
1616: * Construct an IndirectEntry for a constant pool entry type
1617: * that contains one index of another entry.
1618: */
1619: public IndirectEntry(int tag, short index) {
1620: this .tag = tag;
1621: this .index0 = index;
1622: this .index1 = 0;
1623: }
1624:
1625: /**
1626: * Construct an IndirectEntry for a constant pool entry type
1627: * that contains two indexes for other entries.
1628: */
1629: public IndirectEntry(int tag, short index0, short index1) {
1630: this .tag = tag;
1631: this .index0 = index0;
1632: this .index1 = index1;
1633: }
1634:
1635: public void write(DataOutputStream out) throws IOException {
1636: out.writeByte(tag);
1637: out.writeShort(index0);
1638: /*
1639: * If this entry type contains two indexes, write
1640: * out the second, too.
1641: */
1642: if (tag == RuntimeConstants.CONSTANT_FIELD
1643: || tag == RuntimeConstants.CONSTANT_METHOD
1644: || tag == RuntimeConstants.CONSTANT_INTERFACEMETHOD
1645: || tag == RuntimeConstants.CONSTANT_NAMEANDTYPE) {
1646: out.writeShort(index1);
1647: }
1648: }
1649:
1650: public int hashCode() {
1651: return tag + index0 + index1;
1652: }
1653:
1654: public boolean equals(Object obj) {
1655: if (obj instanceof IndirectEntry) {
1656: IndirectEntry other = (IndirectEntry) obj;
1657: if (tag == other.tag && index0 == other.index0
1658: && index1 == other.index1) {
1659: return true;
1660: }
1661: }
1662: return false;
1663: }
1664: }
1665: }
1666: }
|