当前位置: 代码迷 >> JavaScript >> JSR 292 的用法 (MethodHandle.)
  详细解决方案

JSR 292 的用法 (MethodHandle.)

热度:425   发布时间:2012-12-21 12:03:49.0
JSR 292 的用法 (MethodHandle...)
用例转自:http://code.google.com/p/jsr292-cookbook/


Constants
?lazy initialization

package jsr292.cookbook.lazyinit;

import java.io.IOException;
import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.nio.ByteBuffer;

import sun.misc.BASE64Decoder;

public class RT {
  @SuppressWarnings("restriction")
  public static CallSite bootstrap(Lookup lookup, String name, MethodType type, String base64Array) throws IOException {
    BASE64Decoder decoder = new BASE64Decoder();
    byte[] bytes = decoder.decodeBuffer(base64Array);
    
    Class<?> returnType = type.returnType();
    Object value = convertAsArray(bytes, returnType);
    return new ConstantCallSite(MethodHandles.constant(returnType, value));
  }
  
  private static Object convertAsArray(byte[] bytes, Class<?> returnType) {
    if (returnType == byte[].class) {
      return bytes;
    } else
      if (returnType == char[].class) {
        char[] array = new char[bytes.length / 2];
        ByteBuffer.wrap(bytes).asCharBuffer().get(array);
        return array;
      } else
        if (returnType == short[].class) {
          return ByteBuffer.wrap(bytes).asShortBuffer().duplicate().array();
        } else
          if (returnType == int[].class) {
            return ByteBuffer.wrap(bytes).asIntBuffer().duplicate().array();
          } else
            if (returnType == long[].class) {
              return ByteBuffer.wrap(bytes).asLongBuffer().duplicate().array();
            } else
              if (returnType == float[].class) {
                return ByteBuffer.wrap(bytes).asFloatBuffer().duplicate().array();
              } else
                if (returnType == double[].class) {
                  return ByteBuffer.wrap(bytes).asDoubleBuffer().duplicate().array();
                } else {
                  throw new BootstrapMethodError("invalid constant array type "+returnType);
                }
  }
}

Method handle interceptors
?before, after
?try/finally

package jsr292.cookbook.interceptors;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
//import java.util.Map;

public class Interceptors {
  
  public static MethodHandle before(MethodHandle target, MethodHandle before) {
    MethodType beforeType = before.type();
    if (beforeType.returnType() != void.class) {
      throw new IllegalArgumentException("before must return void "+beforeType);
    }
    MethodType targetType = target.type();
    if (beforeType.parameterCount() != targetType.parameterCount()) {
      if (beforeType.parameterCount() > targetType.parameterCount()) {
        throw new IllegalArgumentException("before has too much parameter compare to target "+beforeType+" "+targetType);
      }
      before = MethodHandles.dropArguments(before,
          beforeType.parameterCount(),
          targetType.parameterList().subList(beforeType.parameterCount(), targetType.parameterCount()));  
    }
    
    if (!before.type().equals(targetType.changeReturnType(void.class))) {
      throw new IllegalArgumentException("before parameter types are not compatible with target "+beforeType+" "+targetType);
    }
    
    return MethodHandles.foldArguments(target, before);
  }
  
  public static MethodHandle after(MethodHandle target, MethodHandle after) {
    //FIXME just use filterReturnValue instead !!, when bug fixed
    
    MethodType afterType = after.type();
    if (afterType.returnType() != void.class) {
      throw new IllegalArgumentException("after must return void "+afterType);
    }
    
    MethodType targetType = target.type();
    boolean voidReturnType = targetType.returnType() == void.class;
    MethodType idealAfterType = (voidReturnType)? targetType:
        MethodType.methodType(void.class, targetType.returnType()).
            appendParameterTypes(targetType.parameterList());
    if (afterType.parameterCount() != idealAfterType.parameterCount()) {
      if (afterType.parameterCount() > idealAfterType.parameterCount()) {
        throw new IllegalArgumentException("after has too much parameter compare to return value + target "+afterType+" "+idealAfterType);
      }
      
      after = MethodHandles.dropArguments(after,
          afterType.parameterCount(),
          idealAfterType.parameterList().subList(afterType.parameterCount(), idealAfterType.parameterCount()));  
    }
    
    if (!after.type().equals(idealAfterType)) {
      throw new IllegalArgumentException("after parameter types are not compatible with return value + target "+afterType+" "+idealAfterType);
    }
  
    if (!voidReturnType) {
      MethodHandle identity = MethodHandles.identity(targetType.returnType());
      identity = MethodHandles.dropArguments(identity, 1, target.type().parameterList());
      after = before(identity, after);
    }  
    
    return MethodHandles.foldArguments(after, target);
  }
  
