001: /*
002: * @(#)DynamicProxyFactory.java 1.2 04/12/06
003: *
004: * Copyright (c) 1997-2003 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package pnuts.compiler;
010:
011: import java.io.ByteArrayOutputStream;
012: import java.io.DataOutputStream;
013: import java.io.FileOutputStream;
014: import java.lang.reflect.Constructor;
015: import java.lang.reflect.Method;
016: import java.lang.reflect.Modifier;
017: import java.util.HashMap;
018: import java.util.Map;
019:
020: import pnuts.lang.Pnuts;
021:
022: /**
023: * This class is used to create a proxy class on-the-fly
024: * to replace a reflective method call. This mechanism
025: * improves execution speed, especially with JIT compiler.
026: */
027: public final class DynamicProxyFactory {
028: private final static boolean DEBUG = false;
029:
030: static Map prim_wrapper_table = new HashMap(8);
031: static {
032: prim_wrapper_table.put(int.class, "java.lang.Integer");
033: prim_wrapper_table.put(short.class, "java.lang.Short");
034: prim_wrapper_table.put(long.class, "java.lang.Long");
035: prim_wrapper_table.put(char.class, "java.lang.Character");
036: prim_wrapper_table.put(byte.class, "java.lang.Byte");
037: prim_wrapper_table.put(float.class, "java.lang.Float");
038: prim_wrapper_table.put(double.class, "java.lang.Double");
039: prim_wrapper_table.put(boolean.class, "java.lang.Boolean");
040: }
041:
042: static String wrapperClass(Class returnType) {
043: return (String) prim_wrapper_table.get(returnType);
044: }
045:
046: private static void loadParam(ClassFile cf, Class[] paramTypes,
047: int index) {
048: int nargs = paramTypes.length;
049: if (nargs == 0) {
050: return;
051: }
052: int n = nargs;
053: if (nargs > 6) {
054: n = 6;
055: }
056: int i = 0;
057: for (; i < n; i++) {
058: cf.add((byte) (Opcode.ALOAD_0 + index));
059: cf.add((byte) (Opcode.ICONST_0 + i));
060: cf.add(Opcode.AALOAD);
061: if (paramTypes[i] != Object.class) {
062: castParam(cf, paramTypes[i]);
063: }
064: }
065: for (; i < nargs; i++) {
066: cf.add((byte) (Opcode.ALOAD_0 + index));
067: cf.add(Opcode.BIPUSH, i);
068: cf.add(Opcode.AALOAD);
069: if (paramTypes[i] != Object.class) {
070: castParam(cf, paramTypes[i]);
071: }
072: }
073: }
074:
075: public static byte byte_cast(Object param) {
076: if (param instanceof Character) {
077: return (byte) ((Character) param).charValue();
078: } else if (param instanceof Number) {
079: return ((Number) param).byteValue();
080: } else {
081: throw new ClassCastException(Pnuts.format(param));
082: }
083: }
084:
085: public static int int_cast(Object param) {
086: if (param instanceof Character) {
087: return (int) ((Character) param).charValue();
088: } else if (param instanceof Number) {
089: return ((Number) param).intValue();
090: } else {
091: throw new ClassCastException(Pnuts.format(param));
092: }
093: }
094:
095: public static short short_cast(Object param) {
096: if (param instanceof Character) {
097: return (short) ((Character) param).charValue();
098: } else if (param instanceof Number) {
099: return ((Number) param).shortValue();
100: } else {
101: throw new ClassCastException(Pnuts.format(param));
102: }
103: }
104:
105: public static char char_cast(Object param) {
106: if (param instanceof Character) {
107: return ((Character) param).charValue();
108: } else if (param instanceof Number) {
109: return (char) ((Number) param).intValue();
110: } else {
111: throw new ClassCastException(Pnuts.format(param));
112: }
113: }
114:
115: public static long long_cast(Object param) {
116: if (param instanceof Character) {
117: return (long) ((Character) param).charValue();
118: } else if (param instanceof Number) {
119: return ((Number) param).longValue();
120: } else {
121: throw new ClassCastException(Pnuts.format(param));
122: }
123: }
124:
125: public static float float_cast(Object param) {
126: if (param instanceof Character) {
127: return (float) ((Character) param).charValue();
128: } else if (param instanceof Number) {
129: return ((Number) param).floatValue();
130: } else {
131: throw new ClassCastException(Pnuts.format(param));
132: }
133: }
134:
135: public static double double_cast(Object param) {
136: if (param instanceof Character) {
137: return (double) ((Character) param).charValue();
138: } else if (param instanceof Number) {
139: return ((Number) param).doubleValue();
140: } else {
141: throw new ClassCastException(Pnuts.format(param));
142: }
143: }
144:
145: static void castParam(ClassFile cf, Class paramType) {
146: if (paramType == int.class) {
147: cf.add(Opcode.INVOKESTATIC,
148: "pnuts.compiler.DynamicProxyFactory", "int_cast",
149: "(Ljava/lang/Object;)", "I");
150: } else if (paramType == byte.class) {
151: cf.add(Opcode.INVOKESTATIC,
152: "pnuts.compiler.DynamicProxyFactory", "byte_cast",
153: "(Ljava/lang/Object;)", "B");
154: } else if (paramType == short.class) {
155: cf.add(Opcode.INVOKESTATIC,
156: "pnuts.compiler.DynamicProxyFactory", "short_cast",
157: "(Ljava/lang/Object;)", "S");
158: } else if (paramType == char.class) {
159: cf.add(Opcode.INVOKESTATIC,
160: "pnuts.compiler.DynamicProxyFactory", "char_cast",
161: "(Ljava/lang/Object;)", "C");
162: } else if (paramType == long.class) {
163: cf.add(Opcode.INVOKESTATIC,
164: "pnuts.compiler.DynamicProxyFactory", "long_cast",
165: "(Ljava/lang/Object;)", "J");
166: } else if (paramType == float.class) {
167: cf.add(Opcode.INVOKESTATIC,
168: "pnuts.compiler.DynamicProxyFactory", "float_cast",
169: "(Ljava/lang/Object;)", "F");
170: } else if (paramType == double.class) {
171: cf.add(Opcode.INVOKESTATIC,
172: "pnuts.compiler.DynamicProxyFactory",
173: "double_cast", "(Ljava/lang/Object;)", "D");
174: } else if (paramType == boolean.class) {
175: String wrapper = "java.lang.Boolean";
176: cf.add(Opcode.CHECKCAST, wrapper);
177: cf.add(Opcode.INVOKEVIRTUAL, wrapper, "booleanValue", "()",
178: "Z");
179: } else {
180: cf.add(Opcode.CHECKCAST, paramType.getName());
181: }
182: }
183:
184: static DynamicProxy makeProxy(Method method, CodeLoader loader) {
185: return makeProxy(method.getName(), method.getDeclaringClass(),
186: method.getReturnType(), method.getParameterTypes(),
187: Modifier.isStatic(method.getModifiers()) ? 1 : 0,
188: loader);
189: }
190:
191: static DynamicProxy makeProxy(Constructor cons, CodeLoader loader) {
192: return makeProxy("<init>", cons.getDeclaringClass(),
193: void.class, cons.getParameterTypes(), 2, loader);
194: }
195:
196: static DynamicProxy makeProxy(String methodName,
197: Class declaringClass, Class returnType, Class[] paramTypes,
198: int type, // 0: instance method, 1: static method, 2: constructor
199: CodeLoader loader) {
200: boolean prim = returnType.isPrimitive()
201: && returnType != void.class;
202:
203: String tempClassName = "_" + (loader.nextCount() & 0x7fffffff);
204:
205: ClassFile cf = new ClassFile(tempClassName,
206: "pnuts.compiler.DynamicProxy", null,
207: (short) (Constants.ACC_PUBLIC | Constants.ACC_FINAL));
208: cf.openMethod("<init>", "()V", Constants.ACC_PUBLIC);
209: cf.add(Opcode.ALOAD_0);
210: cf.add(Opcode.INVOKESPECIAL, "pnuts.compiler.DynamicProxy",
211: "<init>", "()", "V");
212: cf.add(Opcode.RETURN);
213: cf.closeMethod();
214:
215: int nargs = paramTypes.length;
216: String sig = null;
217: if (nargs == 0) {
218: sig = "(Ljava/lang/Object;";
219: sig += ")Ljava/lang/Object;";
220: } else {
221: sig = "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
222: }
223: cf.openMethod("invoke", sig, Constants.ACC_PUBLIC);
224: String className = declaringClass.getName();
225:
226: if (type == 2) {
227: cf.add(Opcode.NEW, className);
228: cf.add(Opcode.DUP);
229: } else if (prim) {
230: cf.add(Opcode.NEW, wrapperClass(returnType));
231: cf.add(Opcode.DUP);
232: }
233:
234: if (type == 0) {
235: cf.add(Opcode.ALOAD_1); // target
236: if (declaringClass != Object.class) {
237: cf.add(Opcode.CHECKCAST, className);
238: }
239: }
240:
241: if (paramTypes.length > 0) {
242: loadParam(cf, paramTypes, 2);
243: }
244: if (type == 1) {
245: cf.add(Opcode.INVOKESTATIC, className, methodName,
246: ClassFile.signature(paramTypes), ClassFile
247: .signature(returnType));
248: } else if (type == 0) {
249: if (declaringClass.isInterface()) {
250: cf.add(Opcode.INVOKEINTERFACE, className, methodName,
251: ClassFile.signature(paramTypes), ClassFile
252: .signature(returnType));
253: } else {
254: cf.add(Opcode.INVOKEVIRTUAL, className, methodName,
255: ClassFile.signature(paramTypes), ClassFile
256: .signature(returnType));
257: }
258: } else if (type == 2) {
259: cf.add(Opcode.INVOKESPECIAL, className, methodName,
260: ClassFile.signature(paramTypes), ClassFile
261: .signature(returnType));
262: }
263:
264: if (prim) {
265: cf.add(Opcode.INVOKESPECIAL, wrapperClass(returnType),
266: "<init>", "(" + ClassFile.signature(returnType)
267: + ")", "V");
268: }
269: if (type != 2 && returnType == void.class) {
270: cf.add(Opcode.ACONST_NULL);
271: }
272: cf.add(Opcode.ARETURN);
273: cf.closeMethod();
274:
275: try {
276: if (DEBUG) {
277: FileOutputStream fout = new FileOutputStream("/tmp/"
278: + tempClassName + ".class");
279: DataOutputStream dout = new DataOutputStream(fout);
280: cf.write(dout);
281: fout.close();
282: System.out.println(tempClassName);
283: }
284:
285: ByteArrayOutputStream bout = new ByteArrayOutputStream();
286: DataOutputStream dout = new DataOutputStream(bout);
287: cf.write(dout);
288:
289: Class clazz = loader.define(tempClassName, bout
290: .toByteArray(), 0, bout.size());
291: loader.resolve(clazz);
292: return (DynamicProxy) clazz.newInstance();
293: } catch (ClassCastException cce) {
294: throw cce;
295: } catch (Exception e) {
296: throw new InternalError(e.toString());
297: }
298: }
299: }
|