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 <code>descrName</code> 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 + "]"); } }