  public static MethodHandle tryFinally(MethodHandle target, MethodHandle finallyMH) {
    MethodType finallyType = finallyMH.type();
    if (finallyType.returnType() != void.class) {
      throw new IllegalArgumentException("finally block must return void");
    }
    
    MethodType targetType = target.type();
    if (finallyType.parameterCount() != targetType.parameterCount()) {
      if (finallyType.parameterCount() > targetType.parameterCount()) {
        throw new IllegalArgumentException("finally has too much parameter compare to target "+finallyType+" "+targetType);
      }
      finallyMH = MethodHandles.dropArguments(finallyMH,
          finallyType.parameterCount(),
          targetType.parameterList().subList(finallyType.parameterCount(), targetType.parameterCount()));  
    }

    if (!finallyMH.type().equals(targetType.changeReturnType(void.class))) {
      throw new IllegalArgumentException("finally parameter types are not compatible with target "+finallyType+" "+targetType);
    }
    
    MethodHandle rethrow = MethodHandles.throwException(targetType.returnType(), Throwable.class);
    rethrow = MethodHandles.dropArguments(rethrow, 1, targetType.parameterArray());
    MethodHandle finallyInCatch = MethodHandles.dropArguments(finallyMH, 0, Throwable.class);
    MethodHandle catchMH = MethodHandles.foldArguments(rethrow, finallyInCatch);
    
    if (targetType.returnType() != void.class) {
      MethodHandle identity = MethodHandles.identity(targetType.returnType());
      identity = MethodHandles.dropArguments(identity, 1, target.type().parameterList());
      finallyMH = MethodHandles.foldArguments(identity, finallyMH);
    }
    
    MethodHandle tryCatch = MethodHandles.catchException(target, Throwable.class, catchMH);
    return MethodHandles.foldArguments(finallyMH, tryCatch);
  }
}

Single-dispatch (one receiver)
?inlining caches
  ?cascaded inlining cache
  ?bimorphic inlining cache

package jsr292.cookbook.icache;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;

public class RT {
  static class InliningCacheCallSite extends MutableCallSite {
    private static final int MAX_DEPTH = 3;
    
    final Lookup lookup;
    final String name;
    int depth;

    InliningCacheCallSite(Lookup lookup, String name, MethodType type) {
      super(type);
      this.lookup = lookup;
      this.name = name;
    }
  }
  
  public static CallSite bootstrap(Lookup lookup, String name, MethodType type) {
    InliningCacheCallSite callSite = new InliningCacheCallSite(lookup, name, type);
    
    MethodHandle fallback = FALLBACK.bindTo(callSite);
    fallback = fallback.asCollector(Object[].class, type.parameterCount());
    fallback = fallback.asType(type);
    
    callSite.setTarget(fallback);
    return callSite;
  }
  
  public static boolean checkClass(Class<?> clazz, Object receiver) {
    return receiver.getClass() == clazz;
  }
  
  public static Object fallback(InliningCacheCallSite callSite, Object[] args) throws Throwable {
    MethodType type = callSite.type();
    if (callSite.depth >= InliningCacheCallSite.MAX_DEPTH) {
      // revert to a vtable call
      MethodHandle target = callSite.lookup.findVirtual(type.parameterType(0), callSite.name,
          type.dropParameterTypes(0, 1));
      callSite.setTarget(target);
      return target.invokeWithArguments(args);
    }
    
    Object receiver = args[0];
    Class<?> receiverClass = receiver.getClass();
    MethodHandle target = callSite.lookup.findVirtual(receiverClass, callSite.name,
        type.dropParameterTypes(0, 1));
    target = target.asType(type);
    
    MethodHandle test = CHECK_CLASS.bindTo(receiverClass);
    test = test.asType(test.type().changeParameterType(0, type.parameterType(0)));
    
    MethodHandle guard = MethodHandles.guardWithTest(test, target, callSite.getTarget());
    callSite.depth++;
    
    callSite.setTarget(guard);
    return target.invokeWithArguments(args);
  }
  
  private static final MethodHandle CHECK_CLASS;
  private static final MethodHandle FALLBACK;
  static {
    Lookup lookup = MethodHandles.lookup();
    try {
      CHECK_CLASS = lookup.findStatic(RT.class, "checkClass",
          MethodType.methodType(boolean.class, Class.class, Object.class));
      FALLBACK = lookup.findStatic(RT.class, "fallback",
          MethodType.methodType(Object.class, InliningCacheCallSite.class, Object[].class));
    } catch (ReflectiveOperationException e) {
      throw (AssertionError)new AssertionError().initCause(e);
    }
  }
}


package jsr292.cookbook.bicache;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;

public class RT {
  static class BimorphicCacheCallSite extends MutableCallSite {
    final Lookup lookup;
    final String name;
    
    private Class<?> class1;
    private MethodHandle mh1;
    private Class<?> class2;
    private MethodHandle mh2;

    BimorphicCacheCallSite(Lookup lookup, String name, MethodType type) {
      super(type);
      this.lookup = lookup;
      this.name = name;
    }
    
