public class DynamicLinker {
    private static final String CLASS_NAME = DynamicLinker.class.getName();
    private static final String RELINK_METHOD_NAME = "relink";

    private static final String INITIAL_LINK_CLASS_NAME = "java.lang.invoke.MethodHandleNatives";
    private static final String INITIAL_LINK_METHOD_NAME = "linkCallSite";

    private final LinkerServices linkerServices;
    private final GuardedInvocationFilter prelinkFilter;
    private final int runtimeContextArgCount;
    private final boolean syncOnRelink;
    private final int unstableRelinkThreshold;

    /**
     * Creates a new dynamic linker.
     *
     * @param linkerServices the linkerServices used by the linker, created by the factory.
     * @param prelinkFilter see {@link DynamicLinkerFactory#setPrelinkFilter(GuardedInvocationFilter)}
     * @param runtimeContextArgCount see {@link DynamicLinkerFactory#setRuntimeContextArgCount(int)}
     */
    DynamicLinker(final LinkerServices linkerServices, final GuardedInvocationFilter prelinkFilter, final int runtimeContextArgCount,
            final boolean syncOnRelink, final int unstableRelinkThreshold) {
        if(runtimeContextArgCount < 0) {
            throw new IllegalArgumentException("runtimeContextArgCount < 0");
        }
        if(unstableRelinkThreshold < 0) {
            throw new IllegalArgumentException("unstableRelinkThreshold < 0");
        }
        this.linkerServices = linkerServices;
        this.prelinkFilter = prelinkFilter;
        this.runtimeContextArgCount = runtimeContextArgCount;
        this.syncOnRelink = syncOnRelink;
        this.unstableRelinkThreshold = unstableRelinkThreshold;
    }

    /**
     * Links an invokedynamic call site. It will install a method handle into the call site that invokes the relinking
     * mechanism of this linker. Next time the call site is invoked, it will be linked for the actual arguments it was
     * invoked with.
     *
     * @param callSite the call site to link.
     * @return the callSite, for easy call chaining.
     */
    public <T extends RelinkableCallSite> T link(final T callSite) {
        callSite.initialize(createRelinkAndInvokeMethod(callSite, 0));
        return callSite;
    }

    /**
     * Returns the object representing the lower level linker services of this class that are normally exposed to
     * individual language-specific linkers. While as a user of this class you normally only care about the
     * {@link #link(RelinkableCallSite)} method, in certain circumstances you might want to use the lower level services
     * directly; either to lookup specific method handles, to access the type converters, and so on.
     * @return the object representing the linker services of this class.
     */
    public LinkerServices getLinkerServices() {
        return linkerServices;
    }

    private static final MethodHandle RELINK = Lookup.findOwnSpecial(MethodHandles.lookup(), RELINK_METHOD_NAME,
            MethodHandle.class, RelinkableCallSite.class, int.class, Object[].class);

    private MethodHandle createRelinkAndInvokeMethod(final RelinkableCallSite callSite, final int relinkCount) {
        // Make a bound MH of invoke() for this linker and call site
        final MethodHandle boundRelinker = MethodHandles.insertArguments(RELINK, 0, this, callSite, Integer.valueOf(
                relinkCount));
        // Make a MH that gathers all arguments to the invocation into an Object[]
        final MethodType type = callSite.getDescriptor().getMethodType();
        final MethodHandle collectingRelinker = boundRelinker.asCollector(Object[].class, type.parameterCount());
        return MethodHandles.foldArguments(MethodHandles.exactInvoker(type), collectingRelinker.asType(
                type.changeReturnType(MethodHandle.class)));
    }

    /**
     * Relinks a call site conforming to the invocation arguments.
     *
     * @param callSite the call site itself
     * @param arguments arguments to the invocation
     * @return return the method handle for the invocation
     * @throws Exception rethrows any exception thrown by the linkers
     */
    @SuppressWarnings("unused")
    private MethodHandle relink(final RelinkableCallSite callSite, final int relinkCount, final Object... arguments) throws Exception {
        final CallSiteDescriptor callSiteDescriptor = callSite.getDescriptor();
        final boolean unstableDetectionEnabled = unstableRelinkThreshold > 0;
        final boolean callSiteUnstable = unstableDetectionEnabled && relinkCount >= unstableRelinkThreshold;
        final LinkRequest linkRequest =
                runtimeContextArgCount == 0 ?
                        new LinkRequestImpl(callSiteDescriptor, callSite, relinkCount, callSiteUnstable, arguments) :
                        new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSite, relinkCount, callSiteUnstable, arguments, runtimeContextArgCount);

        GuardedInvocation guardedInvocation = linkerServices.getGuardedInvocation(linkRequest);

        // None found - throw an exception
        if(guardedInvocation == null) {
            throw new NoSuchDynamicMethodException(callSiteDescriptor.toString());
        }

        // If our call sites have a runtime context, and the linker produced a context-stripped invocation, adapt the
        // produced invocation into contextual invocation (by dropping the context...)
        if(runtimeContextArgCount > 0) {
            final MethodType origType = callSiteDescriptor.getMethodType();
            final MethodHandle invocation = guardedInvocation.getInvocation();
            if(invocation.type().parameterCount() == origType.parameterCount() - runtimeContextArgCount) {
                final List<Class<?>> prefix = origType.parameterList().subList(1, runtimeContextArgCount + 1);
                final MethodHandle guard = guardedInvocation.getGuard();
                guardedInvocation = guardedInvocation.dropArguments(1, prefix);
            }
        }

        // Make sure we filter the invocation before linking it into the call site. This is typically used to match the
        // return type of the invocation to the call site.
        guardedInvocation = prelinkFilter.filter(guardedInvocation, linkRequest, linkerServices);
        guardedInvocation.getClass(); // null pointer check

