org.python.expose.generate.DescriptorExposer 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 org.objectweb.asm.Type;
import org.python.core.PyDataDescr;
/**
* Generates a class to expose a descriptor on Python type. One of addMethodGetter or addFieldGetter
* must be called, and possibly one of addMethodSetter and addFieldSetter if this is a settable
* descriptor. If this is a deletable descriptor, addMethodDeleter may be called. There is no
* addFieldDeleter since there's no defined behavior to 'delete' a field.
*/
public class DescriptorExposer extends Exposer {
private Type onType, ofType;
private String name;
private String doc;
private String getterMethodName, getterFieldName, setterMethodName, setterFieldName,
deleterMethodName;
/**
* Creates an exposer that will work on type and have descrName
as its name in
* the type's dict.
*/
public DescriptorExposer(Type onType, String descrName) {
super(PyDataDescr.class, onType.getClassName() + "$" + descrName + "_descriptor", ASSUPER);
this.onType = onType;
name = descrName;
}
/**
* @return - the name this descriptor will be exposed as in its type's dict
*/
public String getName() {
return name;
}
public void addMethodGetter(String methodName, String desc) {
addMethodGetter(methodName, desc, null);
}
public void addMethodGetter(String methodName, String desc, String doc) {
if(hasGetter()) {
error("Descriptor can only have one getter");
}
if(Type.getArgumentTypes(desc).length > 0) {
error("Getter can't take arguments");
}
setOfType(Type.getReturnType(desc));
getterMethodName = methodName;
this.doc = doc;
}
public void addFieldGetter(String fieldName, Type fieldType) {
addFieldGetter(fieldName, fieldType, null);
}
public void addFieldGetter(String fieldName, Type fieldType, String doc) {
if(hasGetter()) {
error("Can only have one getter for a descriptor");
}
setOfType(fieldType);
getterFieldName = fieldName;
this.doc = doc;
}
public boolean hasGetter() {
return getterMethodName != null || getterFieldName != null;
}
private void setOfType(Type type) {
if(ofType == null) {
ofType = type;
} else if(!ofType.equals(type)) {
error("Types of the getter and setter must agree");
}
}
public void addMethodSetter(String methodName, String desc) {
if(hasSetter()) {
error("Descriptor can only have one setter");
}
Type[] args = Type.getArgumentTypes(desc);
if(args.length > 1) {
error("Setter can only take one argument");
}
setOfType(args[0]);
setterMethodName = methodName;
}
public void addFieldSetter(String fieldName, Type fieldType) {
if(hasSetter()) {
error("Descriptor can only have one setter");
}
setOfType(fieldType);
setterFieldName = fieldName;
}
public boolean hasSetter() {
return setterMethodName != null || setterFieldName != null;
}
public void addMethodDeleter(String methodName, String desc) {
deleterMethodName = methodName;
}
public String toString() {
return "DescriptorExposer[class=" + onType.getClassName() + ", name=" + name + "]";
}
@Override
protected void generate() {
if(!hasGetter()) {
error("A descriptor requires at least a get");
}
generateConstructor();
if(getterMethodName != null) {
generateMethodGetter();
} else {
generateFieldGetter();
}
generateImplement("Get", getterMethodName != null || getterFieldName != null);
if(setterMethodName != null) {
generateMethodSetter();
} else if(setterFieldName != null) {
generateFieldSetter();
}
generateImplement("Set", setterMethodName != null || setterFieldName != null);
if(deleterMethodName != null) {
generateMethodDeleter();
}
generateImplement("Delete", deleterMethodName != null);
}
private void generateConstructor() {
startConstructor();
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(name);
if(PRIMITIVES.containsKey(ofType)) {
mv.visitLdcInsn(PRIMITIVES.get(ofType));
} else {
mv.visitLdcInsn(ofType);
}
if (doc == null) {
mv.visitInsn(ACONST_NULL);
} else {
mv.visitLdcInsn(doc);
}
superConstructor(STRING, CLASS, STRING);
endConstructor();
}
private void generateMethodGetter() {
startMethod("invokeGet", OBJECT, PYOBJ);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, onType.getInternalName());
call(onType, getterMethodName, ofType);
if(PRIMITIVES.containsKey(ofType) || ofType.equals(STRING)) {
toPy(ofType);
}
endMethod(ARETURN);
}
private void generateFieldGetter() {
startMethod("invokeGet", OBJECT, PYOBJ);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, onType.getInternalName());
mv.visitFieldInsn(GETFIELD,
onType.getInternalName(),
getterFieldName,
ofType.getDescriptor());
if(PRIMITIVES.containsKey(ofType) || ofType.equals(STRING)) {
toPy(ofType);
}
endMethod(ARETURN);
}
private void generateMethodSetter() {
startMethod("invokeSet", VOID, PYOBJ, OBJECT);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, onType.getInternalName());
mv.visitVarInsn(ALOAD, 2);
if(PRIMITIVES.containsKey(ofType)) {
mv.visitTypeInsn(CHECKCAST, PRIMITIVES.get(ofType).getInternalName());
call(PRIMITIVES.get(ofType), ofType.getClassName() + "Value", ofType);
} else {
mv.visitTypeInsn(CHECKCAST, ofType.getInternalName());
}
call(onType, setterMethodName, VOID, ofType);
endMethod(RETURN);
}
private void generateFieldSetter() {
startMethod("invokeSet", VOID, PYOBJ, OBJECT);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, onType.getInternalName());
mv.visitVarInsn(ALOAD, 2);
if(PRIMITIVES.containsKey(ofType)) {
mv.visitTypeInsn(CHECKCAST, PRIMITIVES.get(ofType).getInternalName());
call(PRIMITIVES.get(ofType), ofType.getClassName() + "Value", ofType);
} else {
mv.visitTypeInsn(CHECKCAST, ofType.getInternalName());
}
mv.visitFieldInsn(PUTFIELD,
onType.getInternalName(),
setterFieldName,
ofType.getDescriptor());
endMethod(RETURN);
}
private void generateMethodDeleter() {
startMethod("invokeDelete", VOID, PYOBJ);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, onType.getInternalName());
call(onType, deleterMethodName, VOID);
endMethod(RETURN);
}
private void generateImplement(String setOrDelete, boolean implementsIt) {
startMethod("implementsDescr" + setOrDelete, BOOLEAN);
mv.visitInsn(implementsIt ? ICONST_1 : ICONST_0);
endMethod(IRETURN);
}
private void error(String reason) {
throw new InvalidExposingException(reason + "[class=" + onType.getClassName() + ", name="
+ name + "]");
}
}