    public synchronized Object fallback(Object[] args) throws Throwable {
      final MethodType type = type();
      if (class1 != null && class2 != null) {
        // bimorphic cache defeated, use a dispatch table instead
        DispatchMap dispatchMap = new DispatchMap() {
          @Override
          protected MethodHandle findMethodHandle(Class<?> receiverClass) throws Throwable {
            MethodHandle target = lookup.findVirtual(receiverClass, name,
                type.dropParameterTypes(0, 1));
            return target.asType(type);
          }
        };
        dispatchMap.populate(class1, mh1, class1, mh2);   // pre-populated with known couples
        class1 = class2 = null;
        mh1 = mh2 = null;
        
        MethodHandle lookupMH = MethodHandles.filterReturnValue(GET_CLASS, LOOKUP_MH.bindTo(dispatchMap));
        lookupMH = lookupMH.asType(MethodType.methodType(MethodHandle.class, type.parameterType(0)));
        MethodHandle target = MethodHandles.foldArguments(MethodHandles.exactInvoker(type), lookupMH);
        setTarget(target);
        return target.invokeWithArguments(args);
      }
      
      Object receiver = args[0];
      Class<?> receiverClass = receiver.getClass();
      MethodHandle target = lookup.findVirtual(receiverClass, name,
          type.dropParameterTypes(0, 1));
      target = target.asType(type);
      
      MethodHandle test = CHECK_CLASS.bindTo(receiverClass);
      test = test.asType(test.type().changeParameterType(0, type.parameterType(0)));
      
      MethodHandle guard = MethodHandles.guardWithTest(test, target, getTarget());
      if (class1 == null) {
        class1 = receiverClass;
        mh1 = target;
      } else {
        class2 = receiverClass;
        mh2 = target;
      }
      
      setTarget(guard);
      return target.invokeWithArguments(args);
    }
  }
  
  public static CallSite bootstrap(Lookup lookup, String name, MethodType type) {
    BimorphicCacheCallSite callSite = new BimorphicCacheCallSite(lookup, name, type);
    
    MethodHandle fallback = FALLBACK.bindTo(callSite);
    fallback = fallback.asCollector(Object[].class, type.parameterCount());
    fallback = fallback.asType(type);
    
    callSite.setTarget(fallback);
    return callSite;
  }
  
  public static boolean checkClass(Class<?> clazz, Object receiver) {
    return receiver.getClass() == clazz;
  }
  
  static final MethodHandle CHECK_CLASS;
  private static final MethodHandle FALLBACK;
  static final MethodHandle GET_CLASS;
  static final MethodHandle LOOKUP_MH;
  static {
    Lookup lookup = MethodHandles.lookup();
    try {
      CHECK_CLASS = lookup.findStatic(RT.class, "checkClass",
          MethodType.methodType(boolean.class, Class.class, Object.class));
      FALLBACK = lookup.findVirtual(BimorphicCacheCallSite.class, "fallback",
          MethodType.methodType(Object.class, Object[].class));
      GET_CLASS = lookup.findVirtual(Object.class, "getClass",
          MethodType.methodType(Class.class));
      LOOKUP_MH =   lookup.findVirtual(DispatchMap.class, "lookup",
          MethodType.methodType(MethodHandle.class, Class.class));
    } catch (ReflectiveOperationException e) {
      throw (AssertionError)new AssertionError().initCause(e);
    }
  }
}

Double dispatch
?visitor
  ?visitor

package jsr292.cookbook.visitor;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;

public class RT {
  public static CallSite bootstrap(Lookup lookup, String name, MethodType type) {
    MutableCallSite callSite = new MutableCallSite(type);
    MethodHandle boot = BOOT.bindTo(callSite);
    boot = boot.asCollector(Object[].class, type.parameterCount());
    boot = boot.asType(type);
    callSite.setTarget(boot);
    return callSite;
    
    //return new ConstantCallSite(dispatcher(type));
  }
  
  private static MethodHandle dispatcher(MethodType type) {
    MethodHandle combiner = MethodHandles.dropArguments(GET_HANDLE, 2, type.parameterList().subList(2, type.parameterCount()));
    combiner = combiner.asType(combiner.type().changeParameterType(0, type.parameterType(0)));
    
    MethodHandle invoker = MethodHandles.exactInvoker(type.changeParameterType(1, Object.class));
    
    MethodHandle target = MethodHandles.foldArguments(invoker, combiner);
    target = target.asType(type);
    return target;
  }
  
  public static Object boot(MutableCallSite callSite, Object[] args) throws Throwable {
    AbstractVisitor visitor = (AbstractVisitor)args[0];
    MethodType type = callSite.type();
    MethodHandle visit = visitor.findHandle(type.parameterType(1));
    if (visit == null) {
      /* no static visit, fallback to dispatcher
      MethodHandle target = dispatcher(type);
      callSite.setTarget(target);
      return target.invokeWithArguments(args);*/
            
      MutableCallSite inliningCache = new MutableCallSite(type);
      MethodHandle fallback = FALLBACK.bindTo(inliningCache);
      fallback = fallback.asCollector(Object[].class, type.parameterCount());
      fallback = fallback.asType(type);
      inliningCache.setTarget(fallback);
      visit = inliningCache.dynamicInvoker();
    }
    
    Class<?> visitorClass = visitor.getClass();
    MethodHandle test = CHECK_CLASS.bindTo(visitorClass);
    test = test.asType(MethodType.methodType(boolean.class, type.parameterType(0)));
    
    MethodHandle deopt = DEOPT.bindTo(callSite);
    deopt = deopt.asCollector(Object[].class, type.parameterCount());
    deopt = deopt.asType(type);
    
    visit = visit.asType(type);
    
    MethodHandle guard = MethodHandles.guardWithTest(test, visit, deopt);
    callSite.setTarget(guard);
    return guard.invokeWithArguments(args);
  }
  