        int newRelinkCount = relinkCount;
        // Note that the short-circuited "&&" evaluation below ensures we'll increment the relinkCount until
        // threshold + 1 but not beyond that. Threshold + 1 is treated as a special value to signal that resetAndRelink
        // has already executed once for the unstable call site; we only want the call site to throw away its current
        // linkage once, when it transitions to unstable.
        if(unstableDetectionEnabled && newRelinkCount <= unstableRelinkThreshold && newRelinkCount++ == unstableRelinkThreshold) {
            callSite.resetAndRelink(guardedInvocation, createRelinkAndInvokeMethod(callSite, newRelinkCount));
        } else {
            callSite.relink(guardedInvocation, createRelinkAndInvokeMethod(callSite, newRelinkCount));
        }
        if(syncOnRelink) {
            MutableCallSite.syncAll(new MutableCallSite[] { (MutableCallSite)callSite });
        }
        return guardedInvocation.getInvocation();
    }

    /**
     * Returns a stack trace element describing the location of the call site currently being linked on the current
     * thread. The operation internally creates a Throwable object and inspects its stack trace, so it's potentially
     * expensive. The recommended usage for it is in writing diagnostics code.
     * @return a stack trace element describing the location of the call site currently being linked, or null if it is
     * not invoked while a call site is being linked.
     */
    public static StackTraceElement getLinkedCallSiteLocation() {
        final StackTraceElement[] trace = new Throwable().getStackTrace();
        for(int i = 0; i < trace.length - 1; ++i) {
            final StackTraceElement frame = trace[i];
            if(isRelinkFrame(frame) || isInitialLinkFrame(frame)) {
                return trace[i + 1];
            }
        }
        return null;
    }

    /**
     * Deprecated because of not precise name.
     * @deprecated Use {@link #getLinkedCallSiteLocation()} instead.
     * @return see non-deprecated method
     */
    @Deprecated
    public static StackTraceElement getRelinkedCallSiteLocation() {
        return getLinkedCallSiteLocation();
    }

    /**
     * Returns true if the frame represents {@code MethodHandleNatives.linkCallSite()}, the frame immediately on top of
     * the call site frame when the call site is being linked for the first time.
     * @param frame the frame
     * @return true if this frame represents {@code MethodHandleNatives.linkCallSite()}
     */
    private static boolean isInitialLinkFrame(final StackTraceElement frame) {
        return testFrame(frame, INITIAL_LINK_METHOD_NAME, INITIAL_LINK_CLASS_NAME);
    }

    /**
     * Returns true if the frame represents {@code DynamicLinker.relink()}, the frame immediately on top of the call
     * site frame when the call site is being relinked (linked for second and subsequent times).
     * @param frame the frame
     * @return true if this frame represents {@code DynamicLinker.relink()}
     */
    private static boolean isRelinkFrame(final StackTraceElement frame) {
        return testFrame(frame, RELINK_METHOD_NAME, CLASS_NAME);
    }

    private static boolean testFrame(final StackTraceElement frame, final String methodName, final String className) {
        return methodName.equals(frame.getMethodName()) && className.equals(frame.getClassName());
    }
} public class DynamicLinkerFactory {
    /**
     * Default value for {@link #setUnstableRelinkThreshold(int) unstable relink threshold}.
     */
    public static final int DEFAULT_UNSTABLE_RELINK_THRESHOLD = 8;

    private boolean classLoaderExplicitlySet = false;
    private ClassLoader classLoader;

    private List<? extends GuardingDynamicLinker> prioritizedLinkers;
    private List<? extends GuardingDynamicLinker> fallbackLinkers;
    private int runtimeContextArgCount = 0;
    private boolean syncOnRelink = false;
    private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
    private GuardedInvocationFilter prelinkFilter;
    private MethodTypeConversionStrategy autoConversionStrategy;
    private MethodHandleTransformer internalObjectsFilter;

    /**
     * Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
     * context class loader at the time of {@link #createLinker()} invocation will be used.
     *
     * @param classLoader the class loader used for the autodiscovery of available linkers.
     */
    public void setClassLoader(final ClassLoader classLoader) {
        this.classLoader = classLoader;
        classLoaderExplicitlySet = true;
    }

    /**
     * Sets the prioritized linkers. Language runtimes using this framework will usually precreate at least the linker
     * for their own language. These linkers will be consulted first in the resulting dynamic linker, before any
     * autodiscovered linkers. If the framework also autodiscovers a linker of the same class as one of the prioritized
     * linkers, it will be ignored and the explicit prioritized instance will be used.
     *
     * @param prioritizedLinkers the list of prioritized linkers. Null can be passed to indicate no prioritized linkers
     * (this is also the default value).
     */
    public void setPrioritizedLinkers(final List<? extends GuardingDynamicLinker> prioritizedLinkers) {
        this.prioritizedLinkers =
                prioritizedLinkers == null ? null : new ArrayList<>(prioritizedLinkers);
    }

    /**
     * Sets the prioritized linkers. Language runtimes using this framework will usually precreate at least the linker
     * for their own language. These linkers will be consulted first in the resulting dynamic linker, before any
     * autodiscovered linkers. If the framework also autodiscovers a linker of the same class as one of the prioritized
     * linkers, it will be ignored and the explicit prioritized instance will be used.
     *
     * @param prioritizedLinkers a list of prioritized linkers.
     */
    public void setPrioritizedLinkers(final GuardingDynamicLinker... prioritizedLinkers) {
        setPrioritizedLinkers(Arrays.asList(prioritizedLinkers));
    }

    /**
     * Sets a single prioritized linker. Identical to calling {@link #setPrioritizedLinkers(List)} with a single-element
     * list.
     *
     * @param prioritizedLinker the single prioritized linker. Must not be null.
     * @throws IllegalArgumentException if null is passed.
     */
    public void setPrioritizedLinker(final GuardingDynamicLinker prioritizedLinker) {
        if(prioritizedLinker == null) {
            throw new IllegalArgumentException("prioritizedLinker == null");
        }
        this.prioritizedLinkers = Collections.singletonList(prioritizedLinker);
    }

    /**
     * Sets the fallback linkers. These linkers will be consulted last in the resulting composite linker, after any
     * autodiscovered linkers. If the framework also autodiscovers a linker of the same class as one of the fallback
     * linkers, it will be ignored and the explicit fallback instance will be used.
     *
     * @param fallbackLinkers the list of fallback linkers. Can be empty to indicate the caller wishes to set no
     * fallback linkers.
     */
    public void setFallbackLinkers(final List<? extends GuardingDynamicLinker> fallbackLinkers) {
        this.fallbackLinkers = fallbackLinkers == null ? null : new ArrayList<>(fallbackLinkers);
    }

    /**
     * Sets the fallback linkers. These linkers will be consulted last in the resulting composite linker, after any
     * autodiscovered linkers. If the framework also autodiscovers a linker of the same class as one of the fallback
     * linkers, it will be ignored and the explicit fallback instance will be used.
     *
     * @param fallbackLinkers the list of fallback linkers. Can be empty to indicate the caller wishes to set no
     * fallback linkers. If it is left as null, the standard fallback {@link BeansLinker} will be used.
     */
    public void setFallbackLinkers(final GuardingDynamicLinker... fallbackLinkers) {
        setFallbackLinkers(Arrays.asList(fallbackLinkers));
    }

    /**
     * Sets the number of arguments in the call sites that represent the stack context of the language runtime creating
     * the linker. If the language runtime uses no context information passed on stack, then it should be zero
     * (the default value). If it is set to nonzero value, then every dynamic call site emitted by this runtime must
     * have the argument list of the form: {@code (this, contextArg1[, contextArg2[, ...]], normalArgs)}. It is
     * advisable to only pass one context-specific argument, though, of an easily recognizable, runtime specific type
     * encapsulating the runtime thread local state.
     *
     * @param runtimeContextArgCount the number of language runtime context arguments in call sites.
     */
    public void setRuntimeContextArgCount(final int runtimeContextArgCount) {
        if(runtimeContextArgCount < 0) {
            throw new IllegalArgumentException("runtimeContextArgCount < 0");
        }
        this.runtimeContextArgCount = runtimeContextArgCount;
    }

    /**
     * Sets whether the linker created by this factory will invoke {@link MutableCallSite#syncAll(MutableCallSite[])}
     * after a call site is relinked. Defaults to false. You probably want to set it to true if your runtime supports
     * multithreaded execution of dynamically linked code.
     * @param syncOnRelink true for invoking sync on relink, false otherwise.
     */
    public void setSyncOnRelink(final boolean syncOnRelink) {
        this.syncOnRelink = syncOnRelink;
    }

    /**
     * Sets the unstable relink threshold; the number of times a call site is relinked after which it will be
     * considered unstable, and subsequent link requests for it will indicate this.
     * @param unstableRelinkThreshold the new threshold. Must not be less than zero. The value of zero means that
     * call sites will never be considered unstable.
     * @see LinkRequest#isCallSiteUnstable()
     */
    public void setUnstableRelinkThreshold(final int unstableRelinkThreshold) {
        if(unstableRelinkThreshold < 0) {
            throw new IllegalArgumentException("unstableRelinkThreshold < 0");
        }
        this.unstableRelinkThreshold = unstableRelinkThreshold;
    }

    /**
     * Set the pre-link filter. This is a {@link GuardedInvocationFilter} that will get the final chance to modify the
     * guarded invocation after it has been created by a component linker and before the dynamic linker links it into
     * the call site. It is normally used to adapt the return value type of the invocation to the type of the call site.
     * When not set explicitly, {@link DefaultPrelinkFilter} will be used.
     * @param prelinkFilter the pre-link filter for the dynamic linker.
     */
    public void setPrelinkFilter(final GuardedInvocationFilter prelinkFilter) {
        this.prelinkFilter = prelinkFilter;
    }

    /**
     * Sets an object representing the conversion strategy for automatic type conversions. After
     * {@link TypeConverterFactory#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has
     * applied all custom conversions to a method handle, it still needs to effect
     * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
     * can usually be automatically applied as per
     * {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
     * However, sometimes language runtimes will want to customize even those conversions for their own call
     * sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
     * ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
     * automatic conversion strategy, that can deal with null values. Note that when the strategy's
     * {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
     * is invoked, the custom language conversions will already have been applied to the method handle, so by
     * design the difference between the handle's current method type and the desired final type will always
     * only be ones that can be subjected to method invocation conversions. The strategy also doesn't need to
     * invoke a final {@code MethodHandle.asType()} as the converter factory will do that as the final step.
     * @param autoConversionStrategy the strategy for applying method invocation conversions for the linker
     * created by this factory.
     */
    public void setAutoConversionStrategy(final MethodTypeConversionStrategy autoConversionStrategy) {
        this.autoConversionStrategy = autoConversionStrategy;
    }

    /**
     * Sets a method handle transformer that is supposed to act as the implementation of this linker factory's linkers'
     * services {@link LinkerServices#filterInternalObjects(java.lang.invoke.MethodHandle)} method.
     * @param internalObjectsFilter a method handle transformer filtering out internal objects, or null.
     */
    public void setInternalObjectsFilter(final MethodHandleTransformer internalObjectsFilter) {
        this.internalObjectsFilter = internalObjectsFilter;
    }

    /**
     * Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
     * the pre-link filter.
     *
     * @return the new dynamic Linker
     */
    public DynamicLinker createLinker() {
        // Treat nulls appropriately
        if(prioritizedLinkers == null) {
            prioritizedLinkers = Collections.emptyList();
        }
        if(fallbackLinkers == null) {
            fallbackLinkers = Collections.singletonList(new BeansLinker());
        }

        // Gather classes of all precreated (prioritized and fallback) linkers.
        // We'll filter out any discovered linkers of the same class.
        final Set<Class<? extends GuardingDynamicLinker>> knownLinkerClasses =
                new HashSet<>();
        addClasses(knownLinkerClasses, prioritizedLinkers);
        addClasses(knownLinkerClasses, fallbackLinkers);

        final ClassLoader effectiveClassLoader = classLoaderExplicitlySet ? classLoader : getThreadContextClassLoader();
        final List<GuardingDynamicLinker> discovered = AutoDiscovery.loadLinkers(effectiveClassLoader);
        // Now, concatenate ...
        final List<GuardingDynamicLinker> linkers =
                new ArrayList<>(prioritizedLinkers.size() + discovered.size()
                        + fallbackLinkers.size());
        // ... prioritized linkers, ...
        linkers.addAll(prioritizedLinkers);
        // ... filtered discovered linkers, ...
        for(final GuardingDynamicLinker linker: discovered) {
            if(!knownLinkerClasses.contains(linker.getClass())) {
                linkers.add(linker);
            }
        }
        // ... and finally fallback linkers.
        linkers.addAll(fallbackLinkers);
        final List<GuardingDynamicLinker> optimized = CompositeTypeBasedGuardingDynamicLinker.optimize(linkers);
        final GuardingDynamicLinker composite;
        switch(linkers.size()) {
            case 0: {
                composite = BottomGuardingDynamicLinker.INSTANCE;
                break;
            }
            case 1: {
                composite = optimized.get(0);
                break;
            }
            default: {
                composite = new CompositeGuardingDynamicLinker(optimized);
                break;
            }
        }

        final List<GuardingTypeConverterFactory> typeConverters = new LinkedList<>();
        for(final GuardingDynamicLinker linker: linkers) {
            if(linker instanceof GuardingTypeConverterFactory) {
                typeConverters.add((GuardingTypeConverterFactory)linker);
            }
        }

        if(prelinkFilter == null) {
            prelinkFilter = new DefaultPrelinkFilter();
        }

        return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters,
                autoConversionStrategy), composite, internalObjectsFilter), prelinkFilter, runtimeContextArgCount,
                syncOnRelink, unstableRelinkThreshold);
    }

    private static ClassLoader getThreadContextClassLoader() {
        return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
            @Override
            public ClassLoader run() {
                return Thread.currentThread().getContextClassLoader();
            }
        }, ClassLoaderGetterContextProvider.GET_CLASS_LOADER_CONTEXT);
    }

    private static void addClasses(final Set<Class<? extends GuardingDynamicLinker>> knownLinkerClasses,
            final List<? extends GuardingDynamicLinker> linkers) {
        for(final GuardingDynamicLinker linker: linkers) {
            knownLinkerClasses.add(linker.getClass());
        }
    }
} public class LinkerServicesFactory {
    public static LinkerServices getLinkerServices(GuardingDynamicLinker linker) {
        return new LinkerServicesImpl(new TypeConverterFactory(new LinkedList<GuardingTypeConverterFactory>(), null), linker, null);
    }
} public class MonomorphicCallSite extends AbstractRelinkableCallSite {
    /**
     * Creates a new call site with monomorphic inline caching strategy.
     * @param descriptor the descriptor for this call site
     */
    public MonomorphicCallSite(final CallSiteDescriptor descriptor) {
        super(descriptor);
    }

    @Override
    public void relink(final GuardedInvocation guardedInvocation, final MethodHandle relink) {
        setTarget(guardedInvocation.compose(relink));
    }

    @Override
    public void resetAndRelink(final GuardedInvocation guardedInvocation, final MethodHandle relink) {
        relink(guardedInvocation, relink);
    }
} public class GuardedInvocation {
    private static final SwitchPoint[] NO_SWITCH_POINTS = new SwitchPoint[0];

    private final MethodHandle invocation;
    private final MethodHandle guard;
    private final Class<? extends Throwable> exception;
    private final SwitchPoint[] switchPoints;

    /**
     * Creates a new guarded invocation. This invocation is unconditional as it has no invalidations.
     *
     * @param invocation the method handle representing the invocation. Must not be null.
     * @throws NullPointerException if invocation is null.
     */
    public GuardedInvocation(final MethodHandle invocation) {
        this(invocation, null, NO_SWITCH_POINTS, null);
    }

    /**
     * Creates a new guarded invocation.
     *
     * @param invocation the method handle representing the invocation. Must not be null.
     * @param guard the method handle representing the guard. Must have the same method type as the invocation, except
     * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null to represent
     * an unconditional invocation, although that is unusual.
     * @throws NullPointerException if invocation is null.
     */
    public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard) {
        this(invocation, guard, NO_SWITCH_POINTS, null);
    }

    /**
     * Creates a new guarded invocation.
     *
     * @param invocation the method handle representing the invocation. Must not be null.
     * @param switchPoint the optional switch point that can be used to invalidate this linkage.
     * @throws NullPointerException if invocation is null.
     */
    public GuardedInvocation(final MethodHandle invocation, final SwitchPoint switchPoint) {
        this(invocation, null, switchPoint, null);
    }

    /**
     * Creates a new guarded invocation.
     *
     * @param invocation the method handle representing the invocation. Must not be null.
     * @param guard the method handle representing the guard. Must have the same method type as the invocation, except
     * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
     * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
     * @param switchPoint the optional switch point that can be used to invalidate this linkage.
     * @throws NullPointerException if invocation is null.
     */
    public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint switchPoint) {
        this(invocation, guard, switchPoint, null);
    }

    /**
     * Creates a new guarded invocation.
     *
     * @param invocation the method handle representing the invocation. Must not be null.
     * @param guard the method handle representing the guard. Must have the same method type as the invocation, except
     * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
     * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
     * @param switchPoint the optional switch point that can be used to invalidate this linkage.
     * @param exception the optional exception type that is expected to be thrown by the invocation and that also
     * invalidates the linkage.
     * @throws NullPointerException if invocation is null.
     */
    public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint switchPoint, final Class<? extends Throwable> exception) {
        invocation.getClass(); // NPE check
        this.invocation = invocation;
        this.guard = guard;
        this.switchPoints = switchPoint == null ? NO_SWITCH_POINTS : new SwitchPoint[] { switchPoint };
        this.exception = exception;
    }

    /**
     * Creates a new guarded invocation
     *
     * @param invocation the method handle representing the invocation. Must not be null.
     * @param guard the method handle representing the guard. Must have the same method type as the invocation, except
     * it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
     * and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
     * @param switchPoints the optional switch points that can be used to invalidate this linkage.
     * @param exception the optional exception type that is expected to be thrown by the invocation and that also
     * invalidates the linkage.
     * @throws NullPointerException if invocation is null.
     */
    public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint[] switchPoints, final Class<? extends Throwable> exception) {
        invocation.getClass(); // NPE check
        this.invocation = invocation;
        this.guard = guard;
        this.switchPoints = switchPoints != null && switchPoints.length > 0 ? switchPoints.clone() : NO_SWITCH_POINTS;
        this.exception = exception;
    }

    /**
     * Returns the invocation method handle.
     *
     * @return the invocation method handle. It will never be null.
     */
    public MethodHandle getInvocation() {
        return invocation;
    }

    /**
     * Returns the guard method handle.
     *
     * @return the guard method handle. Can be null.
     */
    public MethodHandle getGuard() {
        return guard;
    }

    /**
     * Returns an array of switch points that can be used to invalidate the invocation handle. The returned
     * array is a copy of the internal array; changes to it won't be reflected in the invocation.
     *
     * @return an array of switch points that can be used to invalidate the invocation handle.
     */
    public SwitchPoint[] getSwitchPoints() {
        return switchPoints.clone();
    }

    /**
     * Returns the exception type that if thrown should be used to invalidate the linkage.
     *
     * @return the exception type that if thrown should be used to invalidate the linkage. Can be null.
     */
    public Class<? extends Throwable> getException() {
        return exception;
    }

    /**
     * Returns true if and only if this guarded invocation has at least one invalidated switch point.
     * @return true if and only if this guarded invocation has at least one invalidated switch point.
     */
    public boolean hasBeenInvalidated() {
        for (final SwitchPoint sp : switchPoints) {
            if (sp.hasBeenInvalidated()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Asserts that the invocation is of the specified type, and the guard (if present) is of the specified type with a
     * boolean return type.
     *
     * @param type the asserted type
     * @throws WrongMethodTypeException if the invocation and the guard are not of the expected method type.
     */
    public void assertType(final MethodType type) {
        assertType(invocation, type);
        if(guard != null) {
            assertType(guard, type.changeReturnType(Boolean.TYPE));
        }
    }

    /**
     * Creates a new guarded invocation with different methods, preserving the switch point.
     *
     * @param newInvocation the new invocation
     * @param newGuard the new guard
     * @return a new guarded invocation with the replaced methods and the same switch point as this invocation.
     */
    public GuardedInvocation replaceMethods(final MethodHandle newInvocation, final MethodHandle newGuard) {
        return new GuardedInvocation(newInvocation, newGuard, switchPoints, exception);
    }

    private GuardedInvocation replaceMethodsOrThis(final MethodHandle newInvocation, final MethodHandle newGuard) {
        if(newInvocation == invocation && newGuard == guard) {
            return this;
        }
        return replaceMethods(newInvocation, newGuard);
    }

    /**
     * Returns a new guarded invocation that has the passed switch point added to the end of the array of this
     * invocation's switch points.
     * @param newSwitchPoint new switch point, or null for no-op.
     * @return new guarded invocation with the extra switch point (or this invocation if null was passed).
     */
    public GuardedInvocation addSwitchPoint(final SwitchPoint newSwitchPoint) {
        if (newSwitchPoint == null) {
            return this;
        }

        final SwitchPoint[] newSwitchPoints;
        final int l = switchPoints.length;
        newSwitchPoints = new SwitchPoint[l + 1];
        System.arraycopy(switchPoints, 0, newSwitchPoints, 0, l);
        newSwitchPoints[l] = newSwitchPoint;

        return new GuardedInvocation(invocation, guard, newSwitchPoints, exception);
    }

    /**
     * Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation
     * and its guard, if it has one (with return type changed to boolean, and parameter count potentially truncated for
     * the guard). If the invocation already is of the required type, returns this object.
     * @param newType the new type of the invocation.
     * @return a guarded invocation with the new type applied to it.
     */
    public GuardedInvocation asType(final MethodType newType) {
        return replaceMethodsOrThis(invocation.asType(newType), guard == null ? null : Guards.asType(guard, newType));
    }

    /**
     * Changes the type of the invocation, as if {@link LinkerServices#asType(MethodHandle, MethodType)} was applied to
     * its invocation and its guard, if it has one (with return type changed to boolean, and parameter count potentially
     * truncated for the guard). If the invocation already is of the required type, returns this object.
     * @param linkerServices the linker services to use for the conversion
     * @param newType the new type of the invocation.
     * @return a guarded invocation with the new type applied to it.
     */
    public GuardedInvocation asType(final LinkerServices linkerServices, final MethodType newType) {
        return replaceMethodsOrThis(linkerServices.asType(invocation, newType), guard == null ? null :
            Guards.asType(linkerServices, guard, newType));
    }

    /**
     * Changes the type of the invocation, as if {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)} was
     * applied to its invocation and {@link LinkerServices#asType(MethodHandle, MethodType)} applied to its guard, if it
     * has one (with return type changed to boolean, and parameter count potentially truncated for the guard). If the
     * invocation doesn't change its type, returns this object.
     * @param linkerServices the linker services to use for the conversion
     * @param newType the new type of the invocation.
     * @return a guarded invocation with the new type applied to it.
     */
    public GuardedInvocation asTypeSafeReturn(final LinkerServices linkerServices, final MethodType newType) {
        return replaceMethodsOrThis(linkerServices.asTypeLosslessReturn(invocation, newType), guard == null ? null :
            Guards.asType(linkerServices, guard, newType));
    }

    /**
     * Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation
     * and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the
     * required type, returns this object.
     * @param desc a call descriptor whose method type is adapted.
     * @return a guarded invocation with the new type applied to it.
     */
    public GuardedInvocation asType(final CallSiteDescriptor desc) {
        return asType(desc.getMethodType());
    }

    /**
     * Applies argument filters to both the invocation and the guard (if there is one).
     * @param pos the position of the first argumen being filtered
     * @param filters the argument filters
     * @return a filtered invocation
     */
    public GuardedInvocation filterArguments(final int pos, final MethodHandle... filters) {
        return replaceMethods(MethodHandles.filterArguments(invocation, pos, filters), guard == null ? null :
            MethodHandles.filterArguments(guard, pos, filters));
    }

    /**
     * Makes an invocation that drops arguments in both the invocation and the guard (if there is one).
     * @param pos the position of the first argument being dropped
     * @param valueTypes the types of the values being dropped
     * @return an invocation that drops arguments
     */
    public GuardedInvocation dropArguments(final int pos, final List<Class<?>> valueTypes) {
        return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes), guard == null ? null :
            MethodHandles.dropArguments(guard, pos, valueTypes));
    }

    /**
     * Makes an invocation that drops arguments in both the invocation and the guard (if there is one).
     * @param pos the position of the first argument being dropped
     * @param valueTypes the types of the values being dropped
     * @return an invocation that drops arguments
     */
    public GuardedInvocation dropArguments(final int pos, final Class<?>... valueTypes) {
        return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes), guard == null ? null :
            MethodHandles.dropArguments(guard, pos, valueTypes));
    }


    /**
     * Composes the invocation, switchpoint, and the guard into a composite method handle that knows how to fall back.
     * @param fallback the fallback method handle in case switchpoint is invalidated or guard returns false.
     * @return a composite method handle.
     */
    public MethodHandle compose(final MethodHandle fallback) {
        return compose(fallback, fallback, fallback);
    }

    /**
     * Composes the invocation, switchpoint, and the guard into a composite method handle that knows how to fall back.
     * @param switchpointFallback the fallback method handle in case switchpoint is invalidated.
     * @param guardFallback the fallback method handle in case guard returns false.
     * @param catchFallback the fallback method in case the exception handler triggers
     * @return a composite method handle.
     */
    public MethodHandle compose(final MethodHandle guardFallback, final MethodHandle switchpointFallback, final MethodHandle catchFallback) {
        final MethodHandle guarded =
                guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback);

        final MethodHandle catchGuarded = exception == null ? guarded : MethodHandles.catchException(guarded, exception,
                MethodHandles.dropArguments(catchFallback, 0, exception));

        MethodHandle spGuarded = catchGuarded;
        for (final SwitchPoint sp : switchPoints) {
            spGuarded = sp.guardWithTest(spGuarded, switchpointFallback);
        }
        return spGuarded;
    }

    private static void assertType(final MethodHandle mh, final MethodType type) {
        if(!mh.type().equals(type)) {
            throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type());
        }
    }
} public class GuardedTypeConversion {
    private final GuardedInvocation conversionInvocation;
    private final boolean cacheable;

    /**
     * Creates a new guarded type conversion.
     * @param conversionInvocation the guarded invocation representing the conditional type conversion.
     * @param cacheable if true, this type conversion invocation is cacheable. The framework is allowed to
     * keep it around for subsequent linking of type conversions. If false, this type conversion is not
     * cacheable. It can be used to satisfy exactly one linking of a type conversion.
     */
    public GuardedTypeConversion(final GuardedInvocation conversionInvocation, final boolean cacheable) {
        this.conversionInvocation = conversionInvocation;
        this.cacheable = cacheable;
    }

    /**
     * Returns the guarded invocation representing the conditional type conversion.
     * @return the guarded invocation representing the conditional type conversion.
     */
    public GuardedInvocation getConversionInvocation() {
        return conversionInvocation;
    }

    /**
     * Returns whether this type conversion invocation is cacheable. The framework is allowed to keep it
     * around for subsequent linking of type conversions. If false, this type conversion is not cacheable. It
     * can be used to satisfy exactly one linking of a type conversion.
     * @return true if the conversion is cacheable, false otherwise.
     */
    public boolean isCacheable() {
        return cacheable;
    }
} public interface GuardingTypeConverterFactory {
    /**
     * Returns a guarded type conversion that receives an Object of the specified source type and returns an Object
     * converted to the specified target type. The type of the invocation is targetType(sourceType), while the type of
     * the guard is boolean(sourceType). Note that this will never be invoked for type conversions allowed by the JLS
     * 5.3 "Method Invocation Conversion", see {@link TypeUtilities#isMethodInvocationConvertible(Class, Class)} for
     * details. An implementation can assume it is never requested to produce a converter for these conversions.
     *
     * @param sourceType source type
     * @param targetType the target type.
     * @return a guarded type conversion that contains a guarded invocation that can take an object (if it passes guard)
     * and return another object that is its representation coerced into the target type. In case the factory is certain
     * it is unable to handle a conversion, it can return null. In case the factory is certain that it can always handle
     * the conversion, it can return an unconditional invocation (one whose guard is null).
     * @throws Exception if there was an error during creation of the converter
     */
    public GuardedTypeConversion convertToType(Class<?> sourceType, Class<?> targetType) throws Exception;
} public interface LinkerServices {
    /**
     * Similar to {@link MethodHandle#asType(MethodType)} except it also hooks in method handles produced by
     * {@link GuardingTypeConverterFactory} implementations, providing for language-specific type coercing of
     * parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
     * wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
     * it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
     * provided by {@link GuardingTypeConverterFactory} implementations.
     *
     * @param handle target method handle
     * @param fromType the types of source arguments
     * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
     * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)}, and
     * {@link MethodHandles#filterReturnValue(MethodHandle, MethodHandle)} with
     * {@link GuardingTypeConverterFactory}-produced type converters as filters.
     */
    public MethodHandle asType(MethodHandle handle, MethodType fromType);

    /**
     * Similar to {@link #asType(MethodHandle, MethodType)} except it only converts the return type of the method handle
     * when it can be done using a conversion that loses neither precision nor magnitude, otherwise it leaves it
     * unchanged. The idea is that other conversions should not be performed by individual linkers, but instead the
     * {@link DynamicLinkerFactory#setPrelinkFilter(org.dynalang.dynalink.GuardedInvocationFilter) pre-link filter of
     * the dynamic linker} should implement the strategy of dealing with potentially lossy return type conversions in a
     * manner specific to the language runtime.
     *
     * @param handle target method handle
     * @param fromType the types of source arguments
     * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)}, and
     * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
     * {@link GuardingTypeConverterFactory}-produced type converters as filters.
     */
    public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType);

    /**
     * Given a source and target type, returns a method handle that converts between them. Never returns null; in worst
     * case it will return an identity conversion (that might fail for some values at runtime). You rarely need to use
     * this method directly; you should mostly rely on {@link #asType(MethodHandle, MethodType)} instead. You really
     * only need this method if you have a piece of your program that is written in Java, and you need to reuse existing
     * type conversion machinery in a non-invokedynamic context.
     * @param sourceType the type to convert from
     * @param targetType the type to convert to
     * @return a method handle performing the conversion.
     */
    public MethodHandle getTypeConverter(Class<?> sourceType, Class<?> targetType);

    /**
     * Returns true if there might exist a conversion between the requested types (either an automatic JVM conversion,
     * or one provided by any available {@link GuardingTypeConverterFactory}), or false if there definitely does not
     * exist a conversion between the requested types. Note that returning true does not guarantee that the conversion
     * will succeed at runtime (notably, if the "from" or "to" types are sufficiently generic), but returning false
     * guarantees that it would fail.
     *
     * @param from the source type for the conversion
     * @param to the target type for the conversion
     * @return true if there can be a conversion, false if there can not.
     */
    public boolean canConvert(Class<?> from, Class<?> to);

    /**
     * Creates a guarded invocation using the {@link DynamicLinker} that exposes this linker services interface. Linkers
     * can typically use them to delegate linking of wrapped objects.
     *
     * @param linkRequest a request for linking the invocation
     * @return a guarded invocation linked by the top-level linker (or any of its delegates). Can be null if no
     * available linker is able to link the invocation.
     * @throws Exception in case the top-level linker throws an exception
     */
    public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest) throws Exception;

    /**
     * Determines which of the two type conversions from a source type to the two target types is preferred. This is
     * used for dynamic overloaded method resolution. If the source type is convertible to exactly one target type with
     * a method invocation conversion, it is chosen, otherwise available {@link ConversionComparator}s are consulted.
     * @param sourceType the source type.
     * @param targetType1 one potential target type
     * @param targetType2 another potential target type.
     * @return one of Comparison constants that establish which - if any - of the target types is preferable for the
     * conversion.
     */
    public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2);

    /**
     * Modifies the method handle so that any parameters that can receive potentially internal language runtime objects
     * will have a filter added on them to prevent them from escaping, potentially by wrapping them.
     * It can also potentially add an unwrapping filter to the return value.
     * @param target the target method handle
     * @return a method handle with parameters and/or return type potentially filtered for wrapping and unwrapping.
     */
    public MethodHandle filterInternalObjects(final MethodHandle target);

    /**
     * If we could just use Java 8 constructs, then {@code asTypeSafeReturn} would be a method with default
     * implementation. Since we can't do that, we extract common default implementations into this static class.
     */
    public static class Implementation {
        /**
         * Default implementation for {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
         * @param linkerServices the linker services that delegates to this implementation
         * @param handle the passed handle
         * @param fromType the passed type
         * @return the converted method handle, as per the {@code asTypeSafeReturn} semantics.
         */
        public static MethodHandle asTypeLosslessReturn(final LinkerServices linkerServices, final MethodHandle handle, final MethodType fromType) {
            final Class<?> handleReturnType = handle.type().returnType();
            return linkerServices.asType(handle, TypeUtilities.isConvertibleWithoutLoss(handleReturnType, fromType.returnType()) ?
                    fromType : fromType.changeReturnType(handleReturnType));
        }
    }
} public interface TypeBasedGuardingDynamicLinker extends GuardingDynamicLinker {
    /**
     * Returns true if the linker can link an invocation where the first argument (receiver) is of the specified type.
     *
     * @param type the type to link
     * @return true if the linker can link calls for the receiver type, or false otherwise.
     */
    public boolean canLinkType(Class<?> type);
} public class CallSiteDescriptorFactory {
    private static final WeakHashMap<CallSiteDescriptor, WeakReference<CallSiteDescriptor>> publicDescs =
            new WeakHashMap<>();


    private CallSiteDescriptorFactory() {
    }

    /**
     * Creates a new call site descriptor instance. The actual underlying class of the instance is dependent on the
     * passed arguments to be space efficient; i.e. if you  only use the public lookup, you'll get back an
     * implementation that doesn't waste space on storing the lookup object.
     * @param lookup the lookup that determines access rights at the call site. If your language runtime doesn't have
     * equivalents of Java access concepts, just use {@link MethodHandles#publicLookup()}. Must not be null.
     * @param name the name of the method at the call site. Must not be null.
     * @param methodType the type of the method at the call site. Must not be null.
     * @return a call site descriptor representing the input. Note that although the method name is "create", it will
     * in fact return a weakly-referenced canonical instance.
     */
    public static CallSiteDescriptor create(final Lookup lookup, final String name, final MethodType methodType) {
        name.getClass(); // NPE check
        methodType.getClass(); // NPE check
        lookup.getClass(); // NPE check
        final String[] tokenizedName = tokenizeName(name);
        if(isPublicLookup(lookup)) {
            return getCanonicalPublicDescriptor(createPublicCallSiteDescriptor(tokenizedName, methodType));
        }
        return new LookupCallSiteDescriptor(tokenizedName, methodType, lookup);
    }

    static CallSiteDescriptor getCanonicalPublicDescriptor(final CallSiteDescriptor desc) {
        synchronized(publicDescs) {
            final WeakReference<CallSiteDescriptor> ref = publicDescs.get(desc);
            if(ref != null) {
                final CallSiteDescriptor canonical = ref.get();
                if(canonical != null) {
                    return canonical;
                }
            }
            publicDescs.put(desc, new WeakReference<>(desc));
        }
        return desc;
    }

    private static CallSiteDescriptor createPublicCallSiteDescriptor(final String[] tokenizedName, final MethodType methodType) {
        final int l = tokenizedName.length;
        if(l > 0 && tokenizedName[0] == "dyn") {
            if(l == 2) {
                return new UnnamedDynCallSiteDescriptor(tokenizedName[1], methodType);
            } if (l == 3) {
                return new NamedDynCallSiteDescriptor(tokenizedName[1], tokenizedName[2], methodType);
            }
        }
        return new DefaultCallSiteDescriptor(tokenizedName, methodType);
    }

    private static boolean isPublicLookup(final Lookup lookup) {
        return lookup == MethodHandles.publicLookup();
    }

    /**
     * Tokenizes the composite name along colons, as well as {@link NameCodec#decode(String) demangles} and interns
     * the tokens. The first two tokens are not demangled as they are supposed to be the naming scheme and the name of
     * the operation which can be expected to consist of just alphabetical characters.
     * @param name the composite name consisting of colon-separated, possibly mangled tokens.
     * @return an array of tokens
     */
    public static String[] tokenizeName(final String name) {
        final StringTokenizer tok = new StringTokenizer(name, CallSiteDescriptor.TOKEN_DELIMITER);
        final String[] tokens = new String[tok.countTokens()];
        for(int i = 0; i < tokens.length; ++i) {
            String token = tok.nextToken();
            if(i > 1) {
                token = NameCodec.decode(token);
            }
            tokens[i] = token.intern();
        }
        return tokens;
    }

    /**
     * Tokenizes a composite operation name along pipe characters. I.e. if you have a "dyn:getElem|getProp|getMethod"
     * operation, returns a list of ["getElem", "getProp", "getMethod"]. The tokens are not interned.
     * @param desc the call site descriptor with the operation
     * @return a list of tokens
     */
    public static List<String> tokenizeOperators(final CallSiteDescriptor desc) {
        final String ops = desc.getNameToken(CallSiteDescriptor.OPERATOR);
        final StringTokenizer tok = new StringTokenizer(ops, CallSiteDescriptor.OPERATOR_DELIMITER);
        final int count = tok.countTokens();
        if(count == 1) {
            return Collections.singletonList(ops);
        }
        final String[] tokens = new String[count];
        for(int i = 0; i < count; ++i) {
            tokens[i] = tok.nextToken();
        }
        return Arrays.asList(tokens);
    }

    /**
     * Returns a new call site descriptor that is identical to the passed one, except that it has some parameter types
     * removed from its method type.
     * @param desc the original call site descriptor
     * @param start index of the first parameter to remove
     * @param end index of the first parameter to not remove
     * @return a new call site descriptor with modified method type
     */
    public static CallSiteDescriptor dropParameterTypes(final CallSiteDescriptor desc, final int start, final int end) {
        return desc.changeMethodType(desc.getMethodType().dropParameterTypes(start, end));
    }

    /**
     * Returns a new call site descriptor that is identical to the passed one, except that it has a single parameter
     * type changed in its method type.
     * @param desc the original call site descriptor
     * @param num index of the parameter to change
     * @param nptype the new parameter type
     * @return a new call site descriptor with modified method type
     */
    public static CallSiteDescriptor changeParameterType(final CallSiteDescriptor desc, final int num, final Class<?> nptype) {
        return desc.changeMethodType(desc.getMethodType().changeParameterType(num, nptype));
    }

    /**
     * Returns a new call site descriptor that is identical to the passed one, except that it has the return type
     * changed in its method type.
     * @param desc the original call site descriptor
     * @param nrtype the new return type
     * @return a new call site descriptor with modified method type
     */
    public static CallSiteDescriptor changeReturnType(final CallSiteDescriptor desc, final Class<?> nrtype) {
        return desc.changeMethodType(desc.getMethodType().changeReturnType(nrtype));
    }

    /**
     * Returns a new call site descriptor that is identical to the passed one, except that it has additional parameter
     * types inserted into its method type.
     * @param desc the original call site descriptor
     * @param num index at which the new parameters are inserted
     * @param ptypesToInsert the new types to insert
     * @return a new call site descriptor with modified method type
     */
    public static CallSiteDescriptor insertParameterTypes(final CallSiteDescriptor desc, final int num, final Class<?>... ptypesToInsert) {
        return desc.changeMethodType(desc.getMethodType().insertParameterTypes(num, ptypesToInsert));
    }

    /**
     * Returns a new call site descriptor that is identical to the passed one, except that it has additional parameter
     * types inserted into its method type.
     * @param desc the original call site descriptor
     * @param num index at which the new parameters are inserted
     * @param ptypesToInsert the new types to insert
     * @return a new call site descriptor with modified method type
     */
    public static CallSiteDescriptor insertParameterTypes(final CallSiteDescriptor desc, final int num, final List<Class<?>> ptypesToInsert) {
        return desc.changeMethodType(desc.getMethodType().insertParameterTypes(num, ptypesToInsert));
    }
} public class Guards {
    private static final Logger LOG = Logger
            .getLogger(Guards.class.getName(), "org.dynalang.dynalink.support.messages");

    private Guards() {
    }

    /**
     * Creates a guard method handle with arguments of a specified type, but with boolean return value. When invoked, it
     * returns true if the first argument is of the specified class (exactly of it, not a subclass). The rest of the
     * arguments will be ignored.
     *
     * @param clazz the class of the first argument to test for
     * @param type the method type
     * @return a method handle testing whether its first argument is of the specified class.
     */
    @SuppressWarnings("boxing")
    public static MethodHandle isOfClass(final Class<?> clazz, final MethodType type) {
        final Class<?> declaredType = type.parameterType(0);
        if(clazz == declaredType) {
            LOG.log(Level.WARNING, "isOfClassGuardAlwaysTrue", new Object[] { clazz.getName(), 0, type, DynamicLinker.getLinkedCallSiteLocation() });
            return constantTrue(type);
        }
        if(!declaredType.isAssignableFrom(clazz)) {
            LOG.log(Level.WARNING, "isOfClassGuardAlwaysFalse", new Object[] { clazz.getName(), 0, type, DynamicLinker.getLinkedCallSiteLocation() });
            return constantFalse(type);
        }
        return getClassBoundArgumentTest(IS_OF_CLASS, clazz, 0, type);
    }

    /**
     * Creates a method handle with arguments of a specified type, but with boolean return value. When invoked, it
     * returns true if the first argument is instance of the specified class or its subclass). The rest of the arguments
     * will be ignored.
     *
     * @param clazz the class of the first argument to test for
     * @param type the method type
     * @return a method handle testing whether its first argument is of the specified class or subclass.
     */
    public static MethodHandle isInstance(final Class<?> clazz, final MethodType type) {
        return isInstance(clazz, 0, type);
    }

    /**
     * Creates a method handle with arguments of a specified type, but with boolean return value. When invoked, it
     * returns true if the n'th argument is instance of the specified class or its subclass). The rest of the arguments
     * will be ignored.
     *
     * @param clazz the class of the first argument to test for
     * @param pos the position on the argument list to test
     * @param type the method type
     * @return a method handle testing whether its first argument is of the specified class or subclass.
     */
    @SuppressWarnings("boxing")
    public static MethodHandle isInstance(final Class<?> clazz, final int pos, final MethodType type) {
        final Class<?> declaredType = type.parameterType(pos);
        if(clazz.isAssignableFrom(declaredType)) {
            LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { clazz.getName(), pos, type, DynamicLinker.getLinkedCallSiteLocation() });
            return constantTrue(type);
        }
        if(!declaredType.isAssignableFrom(clazz)) {
            LOG.log(Level.WARNING, "isInstanceGuardAlwaysFalse", new Object[] { clazz.getName(), pos, type, DynamicLinker.getLinkedCallSiteLocation() });
            return constantFalse(type);
        }
        return getClassBoundArgumentTest(IS_INSTANCE, clazz, pos, type);
    }

    /**
     * Creates a method handle that returns true if the argument in the specified position is a Java array.
     *
     * @param pos the position in the argument lit
     * @param type the method type of the handle
     * @return a method handle that returns true if the argument in the specified position is a Java array; the rest of
     * the arguments are ignored.
     */
    @SuppressWarnings("boxing")
    public static MethodHandle isArray(final int pos, final MethodType type) {
        final Class<?> declaredType = type.parameterType(pos);
        if(declaredType.isArray()) {
            LOG.log(Level.WARNING, "isArrayGuardAlwaysTrue", new Object[] { pos, type, DynamicLinker.getLinkedCallSiteLocation() });
            return constantTrue(type);
        }
        if(!declaredType.isAssignableFrom(Object[].class)) {
            LOG.log(Level.WARNING, "isArrayGuardAlwaysFalse", new Object[] { pos, type, DynamicLinker.getLinkedCallSiteLocation() });
            return constantFalse(type);
        }
        return asType(IS_ARRAY, pos, type);
    }

    /**
     * Return true if it is safe to strongly reference a class from the referred class loader from a class associated
     * with the referring class loader without risking a class loader memory leak.
     *
     * @param referrerLoader the referrer class loader
     * @param referredLoader the referred class loader
     * @return true if it is safe to strongly reference the class
     */
    public static boolean canReferenceDirectly(final ClassLoader referrerLoader, final ClassLoader referredLoader) {
        if(referredLoader == null) {
            // Can always refer directly to a system class
            return true;
        }
        if(referrerLoader == null) {
            // System classes can't refer directly to any non-system class
            return false;
        }
        // Otherwise, can only refer directly to classes residing in same or
        // parent class loader.

        ClassLoader referrer = referrerLoader;
        do {
            if(referrer == referredLoader) {
                return true;
            }
            referrer = referrer.getParent();
        } while(referrer != null);
        return false;
    }

    private static MethodHandle getClassBoundArgumentTest(final MethodHandle test, final Class<?> clazz, final int pos, final MethodType type) {
        // Bind the class to the first argument of the test
        return asType(test.bindTo(clazz), pos, type);
    }

    /**
     * Takes a guard-test method handle, and adapts it to the requested type, returning a boolean. Only applies
     * conversions as per {@link MethodHandle#asType(MethodType)}.
     * @param test the test method handle
     * @param type the type to adapt the method handle to
     * @return the adapted method handle
     */
    public static MethodHandle asType(final MethodHandle test, final MethodType type) {
        return test.asType(getTestType(test, type));
    }

    /**
     * Takes a guard-test method handle, and adapts it to the requested type, returning a boolean. Applies the passed
     * {@link LinkerServices} object's {@link LinkerServices#asType(MethodHandle, MethodType)}.
     * @param linkerServices the linker services to use for type conversions
     * @param test the test method handle
     * @param type the type to adapt the method handle to
     * @return the adapted method handle
     */
    public static MethodHandle asType(final LinkerServices linkerServices, final MethodHandle test, final MethodType type) {
        return linkerServices.asType(test, getTestType(test, type));
    }

    private static MethodType getTestType(final MethodHandle test, final MethodType type) {
        return type.dropParameterTypes(test.type().parameterCount(),
                type.parameterCount()).changeReturnType(boolean.class);
    }

    private static MethodHandle asType(final MethodHandle test, final int pos, final MethodType type) {
        assert test != null;
        assert type != null;
        assert type.parameterCount() > 0;
        assert pos >= 0 && pos < type.parameterCount();
        assert test.type().parameterCount() == 1;
        assert test.type().returnType() == Boolean.TYPE;
        return MethodHandles.permuteArguments(test.asType(test.type().changeParameterType(0, type.parameterType(pos))),
                type.changeReturnType(Boolean.TYPE), new int[] { pos });
    }

    private static final MethodHandle IS_INSTANCE = Lookup.PUBLIC.findVirtual(Class.class, "isInstance",
            MethodType.methodType(Boolean.TYPE, Object.class));

    private static final MethodHandle IS_OF_CLASS;
    private static final MethodHandle IS_ARRAY;
    private static final MethodHandle IS_IDENTICAL;
    private static final MethodHandle IS_NULL;
    private static final MethodHandle IS_NOT_NULL;

    static {
        final Lookup lookup = new Lookup(MethodHandles.lookup());

        IS_OF_CLASS  = lookup.findOwnStatic("isOfClass",   Boolean.TYPE, Class.class, Object.class);
        IS_ARRAY     = lookup.findOwnStatic("isArray",     Boolean.TYPE, Object.class);
        IS_IDENTICAL = lookup.findOwnStatic("isIdentical", Boolean.TYPE, Object.class, Object.class);
        IS_NULL      = lookup.findOwnStatic("isNull",      Boolean.TYPE, Object.class);
        IS_NOT_NULL  = lookup.findOwnStatic("isNotNull",   Boolean.TYPE, Object.class);
    }

    /**
     * Creates a guard method that tests its only argument for being of an exact particular class.
     * @param clazz the class to test for.
     * @return the desired guard method.
     */
    public static MethodHandle getClassGuard(final Class<?> clazz) {
        return IS_OF_CLASS.bindTo(clazz);
    }

    /**
     * Creates a guard method that tests its only argument for being an instance of a particular class.
     * @param clazz the class to test for.
     * @return the desired guard method.
     */
    public static MethodHandle getInstanceOfGuard(final Class<?> clazz) {
        return IS_INSTANCE.bindTo(clazz);
    }

    /**
     * Creates a guard method that tests its only argument for being referentially identical to another object
     * @param obj the object used as referential identity test
     * @return the desired guard method.
     */
    public static MethodHandle getIdentityGuard(final Object obj) {
        return IS_IDENTICAL.bindTo(obj);
    }

    /**
     * Returns a guard that tests whether the first argument is null.
     * @return a guard that tests whether the first argument is null.
     */
    public static MethodHandle isNull() {
        return IS_NULL;
    }

    /**
     * Returns a guard that tests whether the first argument is not null.
     * @return a guard that tests whether the first argument is not null.
     */
    public static MethodHandle isNotNull() {
        return IS_NOT_NULL;
    }

    @SuppressWarnings("unused")
    private static boolean isNull(final Object obj) {
        return obj == null;
    }

    @SuppressWarnings("unused")
    private static boolean isNotNull(final Object obj) {
        return obj != null;
    }

    @SuppressWarnings("unused")
    private static boolean isArray(final Object o) {
        return o != null && o.getClass().isArray();
    }

    @SuppressWarnings("unused")
    private static boolean isOfClass(final Class<?> c, final Object o) {
        return o != null && o.getClass() == c;
    }

    @SuppressWarnings("unused")
    private static boolean isIdentical(final Object o1, final Object o2) {
        return o1 == o2;
    }

    private static MethodHandle constantTrue(final MethodType type) {
        return constantBoolean(Boolean.TRUE, type);
    }

    private static MethodHandle constantFalse(final MethodType type) {
        return constantBoolean(Boolean.FALSE, type);
    }

    private static MethodHandle constantBoolean(final Boolean value, final MethodType type) {
        return MethodHandles.permuteArguments(MethodHandles.constant(Boolean.TYPE, value),
                type.changeReturnType(Boolean.TYPE));
    }
} public class LinkRequestImpl implements LinkRequest {

    private final CallSiteDescriptor callSiteDescriptor;
    private final Object callSiteToken;
    private final Object[] arguments;
    private final boolean callSiteUnstable;
    private final int linkCount;
    
    /**
     * Creates a new link request.
     *
     * @param callSiteDescriptor the descriptor for the call site being linked
     * @param callSiteToken the opaque token for the call site being linked.
     * @param linkCount how many times this callsite has been linked/relinked
     * @param callSiteUnstable true if the call site being linked is considered unstable
     * @param arguments the arguments for the invocation
     */
    public LinkRequestImpl(final CallSiteDescriptor callSiteDescriptor, final Object callSiteToken, final int linkCount, final boolean callSiteUnstable, final Object... arguments) {
        this.callSiteDescriptor = callSiteDescriptor;
        this.callSiteToken = callSiteToken;
        this.linkCount = linkCount;
        this.callSiteUnstable = callSiteUnstable;
        this.arguments = arguments;
    }

    @Override
    public Object[] getArguments() {
        return arguments != null ? arguments.clone() : null;
    }

    @Override
    public Object getReceiver() {
        return arguments != null && arguments.length > 0 ? arguments[0] : null;
    }

    @Override
    public CallSiteDescriptor getCallSiteDescriptor() {
        return callSiteDescriptor;
    }

    @Override
    public Object getCallSiteToken() {
        return callSiteToken;
    }

    @Override
    public boolean isCallSiteUnstable() {
        return callSiteUnstable;
    }

    @Override
    public int getLinkCount() {
    	return linkCount;
    }
    
    @Override
    public LinkRequest withoutRuntimeContext() {
        return this;
    }

    @Override
    public LinkRequest replaceArguments(final CallSiteDescriptor newCallSiteDescriptor, final Object[] newArguments) {
        return new LinkRequestImpl(newCallSiteDescriptor, callSiteToken, linkCount, callSiteUnstable, newArguments);
    }
} public class LinkerServicesImpl implements LinkerServices {

    private static final RuntimePermission GET_CURRENT_LINK_REQUEST = new RuntimePermission("dynalink.getCurrentLinkRequest");
    private static final ThreadLocal<LinkRequest> threadLinkRequest = new ThreadLocal<>();

    private final TypeConverterFactory typeConverterFactory;
    private final GuardingDynamicLinker topLevelLinker;
    private final MethodHandleTransformer internalObjectsFilter;

    /**
     * Creates a new linker services object.
     *
     * @param typeConverterFactory the type converter factory exposed by the services.
     * @param topLevelLinker the top level linker used by the services.
     * @param internalObjectsFilter a method handle transformer that is supposed to act as the implementation of this
     * services' {@link #filterInternalObjects(java.lang.invoke.MethodHandle)} method.
     */
    public LinkerServicesImpl(final TypeConverterFactory typeConverterFactory,
            final GuardingDynamicLinker topLevelLinker, final MethodHandleTransformer internalObjectsFilter) {
        this.typeConverterFactory = typeConverterFactory;
        this.topLevelLinker = topLevelLinker;
        this.internalObjectsFilter = internalObjectsFilter;
    }

    @Override
    public boolean canConvert(final Class<?> from, final Class<?> to) {
        return typeConverterFactory.canConvert(from, to);
    }

    @Override
    public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
        return typeConverterFactory.asType(handle, fromType);
    }

    @Override
    public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) {
        return Implementation.asTypeLosslessReturn(this, handle, fromType);
    }

    @Override
    public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
        return typeConverterFactory.getTypeConverter(sourceType, targetType);
    }

    @Override
    public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
        return typeConverterFactory.compareConversion(sourceType, targetType1, targetType2);
    }

    @Override
    public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest) throws Exception {
        final LinkRequest prevLinkRequest = threadLinkRequest.get();
        threadLinkRequest.set(linkRequest);
        try {
            return topLevelLinker.getGuardedInvocation(linkRequest, this);
        } finally {
            threadLinkRequest.set(prevLinkRequest);
        }
    }

    @Override
    public MethodHandle filterInternalObjects(final MethodHandle target) {
        return internalObjectsFilter != null ? internalObjectsFilter.transform(target) : target;
    }

    /**
     * Returns the currently processed link request, or null if the method is invoked outside of the linking process.
     * @return the currently processed link request, or null.
     * @throws SecurityException if the calling code doesn't have the {@code "dynalink.getCurrentLinkRequest"} runtime
     * permission.
     */
    public static LinkRequest getCurrentLinkRequest() {
        final SecurityManager sm = System.getSecurityManager();
        if(sm != null) {
            sm.checkPermission(GET_CURRENT_LINK_REQUEST);
        }
        return threadLinkRequest.get();
    }
} public class Lookup {
    private final MethodHandles.Lookup lookup;

    /**
     * Creates a new instance, bound to an instance of {@link java.lang.invoke.MethodHandles.Lookup}.
     *
     * @param lookup the {@link java.lang.invoke.MethodHandles.Lookup} it delegates to.
     */
    public Lookup(final MethodHandles.Lookup lookup) {
        this.lookup = lookup;
    }

    /**
     * A canonical Lookup object that wraps {@link MethodHandles#publicLookup()}.
     */
    public static final Lookup PUBLIC = new Lookup(MethodHandles.publicLookup());

    /**
     * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)}, converting any encountered
     * {@link IllegalAccessException} into an {@link IllegalAccessError}.
     *
     * @param m the method to unreflect
     * @return the unreflected method handle.
     */
    public MethodHandle unreflect(final Method m) {
        return unreflect(lookup, m);
    }

    /**
     * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflect(Method)}, converting any encountered
     * {@link IllegalAccessException} into an {@link IllegalAccessError}.
     *
     * @param lookup the lookup used to unreflect
     * @param m the method to unreflect
     * @return the unreflected method handle.
     */
    public static MethodHandle unreflect(final MethodHandles.Lookup lookup, final Method m) {
        try {
            return lookup.unreflect(m);
        } catch(final IllegalAccessException e) {
            final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect method " + m);
            ee.initCause(e);
            throw ee;
        }
    }

    /**
     * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectGetter(Field)}, converting any encountered
     * {@link IllegalAccessException} into an {@link IllegalAccessError}.
     *
     * @param f the field for which a getter is unreflected
     * @return the unreflected field getter handle.
     */
    public MethodHandle unreflectGetter(final Field f) {
        try {
            return lookup.unreflectGetter(f);
        } catch(final IllegalAccessException e) {
            final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect getter for field " + f);
            ee.initCause(e);
            throw ee;
        }
    }

    /**
     * Performs a {@link java.lang.invoke.MethodHandles.Lookup#findGetter(Class, String, Class)}, converting any
     * encountered {@link IllegalAccessException} into an {@link IllegalAccessError} and {@link NoSuchFieldException}
     * into a {@link NoSuchFieldError}.
     *
     * @param refc the class declaring the field
     * @param name the name of the field
     * @param type the type of the field
     * @return the unreflected field getter handle.
     * @throws IllegalAccessError if the field is inaccessible.
     * @throws NoSuchFieldError if the field does not exist.
     */
    public MethodHandle findGetter(final Class<?>refc, final String name, final Class<?> type) {
        try {
            return lookup.findGetter(refc, name, type);
        } catch(final IllegalAccessException e) {
            final IllegalAccessError ee = new IllegalAccessError("Failed to access getter for field " + refc.getName() +
                    "." + name + " of type " + type.getName());
            ee.initCause(e);
            throw ee;
        } catch(final NoSuchFieldException e) {
            final NoSuchFieldError ee = new NoSuchFieldError("Failed to find getter for field " + refc.getName() +
                    "." + name + " of type " + type.getName());
            ee.initCause(e);
            throw ee;
        }
    }

    /**
     * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectSetter(Field)}, converting any encountered
     * {@link IllegalAccessException} into an {@link IllegalAccessError}.
     *
     * @param f the field for which a setter is unreflected
     * @return the unreflected field setter handle.
     */
    public MethodHandle unreflectSetter(final Field f) {
        try {
            return lookup.unreflectSetter(f);
        } catch(final IllegalAccessException e) {
            final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect setter for field " + f);
            ee.initCause(e);
            throw ee;
        }
    }

    /**
     * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)}, converting any
     * encountered {@link IllegalAccessException} into an {@link IllegalAccessError}.
     *
     * @param c the constructor to unreflect
     * @return the unreflected constructor handle.
     */
    public MethodHandle unreflectConstructor(final Constructor<?> c) {
        return unreflectConstructor(lookup, c);
    }

    /**
     * Performs a {@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor(Constructor)}, converting any
     * encountered {@link IllegalAccessException} into an {@link IllegalAccessError}.
     *
     * @param lookup the lookup used to unreflect
     * @param c the constructor to unreflect
     * @return the unreflected constructor handle.
     */
    public static MethodHandle unreflectConstructor(final MethodHandles.Lookup lookup, final Constructor<?> c) {
        try {
            return lookup.unreflectConstructor(c);
        } catch(final IllegalAccessException e) {
            final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect constructor " + c);
            ee.initCause(e);
            throw ee;
        }
    }

    /**
     * Performs a findSpecial on the underlying lookup. Converts any encountered {@link IllegalAccessException} into an
     * {@link IllegalAccessError} and a {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
     *
     * @param declaringClass class declaring the method
     * @param name the name of the method
     * @param type the type of the method
     * @return a method handle for the method
     * @throws IllegalAccessError if the method is inaccessible.
     * @throws NoSuchMethodError if the method does not exist.
     */
    public MethodHandle findSpecial(final Class<?> declaringClass, final String name, final MethodType type) {
        try {
            return lookup.findSpecial(declaringClass, name, type, declaringClass);
        } catch(final IllegalAccessException e) {
            final IllegalAccessError ee = new IllegalAccessError("Failed to access special method " + methodDescription(
                    declaringClass, name, type));
            ee.initCause(e);
            throw ee;
        } catch(final NoSuchMethodException e) {
            final NoSuchMethodError ee = new NoSuchMethodError("Failed to find special method " + methodDescription(
                    declaringClass, name, type));
            ee.initCause(e);
            throw ee;
        }
    }

    private static String methodDescription(final Class<?> declaringClass, final String name, final MethodType type) {
        return declaringClass.getName() + "#" + name + type;
    }

    /**
     * Performs a findStatic on the underlying lookup. Converts any encountered {@link IllegalAccessException} into an
     * {@link IllegalAccessError} and a {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
     *
     * @param declaringClass class declaring the method
     * @param name the name of the method
     * @param type the type of the method
     * @return a method handle for the method
     * @throws IllegalAccessError if the method is inaccessible.
     * @throws NoSuchMethodError if the method does not exist.
     */
    public MethodHandle findStatic(final Class<?> declaringClass, final String name, final MethodType type) {
        try {
            return lookup.findStatic(declaringClass, name, type);
        } catch(final IllegalAccessException e) {
            final IllegalAccessError ee = new IllegalAccessError("Failed to access static method " + methodDescription(
                    declaringClass, name, type));
            ee.initCause(e);
            throw ee;
        } catch(final NoSuchMethodException e) {
            final NoSuchMethodError ee = new NoSuchMethodError("Failed to find static method " + methodDescription(
                    declaringClass, name, type));
            ee.initCause(e);
            throw ee;
        }
    }

    /**
     * Performs a findVirtual on the underlying lookup. Converts any encountered {@link IllegalAccessException} into an
     * {@link IllegalAccessError} and a {@link NoSuchMethodException} into a {@link NoSuchMethodError}.
     *
     * @param declaringClass class declaring the method
     * @param name the name of the method
     * @param type the type of the method
     * @return a method handle for the method
     * @throws IllegalAccessError if the method is inaccessible.
     * @throws NoSuchMethodError if the method does not exist.
     */
    public MethodHandle findVirtual(final Class<?> declaringClass, final String name, final MethodType type) {
        try {
            return lookup.findVirtual(declaringClass, name, type);
        } catch(final IllegalAccessException e) {
            final IllegalAccessError ee = new IllegalAccessError("Failed to access virtual method " + methodDescription(
                    declaringClass, name, type));
            ee.initCause(e);
            throw ee;
        } catch(final NoSuchMethodException e) {
            final NoSuchMethodError ee = new NoSuchMethodError("Failed to find virtual method " + methodDescription(
                    declaringClass, name, type));
            ee.initCause(e);
            throw ee;
        }
    }

    /**
     * Given a lookup, finds using {@link #findSpecial(Class, String, MethodType)} a method on that lookup's class.
     * Useful in classes' code for convenient linking to their own privates.
     * @param lookup the lookup for the class
     * @param name the name of the method
     * @param rtype the return type of the method
     * @param ptypes the parameter types of the method
     * @return the method handle for the method
     */
    public static MethodHandle findOwnSpecial(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) {
        return new Lookup(lookup).findOwnSpecial(name, rtype, ptypes);
    }


    /**
     * Finds using {@link #findSpecial(Class, String, MethodType)} a method on that lookup's class. Useful in classes'
     * code for convenient linking to their own privates. It's easier to use than {@code findSpecial} in that you can
     * just list the parameter types, and don't have to specify lookup class.
     * @param name the name of the method
     * @param rtype the return type of the method
     * @param ptypes the parameter types of the method
     * @return the method handle for the method
     */
    public MethodHandle findOwnSpecial(final String name, final Class<?> rtype, final Class<?>... ptypes) {
        return findSpecial(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes));
    }

    /**
     * Given a lookup, finds using {@link #findStatic(Class, String, MethodType)} a method on that lookup's class.
     * Useful in classes' code for convenient linking to their own privates. It's easier to use than {@code findStatic}
     * in that you can just list the parameter types, and don't have to specify lookup class.
     * @param lookup the lookup for the class
     * @param name the name of the method
     * @param rtype the return type of the method
     * @param ptypes the parameter types of the method
     * @return the method handle for the method
     */
    public static MethodHandle findOwnStatic(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) {
        return new Lookup(lookup).findOwnStatic(name, rtype, ptypes);
    }

    /**
     * Finds using {@link #findStatic(Class, String, MethodType)} a method on that lookup's class. Useful in classes'
     * code for convenient linking to their own privates. It's easier to use than {@code findStatic} in that you can
     * just list the parameter types, and don't have to specify lookup class.
     * @param name the name of the method
     * @param rtype the return type of the method
     * @param ptypes the parameter types of the method
     * @return the method handle for the method
     */
    public MethodHandle findOwnStatic(final String name, final Class<?> rtype, final Class<?>... ptypes) {
        return findStatic(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes));
    }
} public class TypeConverterFactory {

    private final GuardingTypeConverterFactory[] factories;
    private final ConversionComparator[] comparators;
    private final MethodTypeConversionStrategy autoConversionStrategy;

    private final ClassValue<ClassMap<MethodHandle>> converterMap = new ClassValue<ClassMap<MethodHandle>>() {
        @Override
        protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) {
            return new ClassMap<MethodHandle>(getClassLoader(sourceType)) {
                @Override
                protected MethodHandle computeValue(final Class<?> targetType) {
                    try {
                        return createConverter(sourceType, targetType);
                    } catch (final RuntimeException e) {
                        throw e;
                    } catch (final Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            };
        }
    };

    private final ClassValue<ClassMap<MethodHandle>> converterIdentityMap = new ClassValue<ClassMap<MethodHandle>>() {
        @Override
        protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) {
            return new ClassMap<MethodHandle>(getClassLoader(sourceType)) {
                @Override
                protected MethodHandle computeValue(final Class<?> targetType) {
                    if(!canAutoConvert(sourceType, targetType)) {
                        final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType);
                        if(converter != IDENTITY_CONVERSION) {
                            return converter;
                        }
                    }
                    return IDENTITY_CONVERSION.asType(MethodType.methodType(targetType, sourceType));
                }
            };
        }
    };

    private final ClassValue<ClassMap<Boolean>> canConvert = new ClassValue<ClassMap<Boolean>>() {
        @Override
        protected ClassMap<Boolean> computeValue(final Class<?> sourceType) {
            return new ClassMap<Boolean>(getClassLoader(sourceType)) {
                @Override
                protected Boolean computeValue(final Class<?> targetType) {
                    try {
                        return getTypeConverterNull(sourceType, targetType) != null;
                    } catch (final RuntimeException e) {
                        throw e;
                    } catch (final Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            };
        }
    };

    private static final ClassLoader getClassLoader(final Class<?> clazz) {
        return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
            @Override
            public ClassLoader run() {
                return clazz.getClassLoader();
            }
        }, ClassLoaderGetterContextProvider.GET_CLASS_LOADER_CONTEXT);
    }

    /**
     * Creates a new type converter factory from the available {@link GuardingTypeConverterFactory} instances.
     *
     * @param factories the {@link GuardingTypeConverterFactory} instances to compose.
     * @param autoConversionStrategy conversion strategy for automatic type conversions. After
     * {@link #asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)} has applied all custom
     * conversions to a method handle, it still needs to effect
     * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method invocation conversions} that
     * can usually be automatically applied as per
     * {@link java.lang.invoke.MethodHandle#asType(java.lang.invoke.MethodType)}.
     * However, sometimes language runtimes will want to customize even those conversions for their own call
     * sites. A typical example is allowing unboxing of null return values, which is by default prohibited by
     * ordinary {@code MethodHandles.asType}. In this case, a language runtime can install its own custom
     * automatic conversion strategy, that can deal with null values. Note that when the strategy's
     * {@link MethodTypeConversionStrategy#asType(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType)}
     * is invoked, the custom language conversions will already have been applied to the method handle, so by
     * design the difference between the handle's current method type and the desired final type will always
     * only be ones that can be subjected to method invocation conversions. Can be null, in which case no
     * custom strategy is employed.
     */
    public TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories,
            final MethodTypeConversionStrategy autoConversionStrategy) {
        final List<GuardingTypeConverterFactory> l = new LinkedList<>();
        final List<ConversionComparator> c = new LinkedList<>();
        for(final GuardingTypeConverterFactory factory: factories) {
            l.add(factory);
            if(factory instanceof ConversionComparator) {
                c.add((ConversionComparator)factory);
            }
        }
        this.factories = l.toArray(new GuardingTypeConverterFactory[l.size()]);
        this.comparators = c.toArray(new ConversionComparator[c.size()]);
        this.autoConversionStrategy = autoConversionStrategy;
    }

    /**
     * Similar to {@link MethodHandle#asType(MethodType)} except it also hooks in method handles produced by
     * {@link GuardingTypeConverterFactory} implementations, providing for language-specific type coercing of
     * parameters. For all conversions that are not a JLS method invocation conversion it'll insert
     * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
     * provided by {@link GuardingTypeConverterFactory} implementations. For the remaining JLS method invocation
     * conversions, it will invoke {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)} first
     * if an automatic conversion strategy was specified in the
     * {@link #TypeConverterFactory(Iterable, MethodTypeConversionStrategy) constructor}, and finally apply
     * {@link MethodHandle#asType(MethodType)} for any remaining conversions.
     *
     * @param handle target method handle
     * @param fromType the types of source arguments
     * @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
     * {@link MethodTypeConversionStrategy#asType(MethodHandle, MethodType)}, and
     * {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
     * {@link GuardingTypeConverterFactory} produced type converters as filters.
     */
    public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
        MethodHandle newHandle = handle;
        final MethodType toType = newHandle.type();
        final int l = toType.parameterCount();
        if(l != fromType.parameterCount()) {
            throw new WrongMethodTypeException("Parameter counts differ: " + handle.type() + " vs. " + fromType);
        }
        int pos = 0;
        final List<MethodHandle> converters = new LinkedList<>();
        for(int i = 0; i < l; ++i) {
            final Class<?> fromParamType = fromType.parameterType(i);
            final Class<?> toParamType = toType.parameterType(i);
            if(canAutoConvert(fromParamType, toParamType)) {
                newHandle = applyConverters(newHandle, pos, converters);
            } else {
                final MethodHandle converter = getTypeConverterNull(fromParamType, toParamType);
                if(converter != null) {
                    if(converters.isEmpty()) {
                        pos = i;
                    }
                    converters.add(converter);
                } else {
                    newHandle = applyConverters(newHandle, pos, converters);
                }
            }
        }
        newHandle = applyConverters(newHandle, pos, converters);

        // Convert return type
        final Class<?> fromRetType = fromType.returnType();
        final Class<?> toRetType = toType.returnType();
        if(fromRetType != Void.TYPE && toRetType != Void.TYPE) {
            if(!canAutoConvert(toRetType, fromRetType)) {
                final MethodHandle converter = getTypeConverterNull(toRetType, fromRetType);
                if(converter != null) {
                    newHandle = MethodHandles.filterReturnValue(newHandle, converter);
                }
            }
        }

        // Give change to automatic conversion strategy, if one is present.
        final MethodHandle autoConvertedHandle =
                autoConversionStrategy != null ? autoConversionStrategy.asType(newHandle, fromType) : newHandle;

        // Do a final asType for any conversions that remain.
        return autoConvertedHandle.asType(fromType);
    }

    private static MethodHandle applyConverters(final MethodHandle handle, final int pos, final List<MethodHandle> converters) {
        if(converters.isEmpty()) {
            return handle;
        }
        final MethodHandle newHandle =
                MethodHandles.filterArguments(handle, pos, converters.toArray(new MethodHandle[converters.size()]));
        converters.clear();
        return newHandle;
    }

    /**
     * Returns true if there might exist a conversion between the requested types (either an automatic JVM conversion,
     * or one provided by any available {@link GuardingTypeConverterFactory}), or false if there definitely does not
     * exist a conversion between the requested types. Note that returning true does not guarantee that the conversion
     * will succeed at runtime (notably, if the "from" or "to" types are sufficiently generic), but returning false
     * guarantees that it would fail.
     *
     * @param from the source type for the conversion
     * @param to the target type for the conversion
     * @return true if there can be a conversion, false if there can not.
     */
    public boolean canConvert(final Class<?> from, final Class<?> to) {
        return canAutoConvert(from, to) || canConvert.get(from).get(to).booleanValue();
    }

    /**
     * Determines which of the two type conversions from a source type to the two target types is preferred. This is
     * used for dynamic overloaded method resolution. If the source type is convertible to exactly one target type with
     * a method invocation conversion, it is chosen, otherwise available {@link ConversionComparator}s are consulted.
     * @param sourceType the source type.
     * @param targetType1 one potential target type
     * @param targetType2 another potential target type.
     * @return one of Comparison constants that establish which - if any - of the target types is preferable for the
     * conversion.
     */
    public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
        for(final ConversionComparator comparator: comparators) {
            final Comparison result = comparator.compareConversion(sourceType, targetType1, targetType2);
            if(result != Comparison.INDETERMINATE) {
                return result;
            }
        }
        if(TypeUtilities.isMethodInvocationConvertible(sourceType, targetType1)) {
            if(!TypeUtilities.isMethodInvocationConvertible(sourceType, targetType2)) {
                return Comparison.TYPE_1_BETTER;
            }
        } else if(TypeUtilities.isMethodInvocationConvertible(sourceType, targetType2)) {
            return Comparison.TYPE_2_BETTER;
        }
        return Comparison.INDETERMINATE;
    }

    /**
     * Determines whether it's safe to perform an automatic conversion between the source and target class.
     *
     * @param fromType convert from this class
     * @param toType convert to this class
     * @return true if it's safe to let MethodHandles.convertArguments() to handle this conversion.
     */
    /*private*/ static boolean canAutoConvert(final Class<?> fromType, final Class<?> toType) {
        return TypeUtilities.isMethodInvocationConvertible(fromType, toType);
    }

    /*private*/ MethodHandle getCacheableTypeConverterNull(final Class<?> sourceType, final Class<?> targetType) {
        final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType);
        return converter == IDENTITY_CONVERSION ? null : converter;
    }

    /*private*/ MethodHandle getTypeConverterNull(final Class<?> sourceType, final Class<?> targetType) {
        try {
            return getCacheableTypeConverterNull(sourceType, targetType);
        } catch(final NotCacheableConverter e) {
            return e.converter;
        }
    }

    /*private*/ MethodHandle getCacheableTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
        return converterMap.get(sourceType).get(targetType);
    }

    /**
     * Given a source and target type, returns a method handle that converts between them. Never returns null; in worst
     * case it will return an identity conversion (that might fail for some values at runtime). You can use this method
     * if you have a piece of your program that is written in Java, and you need to reuse existing type conversion
     * machinery in a non-invokedynamic context.
     * @param sourceType the type to convert from
     * @param targetType the type to convert to
     * @return a method handle performing the conversion.
     */
    public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
        try {
            return converterIdentityMap.get(sourceType).get(targetType);
        } catch(final NotCacheableConverter e) {
            return e.converter;
        }
    }

    /*private*/ MethodHandle createConverter(final Class<?> sourceType, final Class<?> targetType) throws Exception {
        final MethodType type = MethodType.methodType(targetType, sourceType);
        final MethodHandle identity = IDENTITY_CONVERSION.asType(type);
        MethodHandle last = identity;
        boolean cacheable = true;
        for(int i = factories.length; i-- > 0;) {
            final GuardedTypeConversion next = factories[i].convertToType(sourceType, targetType);
            if(next != null) {
                cacheable = cacheable && next.isCacheable();
                final GuardedInvocation conversionInvocation = next.getConversionInvocation();
                conversionInvocation.assertType(type);
                last = conversionInvocation.compose(last);
            }
        }
        if(last == identity) {
            return IDENTITY_CONVERSION;
        }
        if(cacheable) {
            return last;
        }
        throw new NotCacheableConverter(last);
    }

    /*private*/ static final MethodHandle IDENTITY_CONVERSION = MethodHandles.identity(Object.class);

    @SuppressWarnings("serial")
    private static class NotCacheableConverter extends RuntimeException {
        final MethodHandle converter;

        NotCacheableConverter(final MethodHandle converter) {
            super("", null, false, false);
            this.converter = converter;
        }
    }
}
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
import org.dynalang.dynalink.DynamicLinker;
import org.dynalang.dynalink.DynamicLinkerFactory;
import org.dynalang.dynalink.LinkerServicesFactory;
import org.dynalang.dynalink.MonomorphicCallSite;
import org.dynalang.dynalink.linker.GuardedInvocation;
import org.dynalang.dynalink.linker.GuardedTypeConversion;
import org.dynalang.dynalink.linker.GuardingTypeConverterFactory;
import org.dynalang.dynalink.linker.LinkerServices;
import org.dynalang.dynalink.linker.TypeBasedGuardingDynamicLinker;
import org.dynalang.dynalink.support.CallSiteDescriptorFactory;
import org.dynalang.dynalink.support.Guards;
import org.dynalang.dynalink.support.LinkRequestImpl;
import org.dynalang.dynalink.support.LinkerServicesImpl;
import org.dynalang.dynalink.support.Lookup;
import org.dynalang.dynalink.support.TypeConverterFactory;
/*
   Copyright 2009-2013 Attila Szegedi

   Licensed under both the Apache License, Version 2.0 (the "Apache License")
   and the BSD License (the "BSD License"), with licensee being free to
   choose either of the two at their discretion.

   You may not use this file except in compliance with either the Apache
   License or the BSD License.

   If you choose to use this file in compliance with the Apache License, the
   following notice applies to you:

       You may obtain a copy of the Apache License at

           http://www.apache.org/licenses/LICENSE-2.0

       Unless required by applicable law or agreed to in writing, software
       distributed under the License is distributed on an "AS IS" BASIS,
       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
       implied. See the License for the specific language governing
       permissions and limitations under the License.

   If you choose to use this file in compliance with the BSD License, the
   following notice applies to you:

       Redistribution and use in source and binary forms, with or without
       modification, are permitted provided that the following conditions are
       met:
       * Redistributions of source code must retain the above copyright
         notice, this list of conditions and the following disclaimer.
       * Redistributions in binary form must reproduce the above copyright
         notice, this list of conditions and the following disclaimer in the
         documentation and/or other materials provided with the distribution.
       * Neither the name of the copyright holder nor the names of
         contributors may be used to endorse or promote products derived from
         this software without specific prior written permission.

       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
       IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
       TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
       BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
       CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
       SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
       BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
       WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
       OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
       ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.dynalang.dynalink.beans;


/**
 *
 * @author Attila Szegedi
 */
