用例转自: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); } } }