  public static boolean checkClass(Class<?> clazz, Object receiver) {
    return receiver.getClass() == clazz;
  }
  
  public static Object deopt(MutableCallSite callSite, Object[] args) throws Throwable {
    // fallback to dispatcher 
    MethodHandle target = dispatcher(callSite.type());
    callSite.setTarget(target);
    return target.invokeWithArguments(args);
  }
  
  public static Object fallback(MutableCallSite callSite, Object[] args) throws Throwable {
    AbstractVisitor visitor = (AbstractVisitor) args[0];
    Object value = args[1];
    
    MethodType type = callSite.type();
    MethodHandle target = visitor.getHandle(value);
    target = target.asType(type);
    
    MethodHandle test = CHECK_CLASS.bindTo(value.getClass());
    test = MethodHandles.dropArguments(test, 0, type.parameterType(0));
    test = test.asType(test.type().changeParameterType(1, type.parameterType(1)));
    
    MethodHandle guard = MethodHandles.guardWithTest(test, target, callSite.getTarget());
    callSite.setTarget(guard);
    return target.invokeWithArguments(args);
  }
  
  private static final MethodHandle GET_HANDLE;
  private static final MethodHandle BOOT;
  private static final MethodHandle CHECK_CLASS;
  private static final MethodHandle DEOPT;
  private static final MethodHandle FALLBACK;
  static {
    Lookup lookup = MethodHandles.lookup();
    try {
      GET_HANDLE = lookup.findVirtual(AbstractVisitor.class, "getHandle",
          MethodType.methodType(MethodHandle.class, Object.class));
      BOOT = lookup.findStatic(RT.class, "boot",
          MethodType.methodType(Object.class, MutableCallSite.class, Object[].class));
      CHECK_CLASS = lookup.findStatic(RT.class, "checkClass",
          MethodType.methodType(boolean.class, Class.class, Object.class));
      DEOPT = lookup.findStatic(RT.class, "deopt",
          MethodType.methodType(Object.class, MutableCallSite.class, Object[].class));
      FALLBACK = lookup.findStatic(RT.class, "fallback",
          MethodType.methodType(Object.class, MutableCallSite.class, Object[].class));
    } catch (ReflectiveOperationException e) {
      throw (AssertionError)new AssertionError().initCause(e);
    }
  }
}

Multi-dispatch (several receivers)
?multi-dispatch (bitset based)
  ?multi-dispatch

package jsr292.cookbook.mdispatch;

import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;

public class RT {
  /*
   * The values of the returned hashmap are or an array of method handles
   * or a metadata selector. 
   */
  private static final ClassValue<HashMap<Selector,Object>> SELECTOR_MAP_VALUE =
    new ClassValue<HashMap<Selector,Object>>() {
      @Override
      protected HashMap<Selector, Object> computeValue(Class<?> type) {
        Lookup lookup = MethodHandles.publicLookup();
        HashMap<Selector, ArrayList<MethodHandle>> map = new HashMap<>();
        for(Method method: type.getMethods()) {
          if (method.isBridge()) {
            continue;  // skip bridge
          }
          
          boolean isStatic = Modifier.isStatic(method.getModifiers());
          Selector selector = new Selector(method.getName(), method.getParameterTypes().length +
              (isStatic?0: 1));
          ArrayList<MethodHandle> list = map.get(selector);
          if (list == null) {
            list = new ArrayList<>();
            map.put(selector, list);
          }
          try {
            method.setAccessible(true);
            MethodHandle mh = lookup.unreflect(method);
            if (!isStatic) {
              // adapt the receiver type to be the one of current class
              mh = mh.asType(mh.type().changeParameterType(0, type));
            }
            list.add(mh);
          } catch (IllegalAccessException e) {
            throw (LinkageError)new LinkageError().initCause(e);
          }
        }
        
        HashMap<Selector, Object> selectorMap = new HashMap<>();
        for(Entry<Selector, ArrayList<MethodHandle>> entry: map.entrySet()) {
          // only store the method handles, create the metadata later when needed
          ArrayList<MethodHandle> mhs = entry.getValue();
          selectorMap.put(entry.getKey(), mhs.toArray(new MethodHandle[mhs.size()]));
        }
        
        return selectorMap;
      }
    };
    
  static class Selector {
    private final String name;
    private final int parameterCount;
    
    public Selector(String name, int parameterCount) {
      this.name = name;
      this.parameterCount = parameterCount;
    }
    
    @Override
    public int hashCode() {
      return name.hashCode() + parameterCount;
    }
    
