001: /*
002: * @(#)StubClassGenerator.java 1.5 06/08/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.jumpimpl.ixc;
028:
029: import javax.microedition.xlet.ixc.StubException;
030:
031: import java.lang.reflect.Method;
032: import java.lang.reflect.AccessibleObject;
033: import java.security.AccessController;
034: import java.io.ByteArrayOutputStream;
035: import java.io.DataOutputStream;
036: import java.io.IOException;
037: import java.security.AccessController;
038: import java.security.PrivilegedAction;
039: import java.rmi.RemoteException;
040: import java.util.Arrays;
041: import java.util.Collection;
042: import java.util.HashMap;
043: import java.util.HashSet;
044: import java.util.Iterator;
045:
046: /**
047: * Instances of this class generate stub classes. They do this
048: * by creating a byte[], and then passing it to the xlet's
049: * ClassLoader. The method on ClassLoader is protected, so
050: * we use reflection wrapped in a doPrivileged block to call it.
051: * <p>
052: * To give an example, suppose that a user class implemens a remote
053: * interface UserIF,
054: * and that interface contains two methods, void frob(Something), and
055: * int glorp(float). This class will automatically generate
056: * a remote stub. The stub will be equivalent to the following class:
057: * <pre>
058: *
059: * package com.sun.jumpimpl.ixc;
060: *
061: * import java.rmi.RemoteException;
062: *
063: * public final class StubClass_stub42
064: * extends com.sun.jumpimpl.ixc.StubObject
065: * implements UserIF {
066: *
067: * public StubClass_stub42(Object registry,
068: * Object target) {
069: * // Arguments are of type ImportedObjectRegistry and RemoteHandle
070: * super(registry, target);
071: * }
072: *
073: * public void frob(Something arg1) throws RemoteException {
074: * com_sun_xlet_execute("frob", new Object[] { arg1 });
075: * }
076: *
077: * public int glorp(float arg1) throws RemoteException {
078: * Object r = com_sun_xlet_execute("glorb",new Object[] { new Float(arg1) });
079: * return ((Integer) r).intValue();
080: * }
081: * }
082: *
083: * </pre>
084: **/
085:
086: // @@ Add the synthetic attribute
087: // @@ Do this with a security manager installed
088: // @@ Make exception handling consistent with 1.2 behavior. Specifically,
089: // RemoteExceptions should be cloned and wrapped, RuntimeExceptions
090: // should be cloned and re-thrown, checked exception that appear in the
091: // interface method's signature should be cloned and re-thrown (probably
092: // with our deprecated friend, Thread.stop(Throwable)), and unexpected
093: // checked exceptions should be cloned and wrapped (there's some exception
094: // under java.rmi specifically for this).
095: public class StubClassGenerator {
096:
097: private ClassLoader xletClassLoader;
098:
099: private static int nextStubNumber = 1;
100:
101: // This could be non-static, because xlets aren't permitted
102: // to share classloaders. However, if for some reason two
103: // xlets ever to share a classloader (due to a bug or a
104: // spec change), then this being static will prevent
105: // a collision.
106:
107: public StubClassGenerator(ClassLoader xletClassLoader) {
108: this .xletClassLoader = xletClassLoader;
109: }
110:
111: //Class generate(RemoteRef remoteRef) throws StubException {
112: Class generate(RemoteObjectType remoteRef) throws StubException {
113: final String stubName = "StubClass_stub" + (nextStubNumber++);
114: byte[] tmp = null;
115: try {
116: tmp = generateClassBytes(stubName, remoteRef);
117: } catch (IOException ex) {
118: throw new StubException("error generating stub", ex);
119: }
120: final byte[] classBytes = tmp;
121:
122: java.lang.reflect.Method tmp2 = null;
123: try {
124: tmp2 = ClassLoader.class.getDeclaredMethod("defineClass",
125: new Class[] { String.class, classBytes.getClass(),
126: int.class, int.class });
127: } catch (NoSuchMethodException ex) {
128: throw new StubException("internal error", ex);
129: }
130: final java.lang.reflect.Method m = tmp2;
131:
132: // We need a privileged block, so that we can call the
133: // protected defineClass method. Since we're doing that
134: // anyway, we define the stub class in our package namespace,
135: // to guarantee there won't be any name collisions.
136: Object result = AccessController
137: .doPrivileged(new PrivilegedAction() {
138: public Object run() {
139: m.setAccessible(new AccessibleObject[] { m },
140: true);
141: // Tempolary disable the security manager here, as it can
142: // error in loadClass for RuntimePermission
143: // accessClassInPackage.sun.mtask.xlet.ixc
144: // (Yes, even in the privileged block)
145: //SecurityManager sm = System.getSecurityManager();
146: //if (sm != null)
147: // System.setSecurityManager(null);
148:
149: Object result = null;
150: try {
151: Object[] args = new Object[] {
152: stubName.replace('/', '.'),
153: classBytes, new Integer(0),
154: new Integer(classBytes.length) };
155:
156: result = m.invoke(xletClassLoader, args);
157: } catch (Throwable t) {
158: result = t;
159: }
160:
161: //if (sm != null) // Set the SecurityManager back
162: // System.setSecurityManager(sm);
163:
164: return result;
165: }
166: });
167: if (result instanceof Class) {
168: return (Class) result;
169: } else if (result instanceof StubException) {
170: throw (StubException) result;
171: } else if (result instanceof NoClassDefFoundError) {
172: Throwable err = (NoClassDefFoundError) result;
173: throw new StubException("Cannot find a class definition",
174: err);
175: } else if (result instanceof Exception) {
176: throw new StubException("getStub() failed",
177: (Exception) result);
178: } else {
179: throw new StubException("getStub() failed: " + result);
180: }
181: }
182:
183: private String descriptorFor(Method m) {
184: String descriptor = "(";
185: Class[] params = m.getParameterTypes();
186: for (int j = 0; j < params.length; j++) {
187: descriptor += TypeInfo.descriptorFor(params[j]);
188: }
189: descriptor += ")";
190: descriptor += TypeInfo.descriptorFor(m.getReturnType());
191: return descriptor;
192: }
193:
194: //
195: // The stub includes methods defined in 'remote interface',
196: // which is an interface that directly or indirectly extends
197: // java.rmi.Remote. In other words,
198: // interface BaseInterface extends java.rmi.Remote
199: // interface ExtendedInterface extends java.rmi.Remote, Xlet
200: // class TestXlet implements ExtendedInterface
201: // then, ExtendedInterface is a 'remote interface' for TestXlet
202: // and methods declared in BaseInterface, ExtendedInterface
203: // and Xlet interface are treated as remote methods.
204: //
205: private byte[] generateClassBytes(String stubName,
206: RemoteObjectType type) throws IOException, StubException {
207: ConstantPool cp = new ConstantPool();
208: ByteArrayOutputStream bos = new ByteArrayOutputStream();
209: DataOutputStream dos = new DataOutputStream(bos);
210: cp.addString("Code"); // For the code attribute
211: cp.addString("Exceptions"); // For the exceptoins attribute
212: String importedObject = "com/sun/jumpimpl/ixc/StubObject";
213: //String importedObject = "sun/mtask/xlet/ixc/StubObject";
214: String constructorDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;)V";
215: String executeDescriptor = "(J[Ljava/lang/Object;)Ljava/lang/Object;";
216: // Add constant pool entries for names derived from the stuff
217: // we're implementing.
218: cp.addClass(importedObject);
219: cp.addClass(stubName);
220: // @@ Need this: cp.addClass("com/sun/xlet/mvmixc/Utils");
221: cp.addClass("java/lang/Object");
222: cp.addClass("java/lang/Exception");
223: cp.addClass("java/rmi/RemoteException");
224: cp.addClass("java/lang/RuntimeException");
225: cp.addClass("java/rmi/UnexpectedException");
226:
227: String[] remoteInterfaces = type.getRemoteInterfaceNames();
228: Long[] methodIDs = (Long[]) type.methodsByID.keySet().toArray(
229: new Long[] {});
230: Method[] remoteMethods = new Method[methodIDs.length];
231: for (int i = 0; i < remoteInterfaces.length; i++) {
232: cp.addClass(remoteInterfaces[i].replace('.', '/'));
233: }
234:
235: HashMap primArgsDone = new HashMap();
236: HashMap primRetsDone = new HashMap();
237:
238: for (int i = 0; i < methodIDs.length; i++) {
239: remoteMethods[i] = (Method) type.methodsByID
240: .get(methodIDs[i]);
241:
242: Method m = remoteMethods[i];
243: cp.addStringConstant(m.getName());
244: cp.addLong(methodIDs[i]);
245: cp.addIfMethodReference(m.getDeclaringClass().getName()
246: .replace('.', '/'), m.getName(), descriptorFor(m));
247: cp.addStringConstant(m.getDeclaringClass().getName());
248: Class rt = m.getReturnType();
249: if (Void.TYPE.equals(rt)) {
250: // do nothing
251: } else if (rt.isPrimitive()) {
252: TypeInfo info = TypeInfo.get(rt);
253: String rtNm = info.primitiveWrapper.getName().replace(
254: '.', '/');
255: cp.addClass(rtNm);
256: cp.addMethodReference(rtNm, info.valueMethod, "()"
257: + info.typeDescriptor);
258: } else {
259: cp.addClass(rt.getName().replace('.', '/'));
260: }
261: Class[] params = m.getParameterTypes();
262: for (int j = 0; j < params.length; j++) {
263: if (params[j].isPrimitive()) {
264: // Don't need to worry about void here
265: TypeInfo info = TypeInfo.get(params[j]);
266: Class p = info.primitiveWrapper;
267: String nm = p.getName().replace('.', '/');
268: if (primArgsDone.get(nm) == null) {
269: primArgsDone.put(nm, nm);
270: cp.addClass(nm);
271: // The constructor for the wrapper class:
272: cp.addMethodReference(nm, "<init>", "("
273: + TypeInfo.descriptorFor(params[j])
274: + ")V");
275: // The TYPE field
276: cp.addField(nm, "TYPE", "Ljava/lang/Class;");
277: }
278: } else {
279: cp.addStringConstant(params[j].getName());
280: }
281: }
282: // Adding exception types
283: Class[] exceptions = m.getExceptionTypes();
284: for (int j = 0; j < exceptions.length; j++) {
285: cp.addClass(exceptions[j].getName().replace('.', '/'));
286: }
287: }
288: cp.addStringConstant("");
289: // Add constructor constants...
290: cp.addString("<init>");
291: cp.addMethodReference(importedObject, "<init>",
292: constructorDescriptor);
293: cp.addMethodReference(importedObject, "com_sun_xlet_execute",
294: executeDescriptor);
295: // For creating UnexpectedException
296: String unexpectedExceptionDescriptor = "(Ljava/lang/String;Ljava/lang/Exception;)V";
297: cp.addMethodReference("java/rmi/UnexpectedException", "<init>",
298: unexpectedExceptionDescriptor);
299:
300: // Now write out the .class!
301: dos.writeInt(0xcafebabe);
302: dos.writeShort(0x3a); // Minor version, JDK 1.1.3
303: dos.writeShort(0x2d); // Major version, JDK 1.1.3
304: // It's what I observed in 1.1.3, which was handy. Nothing magic
305: // about 1.1.3.
306:
307: cp.write(dos); // Constant pool
308: dos.writeShort(0x31); // ACC_SUPER | ACC_PUBLIC | ACC_FINAL
309: // this_class:
310: dos.writeShort(cp.lookupClass(stubName));
311: // super_class:
312: dos.writeShort(cp.lookupClass(importedObject));
313: // Interfaces:
314: dos.writeShort(remoteInterfaces.length);
315: for (int i = 0; i < remoteInterfaces.length; i++) {
316: String nm = remoteInterfaces[i];
317: dos.writeShort(cp.lookupClass(nm.replace('.', '/')));
318: }
319: // Fields:
320: dos.writeShort(0);
321: // Methods:
322: dos.writeShort(remoteMethods.length + 1); // +1 for constructor
323:
324: // First, the constructor:
325: {
326: dos.writeShort(0x1); // PUBLIC
327: dos.writeShort(cp.lookupString("<init>"));
328: dos.writeShort(cp.lookupString(constructorDescriptor));
329: dos.writeShort(1); // 1 attribute, the Code attribute
330: dos.writeShort(cp.lookupString("Code"));
331: int codeLen = 7;
332: dos.writeInt(12 + codeLen); // attribute_length
333: dos.writeShort(10); // max_stack; be conservative
334: dos.writeShort(10); // max_locals; be conservative
335: dos.writeInt(codeLen);
336: // The code:
337: dos.write(0x2a); // aload_0, loading this
338: dos.write(0x2b); // aload_1, loading StubObject
339: dos.write(0x2c); // aload_2, loading RemoteHandle
340: dos.write(0xb7); // invokespecial, e.g. super(xxx) of ...
341: dos.writeShort(cp.lookupMethod(importedObject, "<init>",
342: constructorDescriptor));
343: dos.write(0xb1); // return
344: // The rest of the code attribute:
345: dos.writeShort(0); // exception_table_length
346: dos.writeShort(0); // attribute_count
347: }
348:
349: int executeMethod = cp.lookupMethod(importedObject,
350: "com_sun_xlet_execute", executeDescriptor);
351: // Now the stub methods:
352: for (int i = 0; i < remoteMethods.length; i++) {
353: Method m = remoteMethods[i];
354: Class[] args = m.getParameterTypes();
355: //int maxLocals = 1; // 1 for "this" parameter
356: int maxLocals = 2; // 1 for "this" parameter
357: for (int j = 0; j < args.length; j++) {
358: maxLocals += TypeInfo.localSlotsFor(args[j]);
359: }
360: int codeLen = 9;
361: for (int j = 0; j < args.length; j++) {
362: if (args[j].isPrimitive()) {
363: codeLen += 9 + 4;
364: } else {
365: codeLen += 2 + 4;
366: }
367: }
368: codeLen += 3;
369: Class ret = m.getReturnType();
370: if (Void.TYPE.equals(ret)) {
371: //codeLen += 2;
372: codeLen += 5;
373: } else if (ret.isPrimitive()) {
374: codeLen += 7;
375: } else {
376: codeLen += 4;
377: }
378:
379: int pc_end = codeLen;
380: if (Void.TYPE.equals(ret)) {
381: pc_end -= 4;
382: } else {
383: pc_end -= 1;
384: }
385:
386: // For exception handling.
387: // Each exception catch/throw takes 3 bytes.
388: // For RuntimeException and Exception.
389: codeLen += 6;
390: // For all other catched Exceptions.
391: Class[] exceptions = m.getExceptionTypes();
392: codeLen += (exceptions.length * 3);
393: codeLen += 9; // For creating UnexpectedException
394:
395: dos.writeShort(0x1 | 0x10); // PUBLIC | FINAL
396: dos.writeShort(cp.lookupString(m.getName()));
397: dos.writeShort(cp
398: .lookupString(descriptorFor(remoteMethods[i])));
399: dos.writeShort(2); // attributes_count
400: dos.writeShort(cp.lookupString("Code"));
401:
402: // 8 for max_stock + max_locals + code_length fields;
403: // 4 for attribute_count and exception_length fields;
404: // Each exception table takes 8 bytes,
405: // There are two additional exceptions: Runtime and Exception.
406: int codeAttributeLen = (8 + codeLen + 4
407: + (exceptions.length * 8) + 16);
408: dos.writeInt(codeAttributeLen);
409: dos.writeShort(10); // max_stack; be conservative
410: dos.writeShort(maxLocals);
411: dos.writeInt(codeLen);
412: // Now the code
413: dos.write(0x2a); // aload_0
414: dos.write(0x14); // ldc2_w
415: dos.writeShort(cp.lookupLongConstant(methodIDs[i]));
416: dos.write(0x10); // bipush
417: dos.write(args.length);
418: dos.write(0xbd); // anewarray java.lang.Object
419: dos.writeShort(cp.lookupClass("java/lang/Object"));
420: int slot = 1;
421: for (int j = 0; j < args.length; j++) {
422: dos.write(0x59); // dup
423: dos.write(0x10); // bipush
424: dos.write(j);
425: if (args[j].isPrimitive()) {
426: TypeInfo info = TypeInfo.get(args[j]);
427: Class p = info.primitiveWrapper;
428: String pName = p.getName().replace('.', '/');
429: dos.write(0xbb); // new
430: dos.writeShort(cp.lookupClass(pName));
431: dos.write(0x59); // dup
432: dos.write(info.loadInstruction);
433: dos.write(slot);
434: // Invoke constructor of primitive wrapper type:
435: dos.write(0xb7); // invokespecial
436: String d = "(" + info.typeDescriptor + ")V";
437: dos.writeShort(cp.lookupMethod(pName, "<init>", d));
438: slot += info.localSlots;
439: } else {
440: dos.write(0x19);
441: dos.write(slot);
442: slot++;
443: }
444: dos.write(0x53); // aastore
445: }
446: dos.write(0xb6); // invokevirtual
447:
448: dos.writeShort(executeMethod);
449:
450: if (Void.TYPE.equals(ret)) {
451: dos.write(0x57); // pop
452: //dos.write(0xb1); // return
453: dos.write(0xa7); // goto
454: dos.writeShort(codeLen - pc_end - 1); // target of goto
455: } else if (ret.isPrimitive()) {
456: TypeInfo info = TypeInfo.get(ret);
457: Class wr = info.primitiveWrapper;
458: String wrNm = wr.getName().replace('.', '/');
459: dos.write(0xc0); // checkcast
460: dos.writeShort(cp.lookupClass(wrNm));
461: dos.write(0xb6); // invokevirtual
462: dos.writeShort(cp.lookupMethod(wrNm, info.valueMethod,
463: "()" + info.typeDescriptor));
464: dos.write(info.returnInstruction);
465: } else {
466: dos.write(0xc0); // checkcast
467: dos.writeShort(cp.lookupClass(ret.getName().replace(
468: '.', '/')));
469: dos.write(0xb0); // areturn
470: }
471:
472: // csaito: for catched exceptions
473: for (int j = 0; j < exceptions.length; j++) {
474: dos.write(0x4c); // astore_1
475: dos.write(0x2b); // aload_1
476: dos.write(0xbf); // athrow
477: }
478:
479: {// For RuntimeException
480: dos.write(0x4c); // astore_1
481: dos.write(0x2b); // aload_1
482: dos.write(0xbf); // athrow
483: }
484:
485: {// For General Exception
486: dos.write(0x4c); // astore_1
487: dos.write(0xbb); // new
488: dos.writeShort(cp
489: .lookupClass("java/rmi/UnexpectedException"));
490: dos.write(0x59); // dup
491: dos.write(0x12); // lcd
492: dos.write(cp.lookupStringConstant("")); // lcd
493: dos.write(0x2b); // aload_1
494: dos.write(0xb7); // invokespecial
495: dos.writeShort(cp.lookupMethod(
496: "java/rmi/UnexpectedException", "<init>",
497: unexpectedExceptionDescriptor));
498: dos.write(0xbf); // athrow
499: }
500:
501: // finally, a return.
502: if (Void.TYPE.equals(ret)) {
503: dos.write(0xb1); // return
504: }
505:
506: // The rest of the code attribute:
507: // First, the exception table.
508: dos.writeShort(exceptions.length + 2);// exception_table_length
509:
510: int astore_start;
511: if (Void.TYPE.equals(ret)) {
512: astore_start = pc_end + 3; // after goto
513: } else {
514: astore_start = pc_end + 1; // after return
515: }
516:
517: for (int j = exceptions.length - 1; j >= 0; j--) {
518: dos.writeShort(0); // pc_start
519: dos.writeShort(pc_end); // pc_end
520: dos.writeShort(astore_start);
521: dos.writeShort(cp.lookupClass(exceptions[j].getName()
522: .replace('.', '/')));
523: astore_start += 3;
524: }
525:
526: { // for RuntimeException
527: dos.writeShort(0); // pc_start
528: dos.writeShort(pc_end); // pc_end
529: dos.writeShort(astore_start);
530: dos.writeShort(cp
531: .lookupClass("java/lang/RuntimeException"));
532: astore_start += 3;
533: }
534:
535: { // for general Exception
536: dos.writeShort(0); // pc_start
537: dos.writeShort(pc_end); // pc_end
538: dos.writeShort(astore_start);
539: dos.writeShort(cp.lookupClass("java/lang/Exception"));
540: }
541:
542: dos.writeShort(0); // attribute_count
543: // Now the second attribute of the method
544: dos.writeShort(cp.lookupString("Exceptions"));
545: dos.writeInt(4);
546: dos.writeShort(1);
547: dos.writeShort(cp.lookupClass("java/rmi/RemoteException"));
548: }
549: // Attributes (of ClassFile):
550: dos.writeShort(0);
551: // And we're done!
552: dos.close();
553: // @@: uncomment below to write out the generated stub class
554: /**
555: ** System.out.println("@@ dumping class " + stubName
556: ** + " to /tmp/foo.class file for debug");
557: ** try {
558: ** dos = new DataOutputStream(new java.io.FileOutputStream(
559: ** "/tmp/foo.class"));
560: ** dos.write(bos.toByteArray());
561: ** dos.close();
562: ** } catch (java.io.FileNotFoundException e) {
563: ** System.out.println("@@ cannot write " + stubName
564: ** + " to .class file to this file system");
565: ** } catch (SecurityException se) {
566: ** System.out.println("@@ cannot write " + stubName
567: ** + " to .class file to this file system");
568: ** }
569: **/
570: return bos.toByteArray();
571: }
572: }
|