org.python.expose.generate.Exposer 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.
package org.python.expose.generate;
import java.util.HashMap;
import java.util.Map;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.python.core.BytecodeLoader;
/**
* Base class that handles the basics of generating a single class with asm. Subclass to supply the
* actual functionality of the generated class.
*
*/
public abstract class Exposer implements Opcodes, PyTypes {
/** The current method under construction or null. */
protected MethodVisitor mv;
/** The current class under construction. */
protected ClassVisitor cv;
/** The super class of the type that will be generated. */
private Type superType;
/** The type that will be generated. */
protected Type thisType;
protected Type[] interfacesImplemented;
/** Maps from a primitive type to its wrapper */
protected static final Map PRIMITIVES = new HashMap() {
{
put(BOOLEAN, Type.getType(Boolean.class));
put(BYTE, Type.getType(Byte.class));
put(CHAR, Type.getType(Character.class));
put(Type.DOUBLE_TYPE, Type.getType(Double.class));
put(Type.FLOAT_TYPE, Type.getType(Float.class));
put(INT, Type.getType(Integer.class));
put(Type.LONG_TYPE, Type.getType(Long.class));
put(SHORT, Type.getType(Short.class));
put(VOID, Type.getType(Void.class));
}
};
/**
* @param superClass -
* the super class of the generated class
* @param generatedName -
* the name of the class to generate
*/
public Exposer(Class> superClass, String generatedName, Type...interfacesImplemented) {
superType = Type.getType(superClass);
thisType = Type.getType("L" + generatedName.replace('.', '/') + ";");
this.interfacesImplemented = interfacesImplemented;
}
/**
* Implemented by subclasses to fill in the actual implementation of the class. cv is set to the
* ClassVisitor to be used when this is called.
*/
protected abstract void generate();
/**
* Generates this Exposer and loads it into the given Loader.
*/
protected Class> load(BytecodeLoader.Loader l) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
generate(cw);
return l.loadClassFromBytes(getClassName(), cw.toByteArray());
}
protected Type getGeneratedType() {
return thisType;
}
public String getClassName() {
return thisType.getClassName();
}
public String getInternalName() {
return thisType.getInternalName();
}
/**
* Will call the methods on visitor to generate this class. Only one call to generate may be
* active at a time on a single instance of Exposer. The ClassVisitor is assumed to have been
* constructed with COMPUTE_FRAMES.
*/
public void generate(ClassVisitor visitor) {
assert cv == null;
cv = visitor;
String[] interfaces = new String[interfacesImplemented.length];
for (int i = 0; i < interfaces.length; i++) {
interfaces[i] = interfacesImplemented[i].getInternalName();
}
cv.visit(V1_5,
ACC_PUBLIC,
getInternalName(),
null,
superType.getInternalName(),
interfaces);
generate();
assert mv == null;
cv.visitEnd();
cv = null;
}
/** Calls the constructor on the super class with the given args. */
protected void superConstructor(Type... args) {
callConstructor(superType, args);
}
class Instantiator {
public Instantiator(Type... types) {
this.types = types;
}
/**
* Push args onto the stack corresponding to the types passed to the constructor.
*/
public void pushArgs() {
if(types.length > 0) {
throw new IllegalStateException("If the constuctor takes types as indicated by "
+ "passing their types to Instantiator, pushArgs must be overriden to put "
+ "those args on the stack before the call");
}
};
public Type[] getTypes() {
return types;
}
private Type[] types;
}
/** Instantiates ofType using its no-arg constructor */
protected void instantiate(Type ofType) {
instantiate(ofType, new Instantiator());
}
/**
* Instantiates ofType with its constructor that takes the types returned by inst.getTypes().
* inst should override pushArgs to put arguments of those types on the stack for the call.
*/
protected void instantiate(Type ofType, Instantiator inst) {
mv.visitTypeInsn(NEW, ofType.getInternalName());
mv.visitInsn(DUP);
inst.pushArgs();
callConstructor(ofType, inst.getTypes());
}
/** Calls the constructor on onType with the given args. */
protected void callConstructor(Type onType, Type... args) {
mv.visitMethodInsn(INVOKESPECIAL,
onType.getInternalName(),
"",
methodDesc(VOID, args), false);
}
/** Calls the method on onType with the given return type and argument types. */
protected void call(Type onType, String methodName, Type returnType, Type... args) {
mv.visitMethodInsn(INVOKEVIRTUAL,
onType.getInternalName(),
methodName,
methodDesc(returnType, args), false);
}
/** Calls the static method on onType with the given return type and argument types. */
protected void callStatic(Type onType, String methodName, Type returnType, Type... args) {
mv.visitMethodInsn(INVOKESTATIC,
onType.getInternalName(),
methodName,
methodDesc(returnType, args), false);
}
/** Produces a method descriptor with ret as its return type that takes args. */
protected String methodDesc(Type ret, Type... args) {
return Type.getMethodDescriptor(ret, args);
}
/**
* Starts building a constructor in the class. Must be followed by a call to endConstructor
* before startConstructor or startMethod may be called.
*/
protected void startConstructor(Type... args) {
startMethod("", VOID, args);
}
/** Closes the constructor begun by startConstructor. */
protected void endConstructor() {
endMethod(RETURN);
}
/**
* Starts building a method in the class being generated. Must be followed by a call to
* endMethod before startMethod or startConstructor may be called.
*/
protected void startMethod(String name, Type ret, Type... args) {
assert mv == null;
mv = cv.visitMethod(ACC_PUBLIC, name, methodDesc(ret, args), null, null);
mv.visitCode();
}
/** Closes the method under construction. */
protected void endMethod(int returnCode) {
mv.visitInsn(returnCode);
mv.visitMaxs(0, 0);
mv.visitEnd();
mv = null;
}
/** Loads a field on the instance under construction of ofType onto the stack */
protected void get(String fieldName, Type ofType) {
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, getInternalName(), fieldName, ofType.getDescriptor());
}
/**
* Turns an object of inputType on the top of the stack into an equivalent Py type. Handles
* primitives, void, and String. If void, the top item on the stack isn't touched.
*/
protected void toPy(Type inputType) {
if(inputType.equals(VOID)) {
getStatic(PY, "None", PYOBJ);
} else if(inputType.equals(STRING)) {
Label newString = new Label();
Label end = new Label();
mv.visitInsn(DUP);
mv.visitJumpInsn(IFNONNULL, newString);
mv.visitInsn(POP);
getStatic(PY, "None", PYOBJ);
mv.visitJumpInsn(GOTO, end);
mv.visitLabel(newString);
callStatic(PY, "newString", PYSTR, STRING);
mv.visitLabel(end);
} else if(inputType.equals(BOOLEAN)) {
callStatic(PY, "newBoolean", PYBOOLEAN, BOOLEAN);
} else if(inputType.equals(INT) || inputType.equals(BYTE) || inputType.equals(SHORT)) {
callStatic(PY, "newInteger", PYINTEGER, INT);
} else if(inputType.equals(CHAR)) {
callStatic(PY, "makeCharacter", PYSTR, CHAR);
} else if(inputType.equals(Type.DOUBLE_TYPE)) {
callStatic(PY, "newFloat", PYFLOAT, Type.DOUBLE_TYPE);
} else if(inputType.equals(Type.FLOAT_TYPE)) {
callStatic(PY, "newFloat", PYFLOAT, Type.FLOAT_TYPE);
} else if(inputType.equals(Type.LONG_TYPE)) {
callStatic(PY, "newLong", PYLONG, Type.LONG_TYPE);
}
}
/** Gets a static field from onType of the given type. */
protected void getStatic(Type onType, String fieldName, Type ofType) {
mv.visitFieldInsn(GETSTATIC, onType.getInternalName(), fieldName, ofType.getDescriptor());
}
}