001: // Copyright (c) Corporation for National Research Initiatives
002: package org.python.compiler;
003:
004: import java.util.Hashtable;
005: import java.util.Enumeration;
006: import java.lang.reflect.Method;
007: import java.lang.reflect.Modifier;
008: import java.lang.reflect.Constructor;
009: import java.io.*;
010: import org.python.core.Py;
011:
012: public class ProxyMaker implements ClassConstants {
013: public static final int tBoolean = 0;
014: public static final int tByte = 1;
015: public static final int tShort = 2;
016: public static final int tInteger = 3;
017: public static final int tLong = 4;
018: public static final int tFloat = 5;
019: public static final int tDouble = 6;
020: public static final int tCharacter = 7;
021: public static final int tVoid = 8;
022: public static final int tOther = 9;
023: public static final int tNone = 10;
024:
025: public static Hashtable types = fillTypes();
026:
027: public static Hashtable fillTypes() {
028: Hashtable types = new Hashtable();
029: types.put(Boolean.TYPE, new Integer(tBoolean));
030: types.put(Byte.TYPE, new Integer(tByte));
031: types.put(Short.TYPE, new Integer(tShort));
032: types.put(Integer.TYPE, new Integer(tInteger));
033: types.put(Long.TYPE, new Integer(tLong));
034: types.put(Float.TYPE, new Integer(tFloat));
035: types.put(Double.TYPE, new Integer(tDouble));
036: types.put(Character.TYPE, new Integer(tCharacter));
037: types.put(Void.TYPE, new Integer(tVoid));
038: return types;
039: }
040:
041: public static int getType(Class c) {
042: if (c == null)
043: return tNone;
044: Object i = types.get(c);
045: if (i == null)
046: return tOther;
047: else
048: return ((Integer) i).intValue();
049: }
050:
051: Class super class;
052: Class[] interfaces;
053: Hashtable names;
054: Hashtable super names = new Hashtable();
055: public ClassFile classfile;
056: public String myClass;
057: public boolean isAdapter = false;
058:
059: // Ctor used by makeProxy and AdapterMaker.
060: public ProxyMaker(String classname, Class super class) {
061: this .myClass = "org.python.proxies." + classname;
062: if (super class.isInterface()) {
063: this .super class = Object.class;
064: this .interfaces = new Class[] { super class };
065: } else {
066: this .super class = super class;
067: this .interfaces = new Class[0];
068: }
069: }
070:
071: // Ctor used by javamaker.
072: public ProxyMaker(String myClass, Class super class,
073: Class[] interfaces) {
074: this .myClass = myClass;
075: if (super class == null)
076: super class = Object.class;
077: this .super class = super class;
078: if (interfaces == null)
079: interfaces = new Class[0];
080: this .interfaces = interfaces;
081: }
082:
083: public static String mapClass(Class c) {
084: String name = c.getName();
085: int index = name.indexOf(".");
086: if (index == -1)
087: return name;
088:
089: StringBuffer buf = new StringBuffer(name.length());
090: int last_index = 0;
091: while (index != -1) {
092: buf.append(name.substring(last_index, index));
093: buf.append("/");
094: last_index = index + 1;
095: index = name.indexOf(".", last_index);
096: }
097: buf.append(name.substring(last_index, name.length()));
098: return buf.toString();
099: }
100:
101: public static String mapType(Class type) {
102: if (type.isArray())
103: return "[" + mapType(type.getComponentType());
104:
105: switch (getType(type)) {
106: case tByte:
107: return "B";
108: case tCharacter:
109: return "C";
110: case tDouble:
111: return "D";
112: case tFloat:
113: return "F";
114: case tInteger:
115: return "I";
116: case tLong:
117: return "J";
118: case tShort:
119: return "S";
120: case tBoolean:
121: return "Z";
122: case tVoid:
123: return "V";
124: default:
125: return "L" + mapClass(type) + ";";
126: }
127: }
128:
129: public static String makeSignature(Class[] sig, Class ret) {
130: StringBuffer buf = new StringBuffer();
131: buf.append("(");
132: for (int i = 0; i < sig.length; i++) {
133: buf.append(mapType(sig[i]));
134: }
135: buf.append(")");
136: buf.append(mapType(ret));
137: return buf.toString();
138: }
139:
140: public void doConstants() throws Exception {
141: Code code = classfile.addMethod("<clinit>", "()V",
142: Modifier.STATIC);
143: code.return_();
144: }
145:
146: public static void doReturn(Code code, Class type) throws Exception {
147: switch (getType(type)) {
148: case tNone:
149: break;
150: case tCharacter:
151: case tBoolean:
152: case tByte:
153: case tShort:
154: case tInteger:
155: code.ireturn();
156: break;
157: case tLong:
158: code.lreturn();
159: break;
160: case tFloat:
161: code.freturn();
162: break;
163: case tDouble:
164: code.dreturn();
165: break;
166: case tVoid:
167: code.return_();
168: break;
169: default:
170: code.areturn();
171: break;
172: }
173: }
174:
175: public static void doNullReturn(Code code, Class type)
176: throws Exception {
177: switch (getType(type)) {
178: case tNone:
179: break;
180: case tCharacter:
181: case tBoolean:
182: case tByte:
183: case tShort:
184: case tInteger:
185: code.iconst(0);
186: code.ireturn();
187: break;
188: case tLong:
189: code.ldc(code.pool.Long(0));
190: code.lreturn();
191: break;
192: case tFloat:
193: code.ldc(code.pool.Float((float) 0.));
194: code.freturn();
195: break;
196: case tDouble:
197: code.ldc(code.pool.Double(0.));
198: code.dreturn();
199: break;
200: case tVoid:
201: code.return_();
202: break;
203: default:
204: code.aconst_null();
205: code.areturn();
206: break;
207: }
208: }
209:
210: public void callSuper(Code code, String name, String super class,
211: Class[] parameters, Class ret, String sig) throws Exception {
212: code.aload(0);
213: int local_index;
214: int i;
215: for (i = 0, local_index = 1; i < parameters.length; i++) {
216: switch (getType(parameters[i])) {
217: case tCharacter:
218: case tBoolean:
219: case tByte:
220: case tShort:
221: case tInteger:
222: code.iload(local_index);
223: local_index += 1;
224: break;
225: case tLong:
226: code.lload(local_index);
227: local_index += 2;
228: break;
229: case tFloat:
230: code.fload(local_index);
231: local_index += 1;
232: break;
233: case tDouble:
234: code.dload(local_index);
235: local_index += 2;
236: break;
237: default:
238: code.aload(local_index);
239: local_index += 1;
240: break;
241: }
242: }
243: int meth = code.pool.Methodref(super class, name, sig);
244: code.invokespecial(meth);
245: doReturn(code, ret);
246: }
247:
248: public void doJavaCall(Code code, String name, String type,
249: String jcallName) throws Exception {
250: int jcall = code.pool.Methodref("org/python/core/PyObject",
251: jcallName, "(" + $objArr + ")" + $pyObj);
252:
253: int py2j = code.pool.Methodref("org/python/core/Py", "py2"
254: + name, "(" + $pyObj + ")" + type);
255:
256: code.invokevirtual(jcall);
257: code.invokestatic(py2j);
258: }
259:
260: public void getArgs(Code code, Class[] parameters) throws Exception {
261: if (parameters.length == 0) {
262: int EmptyObjects = code.pool.Fieldref("org/python/core/Py",
263: "EmptyObjects", $pyObjArr);
264: code.getstatic(EmptyObjects);
265: } else {
266: code.iconst(parameters.length);
267: code.anewarray(code.pool.Class("java/lang/Object"));
268: int array = code.getLocal("[org/python/core/PyObject");
269: code.astore(array);
270:
271: int local_index;
272: int i;
273: for (i = 0, local_index = 1; i < parameters.length; i++) {
274: code.aload(array);
275: code.iconst(i);
276:
277: switch (getType(parameters[i])) {
278: case tBoolean:
279: case tByte:
280: case tShort:
281: case tInteger:
282: code.iload(local_index);
283: local_index += 1;
284:
285: int newInteger = code.pool.Methodref(
286: "org/python/core/Py", "newInteger", "(I)"
287: + $pyInteger);
288: code.invokestatic(newInteger);
289: break;
290: case tLong:
291: code.lload(local_index);
292: local_index += 2;
293:
294: int newInteger1 = code.pool.Methodref(
295: "org/python/core/Py", "newInteger", "(J)"
296: + $pyObj);
297: code.invokestatic(newInteger1);
298: break;
299: case tFloat:
300: code.fload(local_index);
301: local_index += 1;
302:
303: int newFloat = code.pool.Methodref(
304: "org/python/core/Py", "newFloat", "(F)"
305: + $pyFloat);
306: code.invokestatic(newFloat);
307: break;
308: case tDouble:
309: code.dload(local_index);
310: local_index += 2;
311:
312: int newFloat1 = code.pool.Methodref(
313: "org/python/core/Py", "newFloat", "(D)"
314: + $pyFloat);
315: code.invokestatic(newFloat1);
316: break;
317: case tCharacter:
318: code.iload(local_index);
319: local_index += 1;
320: int newString = code.pool.Methodref(
321: "org/python/core/Py", "newString", "(C)"
322: + $pyStr);
323: code.invokestatic(newString);
324: break;
325: default:
326: code.aload(local_index);
327: local_index += 1;
328: break;
329: }
330: code.aastore();
331: }
332: code.aload(array);
333: }
334: }
335:
336: public void callMethod(Code code, String name, Class[] parameters,
337: Class ret, Class[] exceptions) throws Exception {
338: Label start = null;
339: Label end = null;
340:
341: String jcallName = "_jcall";
342: int instLocal = 0;
343:
344: if (exceptions.length > 0) {
345: start = code.getLabel();
346: end = code.getLabel();
347: jcallName = "_jcallexc";
348: instLocal = code.getLocal("org/python/core/PyObject");
349: code.astore(instLocal);
350: start.setPosition();
351: code.aload(instLocal);
352: }
353:
354: getArgs(code, parameters);
355:
356: switch (getType(ret)) {
357: case tCharacter:
358: doJavaCall(code, "char", "C", jcallName);
359: break;
360: case tBoolean:
361: doJavaCall(code, "boolean", "Z", jcallName);
362: break;
363: case tByte:
364: case tShort:
365: case tInteger:
366: doJavaCall(code, "int", "I", jcallName);
367: break;
368: case tLong:
369: doJavaCall(code, "long", "J", jcallName);
370: break;
371: case tFloat:
372: doJavaCall(code, "float", "F", jcallName);
373: break;
374: case tDouble:
375: doJavaCall(code, "double", "D", jcallName);
376: break;
377: case tVoid:
378: doJavaCall(code, "void", "V", jcallName);
379: break;
380: default:
381: int jcall = code.pool.Methodref("org/python/core/PyObject",
382: jcallName, "(" + $objArr + ")" + $pyObj);
383: code.invokevirtual(jcall);
384: /* catching exceptions is not vm mandatory
385: Label forname_start =code.getLabel();
386: Label forname_end = code.getLabel();
387: Label forname_exch_start = code.getLabel();
388: Label forname_exch_end = code.getLabel();
389: forname_start.setPosition();
390: */
391: int forname = code.pool.Methodref("java/lang/Class",
392: "forName", "(" + $str + ")" + $clss);
393: code.ldc(ret.getName());
394: code.invokestatic(forname);
395: /*
396: forname_end.setPosition();
397: code.goto_(forname_exch_end);
398: forname_exch_start.setPosition();
399: code.stack = 1;
400: // never reached, but this code keeps the verifier happy
401: code.pop();
402: code.aconst_null();
403: code.dup();
404: forname_exch_end.setPosition();
405:
406: code.addExceptionHandler(forname_start,forname_end,
407: forname_exch_start,
408: code.pool.Class("java/lang/ClassNotFoundException"));
409: */
410: int tojava = code.pool.Methodref("org/python/core/Py",
411: "tojava", "(" + $pyObj + $clss + ")" + $obj);
412: code.invokestatic(tojava);
413: // I guess I need this checkcast to keep the verifier happy
414: code.checkcast(code.pool.Class(mapClass(ret)));
415: break;
416: }
417: if (exceptions.length > 0)
418: end.setPosition();
419:
420: doReturn(code, ret);
421:
422: if (exceptions.length > 0) {
423: boolean throwableFound = false;
424:
425: Label handlerStart = null;
426: for (int i = 0; i < exceptions.length; i++) {
427: handlerStart = code.getLabel();
428: handlerStart.setPosition();
429: code.stack = 1;
430: int excLocal = code.getLocal("java/lang/Throwable");
431: code.astore(excLocal);
432:
433: code.aload(excLocal);
434: code.athrow();
435:
436: code.addExceptionHandler(start, end, handlerStart,
437: code.pool.Class(mapClass(exceptions[i])));
438: doNullReturn(code, ret);
439:
440: code.freeLocal(excLocal);
441: if (exceptions[i] == Throwable.class)
442: throwableFound = true;
443: }
444:
445: if (!throwableFound) {
446: // The final catch (Throwable)
447: handlerStart = code.getLabel();
448: handlerStart.setPosition();
449: code.stack = 1;
450: int excLocal = code.getLocal("java/lang/Throwable");
451: code.astore(excLocal);
452: code.aload(instLocal);
453: code.aload(excLocal);
454:
455: int jthrow = code.pool.Methodref(
456: "org/python/core/PyObject", "_jthrow", "("
457: + $throwable + ")V");
458: code.invokevirtual(jthrow);
459:
460: code.addExceptionHandler(start, end, handlerStart,
461: code.pool.Class("java/lang/Throwable"));
462: code.freeLocal(excLocal);
463: doNullReturn(code, ret);
464: }
465: code.freeLocal(instLocal);
466: }
467: }
468:
469: public void addMethod(Method method, int access) throws Exception {
470: boolean isAbstract = false;
471:
472: if (Modifier.isAbstract(access)) {
473: access = access & ~Modifier.ABSTRACT;
474: isAbstract = true;
475: }
476:
477: Class[] parameters = method.getParameterTypes();
478: Class ret = method.getReturnType();
479: String sig = makeSignature(parameters, ret);
480:
481: String name = method.getName();
482: // System.out.println(name+": "+sig);
483: names.put(name, name);
484:
485: Code code = classfile.addMethod(name, sig, access);
486:
487: code.aload(0);
488: code.ldc(name);
489:
490: if (!isAbstract) {
491: int tmp = code.getLocal("org/python/core/PyObject");
492: int jfindattr = code.pool.Methodref("org/python/core/Py",
493: "jfindattr", "(" + $pyProxy + $str + ")" + $pyObj);
494: code.invokestatic(jfindattr);
495:
496: code.astore(tmp);
497: code.aload(tmp);
498:
499: Label callPython = code.getLabel();
500:
501: code.ifnonnull(callPython);
502:
503: String super class = mapClass(method.getDeclaringClass());
504:
505: callSuper(code, name, super class, parameters, ret, sig);
506: callPython.setPosition();
507: code.aload(tmp);
508: callMethod(code, name, parameters, ret, method
509: .getExceptionTypes());
510:
511: addSuperMethod("super__" + name, name, super class,
512: parameters, ret, sig, access);
513: } else {
514: if (!isAdapter) {
515: int jgetattr = code.pool.Methodref(
516: "org/python/core/Py", "jgetattr", "("
517: + $pyProxy + $str + ")" + $pyObj);
518: code.invokestatic(jgetattr);
519: callMethod(code, name, parameters, ret, method
520: .getExceptionTypes());
521: } else {
522: int jfindattr = code.pool.Methodref(
523: "org/python/core/Py", "jfindattr", "("
524: + $pyProxy + $str + ")" + $pyObj);
525: code.invokestatic(jfindattr);
526: code.dup();
527: Label returnNull = code.getLabel();
528: code.ifnull(returnNull);
529: callMethod(code, name, parameters, ret, method
530: .getExceptionTypes());
531: returnNull.setPosition();
532: code.pop();
533: doNullReturn(code, ret);
534: }
535: }
536: }
537:
538: private String methodString(Method m) {
539: StringBuffer buf = new StringBuffer(m.getName());
540: buf.append(":");
541: Class[] params = m.getParameterTypes();
542: for (int i = 0; i < params.length; i++) {
543: buf.append(params[i].getName());
544: buf.append(",");
545: }
546: return buf.toString();
547: }
548:
549: protected void addMethods(Class c, Hashtable t) throws Exception {
550: Method[] methods = c.getDeclaredMethods();
551: for (int i = 0; i < methods.length; i++) {
552: Method method = methods[i];
553: String s = methodString(method);
554: if (t.containsKey(s))
555: continue;
556: t.put(s, s);
557:
558: int access = method.getModifiers();
559: if (Modifier.isStatic(access) || Modifier.isPrivate(access)) {
560: continue;
561: }
562:
563: if (Modifier.isNative(access)) {
564: access = access & ~Modifier.NATIVE;
565: }
566:
567: if (Modifier.isProtected(access)) {
568: access = (access & ~Modifier.PROTECTED)
569: | Modifier.PUBLIC;
570: if (Modifier.isFinal(access)) {
571: addSuperMethod(methods[i], access);
572: continue;
573: }
574: } else if (Modifier.isFinal(access)) {
575: continue;
576: }
577: addMethod(methods[i], access);
578: }
579:
580: Class sc = c.getSuperclass();
581: if (sc != null)
582: addMethods(sc, t);
583:
584: Class[] interfaces = c.getInterfaces();
585: for (int j = 0; j < interfaces.length; j++) {
586: addMethods(interfaces[j], t);
587: }
588: }
589:
590: public void addConstructor(String name, Class[] parameters,
591: Class ret, String sig, int access) throws Exception {
592: Code code = classfile.addMethod("<init>", sig, access);
593: callSuper(code, "<init>", name, parameters, Void.TYPE, sig);
594: }
595:
596: public void addConstructors(Class c) throws Exception {
597: Constructor[] constructors = c.getDeclaredConstructors();
598: String name = mapClass(c);
599: for (int i = 0; i < constructors.length; i++) {
600: int access = constructors[i].getModifiers();
601: if (Modifier.isPrivate(access))
602: continue;
603: if (Modifier.isNative(access))
604: access = access & ~Modifier.NATIVE;
605: if (Modifier.isProtected(access))
606: access = access & ~Modifier.PROTECTED | Modifier.PUBLIC;
607: Class[] parameters = constructors[i].getParameterTypes();
608: String sig = makeSignature(parameters, Void.TYPE);
609: addConstructor(name, parameters, Void.TYPE, sig, access);
610: }
611: }
612:
613: // Super methods are added for the following three reasons:
614: //
615: // 1) for a protected non-final method add a public method with no
616: // super__ prefix. This gives needed access to this method for
617: // subclasses
618: //
619: // 2) for protected final methods, add a public method with the
620: // super__ prefix. This avoids the danger of trying to override a
621: // final method
622: //
623: // 3) For any other method that is overriden, add a method with the
624: // super__ prefix. This gives access to super. version or the
625: // method.
626: //
627: public void addSuperMethod(Method method, int access)
628: throws Exception {
629: Class[] parameters = method.getParameterTypes();
630: Class ret = method.getReturnType();
631: String sig = makeSignature(parameters, ret);
632: String super class = mapClass(method.getDeclaringClass());
633: String super Name = method.getName();
634: String methodName = super Name;
635: if (Modifier.isFinal(access)) {
636: methodName = "super__" + super Name;
637: access &= ~Modifier.FINAL;
638: }
639: addSuperMethod(methodName, super Name, super class, parameters,
640: ret, sig, access);
641: }
642:
643: public void addSuperMethod(String methodName, String super Name,
644: String declClass, Class[] parameters, Class ret,
645: String sig, int access) throws Exception {
646: if (methodName.startsWith("super__")) {
647: /* rationale: JC java-class, P proxy-class subclassing JC
648: in order to avoid infinite recursion P should define super__foo
649: only if no class between P and JC in the hierarchy defines
650: it yet; this means that the python class needing P is the
651: first that redefines the JC method foo.
652: */
653: try {
654: super class.getMethod(methodName, parameters);
655: return;
656: } catch (NoSuchMethodException e) {
657: } catch (SecurityException e) {
658: return;
659: }
660: }
661: super names.put(methodName, methodName);
662: Code code = classfile.addMethod(methodName, sig, access);
663: callSuper(code, super Name, declClass, parameters, ret, sig);
664: }
665:
666: public void addProxy() throws Exception {
667: // implement PyProxy interface
668: classfile.addField("__proxy", "Lorg/python/core/PyInstance;",
669: Modifier.PROTECTED);
670: // setProxy method
671: Code code = classfile.addMethod("_setPyInstance",
672: "(Lorg/python/core/PyInstance;)V", Modifier.PUBLIC);
673:
674: int field = code.pool.Fieldref(classfile.name, "__proxy",
675: "Lorg/python/core/PyInstance;");
676: code.aload(0);
677: code.aload(1);
678: code.putfield(field);
679: code.return_();
680:
681: // getProxy method
682: code = classfile.addMethod("_getPyInstance",
683: "()Lorg/python/core/PyInstance;", Modifier.PUBLIC);
684: code.aload(0);
685: code.getfield(field);
686: code.areturn();
687:
688: // implement PyProxy interface
689: classfile.addField("__systemState",
690: "Lorg/python/core/PySystemState;", Modifier.PROTECTED
691: | Modifier.TRANSIENT);
692:
693: // setProxy method
694: code = classfile.addMethod("_setPySystemState",
695: "(Lorg/python/core/PySystemState;)V", Modifier.PUBLIC);
696:
697: field = code.pool.Fieldref(classfile.name, "__systemState",
698: "Lorg/python/core/PySystemState;");
699: code.aload(0);
700: code.aload(1);
701: code.putfield(field);
702: code.return_();
703:
704: // getProxy method
705: code = classfile.addMethod("_getPySystemState",
706: "()Lorg/python/core/PySystemState;", Modifier.PUBLIC);
707: code.aload(0);
708: code.getfield(field);
709: code.areturn();
710: }
711:
712: public void addClassDictInit() throws Exception {
713: int n = super names.size();
714:
715: // classDictInit method
716: classfile
717: .addInterface(mapClass(org.python.core.ClassDictInit.class));
718: Code code = classfile.addMethod("classDictInit", "(" + $pyObj
719: + ")V", Modifier.PUBLIC | Modifier.STATIC);
720: code.aload(0);
721: code.ldc("__supernames__");
722:
723: String[] names = new String[n];
724: Enumeration e = super names.keys();
725: for (int i = 0; e.hasMoreElements();)
726: names[i++] = (String) e.nextElement();
727: CodeCompiler.makeStrings(code, names, n);
728: int j2py = code.pool.Methodref("org/python/core/Py", "java2py",
729: "(" + $obj + ")" + $pyObj);
730: code.invokestatic(j2py);
731:
732: int setitem = code.pool.Methodref("org/python/core/PyObject",
733: "__setitem__", "(" + $str + $pyObj + ")V");
734: code.invokevirtual(setitem);
735: code.return_();
736:
737: }
738:
739: public void build() throws Exception {
740: names = new Hashtable();
741: int access = super class.getModifiers();
742: if ((access & Modifier.FINAL) != 0) {
743: throw new InstantiationException(
744: "can't subclass final class");
745: }
746: access = Modifier.PUBLIC | Modifier.SYNCHRONIZED;
747:
748: classfile = new ClassFile(myClass, mapClass(super class), access);
749: addProxy();
750: addConstructors(super class);
751: classfile.addInterface("org/python/core/PyProxy");
752:
753: Hashtable seenmethods = new Hashtable();
754: addMethods(super class, seenmethods);
755: for (int i = 0; i < interfaces.length; i++) {
756: if (interfaces[i].isAssignableFrom(super class)) {
757: Py.writeWarning("compiler",
758: "discarding redundant interface: "
759: + interfaces[i].getName());
760: continue;
761: }
762: classfile.addInterface(mapClass(interfaces[i]));
763: addMethods(interfaces[i], seenmethods);
764: }
765: doConstants();
766: addClassDictInit();
767: }
768:
769: public static String makeProxy(Class super class,
770: OutputStream ostream) throws Exception {
771: ProxyMaker pm = new ProxyMaker(super class.getName(), super class);
772: pm.build();
773: pm.classfile.write(ostream);
774: return pm.myClass;
775: }
776:
777: public static File makeFilename(String name, File dir) {
778: int index = name.indexOf(".");
779: if (index == -1)
780: return new File(dir, name + ".class");
781:
782: return makeFilename(name.substring(index + 1, name.length()),
783: new File(dir, name.substring(0, index)));
784: }
785:
786: // This is not general enough
787: public static OutputStream getFile(String d, String name)
788: throws IOException {
789: File dir = new File(d);
790: File file = makeFilename(name, dir);
791: new File(file.getParent()).mkdirs();
792: //System.out.println("proxy file: "+file);
793: return new FileOutputStream(file);
794: }
795: }
|