读书人

JSR 292 的用法 (MethodHandle.)

发布时间: 2012-12-21 12:03:49 作者: rapoo

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);    }  }}

读书人网 >JavaScript

热点推荐