    @Override
    public boolean equals(Object obj) {
      if (obj == this) {
        return true;
      }
      if (!(obj instanceof Selector)) {
        return false;
      }
      Selector selector = (Selector)obj;
      return parameterCount == selector.parameterCount &&
             name.equals(selector.name);
    }
    
    @Override
    public String toString() {
      return name+'/'+parameterCount;
    }
  }
    
  static MethodHandle getMultiDispatchTarget(Lookup lookup, String name, MethodType type, Class<?> dispatchType) {
    try {
      Selector selector = new Selector(name, type.parameterCount());
      HashMap<Selector, Object> selectorMap = SELECTOR_MAP_VALUE.get(dispatchType);
      Object value = selectorMap.get(selector);
      if (value == null) {
        throw new LinkageError("no public method "+selector+" in "+dispatchType.getName());
      }
      
      SelectorMetadata metadata;
      if (value instanceof MethodHandle[]) {
        MethodHandle[] mhs = (MethodHandle[])value;  
        if (mhs.length == 1) {
          // only one method, no multi-dispatch
          //System.out.println("one virtual linking "+dispatchType.getName()+'.'+name+type+" in "+lookup.lookupClass().getName());
          return mhs[0].asType(type);
        }
        
        try {
          if (mhs.length <= 32) {
            metadata = SmallSelectorMetadata.create(mhs);
          } else {
            throw new UnsupportedOperationException("NYI");
            //selectorMetadata = JumboSelectorMetadata.create(mhs);
          }
        } catch(UnsupportedOperationException e) {
          throw new LinkageError("NIY, "+dispatchType+'.'+selector, e);
        }
        
        // entry is already preallocated, so only one variable is changed
        // there is also a data race but because the code acts as a cache, we don't care
        selectorMap.put(selector, metadata);
      } else {
        metadata = (SelectorMetadata)value;
      }
      return metadata.createMethodHandle(type);
      
    } catch(RuntimeException e) {
      throw new BootstrapMethodError(
          "error while linking "+dispatchType.getName()+'.'+name+type+" in "+lookup.lookupClass().getName(),
          e);
    }
  }
  
  public static CallSite invokestatic(Lookup lookup, String name, MethodType type, Class<?> staticType) {
    return new ConstantCallSite(getMultiDispatchTarget(lookup, name, type, staticType));
  }
  
  static class BimorphicCacheCallSite extends MutableCallSite {
    final Lookup lookup;
    final String name;
    
    private Class<?> class1;
    private MethodHandle mh1;
    private Class<?> class2;
    private MethodHandle mh2;

    BimorphicCacheCallSite(Lookup lookup, String name, MethodType type) {
      super(type);
      this.lookup = lookup;
      this.name = name;
    }
    
    public synchronized Object fallback(Object[] args) throws Throwable {
      if (class1 != null && class2 != null) {
        // bimorphic cache defeated, use a dispatch table instead
        return fallbackToDispatchTable(args);
      }
      
      MethodType type = type();
      Object receiver = args[0];
      Class<?> receiverClass = receiver.getClass();
      MethodHandle target = getMultiDispatchTarget(lookup, name, type, receiverClass);
      
      MethodHandle test = CHECK_CLASS.bindTo(receiverClass);
      test = test.asType(test.type().changeParameterType(0, type.parameterType(0)));
      
      MethodHandle guard = MethodHandles.guardWithTest(test, target, getTarget());
      if (class1 == null) {
        class1 = receiverClass;
        mh1 = target;
      } else {
        class2 = receiverClass;
        mh2 = target;
      }
      
      setTarget(guard);
      return target.invokeWithArguments(args);
    }
    
    private Object fallbackToDispatchTable(Object[] args) throws Throwable {
      assert Thread.holdsLock(this);
      
      final MethodType type = type();
      DispatchMap dispatchMap = new DispatchMap() {
        @Override
        protected MethodHandle findMethodHandle(Class<?> receiverClass) throws Throwable {
          return getMultiDispatchTarget(lookup, name, type, receiverClass);
        }
      };
      dispatchMap.populate(class1, mh1, class1, mh2);   // pre-populated with known couples
      class1 = class2 = null;  
      mh1 = mh2 = null;  // free for GC
      
      MethodHandle lookupMH = MethodHandles.filterReturnValue(GET_CLASS, LOOKUP_MH.bindTo(dispatchMap));
      lookupMH = lookupMH.asType(MethodType.methodType(MethodHandle.class, type.parameterType(0)));
      MethodHandle target = MethodHandles.foldArguments(MethodHandles.exactInvoker(type), lookupMH);
      setTarget(target);
      return target.invokeWithArguments(args);
    }
  }
  
  public static CallSite invokevirtual(Lookup lookup, String name, MethodType type) {
    BimorphicCacheCallSite callSite = new BimorphicCacheCallSite(lookup, name, type);
    
    MethodHandle fallback = FALLBACK.bindTo(callSite);
    fallback = fallback.asCollector(Object[].class, type.parameterCount());
    fallback = fallback.asType(type);
    
    callSite.setTarget(fallback);
    return callSite;
  }
  
