All Downloads are FREE. Search and download functionalities are using the official Maven repository.

kilim.analysis.SAMweaver Maven / Gradle / Ivy

Go to download

Coroutines, continuations, fibers, actors and message passing for the JVM

There is a newer version: 2.0.2-jdk7
Show newest version
// copyright 2016 nqzero, 2014 sriram srinivasan - offered under the terms of the MIT License
package kilim.analysis;

import kilim.Constants;
import kilim.mirrors.CachedClassMirrors.ClassMirror;
import kilim.mirrors.CachedClassMirrors.MethodMirror;
import kilim.mirrors.ClassMirrorNotFoundException;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;

/**
 * {@code SAMweaver} generates code to support functional interfaces (also known
 * as SAM, for Single Abstract Method), where the SAM method is pausable. What
 * makes SAM interfaces special compared to regular interfaces is that they can
 * represent a lambda function in java8. More on this later.
 * 
 * Imagine that a class called X uses a I of the
 * following form:
 * 
 * 
 * interface I {
 *     int foo(double d) throws Pausable;
 * }
 * 
* * Since I is a SAM, {@code CallWeaver} replaces all * invokeinterface I.foo(double) with a static invocation of a tiny * wrapper method ('shim') in X like this: * *
 *      invokestatic X.$shim$2(I callee, double d, Fiber f)
 * 
* * The shim method in turn turns around and calls I.foo(double): * *
 *     private static int X.$shim$2(I callee, double d, Fiber f) {
 *        int ret = callee.f(d, f);
 *        f.setCallee(callee); // this is the purpose of the shim
 *        return ret;
 *     }
 * 
* * The purpose of {@code SAMweaver} is to generate the shim above. * *

Why?

*

* Ordinarily, all hand-written code is modified by the weaver if it contains * definitions or invocations of pausable methods. Lambda expressions however * rely on the VM generating a class at run-time, which implements the * functional interface (I in the example). The problem is that * this class needs to be woven to support kilim Fibers, but we don't have an * easy portable hook to weave it at run-time. *

* As it turns out, practically all the weaving work is already complete at * compile time. This is because, the body of the lambda expression is already * available to the weaver as an ordinary method in the host class * X, and is treated like any another pausable method. In other * words, the transformations at the calling site and in the body of the called * method are as usual. * * All that this is left for the shim to do is to capture the object's reference * (f.setCallee); the fiber needs it while resuming. The call to * setCallee is redundant for ordinary hand-written implementations of SAM * interfaces, but is necessary if the implementation happens to be generated by * the VM as described above. * *

* Of course, all this applies only if the functional method is pausable. */ public class SAMweaver implements Constants { String interfaceName; String methodName; String desc; boolean itf; int index = -1; KilimContext context; public SAMweaver(KilimContext context,String interfaceName, String methodName, String desc, boolean itf) { this.interfaceName = interfaceName; this.methodName = methodName; this.desc = desc; this.itf = itf; this.context = context; } public void setIndex(int index) { this.index = index; } public boolean equals(Object obj) { if (obj instanceof SAMweaver) { SAMweaver that = (SAMweaver) obj; return desc.equals(that.desc) && methodName.equals(that.methodName) && interfaceName.equals(that.interfaceName); } return false; } public String toString() { return interfaceName+"."+methodName+desc + " ->" +getShimMethodName(); } public int hashCode() { return methodName.hashCode() ^ desc.hashCode(); } String getShimMethodName() { assert index >= 0; return SAM_SHIM_PREFIX + index; } String getShimDesc() { String shim = this.desc.replace("(", "(" + TypeDesc.getInterned(this.interfaceName)); return shim.contains(Constants.D_FIBER_LAST_ARG) ? shim : shim.replace(")", Constants.D_FIBER_LAST_ARG); } /** * Generate a method like this: * *

     * private static $shim$1 (I callee, ...args..., f fiber) {
     *    load each arg
     *    call interface.method
     *    f.setCallee(arg0)
     *    xret
     * }
     * 
* * @param cv */ public void accept(ClassVisitor cv) { String shimDesc = getShimDesc(); MethodVisitor mv = cv.visitMethod(ACC_STATIC | ACC_PRIVATE, getShimMethodName(), shimDesc, null, getExceptions()); // load arguments int ivar = 0; for (String argType : TypeDesc.getArgumentTypes(shimDesc)) { int vmt = VMType.toVmType(argType); mv.visitVarInsn(VMType.loadInsn[vmt], ivar); ivar += VMType.category[vmt]; // register width = 2 if double or // long, 1 otherwise } int fiberVar = ivar - 1; // invoke interface String fiberDesc = desc.replace(")", Constants.D_FIBER + ")"); mv.visitMethodInsn(INVOKEINTERFACE, interfaceName, methodName, fiberDesc, true); // store callee object reference in fiber mv.visitVarInsn(ALOAD, fiberVar); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, FIBER_CLASS, "setCallee", "(" + D_OBJECT + ")V", false); // return .. RETURN (if void) or ARETURN, IRETURN, etc. String retDesc = TypeDesc.getReturnTypeDesc(shimDesc); if (retDesc.charAt(0) == 'V') { mv.visitInsn(RETURN); } else { int vmt = VMType.toVmType(retDesc); mv.visitInsn(VMType.retInsn[vmt]); } mv.visitMaxs(0, 0); // maxLocals and maxStackDepth will be computed by asm's MethodWriter mv.visitEnd(); } static String [] internalName(String [] words) { if (words==null) return words; String [] mod = new String[words.length]; for (int ii = 0; ii < mod.length; ii++) mod[ii] = words[ii].replace('.','/'); return mod; } private String[] getExceptions() { try { ClassMirror cm = context.detector.classForName(interfaceName); for (MethodMirror m : cm.getDeclaredMethods()) { if (m.getName().equals(this.methodName) && m.getMethodDescriptor().equals(this.desc)) { // Convert dots to slashes. String[] ret = m.getExceptionTypes(); if (ret != null) return internalName(ret); break; } } } catch (ClassMirrorNotFoundException cmnfe) { } // Should not happen at this stage. If this class weren't found, it // would have created a // problem much earlier on. assert false : "Class Mirror not found for interface " + interfaceName; return new String[0]; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy