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-slim Show documentation
Show all versions of jython-slim 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 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 present to simplify bytecode gen
code.aload(0);
code.ldc(pyName);
code.ldc(declaringClass.getName());
code.invokestatic("org/python/compiler/ProxyCodeHelpers", "notImplementedAbstractMethod",
makeSig($pyExc, $pyProxy, $str, $str));
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 overridden by
* the proxy class. If the proxy wants to perform Python lookup and calling,
* {@link #addMethod(Method, int)} or one of its more complex forms should be called. For
* abstract methods, {@code 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
}
}