public class TestOverloadedDynamicMethod extends TestCase {
    private BeanLinker linker;
    private LinkerServices linkerServices;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        linker = new BeanLinker(Test1.class);
        linkerServices = LinkerServicesFactory.getLinkerServices(linker);
    }

    public void testNoneMatchSignature() {
        final DynamicMethod dm = linker.getDynamicMethod("add");
        assertTrue(dm instanceof OverloadedDynamicMethod);

        // No zero-arg adds
        assertNull(getInvocation(dm, MethodType.methodType(int.class, Object.class), null));

        // No single-arg String add
        MethodHandle inv =
                getInvocation(dm, MethodType.methodType(String.class, Object.class, String.class), linkerServices);
        assertNull(String.valueOf(inv), inv);
    }

    public static MethodHandle getInvocation(DynamicMethod dm, MethodType methodType, LinkerServices linkerServices) {
        return dm.getInvocation(CallSiteDescriptorFactory.create(MethodHandles.publicLookup(), "", methodType), linkerServices);
    }

    public void testExactMatchSignature() throws Throwable {
        final DynamicMethod dm = linker.getDynamicMethod("add");
        // Two-arg String add should make a match
        final MethodHandle mh = getInvocation(dm, MethodType.methodType(String.class, Object.class, String.class,
                String.class), linkerServices);
        assertNotNull(mh);
        // Must be able to invoke it with two strings
        assertEquals("x", mh.invokeWithArguments(new Test1(), "a", "b"));
        // Must not be able to invoke it with two ints
        try {
            mh.invokeWithArguments(new Test1(), 1, 2);
        } catch(ClassCastException e) {
            // This is expected
        }
    }

    public void testVeryGenericSignature() throws Throwable {
        final DynamicMethod dm = linker.getDynamicMethod("add");
        // Two-arg String add should make a match
        final MethodHandle mh = getInvocation(dm, MethodType.methodType(Object.class, Object.class, Object.class,
                Object.class), linkerServices);
        assertNotNull(mh);
        // Must be able to invoke it with two Strings
        assertEquals("x", mh.invokeWithArguments(new Test1(), "a", "b"));
        // Must be able to invoke it with two ints
        assertEquals(1, mh.invokeWithArguments(new Test1(), 1, 2));
        // Must be able to invoke it with explicitly packed varargs
        assertEquals(4, mh.invokeWithArguments(new Test1(), 1, new int[] { 2 }));
    }

    public void testStringFormat() throws Throwable {
        DynamicLinker linker = new DynamicLinkerFactory().createLinker();
        MonomorphicCallSite callSite = new MonomorphicCallSite(CallSiteDescriptorFactory.create(
                MethodHandles.publicLookup(), "dyn:callMethod:format", MethodType.methodType(Object.class,
                        Object.class, Object.class, Object.class, Object.class)));
        linker.link(callSite);
        System.out.println(callSite.dynamicInvoker().invokeWithArguments(StaticClass.forClass(String.class),
                "%4.0f %4.0f", 12f, 1f));
    }

    public void testIntOrDouble() throws Throwable {
        final DynamicMethod dm = linker.getDynamicMethod("intOrDouble");
        final MethodHandle mh = getInvocation(dm, MethodType.methodType(Object.class, Object.class, Object.class),
                linkerServices);
        assertNotNull(mh);
        assertEquals("int", mh.invokeWithArguments(new Test1(), 1));
        assertEquals("double", mh.invokeWithArguments(new Test1(), 1.0));
    }

    public void testStringOrDouble() throws Throwable {
        final DynamicMethod dm = linker.getDynamicMethod("stringOrDouble");
        final MethodHandle mh = getInvocation(dm, MethodType.methodType(Object.class, Object.class, Object.class),
                linkerServices);
        assertNotNull(mh);
        assertEquals("double", mh.invokeWithArguments(new Test1(), 1));
        assertEquals("double", mh.invokeWithArguments(new Test1(), 1.0));
    }

    public void testVarArg() throws Throwable {
        final DynamicMethod dm = linker.getDynamicMethod("boo");
        // we want to link to the one-arg invocation
        assertBooReturns(1, linkerServices, 1);

        // we want to link to the vararg invocation
        assertBooReturns(2, linkerServices);

        // we want to link to the vararg invocation
        assertBooReturns(2, linkerServices, 1, 2);

        // If we're linking with a converter that knows how to convert double to int, then we want to make sure we
        // don't link to the vararg (Class, int[]) invocation but to the (Class, int) invocation.
        final LinkerServices ls = new LinkerServicesImpl(new TypeConverterFactory(Collections.singleton(