  public static boolean checkClass(Class<?> clazz, Object receiver) {
    return receiver.getClass() == clazz;
  }
  
  static final MethodHandle CHECK_CLASS;
  private static final MethodHandle FALLBACK;
  static final MethodHandle GET_CLASS;
  static final MethodHandle LOOKUP_MH;
  static {
    Lookup lookup = MethodHandles.lookup();
    try {
      CHECK_CLASS = lookup.findStatic(RT.class, "checkClass",
          MethodType.methodType(boolean.class, Class.class, Object.class));
      FALLBACK = lookup.findVirtual(BimorphicCacheCallSite.class, "fallback",
          MethodType.methodType(Object.class, Object[].class));
      GET_CLASS = lookup.findVirtual(Object.class, "getClass",
          MethodType.methodType(Class.class));
      LOOKUP_MH =   lookup.findVirtual(DispatchMap.class, "lookup",
          MethodType.methodType(MethodHandle.class, Class.class));
    } catch (ReflectiveOperationException e) {
      throw (AssertionError)new AssertionError().initCause(e);
    }
  }
}

Callee adaptation
?verified entry point & vtable
?memoization

package jsr292.cookbook.vep;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.util.HashMap;

public class RT {
  private static final ClassValue<HashMap<String, MethodHandle>> entryPointClassValue =
    new ClassValue<HashMap<String,MethodHandle>>() { 
      @Override
      protected HashMap<String, MethodHandle> computeValue(Class<?> type) {
        return new HashMap<String, MethodHandle>();
      }
    };
 
  static class VEPCallsite extends MutableCallSite {
    final String name;
    final Lookup lookup;
    
    public VEPCallsite(Lookup lookup, String name, MethodType type) {
      super(type);
      this.lookup = lookup;
      this.name = name;
    }
  }
    
  public static CallSite bootstrap(Lookup lookup, String name, MethodType type) {
    VEPCallsite callsite = new VEPCallsite(lookup, name, type);
    
    MethodHandle target = INSTALL_VEP.bindTo(callsite);
    target = target.asCollector(Object[].class, type.parameterCount());
    target = target.asType(type);
    
    callsite.setTarget(target);
    return callsite;
  }
  
  public static Object installVEP(VEPCallsite callSite, Object[] args) throws Throwable {
    Object receiver = args[0];
    Class<?> receiverClass = receiver.getClass();
    MethodType type = callSite.type();
    if (receiverClass == type.parameterType(0)) { // no need to use a dynamic checkcast
      return fallback(callSite, args);
    }
    
    String name = callSite.name;
    String selector = name + type.toMethodDescriptorString();
    HashMap<String, MethodHandle> vtable = entryPointClassValue.get(receiverClass);
    MethodHandle mh = vtable.get(selector);
    if (mh == null) {
      //System.out.println("construct "+receiverClass.getName()+ " " + name + " " + type);
      
      MethodHandle target = callSite.lookup.findVirtual(receiverClass, name, type.dropParameterTypes(0, 1));
      target = target.asType(type.changeParameterType(0, Object.class));
      target = MethodHandles.dropArguments(target, 0, VEPCallsite.class);
      
      MethodHandle test = CHECK_CLASS.bindTo(receiverClass);
      test = MethodHandles.dropArguments(test, 0, VEPCallsite.class);
      
      MethodHandle fallback = FALLBACK.asCollector(Object[].class, type.parameterCount());
      fallback = fallback.asType(target.type());
      
      mh = MethodHandles.guardWithTest(test, target, fallback);
      vtable.put(selector, mh);
    }
    
    mh = mh.bindTo(callSite);
    mh = mh.asType(type);
    callSite.setTarget(mh);
    return mh.invokeWithArguments(args);
  }
  
  public static Object fallback(VEPCallsite callSite, Object[] args) throws Throwable {
    // install a vtable call
    //System.out.println("fallback " + callSite.name + " " + callSite.type());
    
    MethodType type = callSite.type();
    MethodHandle target = callSite.lookup.findVirtual(type.parameterType(0), callSite.name, type.dropParameterTypes(0, 1));
    callSite.setTarget(target);
    return target.invokeWithArguments(args);
  }
  
  public static boolean checkClass(Class<?> clazz, Object receiver) {
    return receiver.getClass() == clazz;
  }
  
  private static final MethodHandle INSTALL_VEP;
  private static final MethodHandle CHECK_CLASS;
  private static final MethodHandle FALLBACK;
  static {
    Lookup lookup = MethodHandles.lookup();
    try {
      INSTALL_VEP = lookup.findStatic(RT.class, "installVEP",
          MethodType.methodType(Object.class, VEPCallsite.class, Object[].class));
      CHECK_CLASS = lookup.findStatic(RT.class, "checkClass",
          MethodType.methodType(boolean.class, Class.class, Object.class));
      FALLBACK = lookup.findStatic(RT.class, "fallback",
          MethodType.methodType(Object.class, VEPCallsite.class, Object[].class));
    } catch (ReflectiveOperationException e) {
      throw (AssertionError)new AssertionError().initCause(e);
    }
  }
}


