def binaryLambda( objClass, method_name, argClass, interface=BiConsumer, interface_method="accept", classname=None, return_type="V", # can be a class too as_instance=True ): # return as an instance when True, as a class when False """ Define a interface<O, A> with an interface_method with body O.method_name(A). For example, a BiConsumer with an "accept" method that takes two arguments an has arg1.method_name(arg2) as body. """ if classname is None: classname = "asm/loop/%s_%s_%s_%s" % ( interface.getSimpleName(), objClass.getSimpleName(), method_name, argClass.getSimpleName()) cw = initClass(classname, class_parameters=[("O", objClass), ("A", argClass)], interfaces=[interface], interfaces_parameters={interface: ["O", "A"]}) mv = initMethod(cw, interface_method, argument_classes=[objClass, argClass], return_type=return_type) mv.visitCode() # implement Obj.method_name(Arg) mv.visitVarInsn(Opcodes.ALOAD, 1) # The first argument of the interface_method mv.visitVarInsn(Opcodes.ALOAD, 2) # The second argument of the interface_method # Use the first loaded object as the object onto which to invoke method_name # and the second loaded object as its argument, so Obj.method_name(Arg) mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, objClass.getName().replace(".", "/"), method_name, "(L%s;)V" % argClass.getName().replace(".", "/"), False) mv.visitInsn(Opcodes.RETURN) mv.visitMaxs(2, 3) mv.visitEnd() # The interface_method was from an interface, so add a bridge method that checks casts initMethodObj(cw, classname, interface_method, argument_classes=[objClass, argClass], return_type=return_type) cw.visitEnd() lambdaClass = CustomClassLoader().defineClass(classname, cw.toByteArray()) return lambdaClass.newInstance() if as_instance else lambdaClass
def defineBiConsumerTypeSet2(imglib2Type, classname=None, return_type="V"): # can be a class too """ Exactly the same as defineBiConsumerTypeSet but using lib.asm library functions to cut to the chase. """ if classname is None: classname = "asm/loop/BiConsumer_%s_set" % imglib2Type.getSimpleName() cw = initClass(classname, class_parameters=[("T", imglib2Type)], interfaces=[BiConsumer], interfaces_parameters={BiConsumer: ["T", "T"]}) mv = initMethod(cw, "accept", argument_classes=[imglib2Type, imglib2Type], return_type=return_type) mv.visitCode() # implement t2.set(t1) mv.visitVarInsn(Opcodes.ALOAD, 2) # load second argument first mv.visitVarInsn(Opcodes.ALOAD, 1) # then load the first argument # Use the first loaded object as the object onto which to invoke "set", and the second loaded object as its argument, so t2.set(t1) typeClassname = imglib2Type.getName().replace(".", "/") mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, typeClassname, "set", "(L%s;)V" % typeClassname, False) mv.visitInsn(Opcodes.RETURN) mv.visitMaxs(2, 3) mv.visitEnd() # The "accept" method was from an interface, so add a bridge method that checks casts initMethodObj(cw, classname, "accept", argument_classes=[imglib2Type, imglib2Type], return_type=return_type) cw.visitEnd() loader = CustomClassLoader() biconsumerClass = loader.defineClass(classname, cw.toByteArray()) return biconsumerClass
def nthLambda( objClass, method_name, argClasses, # a list, can be empty, max 14 elements interface, # the interface with at least one generic type for objClass interface_method, # with as many arguments as argClasses classname=None, return_type="V", # can be a class too as_instance=True ): # return as an instance when True, as a class when False """ Define an interface<O, A1, A2, ...> with an interface_method with body O.method_name(A1, A2, ...). For example, a BiConsumer with an "accept(O arg1, A1 arg2)" method that takes two arguments an has arg1.method_name(arg2) as body. objClass: the class of the object onto which to invoke the method of name method_name with arguments of class as in argClasses. method_name: the name of the method to invoke on the objClass instance passed as the first argument of the interface method. argClasses: list of classes for the arguments of the method of method_name in objClass. interface: the class of the interface to implement with as many generic parameters as 1 + len(argClasses), e.g., BiConsumer<O, A>. interface_method: the name of the method to implement for the interface, e.g., "accept" for BiConsumer. classname: the name of the class to create implementing the interface. Will be autogenerated when None. return_type: return type of the interface method; defaults to "V" (void), can also be a class. as_instance: when True (default), return a new instance of the newly created class, otherwise return the class. """ if classname is None: classname = "asm/loop/%s_%s_%s_%s" % ( interface.getSimpleName(), objClass.getSimpleName(), method_name, "_".join(argClass.getSimpleName() for argClass in argClasses)) if len(argClasses) > 14: print "ERROR: nthLambda can't have more han 13 argClasses." return None parameters = [("O", objClass)] + zip("ABCDEFGHIJKLMN", argClasses) cw = initClass(classname, class_parameters=parameters, interfaces=[interface], interfaces_parameters={ interface: [letter for letter, _ in parameters] }) mv = initMethod(cw, interface_method, argument_classes=[objClass] + argClasses, return_type=return_type) mv.visitCode() # implement Obj.method_name(Arg1, Arg2, ...) for i in xrange(1, 1 + len(argClasses) + 1): mv.visitVarInsn(Opcodes.ALOAD, i) # 1-based # Use the first loaded object as the object onto which to invoke method_name # and the second loaded object as its argument, so Obj.method_name(Arg) mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, objClass.getName().replace(".", "/"), method_name, "(%s)V" % "".join("L%s;" % argClass.getName().replace(".", "/") for argClass in argClasses), False) mv.visitInsn(Opcodes.RETURN) mv.visitMaxs(1 + len(argClasses), 2 + len(argClasses)) mv.visitEnd() # The interface_method was from an interface, so add a bridge method that checks casts initMethodObj(cw, classname, interface_method, argument_classes=[objClass] + argClasses, return_type=return_type) cw.visitEnd() lambdaClass = CustomClassLoader().defineClass(classname, cw.toByteArray()) return lambdaClass.newInstance() if as_instance else lambdaClass
c.visitVarInsn(Opcodes.ALOAD, 0) c.visitVarInsn(Opcodes.ALOAD, 1) field = { "classname": "my/UnsignedByteToFloatAccess", "name": "sampler", "descriptor": "Lnet/imglib2/Sampler;" } c.visitFieldInsn(Opcodes.PUTFIELD, field["classname"], field["name"], field["descriptor"]) c.visitInsn(Opcodes.RETURN) c.visitMaxs(2, 2) c.visitEnd() # Declare getValue and setValue methods gv = initMethod(facc, "getValue", access=Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, descriptor="(I)F") gv.visitVarInsn(Opcodes.ALOAD, 0) gv.visitFieldInsn(Opcodes.GETFIELD, field["classname"], field["name"], field["descriptor"]) gv.visitMethodInsn( Opcodes.INVOKEINTERFACE, Type.getInternalName(Sampler), "get", "()L%s;" % Type.getInternalName(Object), # isn't this weird? Why Object? True) gv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(UnsignedByteType)) gv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(UnsignedByteType), "getRealFloat", "()F", False) gv.visitInsn(Opcodes.FRETURN) # native float return
def defineSamplerConverter( fromType, toType, classname="", toAccess=None, fromMethod="getRealFloat", fromMethodReturnType="F", # F: native float; if a class, use: "L%s;" % Type.getInternalName(TheClass) toMethod="setReal", toMethodArgType="F"): # F: native float """ A class implementing SamplerConverter, in asm for high-performance (25x jython's speed). fromType: the source type to convert like e.g. UnsignedByteType. toType: the target type, like e.g. FloatType. classname: optional, the fully qualified name for the new class implementing SamplerConverter. fromMethod: the name of the fromType class method to use for getting its value. Defaults to "getRealFloat" from the RealType interface. fromMethodReturnType: a single letter, like: 'F': float, 'D': double, 'C': char, 'B': byte, 'Z': boolean, 'S': short, 'I': integer, 'J': long See: https://gitlab.ow2.org/asm/asm/blob/master/asm/src/main/java/org/objectweb/asm/Frame.java toMethod: the name of the toType class method for setting the value. Defaults to "setReal" from the RealType interface. toMethodArgType: a single letter, like: 'F': float, 'D': double, 'C': char, 'B': byte, 'Z': boolean, 'S': short, 'I': integer, 'J': long toAccess: the interface to implement, such as FloatAccess. Optional, will be guessed. """ if toAccess is None: toTypeName = toType.getSimpleName() name = toTypeName[0:toTypeName.rfind("Type")] if name.startswith("Unsigned"): name = name[8:] toAccessName = "net.imglib2.img.basictypeaccess.%sAccess" % name toAccess = CustomClassLoader().loadClass( "net.imglib2.img.basictypeaccess.%sAccess" % name) if "" == classname: classname = "asm/converters/%sTo%sSamplerConverter" % ( fromType.getSimpleName(), toType.getSimpleName()) access_classname = "asm/converters/%sTo%sAccess" % ( fromType.getSimpleName(), toType.getSimpleName()) # First *Access class like e.g. FloatAccess facc = initClass(access_classname, access=Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, interfaces=[toAccess], interfaces_parameters={}, with_default_constructor=False) # private final "sampler" field f = facc.visitField( Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, "sampler", "L%s;" % Type.getInternalName(Sampler), "L%s<+L%s;>;" % tuple(imap(Type.getInternalName, (Sampler, fromType))), None) # The constructor has to initialize the field "sampler" c = initConstructor(facc, descriptor="(Lnet/imglib2/Sampler;)V", signature="(Lnet/imglib2/Sampler<+L%s;>;)V" % Type.getInternalName(fromType)) # The 'c' constructor already invoked <init> # Now onto the rest of the constructor body: c.visitVarInsn(Opcodes.ALOAD, 0) c.visitVarInsn(Opcodes.ALOAD, 1) field = { "classname": access_classname, "name": "sampler", "descriptor": "Lnet/imglib2/Sampler;" } c.visitFieldInsn(Opcodes.PUTFIELD, field["classname"], field["name"], field["descriptor"]) c.visitInsn(Opcodes.RETURN) c.visitMaxs(2, 2) c.visitEnd() # Declare getValue and setValue methods gv = initMethod(facc, "getValue", access=Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, descriptor="(I)%s" % toMethodArgType) # e.g. "F" for native float gv.visitVarInsn(Opcodes.ALOAD, 0) gv.visitFieldInsn(Opcodes.GETFIELD, field["classname"], field["name"], field["descriptor"]) gv.visitMethodInsn( Opcodes.INVOKEINTERFACE, Type.getInternalName(Sampler), "get", "()L%s;" % Type.getInternalName(Object), # isn't this weird? Why Object? True) gv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(fromType)) print Type.getInternalName(fromType), fromMethod, fromMethodReturnType gv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName(fromType), fromMethod, # e.g. getRealFloat "()%s" % fromMethodReturnType, # e.g. F for native float False) # Figure out the return and the loading instructions: primitive or object class # (NOTE: will not work for array, that starts with '[') if fromMethodReturnType in ["F", "D"]: # 'F' for float, 'D' for double ret = fromMethodReturnType + "RETURN" load = fromMethodReturnType + "LOAD" elif 'J' == fromMethodReturnType: # 'J' is for long ret = "LRETURN" load = "LLOAD" elif fromMethodReturnType in [ "S", "I", "B", "C", "Z" ]: # 'C': char, 'B': byte, 'Z', boolean, 'S', short, 'I': integer ret = "IRETURN" load = "ILOAD" else: ret = "ARETURN" # object class load = "ALOAD" gv.visitInsn(Opcodes.__getattribute__( Opcodes, ret)._doget(Opcodes)) # Opcodes.FRETURN: native float return gv.visitMaxs(1, 2) gv.visitEnd() sv = initMethod(facc, "setValue", access=Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, descriptor="(I%s)V" % toMethodArgType) # e.g. "F" for native float sv.visitVarInsn(Opcodes.ALOAD, 0) sv.visitFieldInsn(Opcodes.GETFIELD, field["classname"], field["name"], field["descriptor"]) sv.visitMethodInsn( Opcodes.INVOKEINTERFACE, Type.getInternalName(Sampler), "get", "()L%s;" % Type.getInternalName(Object), # isn't this weird? Why Object? True) sv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(fromType)) sv.visitVarInsn( Opcodes.__getattribute__(Opcodes, load)._doget(Opcodes), 2) # e.g. Opcodes.FLOAD sv.visitMethodInsn( Opcodes.INVOKEVIRTUAL, Type.getInternalName(fromType), toMethod, # e.g. setReal "(%s)V" % toMethodArgType, # e.g. 'F' for native float False) sv.visitInsn(Opcodes.RETURN) sv.visitMaxs(2, 3) sv.visitEnd() # The SamplerConverter outer class cw = initClass( classname, interfaces=[SamplerConverter], interfaces_parameters={SamplerConverter: [fromType, toType]}) # In the signature, the + sign is for e.g. <? extends UnignedByteType> # Here, the signature is the same as the descriptor, but with parameter types # descriptor="(Lnet/imglib2/Sampler;)Lnet/imglib2/type/numeric/real/FloatType;" # signature="(Lnet/imglib2/Sampler<+Lnet/imglib2/type/numeric/integer/UnsignedByteType;>;)Lnet/imglib2/type/numeric/real/FloatType;", m = initMethod( cw, "convert", argument_classes=[Sampler], argument_parameters=[fromType], argument_prefixes=['+'], # '+' means: <? extends UnsignedByteType> return_type=toType) m.visitCode() m.visitTypeInsn(Opcodes.NEW, Type.getInternalName(toType)) m.visitInsn(Opcodes.DUP) m.visitTypeInsn(Opcodes.NEW, access_classname) m.visitInsn(Opcodes.DUP) m.visitVarInsn(Opcodes.ALOAD, 1) m.visitMethodInsn( Opcodes.INVOKESPECIAL, # invoke new access_classname, "<init>", # constructor "(L%s;)V" % Type.getInternalName(Sampler), False) m.visitMethodInsn( Opcodes. INVOKESPECIAL, # create new toType with the *Access as argument Type.getInternalName(toType), "<init>", # constructor "(L%s;)V" % Type.getInternalName(toAccess), False) m.visitInsn( Opcodes.ARETURN) # ARETURN: return the object at the top of the stack m.visitMaxs( 5, 2 ) # 5 stack slots: the two NEW calls, 1 ALOAD, 2 DUP (I think). And 2 local variables: this, and a method argument. m.visitEnd() # If bridge is not defined, the above 'convert' method cannot be invoked: would fail with AbstractMethodException # To be fair, the TextWriter on the compiled java version of this class did use the bridge. # The surprising bit is that, in test_asm_class_generation.py, the bridge is not necessary # even though the class is rather similar overall. bridge = cw.visitMethod( Opcodes.ACC_PUBLIC | Opcodes.ACC_VOLATILE | Opcodes.ACC_BRIDGE, "convert", "(L%s;)L%s;" % tuple(imap(Type.getInternalName, (Sampler, Object))), None, None) bridge.visitCode() bridge.visitVarInsn(Opcodes.ALOAD, 0) bridge.visitVarInsn(Opcodes.ALOAD, 1) bridge.visitMethodInsn( Opcodes.INVOKEVIRTUAL, classname, "convert", "(L%s;)L%s;" % tuple(imap(Type.getInternalName, (Sampler, toType))), # descriptor False) bridge.visitInsn(Opcodes.ARETURN) bridge.visitMaxs(2, 2) bridge.visitEnd() # Load both classes loader = CustomClassLoader() accessClass = loader.defineClass(access_classname, facc.toByteArray()) samplerClass = loader.defineClass(classname, cw.toByteArray()) return samplerClass