package org.python.expose.generate; import org.objectweb.asm.Label; import org.objectweb.asm.Type; import org.python.core.PyBuiltinMethod; import org.python.core.PyBuiltinMethodNarrow; import org.python.expose.ExposedMethod; import org.python.expose.MethodType; /** * Generates a class to call a given method with the {@link ExposedMethod} annotation as a method on * a builtin Python type. */ public class InstanceMethodExposer extends MethodExposer { MethodType type; public InstanceMethodExposer(Type onType, int access, String methodName, String desc, String typeName) { this(onType, access, methodName, desc, typeName, new String[0], new String[0], MethodType.DEFAULT, ""); } public InstanceMethodExposer(Type onType, int access, String methodName, String desc, String typeName, String[] asNames, String[] defaults, MethodType type, String doc) { super(onType, methodName, Type.getArgumentTypes(desc), Type.getReturnType(desc), typeName, asNames, defaults, isWide(desc) ? PyBuiltinMethod.class : PyBuiltinMethodNarrow.class, doc); if ((access & ACC_STATIC) != 0) { throwInvalid("@ExposedMethod can't be applied to static methods"); } if (isWide(args)) { if (defaults.length > 0) { throwInvalid("Can't have defaults on a method that takes PyObject[], String[]"); } } this.type = type; } protected void checkSelf() { mv.visitTypeInsn(CHECKCAST, onType.getInternalName()); } protected void makeCall() { // Actually call the exposed method call(onType, methodName, returnType, args); if (type == MethodType.BINARY) { checkBinaryResult(); } else if (type == MethodType.CMP) { checkCmpResult(); } } /** Throw NotImplemented if a binary method returned null. */ private void checkBinaryResult() { // If this is a binary method, mv.visitInsn(DUP); Label regularReturn = new Label(); mv.visitJumpInsn(IFNONNULL, regularReturn); getStatic(PY, "NotImplemented", PYOBJ); mv.visitInsn(ARETURN); mv.visitLabel(regularReturn); } /** Throw a type error if a cmp method returned -2. */ private void checkCmpResult() { mv.visitInsn(DUP); mv.visitIntInsn(BIPUSH, -2); Label regularReturn = new Label(); mv.visitJumpInsn(IF_ICMPNE, regularReturn); // tediously build an error message based on the type name instantiate(STRING_BUILDER, new Instantiator(STRING) { public void pushArgs() { mv.visitLdcInsn(typeName + ".__cmp__(x,y) requires y to be '" + typeName + "', not a '"); } }); mv.visitVarInsn(ALOAD, !needsThreadState(args) ? 1 : 2); call(PYOBJ, "getType", PYTYPE); call(PYTYPE, "fastGetName", STRING); call(STRING_BUILDER, "append", STRING_BUILDER, STRING); mv.visitLdcInsn("'"); call(STRING_BUILDER, "append", STRING_BUILDER, STRING); call(STRING_BUILDER, "toString", STRING); // throw a type error with our excellent message since this was of the wrong type. callStatic(PY, "TypeError", PYEXCEPTION, STRING); mv.visitInsn(ATHROW); mv.visitLabel(regularReturn); } private static boolean isWide(String methDescriptor) { return isWide(Type.getArgumentTypes(methDescriptor)); } }