// Copyright (c) Corporation for National Research Initiatives package org.python.compiler; import java.util.Hashtable; import java.util.Enumeration; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Constructor; import java.io.*; import org.python.core.Py; public class ProxyMaker implements ClassConstants { public static final int tBoolean = 0; public static final int tByte = 1; public static final int tShort = 2; public static final int tInteger = 3; public static final int tLong = 4; public static final int tFloat = 5; public static final int tDouble = 6; public static final int tCharacter = 7; public static final int tVoid = 8; public static final int tOther = 9; public static final int tNone = 10; public static Hashtable types = fillTypes(); public static Hashtable fillTypes() { Hashtable types = new Hashtable(); types.put(Boolean.TYPE, new Integer(tBoolean)); types.put(Byte.TYPE, new Integer(tByte)); types.put(Short.TYPE, new Integer(tShort)); types.put(Integer.TYPE, new Integer(tInteger)); types.put(Long.TYPE, new Integer(tLong)); types.put(Float.TYPE, new Integer(tFloat)); types.put(Double.TYPE, new Integer(tDouble)); types.put(Character.TYPE, new Integer(tCharacter)); types.put(Void.TYPE, new Integer(tVoid)); return types; } public static int getType(Class c) { if (c == null) return tNone; Object i = types.get(c); if (i == null) return tOther; else return ((Integer) i).intValue(); } Class superclass; Class[] interfaces; Hashtable names; Hashtable supernames = new Hashtable(); public ClassFile classfile; public String myClass; public boolean isAdapter = false; // Ctor used by makeProxy and AdapterMaker. public ProxyMaker(String classname, Class superclass) { this.myClass = "org.python.proxies." + classname; if (superclass.isInterface()) { this.superclass = Object.class; this.interfaces = new Class[] { superclass }; } else { this.superclass = superclass; this.interfaces = new Class[0]; } } // Ctor used by javamaker. public ProxyMaker(String myClass, Class superclass, Class[] interfaces) { this.myClass = myClass; if (superclass == null) superclass = Object.class; this.superclass = superclass; if (interfaces == null) interfaces = new Class[0]; this.interfaces = interfaces; } public static String mapClass(Class c) { String name = c.getName(); int index = name.indexOf("."); if (index == -1) return name; StringBuffer buf = new StringBuffer(name.length()); int last_index = 0; while (index != -1) { buf.append(name.substring(last_index, index)); buf.append("/"); last_index = index + 1; index = name.indexOf(".", last_index); } buf.append(name.substring(last_index, name.length())); return buf.toString(); } public static String mapType(Class type) { if (type.isArray()) return "[" + mapType(type.getComponentType()); switch (getType(type)) { case tByte: return "B"; case tCharacter: return "C"; case tDouble: return "D"; case tFloat: return "F"; case tInteger: return "I"; case tLong: return "J"; case tShort: return "S"; case tBoolean: return "Z"; case tVoid: return "V"; default: return "L" + mapClass(type) + ";"; } } public static String makeSignature(Class[] sig, Class ret) { StringBuffer buf = new StringBuffer(); buf.append("("); for (int i = 0; i < sig.length; i++) { buf.append(mapType(sig[i])); } buf.append(")"); buf.append(mapType(ret)); return buf.toString(); } public void doConstants() throws Exception { Code code = classfile.addMethod("<clinit>", "()V", Modifier.STATIC); code.return_(); } public static void doReturn(Code code, Class type) throws Exception { switch (getType(type)) { case tNone: break; case tCharacter: case tBoolean: case tByte: case tShort: case tInteger: code.ireturn(); break; case tLong: code.lreturn(); break; case tFloat: code.freturn(); break; case tDouble: code.dreturn(); break; case tVoid: code.return_(); break; default: code.areturn(); break; } } public static void doNullReturn(Code code, Class type) throws Exception { switch (getType(type)) { case tNone: break; case tCharacter: case tBoolean: case tByte: case tShort: case tInteger: code.iconst(0); code.ireturn(); break; case tLong: code.ldc(code.pool.Long(0)); code.lreturn(); break; case tFloat: code.ldc(code.pool.Float((float) 0.)); code.freturn(); break; case tDouble: code.ldc(code.pool.Double(0.)); code.dreturn(); break; case tVoid: code.return_(); break; default: code.aconst_null(); code.areturn(); break; } } public void callSuper(Code code, String name, String superclass, Class[] parameters, Class ret, String sig) throws Exception { code.aload(0); int local_index; int i; for (i = 0, local_index = 1; i < parameters.length; i++) { switch (getType(parameters[i])) { case tCharacter: case tBoolean: case tByte: case tShort: case tInteger: code.iload(local_index); local_index += 1; break; case tLong: code.lload(local_index); local_index += 2; break; case tFloat: code.fload(local_index); local_index += 1; break; case tDouble: code.dload(local_index); local_index += 2; break; default: code.aload(local_index); local_index += 1; break; } } int meth = code.pool.Methodref(superclass, name, sig); code.invokespecial(meth); doReturn(code, ret); } public void doJavaCall(Code code, String name, String type, String jcallName) throws Exception { int jcall = code.pool.Methodref("org/python/core/PyObject", jcallName, "(" + $objArr + ")" + $pyObj); int py2j = code.pool.Methodref("org/python/core/Py", "py2" + name, "(" + $pyObj + ")" + type); code.invokevirtual(jcall); code.invokestatic(py2j); } public void getArgs(Code code, Class[] parameters) throws Exception { if (parameters.length == 0) { int EmptyObjects = code.pool.Fieldref("org/python/core/Py", "EmptyObjects", $pyObjArr); code.getstatic(EmptyObjects); } else { code.iconst(parameters.length); code.anewarray(code.pool.Class("java/lang/Object")); int array = code.getLocal("[org/python/core/PyObject"); code.astore(array); int local_index; int i; for (i = 0, local_index = 1; i < parameters.length; i++) { code.aload(array); code.iconst(i); switch (getType(parameters[i])) { case tBoolean: case tByte: case tShort: case tInteger: code.iload(local_index); local_index += 1; int newInteger = code.pool.Methodref("org/python/core/Py", "newInteger", "(I)" + $pyInteger); code.invokestatic(newInteger); break; case tLong: code.lload(local_index); local_index += 2; int newInteger1 = code.pool.Methodref("org/python/core/Py", "newInteger", "(J)" + $pyObj); code.invokestatic(newInteger1); break; case tFloat: code.fload(local_index); local_index += 1; int newFloat = code.pool.Methodref("org/python/core/Py", "newFloat", "(F)" + $pyFloat); code.invokestatic(newFloat); break; case tDouble: code.dload(local_index); local_index += 2; int newFloat1 = code.pool.Methodref("org/python/core/Py", "newFloat", "(D)" + $pyFloat); code.invokestatic(newFloat1); break; case tCharacter: code.iload(local_index); local_index += 1; int newString = code.pool.Methodref("org/python/core/Py", "newString", "(C)" + $pyStr); code.invokestatic(newString); break; default: code.aload(local_index); local_index += 1; break; } code.aastore(); } code.aload(array); } } public void callMethod(Code code, String name, Class[] parameters, Class ret, Class[] exceptions) throws Exception { Label start = null; Label end = null; String jcallName = "_jcall"; int instLocal = 0; if (exceptions.length > 0) { start = code.getLabel(); end = code.getLabel(); jcallName = "_jcallexc"; instLocal = code.getLocal("org/python/core/PyObject"); code.astore(instLocal); start.setPosition(); code.aload(instLocal); } getArgs(code, parameters); switch (getType(ret)) { case tCharacter: doJavaCall(code, "char", "C", jcallName); break; case tBoolean: doJavaCall(code, "boolean", "Z", jcallName); break; case tByte: case tShort: case tInteger: doJavaCall(code, "int", "I", jcallName); break; case tLong: doJavaCall(code, "long", "J", jcallName); break; case tFloat: doJavaCall(code, "float", "F", jcallName); break; case tDouble: doJavaCall(code, "double", "D", jcallName); break; case tVoid: doJavaCall(code, "void", "V", jcallName); break; default: int jcall = code.pool.Methodref("org/python/core/PyObject", jcallName, "(" + $objArr + ")" + $pyObj); code.invokevirtual(jcall); /* catching exceptions is not vm mandatory Label forname_start =code.getLabel(); Label forname_end = code.getLabel(); Label forname_exch_start = code.getLabel(); Label forname_exch_end = code.getLabel(); forname_start.setPosition(); */ int forname = code.pool.Methodref("java/lang/Class", "forName", "(" + $str + ")" + $clss); code.ldc(ret.getName()); code.invokestatic(forname); /* forname_end.setPosition(); code.goto_(forname_exch_end); forname_exch_start.setPosition(); code.stack = 1; // never reached, but this code keeps the verifier happy code.pop(); code.aconst_null(); code.dup(); forname_exch_end.setPosition(); code.addExceptionHandler(forname_start,forname_end, forname_exch_start, code.pool.Class("java/lang/ClassNotFoundException")); */ int tojava = code.pool.Methodref("org/python/core/Py", "tojava", "(" + $pyObj + $clss + ")" + $obj); code.invokestatic(tojava); // I guess I need this checkcast to keep the verifier happy code.checkcast(code.pool.Class(mapClass(ret))); break; } if (exceptions.length > 0) end.setPosition(); doReturn(code, ret); if (exceptions.length > 0) { boolean throwableFound = false; Label handlerStart = null; for (int i = 0; i < exceptions.length; i++) { handlerStart = code.getLabel(); handlerStart.setPosition(); code.stack = 1; int excLocal = code.getLocal("java/lang/Throwable"); code.astore(excLocal); code.aload(excLocal); code.athrow(); code.addExceptionHandler(start, end, handlerStart, code.pool.Class(mapClass(exceptions[i]))); doNullReturn(code, ret); code.freeLocal(excLocal); if (exceptions[i] == Throwable.class) throwableFound = true; } if (!throwableFound) { // The final catch (Throwable) handlerStart = code.getLabel(); handlerStart.setPosition(); code.stack = 1; int excLocal = code.getLocal("java/lang/Throwable"); code.astore(excLocal); code.aload(instLocal); code.aload(excLocal); int jthrow = code.pool.Methodref("org/python/core/PyObject", "_jthrow", "(" + $throwable + ")V"); code.invokevirtual(jthrow); code.addExceptionHandler(start, end, handlerStart, code.pool.Class("java/lang/Throwable")); code.freeLocal(excLocal); doNullReturn(code, ret); } code.freeLocal(instLocal); } } public void addMethod(Method method, int access) throws Exception { boolean isAbstract = false; if (Modifier.isAbstract(access)) { access = access & ~Modifier.ABSTRACT; isAbstract = true; } Class[] parameters = method.getParameterTypes(); Class ret = method.getReturnType(); String sig = makeSignature(parameters, ret); String name = method.getName(); // System.out.println(name+": "+sig); names.put(name, name); Code code = classfile.addMethod(name, sig, access); code.aload(0); code.ldc(name); if (!isAbstract) { int tmp = code.getLocal("org/python/core/PyObject"); int jfindattr = code.pool .Methodref("org/python/core/Py", "jfindattr", "(" + $pyProxy + $str + ")" + $pyObj); code.invokestatic(jfindattr); code.astore(tmp); code.aload(tmp); Label callPython = code.getLabel(); code.ifnonnull(callPython); String superclass = mapClass(method.getDeclaringClass()); callSuper(code, name, superclass, parameters, ret, sig); callPython.setPosition(); code.aload(tmp); callMethod(code, name, parameters, ret, method.getExceptionTypes()); addSuperMethod("super__" + name, name, superclass, parameters, ret, sig, access); } else { if (!isAdapter) { int jgetattr = code.pool.Methodref("org/python/core/Py", "jgetattr", "(" + $pyProxy + $str + ")" + $pyObj); code.invokestatic(jgetattr); callMethod(code, name, parameters, ret, method.getExceptionTypes()); } else { int jfindattr = code.pool.Methodref("org/python/core/Py", "jfindattr", "(" + $pyProxy + $str + ")" + $pyObj); code.invokestatic(jfindattr); code.dup(); Label returnNull = code.getLabel(); code.ifnull(returnNull); callMethod(code, name, parameters, ret, method.getExceptionTypes()); returnNull.setPosition(); code.pop(); doNullReturn(code, ret); } } } private String methodString(Method m) { StringBuffer buf = new StringBuffer(m.getName()); buf.append(":"); Class[] params = m.getParameterTypes(); for (int i = 0; i < params.length; i++) { buf.append(params[i].getName()); buf.append(","); } return buf.toString(); } protected void addMethods(Class c, Hashtable t) throws Exception { Method[] methods = c.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; String s = methodString(method); if (t.containsKey(s)) continue; t.put(s, s); int access = method.getModifiers(); if (Modifier.isStatic(access) || Modifier.isPrivate(access)) { continue; } if (Modifier.isNative(access)) { access = access & ~Modifier.NATIVE; } if (Modifier.isProtected(access)) { access = (access & ~Modifier.PROTECTED) | Modifier.PUBLIC; if (Modifier.isFinal(access)) { addSuperMethod(methods[i], access); continue; } } else if (Modifier.isFinal(access)) { continue; } addMethod(methods[i], access); } Class sc = c.getSuperclass(); if (sc != null) addMethods(sc, t); Class[] interfaces = c.getInterfaces(); for (int j = 0; j < interfaces.length; j++) { addMethods(interfaces[j], t); } } public void addConstructor(String name, Class[] parameters, Class ret, String sig, int access) throws Exception { Code code = classfile.addMethod("<init>", sig, access); callSuper(code, "<init>", name, parameters, Void.TYPE, sig); } public void addConstructors(Class c) throws Exception { Constructor[] constructors = c.getDeclaredConstructors(); String name = mapClass(c); for (int i = 0; i < constructors.length; i++) { int access = constructors[i].getModifiers(); if (Modifier.isPrivate(access)) continue; if (Modifier.isNative(access)) access = access & ~Modifier.NATIVE; if (Modifier.isProtected(access)) access = access & ~Modifier.PROTECTED | Modifier.PUBLIC; Class[] parameters = constructors[i].getParameterTypes(); String sig = makeSignature(parameters, Void.TYPE); addConstructor(name, parameters, Void.TYPE, sig, access); } } // Super methods are added for the following three reasons: // // 1) for a protected non-final method add a public method with no // super__ prefix. This gives needed access to this method for // subclasses // // 2) for protected final methods, add a public method with the // super__ prefix. This avoids the danger of trying to override a // final method // // 3) For any other method that is overriden, add a method with the // super__ prefix. This gives access to super. version or the // method. // public void addSuperMethod(Method method, int access) throws Exception { Class[] parameters = method.getParameterTypes(); Class ret = method.getReturnType(); String sig = makeSignature(parameters, ret); String superclass = mapClass(method.getDeclaringClass()); String superName = method.getName(); String methodName = superName; if (Modifier.isFinal(access)) { methodName = "super__" + superName; access &= ~Modifier.FINAL; } addSuperMethod(methodName, superName, superclass, parameters, ret, sig, access); } public void addSuperMethod(String methodName, String superName, String declClass, Class[] parameters, Class ret, String sig, int access) throws Exception { if (methodName.startsWith("super__")) { /* rationale: JC java-class, P proxy-class subclassing JC in order to avoid infinite recursion P should define super__foo only if no class between P and JC in the hierarchy defines it yet; this means that the python class needing P is the first that redefines the JC method foo. */ try { superclass.getMethod(methodName, parameters); return; } catch (NoSuchMethodException e) { } catch (SecurityException e) { return; } } supernames.put(methodName, methodName); Code code = classfile.addMethod(methodName, sig, access); callSuper(code, superName, declClass, parameters, ret, sig); } public void addProxy() throws Exception { // implement PyProxy interface classfile.addField("__proxy", "Lorg/python/core/PyInstance;", Modifier.PROTECTED); // setProxy method Code code = classfile.addMethod("_setPyInstance", "(Lorg/python/core/PyInstance;)V", Modifier.PUBLIC); int field = code.pool.Fieldref(classfile.name, "__proxy", "Lorg/python/core/PyInstance;"); code.aload(0); code.aload(1); code.putfield(field); code.return_(); // getProxy method code = classfile.addMethod("_getPyInstance", "()Lorg/python/core/PyInstance;", Modifier.PUBLIC); code.aload(0); code.getfield(field); code.areturn(); // implement PyProxy interface classfile.addField("__systemState", "Lorg/python/core/PySystemState;", Modifier.PROTECTED | Modifier.TRANSIENT); // setProxy method code = classfile.addMethod("_setPySystemState", "(Lorg/python/core/PySystemState;)V", Modifier.PUBLIC); field = code.pool.Fieldref(classfile.name, "__systemState", "Lorg/python/core/PySystemState;"); code.aload(0); code.aload(1); code.putfield(field); code.return_(); // getProxy method code = classfile.addMethod("_getPySystemState", "()Lorg/python/core/PySystemState;", Modifier.PUBLIC); code.aload(0); code.getfield(field); code.areturn(); } public void addClassDictInit() throws Exception { int n = supernames.size(); // classDictInit method classfile.addInterface(mapClass(org.python.core.ClassDictInit.class)); Code code = classfile.addMethod("classDictInit", "(" + $pyObj + ")V", Modifier.PUBLIC | Modifier.STATIC); code.aload(0); code.ldc("__supernames__"); String[] names = new String[n]; Enumeration e = supernames.keys(); for (int i = 0; e.hasMoreElements();) names[i++] = (String) e.nextElement(); CodeCompiler.makeStrings(code, names, n); int j2py = code.pool.Methodref("org/python/core/Py", "java2py", "(" + $obj + ")" + $pyObj); code.invokestatic(j2py); int setitem = code.pool.Methodref("org/python/core/PyObject", "__setitem__", "(" + $str + $pyObj + ")V"); code.invokevirtual(setitem); code.return_(); } public void build() throws Exception { names = new Hashtable(); int access = superclass.getModifiers(); if ((access & Modifier.FINAL) != 0) { throw new InstantiationException("can't subclass final class"); } access = Modifier.PUBLIC | Modifier.SYNCHRONIZED; classfile = new ClassFile(myClass, mapClass(superclass), access); addProxy(); addConstructors(superclass); classfile.addInterface("org/python/core/PyProxy"); Hashtable seenmethods = new Hashtable(); addMethods(superclass, seenmethods); for (int i = 0; i < interfaces.length; i++) { if (interfaces[i].isAssignableFrom(superclass)) { Py.writeWarning("compiler", "discarding redundant interface: " + interfaces[i].getName()); continue; } classfile.addInterface(mapClass(interfaces[i])); addMethods(interfaces[i], seenmethods); } doConstants(); addClassDictInit(); } public static String makeProxy(Class superclass, OutputStream ostream) throws Exception { ProxyMaker pm = new ProxyMaker(superclass.getName(), superclass); pm.build(); pm.classfile.write(ostream); return pm.myClass; } public static File makeFilename(String name, File dir) { int index = name.indexOf("."); if (index == -1) return new File(dir, name + ".class"); return makeFilename(name.substring(index + 1, name.length()), new File(dir, name.substring(0, index))); } // This is not general enough public static OutputStream getFile(String d, String name) throws IOException { File dir = new File(d); File file = makeFilename(name, dir); new File(file.getParent()).mkdirs(); //System.out.println("proxy file: "+file); return new FileOutputStream(file); } }