src.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 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.
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());
}
}