package jsr292.cookbook.memoize;

import java.lang.invoke.CallSite;
import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.util.HashMap;

public class RT {
  private static ClassValue<HashMap<String, HashMap<Object,Object>>> cacheTables =
    new ClassValue<HashMap<String,HashMap<Object,Object>>>() {
      @Override
      protected HashMap<String, HashMap<Object, Object>> computeValue(Class<?> type) {
        return new HashMap<String, HashMap<Object,Object>>();
      }
    };
  
  public static CallSite bootstrap(Lookup lookup, String name, MethodType type, Class<?> staticType) throws ReflectiveOperationException {
    MethodHandle target = lookup.findStatic(staticType, name, type);
    
    HashMap<String, HashMap<Object, Object>> cacheTable = cacheTables.get(staticType);
    String selector = name + type.toMethodDescriptorString();
    HashMap<Object, Object> cache = cacheTable.get(selector);
    if (cache == null) {
      cache = new HashMap<Object, Object>();
      cacheTable.put(selector, cache);
    }
    
    MethodHandle identity = MethodHandles.identity(type.returnType());
    identity = identity.asType(identity.type().changeParameterType(0, Object.class));
    identity = MethodHandles.dropArguments(identity, 1, type.parameterType(0));
    
    /*FIXME fold doesn't work if combiner returns void !
    MethodHandle cachePut = MAP_PUT.bindTo(cache);
    cachePut = MethodHandles.permuteArguments(cachePut,
        MethodType.methodType(void.class, Object.class, Object.class),
        1, 0);
    cachePut = cachePut.asType(MethodType.methodType(void.class, type.parameterType(0), type.returnType()));
    
    MethodHandle identity2 = MethodHandles.dropArguments(identity, 1, type.parameterType(0));
    
    MethodHandle update = MethodHandles.foldArguments(identity2, cachePut);
    update = update.asType(type.insertParameterTypes(0, type.returnType()));
    */
    
    MethodHandle update = UPDATE.bindTo(cache);
    update = update.asType(type.insertParameterTypes(0, type.returnType()));
    
    MethodHandle fallback = MethodHandles.foldArguments(update, target);
    fallback = MethodHandles.dropArguments(fallback, 0, Object.class);
    
    MethodHandle combiner = MethodHandles.guardWithTest(NOT_NULL, identity, fallback);  // (Object,int)int
    
    MethodHandle cacheQuerier = MAP_GET.bindTo(cache);
    cacheQuerier = cacheQuerier.asType(MethodType.methodType(Object.class, type.parameterType(0)));
    
    MethodHandle memoize = MethodHandles.foldArguments(combiner, cacheQuerier);
    return new ConstantCallSite(memoize);
  }
  
  public static boolean notNull(Object receiver) {
    return receiver != null;
  }
  
  public static Object update(HashMap<Object, Object> cache, Object result, Object arg) {
    cache.put(arg, result);
    return result;
  }
  
  private static final MethodHandle NOT_NULL;
  private static final MethodHandle MAP_GET;
  //private static final MethodHandle MAP_PUT;
  private static final MethodHandle UPDATE;
  static {
    Lookup lookup = MethodHandles.lookup();
    try {
      NOT_NULL = lookup.findStatic(RT.class, "notNull",
          MethodType.methodType(boolean.class, Object.class));
      MAP_GET = lookup.findVirtual(HashMap.class, "get",
          MethodType.methodType(Object.class, Object.class));
      /*MAP_PUT = lookup.findVirtual(HashMap.class, "put",
          MethodType.methodType(Object.class, Object.class, Object.class)).
          asType(MethodType.methodType(void.class, HashMap.class, Object.class, Object.class));*/
      UPDATE = lookup.findStatic(RT.class, "update",
          MethodType.methodType(Object.class, HashMap.class, Object.class, Object.class));
    } catch (ReflectiveOperationException e) {
      throw (AssertionError)new AssertionError().initCause(e);
    }
  }
}

Mutable metaclass
?metaclass & invalidation

package jsr292.cookbook.metaclass;

import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;

public class RT {
 
  public static class InvokeStaticCallSite extends MutableCallSite {
    private final Lookup lookup;
    private final String name;
    private final Class<?> ownerType;
    private final MethodHandle fallback;

    public InvokeStaticCallSite(Lookup lookup, String name, MethodType type, Class<?> ownerType) {
      super(type);
      this.lookup = lookup;
      this.name = name;
      this.ownerType = ownerType;
      
      MethodHandle fallback = MethodHandles.insertArguments(STATIC_FALLBACK, 0, this);
      fallback = fallback.asCollector(Object[].class, type.parameterCount());
      fallback = fallback.asType(type);
      this.fallback = fallback;
    }
    
