src.org.python.expose.generate.MethodExposer 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 org.objectweb.asm.Type;
public abstract class MethodExposer extends Exposer {
protected String[] defaults;
protected final String[] asNames;
protected final String prefix, typeName;
protected final Type[] args;
protected final String methodName;
protected final Type onType, returnType;
protected final String doc;
public MethodExposer(Type onType,
String methodName,
Type[] args,
Type returnType,
String typeName,
String[] asNames,
String[] defaults,
Class superClass,
String doc) {
super(superClass, onType.getClassName() + "$" + methodName + "_exposer");
this.onType = onType;
this.methodName = methodName;
this.args = args;
this.typeName = typeName;
this.doc = doc;
String prefix = typeName;
int lastDot = prefix.lastIndexOf('.');
if (lastDot != -1) {
prefix = prefix.substring(lastDot + 1);
}
this.prefix = prefix;
this.asNames = asNames;
this.returnType = returnType;
this.defaults = defaults;
for(String name : getNames()) {
if(name.equals("__new__")) {
throwInvalid("@ExposedNew must be used to create __new__, not @ExposedMethod");
}
}
}
protected void throwInvalid(String msg) {
throw new InvalidExposingException(msg + "[method=" + onType.getClassName() + "."
+ methodName + "]");
}
/**
* @return the names this method will be exposed as. Must be at least length 1.
*/
public String[] getNames() {
if(asNames.length == 0) {
String name = methodName;
if(name.startsWith(prefix + "_")) {
name = methodName.substring((prefix + "_").length());
}
return new String[] {name};
}
return asNames;
}
protected void generate() {
generateNamedConstructor();
generateFullConstructor();
generateBind();
if(isWide(args)) {
generateWideCall();
} else {
for(int i = 0; i < defaults.length + 1; i++) {
generateCall(i);
}
}
}
private void generateFullConstructor() {
startConstructor(PYTYPE, PYOBJ, BUILTIN_INFO);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 2);
mv.visitVarInsn(ALOAD, 3);
superConstructor(PYTYPE, PYOBJ, BUILTIN_INFO);
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(doc);
mv.visitFieldInsn(PUTFIELD,
BUILTIN_FUNCTION.getInternalName(),
"doc",
STRING.getDescriptor());
endConstructor();
}
private void generateNamedConstructor() {
startConstructor(STRING);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
if(isWide(args)) {
superConstructor(STRING);
} else {
mv.visitLdcInsn(args.length + 1 - defaults.length);
mv.visitLdcInsn(args.length + 1);
superConstructor(STRING, INT, INT);
}
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(doc);
mv.visitFieldInsn(PUTFIELD,
BUILTIN_FUNCTION.getInternalName(),
"doc",
STRING.getDescriptor());
endConstructor();
}
private void generateBind() {
startMethod("bind", BUILTIN_FUNCTION, PYOBJ);
instantiate(thisType, new Instantiator(PYTYPE, PYOBJ, BUILTIN_INFO) {
public void pushArgs() {
mv.visitVarInsn(ALOAD, 0);
call(thisType, "getType", PYTYPE);
mv.visitVarInsn(ALOAD, 1);
get("info", BUILTIN_INFO);
}
});
endMethod(ARETURN);
}
private void generateWideCall() {
boolean needsThreadState = needsThreadState(args);
int offset = needsThreadState ? 1 : 0;
Type[] callArgs;
if (needsThreadState) {
callArgs = new Type[] {THREAD_STATE, APYOBJ, ASTRING};
} else {
callArgs = new Type[] {APYOBJ, ASTRING};
}
startMethod("__call__", PYOBJ, callArgs);
loadSelfAndThreadState();
mv.visitVarInsn(ALOAD, offset + 1);
mv.visitVarInsn(ALOAD, offset + 2);
makeCall();
toPy(returnType);
endMethod(ARETURN);
if (needsThreadState) {
generateCallNoThreadState(callArgs);
}
}
private boolean hasDefault(int argIndex) {
return defaults.length - args.length + argIndex >= 0;
}
private String getDefault(int argIndex) {
return defaults[defaults.length - args.length + argIndex];
}
private void generateCall(int numDefaults) {
boolean needsThreadState = needsThreadState(args);
int requiredLength = args.length - numDefaults;
int offset = needsThreadState ? 1 : 0;
// We always have one used local for self, and possibly one for ThreadState
int usedLocals = 1 + offset;
Type[] callArgs = new Type[requiredLength];
if (needsThreadState) {
callArgs[0] = THREAD_STATE;
}
for(int i = offset; i < callArgs.length; i++) {
callArgs[i] = PYOBJ;
}
startMethod("__call__", PYOBJ, callArgs);
loadSelfAndThreadState();
// Push the passed in callArgs onto the stack, and convert them if necessary
int i;
for(i = offset; i < requiredLength; i++) {
mv.visitVarInsn(ALOAD, usedLocals++);
if(PRIMITIVES.containsKey(args[i])) {
callStatic(PY, "py2" + args[i].getClassName(), args[i], PYOBJ);
} else if(args[i].equals(STRING)) {
if(hasDefault(i) && getDefault(i).equals("null")) {
call(PYOBJ, "asStringOrNull", STRING);
} else {
call(PYOBJ, "asString", STRING);
}
}
}
// Push the defaults onto the stack
for(; i < args.length; i++) {
pushDefault(getDefault(i), args[i]);
}
makeCall();
toPy(returnType);
endMethod(ARETURN);
if (needsThreadState) {
generateCallNoThreadState(callArgs);
}
}
private void generateCallNoThreadState(Type[] callArgs) {
Type[] noThreadStateArgs = new Type[callArgs.length - 1];
System.arraycopy(callArgs, 1, noThreadStateArgs, 0, noThreadStateArgs.length);
startMethod("__call__", PYOBJ, noThreadStateArgs);
mv.visitVarInsn(ALOAD, 0);
callStatic(PY, "getThreadState", THREAD_STATE);
for (int i = 0; i < noThreadStateArgs.length; i++) {
mv.visitVarInsn(ALOAD, i + 1);
}
call(thisType, "__call__", PYOBJ, callArgs);
endMethod(ARETURN);
}
protected void loadSelfAndThreadState() {
// Push self on the stack so we can call it
get("self", PYOBJ);
checkSelf();
// Load ThreadState if necessary
loadThreadState();
}
protected void loadThreadState() {
if (needsThreadState(args)) {
mv.visitVarInsn(ALOAD, 1);
}
}
protected abstract void checkSelf();
protected abstract void makeCall();
private void pushDefault(String def, Type arg) {
if(def.equals("Py.None")) {
getStatic(PY, "None", PYOBJ);
} else if(def.equals("null")) {
mv.visitInsn(ACONST_NULL);
} else if(arg.equals(Type.LONG_TYPE)) {
// For primitive types, parse using the Java wrapper for that type and push onto the
// stack as a constant. If the default is malformed, a NumberFormatException will be
// raised.
mv.visitLdcInsn(Long.valueOf(def));
} else if(arg.equals(INT)) {
mv.visitLdcInsn(Integer.valueOf(def));
} else if(arg.equals(BYTE)) {
// byte, char, boolean and short go as int constants onto the stack, so convert them
// to ints to get the right type
mv.visitLdcInsn(Byte.valueOf(def).intValue());
} else if(arg.equals(SHORT)) {
mv.visitLdcInsn(Short.valueOf(def).intValue());
} else if(arg.equals(CHAR)) {
if(def.length() != 1) {
throwInvalid("A default for a char argument must be one character in length");
}
mv.visitLdcInsn(def.charAt(0));
} else if(arg.equals(BOOLEAN)) {
mv.visitLdcInsn(Boolean.valueOf(def) ? 1 : 0);
} else if(arg.equals(Type.FLOAT_TYPE)) {
mv.visitLdcInsn(Float.valueOf(def));
} else if(arg.equals(Type.DOUBLE_TYPE)) {
mv.visitLdcInsn(Double.valueOf(def));
}
}
protected static boolean needsThreadState(Type[] args) {
return args.length > 0 && args[0].equals(THREAD_STATE);
}
protected static boolean isWide(Type[] args) {
int offset = needsThreadState(args) ? 1 : 0;
return args.length == 2 + offset
&& args[offset].equals(APYOBJ) && args[offset + 1].equals(ASTRING);
}
}