Bootstrap method question
Rémi Forax
forax at univ-mlv.fr
Tue Jun 22 06:16:30 PDT 2010
Le 22/06/2010 12:40, Howard Lovatt a écrit :
> Rémi,
>
> Many thanks, collectArguments was indeed the bug. My multiple dispatch
> example is now working.
>
> As an aside I am still surprised that neither collectArguments or the
> call to the MethodHandle threw an exception.
>
> -- Howard.
>
It's a bug, collectArguments should throw an exception because
the last parameter type of the method handle is not an array
or a supertype of an array (Object, Serializable, Cloneable).
The open question is why it doesn't throw a ClassCastException ?
Rémi
> On 21 June 2010 18:15, Rémi Forax<forax at univ-mlv.fr> wrote:
>
>> I think I 've found the bug,
>> in Dispatcher you use collectArguments instead of convertArguments.
>>
>> Rémi
>>
>>
>> Le 21/06/2010 04:26, Howard Lovatt a écrit :
>>
>>> Hi Rémi,
>>>
>>> A more complete listing is:
>>>
>>> public final class CostCalculatorDispatcher {
>>> private static final Map<MethodType, MethodHandle> mhs = new
>>> HashMap<>();
>>>
>>> private static final String costName = "cost";
>>>
>>> private static final MethodHandle slowPathHandle =
>>> MethodHandles.lookup().findStatic(
>>> CostCalculatorDispatcher.class, "slowPath",
>>> MethodType.methodType( double.class, CallSite.class,
>>> CostCalculator.class, Item.class ) );
>>>
>>> public static void register( final MethodHandle from ) {
>>> Dispatchers.add( mhs, from, costName ); }
>>>
>>> public static CallSite bootstrap( final Class<?> notUsed, final
>>> String name, final MethodType type ) {
>>> Dispatchers.checkName( name, costName );
>>> return Dispatchers.dispatcher( slowPathHandle, type );
>>> }
>>>
>>> private static double slowPath( final CallSite cS, final
>>> CostCalculator cC, final Item i )
>>> throws Throwable {
>>> System.out.println( "cS = " + cS );
>>> System.out.println( "cC = " + cC );
>>> System.out.println( "i = " + i );
>>> final MethodHandle nearest = Dispatchers.nearest( mhs.values(),
>>> double.class, cC, i );
>>> System.out.println( "nearest = " + Dispatchers.toString( nearest )
>>> );
>>> final MethodHandle converted = MethodHandles.collectArguments(
>>> nearest, cS.getTarget().type() );
>>> System.out.println( "converted = " + Dispatchers.toString( converted )
>>> );
>>> cS.setTarget( converted );
>>> return converted.<double>invokeExact( cC, i );
>>> }
>>> }
>>>
>>>
>>>
>>>
>>> public final class Dispatchers {
>>> [snip]
>>>
>>> /**
>>> * Add given {@code MethodHandle} to given {@code Map} and throw an
>>> exception if it is already
>>> * in the {@code Map} or if it has the wrong name.
>>> *
>>> * @param mhs {@code Map} of {@code MethodType}s to
>>> {@code MethodHandle}s to be added to
>>> * @param mh {@code MethodHandle} to add to {@code Map}
>>> * @param expectedName the name {@code mh} should have
>>> *
>>> * @throws PECBug if {@code mh} is already in {@code mhs} or
>>> if the name of {@code mh}
>>> * isn't {@code expectedName}
>>> */
>>> public static void add( final Map<MethodType, MethodHandle> mhs,
>>> final MethodHandle mh, final String expectedName ) {
>>> if ( !mh.toString().equals( expectedName ) ) {
>>> throw new PECBug( "MethodHandle, " + toString( mh ) + ", doesn't
>>> have expected name, " + expectedName );
>>> }
>>> if ( mhs.put( mh.type(), mh ) != null ) {
>>> throw new PECBug( "Given MethodHandle, " + toString( mh ) + ",
>>> already in map, " + toString( mhs.values() ) );
>>> }
>>> }
>>>
>>> /**
>>> * Check that the name given matches the expected name.
>>> *
>>> * @param name given name
>>> * @param expectedName expected name
>>> *
>>> * @throws PECBug if names don't match
>>> */
>>> public static void checkName( final String name, final String
>>> expectedName ) throws PECBug {
>>> if ( !name.equals( expectedName ) ) {
>>> throw new PECBug( "Name, " + name + ", isn't " + expectedName );
>>> }
>>> }
>>>
>>> /**
>>> * Return a call site that calls the given {@code MethodHandle}, the
>>> purpose is to delay calling
>>> * an actual method until the runtime types are known. The given
>>> handle when called returns the
>>> * a handle to the nearest available method (in a
>>> symmetric-multiple-dispatch sense).
>>> * Details: wrap the given {@code MethodHandle} in another handle
>>> that binds the first
>>> * argument of the given handle to the returned {@code CallSite} and
>>> then set the site
>>> * to call this second handle and return the site.
>>> *
>>> * @param slowPath handle to a method that returns a handle to
>>> the nearest fit
>>> * multiple-dispatch method
>>> * @param type the declared, not dynamic, type of the
>>> required multiple dispatch method
>>> *
>>> * @return the {@code MethodHandle} wrapped in a call site and with
>>> its first argument bound to
>>> * the call site
>>> */
>>> public static CallSite dispatcher( final MethodHandle slowPath,
>>> final MethodType type) {
>>> final CallSite callSite = new CallSite();
>>> final MethodHandle bound = MethodHandles.insertArguments(
>>> slowPath, 0, callSite );
>>> System.out.println( "bound = " + toString( bound ) );
>>> final MethodHandle converted = MethodHandles.collectArguments(
>>> bound, type );
>>> System.out.println( "converted = " + toString( converted ) );
>>> callSite.setTarget( converted );
>>> return callSite;
>>> }
>>>
>>> /**
>>> * Find the {@code MethodHandle} in the given {@code Collection}
>>> that has the closest type match,
>>> * in a symmetric multiple dispatch sense, to the given arguments.
>>> *
>>> * @param mhs {@code Collection} of {@code MethodHandle}s
>>> that are compared to {@code type}
>>> * @param returnType the return type of the returned {@code
>>> MethodHandle}
>>> * @param args the arguments for the returned {@code
>>> MethodHandle} (used for types only)
>>> *
>>> * @return the nearest {@code MethodHandle}
>>> *
>>> * @throws MultipleDispatchException if no unique, compatible method
>>> exists
>>> * @throws PECBug if there is a bug in the the
>>> code to find the nearest
>>> * multiple dispatch method
>>> */
>>> public static MethodHandle nearest( final Collection<MethodHandle> mhs,
>>> final Class<?> returnType, final Object... args ) throws
>>> MultipleDispatchException, PECBug {
>>> final Class<?>[] argTypes = new Class<?>[ args.length ];
>>> for ( int i = 0; i< args.length; i++ ) { argTypes[ i ] = args[ i
>>> ].getClass(); }
>>> final MethodType mt = MethodType.methodType( returnType, argTypes );
>>> return nearest( mhs, mt );
>>> }
>>>
>>> /**
>>> * Find the {@code MethodHandle} in the given {@code Collection}
>>> that has the closest type match,
>>> * in a symmetric multiple dispatch sense, to the given type.
>>> *
>>> * @param mhs {@code Collection} of {@code MethodHandle}s that
>>> are compared to {@code type}
>>> * @param type the required type for the returned method handle
>>> *
>>> * @return the nearest {@code MethodHandle}
>>> *
>>> * @throws MultipleDispatchException if no unique, compatible method
>>> exists
>>> * @throws PECBug if there is a bug in the the
>>> code to find the nearest
>>> * multiple dispatch method
>>> */
>>> private static MethodHandle nearest( final Collection<MethodHandle>
>>> mhs, final MethodType type )
>>> throws MultipleDispatchException, PECBug {
>>> [snip]
>>> }
>>> [snip]
>>> }
>>>
>>> Your help is much appreciated,
>>>
>>> -- Howard.
>>>
>>> On 19 June 2010 21:00, Rémi Forax<forax at univ-mlv.fr> wrote:
>>>
>>>
>>>> Le 19/06/2010 08:40, Howard Lovatt a écrit :
>>>>
>>>>
>>>>> @Rémi,
>>>>>
>>>>> Thanks for the advise about using a "slowPath" method to discover the
>>>>> runtime types. I have coded up something along the lines you
>>>>> suggested:
>>>>>
>>>>> private static double slowPath( final CallSite callSite, final
>>>>> CostCalculator cC, final Item i ) throws Throwable {
>>>>> System.out.println("callSite = " + callSite + ", CostCalculator =
>>>>> " + cC + ", i = " + i );
>>>>> final MethodHandle nearest = Dispatchers.nearest( mhs.values(),
>>>>> double.class, cC, i );
>>>>> System.out.println( "nearest = " + Dispatchers.toString( nearest )
>>>>> );
>>>>> final MethodHandle converted = MethodHandles.collectArguments(
>>>>> nearest, callSite.getTarget().type() );
>>>>> System.out.println( "converted = " + Dispatchers.toString( converted
>>>>> )
>>>>> );
>>>>> callSite.setTarget( converted );
>>>>> return converted.<double>invokeExact( cC, i );
>>>>> }
>>>>>
>>>>> When run I get the following:
>>>>>
>>>>> callSite = CallSite#29596205[from invokedynamicmultipledispatch.Cost :
>>>>>
>>>>>
>>>>> (invokedynamicmultipledispatch.CostCalculator,invokedynamicmultipledispatch.Item)double
>>>>> => slowPath], CostCalculator =
>>>>> invokedynamicmultipledispatch.Price at bf2d5e, i =
>>>>> [Ljava.lang.Object;@13bad12
>>>>> Exception in thread "main"
>>>>> invokedynamicmultipledispatch.MultipleDispatchException
>>>>> at invokedynamicmultipledispatch.Cost.cost(Cost.java:62)
>>>>> at invokedynamicmultipledispatch.Cost.main(Cost.java:51)
>>>>> Caused by: invokedynamicmultipledispatch.MultipleDispatchException:
>>>>> Cannot find type,
>>>>> (invokedynamicmultipledispatch.Price,java.lang.Object[])double,
>>>>> compatible method in,
>>>>>
>>>>>
>>>>> [cost(invokedynamicmultipledispatch.Postage,invokedynamicmultipledispatch.CD)double,
>>>>>
>>>>>
>>>>> cost(invokedynamicmultipledispatch.CostCalculator,invokedynamicmultipledispatch.Item)double,
>>>>>
>>>>>
>>>>> cost(invokedynamicmultipledispatch.Price,invokedynamicmultipledispatch.Book)double,
>>>>>
>>>>>
>>>>> cost(invokedynamicmultipledispatch.Price,invokedynamicmultipledispatch.CD)double,
>>>>>
>>>>>
>>>>> cost(invokedynamicmultipledispatch.Postage,invokedynamicmultipledispatch.Book)double]
>>>>> at
>>>>> invokedynamicmultipledispatch.Dispatchers.nearest(Dispatchers.java:182)
>>>>> at
>>>>> invokedynamicmultipledispatch.Dispatchers.nearest(Dispatchers.java:145)
>>>>> at
>>>>>
>>>>> invokedynamicmultipledispatch.CostCalculatorDispatcher.slowPath(CostCalculatorDispatcher.java:35)
>>>>> at sun.dyn.FromGeneric$A2.invoke_D2(FromGeneric.java:534)
>>>>> at sun.dyn.FilterGeneric$F2.invoke_C1(FilterGeneric.java:550)
>>>>> at sun.dyn.ToGeneric$A2.target(ToGeneric.java:661)
>>>>> at sun.dyn.ToGeneric$A2.targetA2(ToGeneric.java:662)
>>>>> at sun.dyn.ToGeneric$A2.invoke_D(ToGeneric.java:671)
>>>>> at invokedynamicmultipledispatch.Cost.cost(Cost.java:60)
>>>>> ... 1 more
>>>>>
>>>>> The interesting bit is "i = [Ljava.lang.Object;@13bad12" on the first
>>>>> line of the printout (ignoring wrapping). This Object[] contains 1
>>>>> element, the expected Item. What is not expected is wrapping this in
>>>>> an array. Also this is a type unsafe at runtime since an array is
>>>>> passed into a method in a position where an Item is coded.
>>>>>
>>>>> Any idea what is going on?
>>>>>
>>>>> -- Howard.
>>>>>
>>>>>
>>>>>
>>>> And what is the code of the bootstrap method ?
>>>>
>>>> Rémi
>>>>
>>>>
>>>>
>>>
>>>
>>>
>>
>>
>
>
>
More information about the mlvm-dev
mailing list