    MethodHandle staticSwitch() {
      MetaClass metaClass = MetaClass.getMetaClass(ownerType);
      MethodType type = type();
      MethodHandle mh;
      SwitchPoint switchPoint;
      synchronized(MetaClass.MUTATION_LOCK) {
        mh = metaClass.staticLookup(name, type);
        switchPoint = metaClass.switchPoint;
      }
      
      if (mh == null) {
        try {
          mh = lookup.findStatic(ownerType, name, type);
        } catch (ReflectiveOperationException e) {
          throw new BootstrapMethodError(e);
        }
      }
      mh = mh.asType(type);
      return switchPoint.guardWithTest(mh, fallback);
    }
  }
  
  
  public static CallSite invokestatic(Lookup lookup, String name, MethodType type, Class<?> ownerType) {
    InvokeStaticCallSite callsite = new InvokeStaticCallSite(lookup, name, type, ownerType);
    callsite.setTarget(callsite.staticSwitch());
    return callsite;
  }
  
  public static Object staticFallback(InvokeStaticCallSite callSite, Object[] args) throws Throwable {
    MethodHandle switchGuard = callSite.staticSwitch();
    callSite.setTarget(switchGuard);
    return switchGuard.invokeWithArguments(args);
  }
  
  
  public static class InvokeVirtualCallSite extends MutableCallSite {
    private final Lookup lookup;
    private final String name;
    final MethodHandle checkClass;
    final MethodHandle switchFallback;
    final MethodHandle dispatchFallback;

    public InvokeVirtualCallSite(Lookup lookup, String name, MethodType type) {
      super(type);
      this.lookup = lookup;
      this.name = name;
      
      MethodHandle checkClass = MethodHandles.dropArguments(CHECK_CLASS, 2, type.dropParameterTypes(0, 1).parameterList());
      checkClass = checkClass.asType(type().changeReturnType(boolean.class).insertParameterTypes(0, Class.class));
      this.checkClass = checkClass;
      
      MethodHandle switchFallback = MethodHandles.insertArguments(VIRTUAL_FALLBACK, 0, this, true);
      switchFallback = switchFallback.asCollector(Object[].class, type.parameterCount());
      switchFallback = switchFallback.asType(type);
      this.switchFallback = switchFallback;
      
      MethodHandle dispatchFallback = MethodHandles.insertArguments(VIRTUAL_FALLBACK, 0, this, false);
      dispatchFallback = dispatchFallback.asCollector(Object[].class, type.parameterCount());
      dispatchFallback = dispatchFallback.asType(type);
      this.dispatchFallback = dispatchFallback;
    }
    
    MethodHandle virtualSwitch(Class<?> receiverType) {
      MetaClass metaClass = MetaClass.getMetaClass(receiverType);
      MethodType type = type().dropParameterTypes(0, 1);
      MethodHandle mh;
      SwitchPoint switchPoint;
      synchronized(MetaClass.MUTATION_LOCK) {
        mh = metaClass.virtualLookup(name, type);
        switchPoint = metaClass.switchPoint;
      }
      
      if (mh == null) {
        try {
          mh = lookup.findVirtual(receiverType, name, type);
        } catch (ReflectiveOperationException e) {
          throw new BootstrapMethodError(e);
        }
      }
      mh = mh.asType(type());
      return switchPoint.guardWithTest(mh, switchFallback);
    }
  }
  
  public static CallSite invokevirtual(Lookup lookup, String name, MethodType type) {
    InvokeVirtualCallSite callSite = new InvokeVirtualCallSite(lookup, name, type);
    callSite.setTarget(callSite.dispatchFallback);
    return callSite;
  }
  
  public static boolean checkClass(Class<?> clazz, Object receiver) {
    return receiver.getClass() == clazz;
  }
  
  public static Object virtualFallback(InvokeVirtualCallSite callSite, boolean reset, Object[] args) throws Throwable {
    Class<?> receiverClass = args[0].getClass();
    MethodHandle virtualSwitch = callSite.virtualSwitch(receiverClass);
    
    MethodHandle fallback = (reset)? callSite.dispatchFallback: callSite.getTarget();
    
    MethodHandle guard = MethodHandles.guardWithTest(callSite.checkClass.bindTo(receiverClass),
        virtualSwitch,
        fallback);
      
    callSite.setTarget(guard);
    return virtualSwitch.invokeWithArguments(args);
  }
  
  
  
  
  static final MethodHandle STATIC_FALLBACK;
  static final MethodHandle VIRTUAL_FALLBACK;
  static final MethodHandle CHECK_CLASS;
  static {
    Lookup lookup = MethodHandles.lookup();
    try {
      STATIC_FALLBACK = lookup.findStatic(RT.class, "staticFallback",
          MethodType.methodType(Object.class, InvokeStaticCallSite.class, Object[].class));
      VIRTUAL_FALLBACK = lookup.findStatic(RT.class, "virtualFallback",
          MethodType.methodType(Object.class, InvokeVirtualCallSite.class, boolean.class, Object[].class));
      CHECK_CLASS = lookup.findStatic(RT.class, "checkClass",
          MethodType.methodType(boolean.class, Class.class, Object.class));
    } catch (ReflectiveOperationException e) {
      throw (AssertionError)new AssertionError().initCause(e);
    }
  }
}