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<Type, Type> PRIMITIVES = new HashMap<Type, Type>() { { 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(), "<init>", methodDesc(VOID, args)); } /** 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)); } /** 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)); } /** 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("<init>", 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()); } }