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