src.org.python.compiler.ProxyMaker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jython Show documentation
Show all versions of jython Show documentation
Jython is an implementation of the high-level, dynamic, object-oriented
language Python written in 100% Pure Java, and seamlessly integrated with
the Java platform. It thus allows you to run Python on any Java platform.
// Copyright (c) Corporation for National Research Initiatives
package org.python.compiler;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Set;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.python.core.Py;
import org.python.core.PyException;
import org.python.util.Generic;
import static org.python.util.CodegenUtils.p;
import static org.python.util.CodegenUtils.sig;
public class ProxyMaker extends ProxyCodeHelpers implements ClassConstants, Opcodes
{
protected final Class> superclass;
protected final Class>[] interfaces;
Set names;
Set supernames = Generic.set();
Set namesAndSigs; // name+signature pairs
public ClassFile classfile;
/** The name of the class to build. */
public String myClass;
/**
* Creates a proxy class maker that produces classes named
* org.python.proxies.(superclassName)
with superclass
as an
* implemented interface or extended class, depending on the its type.
*
* @deprecated - Use {@link ProxyMaker#ProxyMaker(String, Class, Class[])
*/
@Deprecated
public ProxyMaker(String superclassName, Class> superclass) {
this("org.python.proxies." + superclassName,
superclass.isInterface() ? Object.class : superclass,
superclass.isInterface() ? new Class>[] { superclass} : new Class>[0]);
}
/**
* Creates a proxy class maker that produces classes named proxyClassName
that
* extends superclass
and implements the interfaces in interfaces
.
*/
public ProxyMaker(String proxyClassName, Class> superclass, Class>... interfaces) {
this.myClass = proxyClassName;
if (superclass == null) {
superclass = Object.class;
}
if (superclass.isInterface()) {
throw new IllegalArgumentException("Given an interface, " + superclass.getName()
+ ", for a proxy superclass");
}
this.superclass = superclass;
if (interfaces == null) {
interfaces = new Class[0];
}
for (Class> interfac : interfaces) {
if (!interfac.isInterface()) {
throw new IllegalArgumentException(
"All classes in the interfaces array must be interfaces, unlike "
+ interfac.getName());
}
}
this.interfaces = interfaces;
}
public void doConstants() throws Exception {
Code code = classfile.addMethod("", makeSig("V"), Modifier.STATIC);
code.return_();
}
public void callSuper(Code code,
String name,
String superclass,
Class>[] parameters,
Class> ret,
boolean doReturn) throws Exception {
code.aload(0);
int local_index;
int i;
for (i=0, local_index=1; i[] parameters) throws Exception {
if (parameters.length == 0) {
code.getstatic("org/python/core/Py", "EmptyObjects", $pyObjArr);
} else {
code.iconst(parameters.length);
code.anewarray("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,
Class> ret,
Class>[] exceptions) throws Exception {
Label start = null;
Label end = null;
String jcallName = "_jcall";
int instLocal = 0;
if (exceptions.length > 0) {
start = new Label();
end = new Label();
jcallName = "_jcallexc";
instLocal = code.getLocal("org/python/core/PyObject");
code.astore(instLocal);
code.label(start);
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:
code.invokevirtual("org/python/core/PyObject", jcallName, makeSig($pyObj, $objArr));
code.ldc(ret.getName());
code.invokestatic("java/lang/Class","forName", makeSig($clss, $str));
code.invokestatic("org/python/core/Py", "tojava", makeSig($obj, $pyObj, $clss));
// I guess I need this checkcast to keep the verifier happy
code.checkcast(mapClass(ret));
break;
}
if (end != null) {
code.label(end);
}
doReturn(code, ret);
if (exceptions.length > 0) {
boolean throwableFound = false;
Label handlerStart = null;
for (Class> exception : exceptions) {
handlerStart = new Label();
code.label(handlerStart);
int excLocal = code.getLocal("java/lang/Throwable");
code.astore(excLocal);
code.aload(excLocal);
code.athrow();
code.visitTryCatchBlock(start, end, handlerStart, mapClass(exception));
doNullReturn(code, ret);
code.freeLocal(excLocal);
if (exception == Throwable.class)
throwableFound = true;
}
if (!throwableFound) {
// The final catch (Throwable)
handlerStart = new Label();
code.label(handlerStart);
int excLocal = code.getLocal("java/lang/Throwable");
code.astore(excLocal);
code.aload(instLocal);
code.aload(excLocal);
code.invokevirtual("org/python/core/PyObject", "_jthrow", makeSig("V", $throwable));
code.visitTryCatchBlock(start, end, handlerStart, "java/lang/Throwable");
code.freeLocal(excLocal);
doNullReturn(code, ret);
}
code.freeLocal(instLocal);
}
}
public void addMethod(Method method, int access) throws Exception {
addMethod(method.getName(), method.getReturnType(), method.getParameterTypes(),
method.getExceptionTypes(), access, method.getDeclaringClass());
}
/**
* Adds a method of the given name to the class being implemented. If
* declaringClass
is null, the generated method will expect to find an object of
* the method's name in the Python object and call it. If it isn't null, if an object is found
* in the Python object, it'll be called. Otherwise the superclass will be called. No checking
* is done to guarantee that the superclass has a method with the same signature.
*/
public void addMethod(String name,
Class> ret,
Class>[] parameters,
Class>[] exceptions,
int access,
Class> declaringClass) throws Exception {
addMethod(name, name, ret, parameters, exceptions, access, declaringClass, null, null);
}
/**
* Generates and adds a proxy method to the proxy class
*
* @param name: name of the java method
* @param pyName: name of the python method to which the java method
* proxies (useful for clamped objects)
*
* @param ret: return type
* @param parameters: parameter types
* @param exceptions: throwable exception types
* @param access
* @param declaringClass
* @param methodAnnotations: method annotations
* @param parameterAnnotations: parameter annotations
* @throws Exception
*/
public void addMethod(String name,
String pyName,
Class> ret,
Class>[] parameters,
Class>[] exceptions,
int access,
Class> declaringClass,
AnnotationDescr[] methodAnnotations,
AnnotationDescr[][]parameterAnnotations) throws Exception {
boolean isAbstract = false;
if (Modifier.isAbstract(access)) {
access = access & ~Modifier.ABSTRACT;
isAbstract = true;
}
String sig = makeSig(ret, parameters);
String[] exceptionTypes = mapExceptions(exceptions);
names.add(name);
Code code = null;
if (methodAnnotations != null && parameterAnnotations != null) {
code = classfile.addMethod(name, sig, access, exceptionTypes, methodAnnotations, parameterAnnotations);
} else {
code = classfile.addMethod(name, sig, access, exceptionTypes);
}
code.aload(0);
code.ldc(pyName);
if (!isAbstract) {
int tmp = code.getLocal("org/python/core/PyObject");
code.invokestatic("org/python/compiler/ProxyMaker", "findPython",
makeSig($pyObj, $pyProxy, $str));
code.astore(tmp);
code.aload(tmp);
Label callPython = new Label();
code.ifnonnull(callPython);
String superClass = mapClass(declaringClass);
callSuper(code, name, superClass, parameters, ret, true);
code.label(callPython);
code.aload(tmp);
callMethod(code, name, parameters, ret, exceptions);
addSuperMethod("super__"+name, name, superClass, parameters,
ret, sig, access);
} else {
code.invokestatic("org/python/compiler/ProxyMaker", "findPython",
makeSig($pyObj, $pyProxy, $str));
code.dup();
Label returnNull = new Label();
code.ifnull(returnNull);
callMethod(code, name, parameters, ret, exceptions);
code.label(returnNull);
code.pop();
// throw an exception if we cannot load a Python method for this abstract method
// note that the unreachable return is simply to simplify bytecode gen
code.ldc("");
code.invokestatic(p(Py.class), "NotImplementedError",
sig(PyException.class, String.class));
code.checkcast(p(Throwable.class));
code.athrow();
doNullReturn(code, ret);
}
}
/**
* A constructor that is also a method (!)
*/
public void addConstructorMethodCode(String pyName,
Class>[] parameters,
Class>[] exceptions,
int access,
Class> declaringClass,
Code code) throws Exception {
code.aload(0);
code.ldc(pyName);
int tmp = code.getLocal("org/python/core/PyObject");
code.invokestatic("org/python/compiler/ProxyMaker", "findPython",
makeSig($pyObj, $pyProxy, $str));
code.astore(tmp);
code.aload(tmp);
callMethod(code, "", parameters, Void.TYPE, exceptions);
}
private String methodString(Method m) {
StringBuffer buf = new StringBuffer(m.getName());
buf.append(":");
Class>[] params = m.getParameterTypes();
for (Class> param : params) {
buf.append(param.getName());
buf.append(",");
}
return buf.toString();
}
protected void addMethods(Class> c, Set t) throws Exception {
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
if (!t.add(methodString(method))) {
continue;
}
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(method, access);
continue;
}
} else if (Modifier.isFinal(access)) {
continue;
} else if (!Modifier.isPublic(access)) {
continue; // package protected by process of elimination; we can't override
}
addMethod(method, access);
}
Class> sc = c.getSuperclass();
if (sc != null) {
addMethods(sc, t);
}
for (Class> iface : c.getInterfaces()) {
addMethods(iface, t);
}
}
public void addConstructor(String name,
Class>[] parameters,
Class> ret,
String sig,
int access) throws Exception {
Code code = classfile.addMethod("", sig, access);
callSuper(code, "", name, parameters, Void.TYPE, true);
}
public void addConstructors(Class> c) throws Exception {
Constructor>[] constructors = c.getDeclaredConstructors();
String name = mapClass(c);
for (Constructor> constructor : constructors) {
int access = constructor.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 = constructor.getParameterTypes();
addConstructor(name, parameters, Void.TYPE, makeSig(Void.TYPE, parameters), access);
}
}
protected void addClassAnnotation(AnnotationDescr annotation) {
classfile.addClassAnnotation(annotation);
}
// 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 overridden, 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 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, makeSig(ret, parameters), 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) {
// OK, no one else defines it, so we need to
} catch (SecurityException e) {
return;
}
}
supernames.add(methodName);
Code code = classfile.addMethod(methodName, sig, access);
callSuper(code, superName, declClass, parameters, ret, true);
}
public void addProxy() throws Exception {
// implement PyProxy interface
classfile.addField("__proxy", $pyObj, Modifier.PROTECTED);
// setProxy methods
Code code = classfile.addMethod("_setPyInstance", makeSig("V", $pyObj), Modifier.PUBLIC);
code.aload(0);
code.aload(1);
code.putfield(classfile.name, "__proxy", $pyObj);
code.return_();
// getProxy method
code = classfile.addMethod("_getPyInstance", makeSig($pyObj), Modifier.PUBLIC);
code.aload(0);
code.getfield(classfile.name, "__proxy", $pyObj);
code.areturn();
String pySys = "Lorg/python/core/PySystemState;";
// implement PyProxy interface
classfile.addField("__systemState", pySys, Modifier.PROTECTED | Modifier.TRANSIENT);
// setProxy method
code = classfile.addMethod("_setPySystemState",
makeSig("V", pySys),
Modifier.PUBLIC);
code.aload(0);
code.aload(1);
code.putfield(classfile.name, "__systemState", pySys);
code.return_();
// getProxy method
code = classfile.addMethod("_getPySystemState", makeSig(pySys), Modifier.PUBLIC);
code.aload(0);
code.getfield(classfile.name, "__systemState", pySys);
code.areturn();
}
public void addClassDictInit() throws Exception {
// classDictInit method
classfile.addInterface(mapClass(org.python.core.ClassDictInit.class));
Code code = classfile.addMethod("classDictInit", makeSig("V", $pyObj),
Modifier.PUBLIC | Modifier.STATIC);
code.aload(0);
code.ldc("__supernames__");
int strArray = CodeCompiler.makeStrings(code, supernames);
code.aload(strArray);
code.freeLocal(strArray);
code.invokestatic("org/python/core/Py", "java2py", makeSig($pyObj, $obj));
code.invokevirtual("org/python/core/PyObject", "__setitem__", makeSig("V", $str, $pyObj));
code.return_();
}
/**
* Builds this proxy and writes its bytecode to out
.
*/
public void build(OutputStream out) throws Exception {
build();
classfile.write(out);
}
public void build() throws Exception {
names = Generic.set();
namesAndSigs = Generic.set();
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();
visitConstructors();
classfile.addInterface("org/python/core/PyProxy");
visitClassAnnotations();
visitMethods();
doConstants();
addClassDictInit();
}
/**
* Visits all methods declared on the given class and classes in its inheritance hierarchy.
* Methods visible to subclasses are added to seen
.
*/
protected void visitMethods(Class> klass) throws Exception {
for (Method method : klass.getDeclaredMethods()) {
// make sure we have only one name + signature pair available per method
if (!namesAndSigs.add(methodString(method))) {
continue;
}
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(method, access);
continue;
}
} else if (Modifier.isFinal(access)) {
continue;
} else if (!Modifier.isPublic(access)) {
continue; // package protected by process of elimination; we can't override
}
addMethod(method, access);
}
Class> superClass = klass.getSuperclass();
if (superClass != null) {
visitMethods(superClass);
}
for (Class> iface : klass.getInterfaces()) {
visitMethods(iface);
}
}
/**
* Called for every method on the proxy's superclass and interfaces that can be overriden by the
* proxy class. If the proxy wants to perform Python lookup and calling for the method,
* {@link #addMethod(Method)} should be called. For abstract methods, addMethod must be called.
*/
protected void visitMethod(Method method) throws Exception {
addMethod(method, method.getModifiers());
}
protected void visitMethods() throws Exception {
visitMethods(superclass);
for (Class> iface : interfaces) {
if (iface.isAssignableFrom(superclass)) {
Py.writeWarning("compiler", "discarding redundant interface: " + iface.getName());
continue;
}
classfile.addInterface(mapClass(iface));
visitMethods(iface);
}
}
/** Adds a constructor that calls through to superclass. */
protected void addConstructor(Class>[] parameters, int access) throws Exception {
String sig = makeSig(Void.TYPE, parameters);
Code code = classfile.addMethod("", sig, access);
callSuper(code, "", mapClass(superclass), parameters, Void.TYPE, true);
}
/**
* Called for every constructor on the proxy's superclass that can be overridden by
* the proxy class.
*/
protected void visitConstructor(Constructor> constructor) throws Exception {
/* Need a fancy constructor for the Java side of things */
callInitProxy(constructor.getParameterTypes(), addOpenConstructor(constructor));
}
/**
* Adds a constructor that calls through to the superclass constructor with the same signature
* and leaves the returned Code open for more operations. The caller of this method must add a
* return to the Code.
*/
protected Code addOpenConstructor(Constructor> constructor) throws Exception {
String sig = makeSig(Void.TYPE, constructor.getParameterTypes());
Code code = classfile.addMethod("", sig, constructor.getModifiers());
callSuper(code, "", mapClass(superclass), constructor.getParameterTypes(), Void.TYPE, true);
return code;
}
/**
* Calls __initProxy__ on this class with the given types of parameters, which must be
* available as arguments to the currently called method in the order of the parameters.
*/
protected void callInitProxy(Class>[] parameters, Code code) throws Exception {
code.visitVarInsn(ALOAD, 0);
getArgs(code, parameters);
code.visitMethodInsn(INVOKEVIRTUAL, classfile.name, "__initProxy__", makeSig("V", $objArr), false);
code.visitInsn(RETURN);
}
/**
* Visits constructors from this proxy's superclass.
*/
protected void visitConstructors() throws Exception {
addConstructors(superclass);
}
protected void visitClassAnnotations() throws Exception {
// ProxyMaker itself does nothing with class annotations for now
}
}