Discussion:
Strange observation: MethodHandle.invokeWithArguments() would not work, whereas Method.invoke() would with the very same arguments
John Rose
2018-03-02 01:47:21 UTC
Permalink
While testing a rather complex one (an adaption of the JavaFX address book example enhanced with a
BarChart, [1]), that exhibits a very strange behavior: when setting the values for the CategoryAxis
supplying an ObservableList of the month names in the current Locale, using a MethodHandle and
I just happened to see your message about your adventures with method handle:

http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-February/013603.html <http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-February/013603.html>

This isn't really a jigsaw question, so I'm replying to mlvm-dev.

It looks like you are mixing or interconverting arrays of strings
with lists of strings. The print statements and CCE show that
you are passing an array of strings into a place which expects
a single string, and the print statements suggest you are
in fact passing a list containing a string array into a place
which expects a list of strings. Either way there are too
many brackets in your actual argument.

The prime suspect when the number of brackets is off by one
is varargs. You code might be failing because of surprises
in the overload resolution of invokeWA, which accepts
a varargs Object array *and* a single List.

Is your runtime invoke mechanism treating invokeWA as an ordinary
method? It is rather extraordinary, and may warrant a second look.

String str;
Object obj;
Object[] aobj;
String[] astr;
List lst;

plain single arity invocations:

1 mh.invokeWithArguments(str) => new Object[] { str }
2 mh.invokeWithArguments(obj) => new Object[] { obj }

and yet:

3 mh.invokeWithArguments(aobj) => aobj (multiple args)
4 mh.invokeWithArguments(astr) => astr (multiple args again!)
5 mh.invokeWithArguments(lst) => lst.toArray() (multiple args again!)

but again, a cast removes varargs:

6 mh.invokeWithArguments((Object) aobj) => new Object[] { aobj }
7 mh.invokeWithArguments((Object) astr) => new Object[] { astr }
8 mh.invokeWithArguments((Object) lst) => new Object[] { lst }

Your bug looks like a confusion between two of these,
perhaps 5 and 8.

Regards,
— John
Rony G. Flatscher
2018-03-03 18:02:55 UTC
Permalink
John, thank you very much for your kind reply and hints!
Post by John Rose
While testing a rather complex one (an adaption of the JavaFX address book example enhanced with a
BarChart, [1]), that exhibits a very strange behavior: when setting the values for the CategoryAxis
supplying an ObservableList of the month names in the current Locale, using a MethodHandle and
http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-February/013603.html
This isn't really a jigsaw question, so I'm replying to mlvm-dev.
Yes, I just was not sure where to post such an e-mail (as on jigsaw MethodHandle's were suggested I
posted it there).
Post by John Rose
It looks like you are mixing or interconverting arrays of strings
with lists of strings.  The print statements and CCE show that
you are passing an array of strings into a place which expects
a single string, and the print statements suggest you are
in fact passing a list containing a string array into a place
which expects a list of strings.  Either way there are too
many brackets in your actual argument.
The prime suspect when the number of brackets is off by one
is varargs.  You code might be failing because of surprises
in the overload resolution of invokeWA, which accepts
a varargs Object array *and* a single List.
Is your runtime invoke mechanism treating invokeWA as an ordinary
method?  It is rather extraordinary, and may warrant a second look.
String str;
Object obj;
Object[] aobj;
String[] astr;
List lst;
1 mh.invokeWithArguments(str) => new Object[] { str }
2 mh.invokeWithArguments(obj) => new Object[] { obj }
3 mh.invokeWithArguments(aobj) => aobj (multiple args)
4 mh.invokeWithArguments(astr) => astr (multiple args again!)
5 mh.invokeWithArguments(lst) => lst.toArray() (multiple args again!)
6 mh.invokeWithArguments((Object) aobj) => new Object[] { aobj }
7 mh.invokeWithArguments((Object) astr) => new Object[] { astr }
8 mh.invokeWithArguments((Object) lst) => new Object[] { lst }
Your bug looks like a confusion between two of these,
perhaps 5 and 8.
The invocation occurs with an aobj kind of argument (an array of coerced arguments matching the
types of the parameterTypes array), defined as "Object [] coercedArgs=..."); nevertheless will make
sure to cast explicitly everywhere invokeWithArguments() gets used (there are
MethodHandle.invoke(...) as well, which I will replace with invokeWithArguments instead, just to
have everything use the same invocation method consistently in case arguments need to be supplied;
also foregoing bindTo().invoke(...), replacing it with appropriate invokeWithArguments() for
consistency).

---rony

P.S.: After having rewritten the Java reflection (adding caching of Field, Method, Constructor
objects together with their corresponding MethodHandle objects) part I also rewrote some of the core
ooRexx/C++/JNI stuff to remove old-standing (18+ years) code with modern ooRexx-4.x-API code, which
allows to forgo many of the old restrictions, and improving speed in those corners as well (still
some cleanup to do). After getting the test units to pass, I turned to this application, and that
problem using MethodHandle invocations only does not surface anymore! 
Rony G. Flatscher
2018-03-04 18:05:56 UTC
Permalink
OK, went back to do the changes (making sure to cast invokeWithArguments(...) to (Object[]) and
discovered that I forgot a hard-coded override to use core reflection in all cases.

Removing that hard-coded flag to use MethodHandles instead made the problem reappear, so I once more
went after it in the past hours and think I can now explain this more exact: the case in question is
a sequence of the following (transcribed to Java) statements that are executed in the BSF4ooRexx
bridge on behalf of the Rexx program (which itself was transcribed from a Java example found on the
Internet):

* "javafx.collections.FXCollections.observableArrayList()" returns an object that implements the
interface "javafx.collections.ObservableList", actually an instance of the non-exported
"com.sun.javafx.collections.ObservableListWrapper" class,
* a String array of month names gets turned into an ArrayList using
"java.util.Arrays.asList(monthNames)" returning listOfMonthNames (a "java.util.Arrays$ArrayList")
* "ObservableList.addAll(listOfMonthNames)" gets invoked,
* supplying the resulting ObservableListWrapper as an argument to
"javafx.scene.chart.CategoryAxis.setCategories(javafx.collections.ObservableList)" works when
invoked via core reflection, but causes an exception if invoked via a MethodHandle (the
exception being: "java.lang.ClassCastException: java.base/[Ljava.lang.String; cannot be cast to
java.base/java.lang.String")!

The problem, it turns out is the use of "addAll(listOfMonthNames)" which causes the ObservableList
to contain a single list member "java.util.Arrays$***@xxx" (toString() yielding
"[Ljava.lang.String;@yyy"). The core reflection invocations work without an error, the
MethodHandle.invokeWithArguments(...) version causes the above exception.

Changing the code to use the String array monthNames (not its list version) as an argument (hence:
"ObservableList.addAll(monthNames)" ) makes it run with both, the core reflection and the
MethodHandle invocation versions.

---rony
Post by Rony G. Flatscher
John, thank you very much for your kind reply and hints!
Post by John Rose
While testing a rather complex one (an adaption of the JavaFX address book example enhanced with a
BarChart, [1]), that exhibits a very strange behavior: when setting the values for the CategoryAxis
supplying an ObservableList of the month names in the current Locale, using a MethodHandle and
http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-February/013603.html
This isn't really a jigsaw question, so I'm replying to mlvm-dev.
Yes, I just was not sure where to post such an e-mail (as on jigsaw MethodHandle's were suggested
I posted it there).
Post by John Rose
It looks like you are mixing or interconverting arrays of strings
with lists of strings.  The print statements and CCE show that
you are passing an array of strings into a place which expects
a single string, and the print statements suggest you are
in fact passing a list containing a string array into a place
which expects a list of strings.  Either way there are too
many brackets in your actual argument.
The prime suspect when the number of brackets is off by one
is varargs.  You code might be failing because of surprises
in the overload resolution of invokeWA, which accepts
a varargs Object array *and* a single List.
Is your runtime invoke mechanism treating invokeWA as an ordinary
method?  It is rather extraordinary, and may warrant a second look.
String str;
Object obj;
Object[] aobj;
String[] astr;
List lst;
1 mh.invokeWithArguments(str) => new Object[] { str }
2 mh.invokeWithArguments(obj) => new Object[] { obj }
3 mh.invokeWithArguments(aobj) => aobj (multiple args)
4 mh.invokeWithArguments(astr) => astr (multiple args again!)
5 mh.invokeWithArguments(lst) => lst.toArray() (multiple args again!)
6 mh.invokeWithArguments((Object) aobj) => new Object[] { aobj }
7 mh.invokeWithArguments((Object) astr) => new Object[] { astr }
8 mh.invokeWithArguments((Object) lst) => new Object[] { lst }
Your bug looks like a confusion between two of these,
perhaps 5 and 8.
The invocation occurs with an aobj kind of argument (an array of coerced arguments matching the
types of the parameterTypes array), defined as "Object [] coercedArgs=..."); nevertheless will
make sure to cast explicitly everywhere invokeWithArguments() gets used (there are
MethodHandle.invoke(...) as well, which I will replace with invokeWithArguments instead, just to
have everything use the same invocation method consistently in case arguments need to be supplied;
also foregoing bindTo().invoke(...), replacing it with appropriate invokeWithArguments() for
consistency).
---rony
P.S.: After having rewritten the Java reflection (adding caching of Field, Method, Constructor
objects together with their corresponding MethodHandle objects) part I also rewrote some of the
core ooRexx/C++/JNI stuff to remove old-standing (18+ years) code with modern ooRexx-4.x-API code,
which allows to forgo many of the old restrictions, and improving speed in those corners as well
(still some cleanup to do). After getting the test units to pass, I turned to this application,
and that problem using MethodHandle invocations only does not surface anymore! 
Rony G. Flatscher
2018-03-08 17:42:56 UTC
Permalink
Hmm, I ran into another such problem, where using core reflection invoke(...) succeeds whereas using
MethodHandle.invokeWithArguments(...) does not.

The problem is also linked to the resulting object from Arrays.asList(T... a), this time in the
context of a stream example causing a list that contains a single String array, instead of a list
that contains the String array elements as its individual list elements.

Here is the ooRexx code (the tilde '~' is the explicit message operator, Rexx is caseless and
dynamically typed), which was originally transcribed 1:1 from a Java example and which works with
core reflection:

-- define a Rexx string containing letter 'k' in some of its words wordstring="Just a bunch of
words to test for killer items containing a k" -- turn Rexx-string into a Java-string: this
allows us to use Java's String methods refWordString=.bsf~new("java.lang.String", wordstring) --
convert the Java string into a Java List (a Collection):
alist=bsf.loadClass("java.util.Arrays")~asList(refWordString~split(" ")) -- create a RexxProxy
of our Worker class which implements the two functional interface -- methods that we use
rexxWorker=BsfCreateRexxProxy(.worker~new, , "java.util.function.Predicate",
"java.util.function.Consumer") -- now run a filter stream operation on the list -- (the filter
just selects words containing the letter 'k' or 'K') sa=alist~stream~filter(rexxWorker)~toArray
-- "filter" employs the Predicate interface -- print the results for verification: loop y over
sa say y end say "-----------------------" - now run a foreach operation on a stream -- the
consumer here just prints inputs with some surrounding brackets alist~stream~foreach(rexxWorker)
-- "forEach" employs the Consumer interface ::requires BSF.CLS -- get access to the Java bridge
::class Worker -- define Rexx class that implements the functional methods -- implements the
interface java.util.function.Predicate ::method test -- will return .true for strings containing
'k' or 'K', .false else use arg s -- fetch argument (from Java) return s~caselessPos('k')>0 --
implements the interface java.util.function.Consumer ::method accept -- will show each string
use arg s -- fetch argument (from Java) say ">>"s"<<" -- show argument

The solution here was the same as already described earlier: in order to use
MethodHandle.invokeWithArguments() do not use the result of Arrays.asList(array) and apply stream()
on it , but rather create the stream directly with Arrays.stream(array) each time a stream is needed.

It is as if the object returned by Arrays.asList(...) is being handled differently if using the core
reflection invoke(...) compared to using MethodHandle.invokeWithArguments(...) for unknown reasons
(the arguments are processed the same in both cases).

---rony
Post by Rony G. Flatscher
OK, went back to do the changes (making sure to cast invokeWithArguments(...) to (Object[]) and
discovered that I forgot a hard-coded override to use core reflection in all cases.
Removing that hard-coded flag to use MethodHandles instead made the problem reappear, so I once
more went after it in the past hours and think I can now explain this more exact: the case in
question is a sequence of the following (transcribed to Java) statements that are executed in the
BSF4ooRexx bridge on behalf of the Rexx program (which itself was transcribed from a Java example
* "javafx.collections.FXCollections.observableArrayList()" returns an object that implements the
interface "javafx.collections.ObservableList", actually an instance of the non-exported
"com.sun.javafx.collections.ObservableListWrapper" class,
* a String array of month names gets turned into an ArrayList using
"java.util.Arrays.asList(monthNames)" returning listOfMonthNames (a "java.util.Arrays$ArrayList")
* "ObservableList.addAll(listOfMonthNames)" gets invoked,
* supplying the resulting ObservableListWrapper as an argument to
"javafx.scene.chart.CategoryAxis.setCategories(javafx.collections.ObservableList)" works when
invoked via core reflection, but causes an exception if invoked via a MethodHandle (the
exception being: "java.lang.ClassCastException: java.base/[Ljava.lang.String; cannot be cast
to java.base/java.lang.String")!
The problem, it turns out is the use of "addAll(listOfMonthNames)" which causes the ObservableList
MethodHandle.invokeWithArguments(...) version causes the above exception.
"ObservableList.addAll(monthNames)" ) makes it run with both, the core reflection and the
MethodHandle invocation versions.
---rony
Post by Rony G. Flatscher
John, thank you very much for your kind reply and hints!
Post by John Rose
While testing a rather complex one (an adaption of the JavaFX address book example enhanced with a
BarChart, [1]), that exhibits a very strange behavior: when setting the values for the CategoryAxis
supplying an ObservableList of the month names in the current Locale, using a MethodHandle and
http://mail.openjdk.java.net/pipermail/jigsaw-dev/2018-February/013603.html
This isn't really a jigsaw question, so I'm replying to mlvm-dev.
Yes, I just was not sure where to post such an e-mail (as on jigsaw MethodHandle's were suggested
I posted it there).
Post by John Rose
It looks like you are mixing or interconverting arrays of strings
with lists of strings.  The print statements and CCE show that
you are passing an array of strings into a place which expects
a single string, and the print statements suggest you are
in fact passing a list containing a string array into a place
which expects a list of strings.  Either way there are too
many brackets in your actual argument.
The prime suspect when the number of brackets is off by one
is varargs.  You code might be failing because of surprises
in the overload resolution of invokeWA, which accepts
a varargs Object array *and* a single List.
Is your runtime invoke mechanism treating invokeWA as an ordinary
method?  It is rather extraordinary, and may warrant a second look.
String str;
Object obj;
Object[] aobj;
String[] astr;
List lst;
1 mh.invokeWithArguments(str) => new Object[] { str }
2 mh.invokeWithArguments(obj) => new Object[] { obj }
3 mh.invokeWithArguments(aobj) => aobj (multiple args)
4 mh.invokeWithArguments(astr) => astr (multiple args again!)
5 mh.invokeWithArguments(lst) => lst.toArray() (multiple args again!)
6 mh.invokeWithArguments((Object) aobj) => new Object[] { aobj }
7 mh.invokeWithArguments((Object) astr) => new Object[] { astr }
8 mh.invokeWithArguments((Object) lst) => new Object[] { lst }
Your bug looks like a confusion between two of these,
perhaps 5 and 8.
The invocation occurs with an aobj kind of argument (an array of coerced arguments matching the
types of the parameterTypes array), defined as "Object [] coercedArgs=..."); nevertheless will
make sure to cast explicitly everywhere invokeWithArguments() gets used (there are
MethodHandle.invoke(...) as well, which I will replace with invokeWithArguments instead, just to
have everything use the same invocation method consistently in case arguments need to be
supplied; also foregoing bindTo().invoke(...), replacing it with appropriate
invokeWithArguments() for consistency).
---rony
P.S.: After having rewritten the Java reflection (adding caching of Field, Method, Constructor
objects together with their corresponding MethodHandle objects) part I also rewrote some of the
core ooRexx/C++/JNI stuff to remove old-standing (18+ years) code with modern ooRexx-4.x-API
code, which allows to forgo many of the old restrictions, and improving speed in those corners as
well (still some cleanup to do). After getting the test units to pass, I turned to this
application, and that problem using MethodHandle invocations only does not surface anymore! 
Rony G. Flatscher
2018-03-11 19:22:45 UTC
Permalink
Well, still trying to find out what the reason is, that core reflection's invoke behaves differently
to MethodHandle's invokeWithArguments in one single case so far (using the method
java.utli.Arrays.asList(...)).

Here is a little Java program that excercises reflective access to
"java.util.Arrays.asList​(T... a)" using core reflection, i.e. "Method.invoke​(Object obj, Object...
args)" and "MethodHandle.invokeWithArguments​(Object... arguments)":

import java.util.*;

import java.lang.reflect.*;
import java.lang.invoke.*;

class DemoAsListProblem
{
public static void main (String args[])
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[1] (main) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");
List listNames=Arrays.asList(arrNames);
System.out.println("[2] (main) after invoking Arrays.asList(arrNames), listNames=\""+listNames+"\"");
System.out.println("\ninvoking testReflective() ...\n");

testReflective();
}

public static void testReflective()
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[3] (testReflective) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");

Method methAsList=null;
List listNames=null;
try {
Class paramTypes[]=new Class[] { (new Object[0]).getClass() };
methAsList=Arrays.class.getDeclaredMethod("asList", paramTypes);
System.out.println("--- (core reflection) Method object asList: "+methAsList);
System.out.println("\n--- (core reflection) now invoking Arrays.asList() via Method.invoke(...):");

listNames=(List) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ); // static method
System.out.println("[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");

listNames=(List) methAsList.invoke( null, (Object) new Object[]{arrNames} ); // static method
System.out.println("[4b] --- (CR) methAsList.invoke( null, (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");

// "java.lang.IllegalArgumentException: wrong number of arguments":
// listNames=(List) methAsList.invoke( null, (Object[]) arrNames ); // static method
// System.out.println("[5a] --- (CR) methAsList.invoke( null, (Object[]) arrNames ) -> listNames=\""+listNames+"\"");

listNames=(List) methAsList.invoke( null, (Object) arrNames ); // static method
System.out.println("[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames=\""+listNames+"\"");
}
catch (Throwable t)
{
System.err.println("oops #1: "+t);
t.printStackTrace();
System.exit(-1);
}

System.out.println("\n--- (MH) now invoking Arrays.asList() via MethodHandle.invokeWithArguments(...):");
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle mh=null;
try {
mh=lookup.unreflect(methAsList);
System.out.println("--- (MH) unreflected MethodHandle mh: "+mh);

listNames=(List) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} );
System.out.println("[6a] --- (MH) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");

listNames=(List) mh.invokeWithArguments( (Object) new Object[]{arrNames} );
System.out.println("[6b] --- (MH) mh.invokeWithArguments( (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");

listNames=(List) mh.invokeWithArguments( (Object[]) arrNames );
System.out.println("[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames=\""+listNames+"\"");

listNames=(List) mh.invokeWithArguments( (Object) arrNames );
System.out.println("[7b] --- (MH) mh.invokeWithArguments( (Object) arrNames ) -> listNames=\""+listNames+"\"");

}
catch (Throwable t)
{
System.err.println("oops #2: "+t);
t.printStackTrace();
System.exit(-2);
}
}
}

Compiling and running it under 9.0.4 yields the following output:

[1] (main) arrNames="[Ljava.lang.String;@27ddd392", .toString()="[Ljava.lang.String;@27ddd392"
[2] (main) after invoking Arrays.asList(arrNames), listNames="[anne, bert, celine]"

invoking testReflective() ...

[3] (testReflective) arrNames="[Ljava.lang.String;@2a18f23c", .toString()="[Ljava.lang.String;@2a18f23c"
--- (core reflection) Method object asList: public static java.util.List java.util.Arrays.asList(java.lang.Object[])

--- (core reflection) now invoking Arrays.asList() via Method.invoke(...):
[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames="[anne, bert, celine]"
[4b] --- (CR) methAsList.invoke( null, (Object) new Object[]{arrNames} ) -> listNames="[[Ljava.lang.String;@2a18f23c]"
[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames="[anne, bert, celine]"

--- (MH) now invoking Arrays.asList() via MethodHandle.invokeWithArguments(...):
--- (MH) unreflected MethodHandle mh: MethodHandle(Object[])List
[6a] --- (MH) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} ) -> listNames="[[Ljava.lang.String;@2a18f23c]"
[6b] --- (MH) mh.invokeWithArguments( (Object) new Object[]{arrNames} ) -> listNames="[[Ljava.lang.Object;@13a57a3b]"
[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames="[anne, bert, celine]"
[7b] --- (MH) mh.invokeWithArguments( (Object) arrNames ) -> listNames="[[Ljava.lang.String;@2a18f23c]"

So a String array is turned into a List using Arrays.asList(strArray). Doing it with core reflection
yields different results to doing it with invokeWithArguments().

I would have expected that [4a] and [6a] would behave the same.

Using reflective invoke() and invokeWithArguments() has been working for my bridge for all the test
units (using literally the same arguments in both cases) interchangeably, the one exception is
Arrays.asList().

Maybe I am not seeing the obvious (having done too much "close-up tests" for quite some time now).
So any hint, insight, help would be really appreciated!

---rony
Rony G. Flatscher
2018-06-08 10:07:53 UTC
Permalink
Post by Rony G. Flatscher
Well, still trying to find out what the reason is, that core reflection's invoke behaves
differently to MethodHandle's invokeWithArguments in one single case so far (using the method
java.utli.Arrays.asList(...)).
Here is a little Java program that excercises reflective access to
"java.util.Arrays.asList​(T... a)" using core reflection, i.e. "Method.invoke​(Object obj,
import java.util.*;
import java.lang.reflect.*;
import java.lang.invoke.*;
class DemoAsListProblem
{
public static void main (String args[])
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[1] (main) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");
List listNames=Arrays.asList(arrNames);
System.out.println("[2] (main) after invoking Arrays.asList(arrNames), listNames=\""+listNames+"\"");
System.out.println("\ninvoking testReflective() ...\n");
testReflective();
}
public static void testReflective()
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[3] (testReflective) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");
Method methAsList=null;
List listNames=null;
try {
Class paramTypes[]=new Class[] { (new Object[0]).getClass() };
methAsList=Arrays.class.getDeclaredMethod("asList", paramTypes);
System.out.println("--- (core reflection) Method object asList: "+methAsList);
System.out.println("\n--- (core reflection) now invoking Arrays.asList() via Method.invoke(...):");
listNames=(List) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ); // static method
System.out.println("[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) methAsList.invoke( null, (Object) new Object[]{arrNames} ); // static method
System.out.println("[4b] --- (CR) methAsList.invoke( null, (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
// listNames=(List) methAsList.invoke( null, (Object[]) arrNames ); // static method
// System.out.println("[5a] --- (CR) methAsList.invoke( null, (Object[]) arrNames ) -> listNames=\""+listNames+"\"");
listNames=(List) methAsList.invoke( null, (Object) arrNames ); // static method
System.out.println("[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames=\""+listNames+"\"");
}
catch (Throwable t)
{
System.err.println("oops #1: "+t);
t.printStackTrace();
System.exit(-1);
}
System.out.println("\n--- (MH) now invoking Arrays.asList() via MethodHandle.invokeWithArguments(...):");
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle mh=null;
try {
mh=lookup.unreflect(methAsList);
System.out.println("--- (MH) unreflected MethodHandle mh: "+mh);
listNames=(List) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} );
System.out.println("[6a] --- (MH) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object) new Object[]{arrNames} );
System.out.println("[6b] --- (MH) mh.invokeWithArguments( (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object[]) arrNames );
System.out.println("[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object) arrNames );
System.out.println("[7b] --- (MH) mh.invokeWithArguments( (Object) arrNames ) -> listNames=\""+listNames+"\"");
}
catch (Throwable t)
{
System.err.println("oops #2: "+t);
t.printStackTrace();
System.exit(-2);
}
}
}
[2] (main) after invoking Arrays.asList(arrNames), listNames="[anne, bert, celine]"
invoking testReflective() ...
--- (core reflection) Method object asList: public static java.util.List java.util.Arrays.asList(java.lang.Object[])
[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames="[anne, bert, celine]"
[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames="[anne, bert, celine]"
--- (MH) unreflected MethodHandle mh: MethodHandle(Object[])List
[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames="[anne, bert, celine]"
So a String array is turned into a List using Arrays.asList(strArray). Doing it with core
reflection yields different results to doing it with invokeWithArguments().
I would have expected that [4a] and [6a] would behave the same.
Using reflective invoke() and invokeWithArguments() has been working for my bridge for all the
test units (using literally the same arguments in both cases) interchangeably, the one exception
is Arrays.asList().
Maybe I am not seeing the obvious (having done too much "close-up tests" for quite some time now).
So any hint, insight, help would be really appreciated!
---rony
As a few months have gone by without any follow-ups, I have been wondering whether there is a
solution at all, if this is a problem rooted in the current implementation of MethodHandle methods
not being 100% compatible with reflective invoke (which may imply that one needs to stick to use
reflective invoke and forgo MethodHandle invokes for good).

---rony
Peter Levart
2018-06-08 16:23:46 UTC
Permalink
Hi Rony,

I think what you found is a discrepancy between handling of varargs
methods using reflection vs. method handle's .invokeWithArguments().

Reflection basically ignores the fact that some methods are varargs
methods. It treats them exactly the same as if they had an array
parameter (not a varargs parameter).

For example, Arrays.asList() is a varargs method:

    public static <T> List<T> asList(T ... elements)

But reflection treats is exactly the same as the following method:

    public static <T> List<T> asList(T[] elements)

That's because varargs were introduced to Java in Java 5 as a kind of
compilation sugar, implemented as an array parameter, while reflection
is an older beast and wasn't updated to behave differently with varargs
methods.

MethodHandle(s) came later, in Java 7, and took into consideration
varargs methods.

So where's the problem? When you say in Java:

        Method asListMethod = Arrays.class.getMethod("asList",
Object[].class);
        String[] elements = {"a", "b", "c"};

        System.out.println(
            asListMethod.invoke(null, elements)
        );

You get: IllegalArgumentException: wrong number of arguments

Because Method.invoke() is also a varargs method:

    public Object invoke(Object obj, Object... args)

... so java compiler (in order to be backwards source compatible with
pre-varargs methods) treats this invocation in a way that just passes
the String[] elements to the invoke method via the args parameter
without any conversion (because String[] is a subtype of Object[]).
Mathod.invoke then treats elements of the args[] array as individual
verbatim parameters to be passed to the method when invoking it. As
reflection treats Arrays.asList() as a normal no-varargs method which
accepts a single Objet[] parameter and args[] contains 3 elements to be
passed to a method with 3 parameters, exception occurs.


But when you use method handles and .invokeWithArguments():

        Method asListMethod = Arrays.class.getMethod("asList",
Object[].class);
        MethodHandle asListMH =
MethodHandles.lookup().unreflect(asListMethod);

        String[] elements = {"a", "b", "c"};

        System.out.println(
            asListMH.invokeWithArguments(elements)
        );

... the following happens: MethodHandle.invokeWithArguments() is also a
varargs method:

    public Object invokeWithArguments(Object... arguments)

... so java compiler (in order to be backwards source compatible with
pre-varargs methods) treats this invocation in a way that just passes
the String[] elements to the invokeWithArguments method via the single
'arguments' parameter without any conversion (because String[] is a
subtype of Object[]). MethodHandle.invokeWithArguments therefore takes 3
array elements and tries to invoke the underlying asList method with
them. It observes that Arrays.asList is a varargs method, so it wraps
the 3 "wannabe parameters" with the Object[] and passes the array to the
asList method as single parameter. The result of above code is therefore:

    [a, b, c]

If you want MethodHandle.invokeWithArguments() to treat "wannabe
parameters" passed to it as verbatim parameters regardless of the
variable/fixed arity of the method to be invoked, then you can transform
the variable arity MethodHandle to a fixed arity MH:

        Method asListMethod = Arrays.class.getMethod("asList",
Object[].class);

        String[] elements = {"a", "b", "c"};

        System.out.println(
            asListMethod.invoke(null, (Object) elements)
        );

        MethodHandle asListMH =
MethodHandles.lookup().unreflect(asListMethod);
        asListMH = asListMH.asFixedArity();

        System.out.println(
            asListMH.invokeWithArguments((Object) elements)
        );

Here you have it. Both of the above invocations produce equal output:

    [a, b, c]
    [a, b, c]


What happens here is the following (will only describe the MH case -
Method case is similar):

MethodHandle.invokeWithArguments() is a varargs method:

    public Object invokeWithArguments(Object... arguments)

... so java compiler this time wraps the '(Object) elements' value with
an Object[] because the value is of Object type (which is not a subtype
of Object[]) - no backwards compatibility needed here, varargs
conversion kicks in in the javac. MethodHandle.invokeWithArguments
therefore takes an array with 1 element (the element being 'elements'
array) and tries to invoke the underlying asList method with it. This
time it observes that the method is not a varargs method, (because
MethodHandle was transformed to fixed arity method handle) so it passes
the single 'elements' wannabee parameter to the Arrays.asList single
Object[] parameter - this invocation works because fixed arity asList()
takes Object[] and 'elements' is a String[]...

Hope this helps you understand what's going on.

Regards, Peter
Post by Rony G. Flatscher
Post by Rony G. Flatscher
Well, still trying to find out what the reason is, that core
reflection's invoke behaves differently to MethodHandle's
invokeWithArguments in one single case so far (using the method
java.utli.Arrays.asList(...)).
Here is a little Java program that excercises reflective access to
"java.util.Arrays.asList​(T... a)" using core reflection, i.e.
"Method.invoke​(Object obj, Object... args)" and
import java.util.*;
import java.lang.reflect.*;
import java.lang.invoke.*;
class DemoAsListProblem
{
public static void main (String args[])
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[1] (main) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");
List listNames=Arrays.asList(arrNames);
System.out.println("[2] (main) after invoking Arrays.asList(arrNames), listNames=\""+listNames+"\"");
System.out.println("\ninvoking testReflective() ...\n");
testReflective();
}
public static void testReflective()
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[3] (testReflective) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");
Method methAsList=null;
List listNames=null;
try {
Class paramTypes[]=new Class[] { (new Object[0]).getClass() };
methAsList=Arrays.class.getDeclaredMethod("asList", paramTypes);
System.out.println("--- (core reflection) Method object asList: "+methAsList);
System.out.println("\n--- (core reflection) now invoking Arrays.asList() via Method.invoke(...):");
listNames=(List) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ); // static method
System.out.println("[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) methAsList.invoke( null, (Object) new Object[]{arrNames} ); // static method
System.out.println("[4b] --- (CR) methAsList.invoke( null, (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
// listNames=(List) methAsList.invoke( null, (Object[]) arrNames ); // static method
// System.out.println("[5a] --- (CR) methAsList.invoke( null, (Object[]) arrNames ) -> listNames=\""+listNames+"\"");
listNames=(List) methAsList.invoke( null, (Object) arrNames ); // static method
System.out.println("[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames=\""+listNames+"\"");
}
catch (Throwable t)
{
System.err.println("oops #1: "+t);
t.printStackTrace();
System.exit(-1);
}
System.out.println("\n--- (MH) now invoking Arrays.asList() via MethodHandle.invokeWithArguments(...):");
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle mh=null;
try {
mh=lookup.unreflect(methAsList);
System.out.println("--- (MH) unreflected MethodHandle mh: "+mh);
listNames=(List) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} );
System.out.println("[6a] --- (MH) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object) new Object[]{arrNames} );
System.out.println("[6b] --- (MH) mh.invokeWithArguments( (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object[]) arrNames );
System.out.println("[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object) arrNames );
System.out.println("[7b] --- (MH) mh.invokeWithArguments( (Object) arrNames ) -> listNames=\""+listNames+"\"");
}
catch (Throwable t)
{
System.err.println("oops #2: "+t);
t.printStackTrace();
System.exit(-2);
}
}
}
[2] (main) after invoking Arrays.asList(arrNames), listNames="[anne, bert, celine]"
invoking testReflective() ...
--- (core reflection) Method object asList: public static java.util.List java.util.Arrays.asList(java.lang.Object[])
[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames="[anne, bert, celine]"
[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames="[anne, bert, celine]"
--- (MH) unreflected MethodHandle mh: MethodHandle(Object[])List
[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames="[anne, bert, celine]"
So a String array is turned into a List using
Arrays.asList(strArray). Doing it with core reflection yields
different results to doing it with invokeWithArguments().
I would have expected that [4a] and [6a] would behave the same.
Using reflective invoke() and invokeWithArguments() has been working
for my bridge for all the test units (using literally the same
arguments in both cases) interchangeably, the one exception is
Arrays.asList().
Maybe I am not seeing the obvious (having done too much "close-up
tests" for quite some time now). So any hint, insight, help would be
really appreciated!
---rony
As a few months have gone by without any follow-ups, I have been
wondering whether there is a solution at all, if this is a problem
rooted in the current implementation of MethodHandle methods not being
100% compatible with reflective invoke (which may imply that one needs
to stick to use reflective invoke and forgo MethodHandle invokes for
good).
---rony
_______________________________________________
mlvm-dev mailing list
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
Rony G. Flatscher
2018-06-10 12:16:54 UTC
Permalink
Dear Peter:

thank you very much for your thorough and extensive analysis taking the evolvement of Java into account!

You use the Java compiler and its behaviour (which adds syntax sugar to the Java language and is
known to stick strictly to the Java language specification) for explanations and as reference, which
is also what I have been doing in the past to counter-check the implemented behaviour of the bridge.

The Java compiler has a very different outset: *at compilation time* it knows everything it needs to
know for compiling. If something is missing or wrong at compilation time it is able to stop
compilation raising appropriate errors, thereby inhibiting the successful creation of Java classes
as long as the Java programmer does not correct erroneous code or supplies missing vital
information. Java being a strictly typed language allows one to cast arguments to force the compiler
to pick the method the Java programmer had in mind in the case that the compiler could choose one
from a set of a signatures.

By contrast a bridge for an interpreted, dynamically typed language (like ooRexx) needs to reflect
and infer at *runtime* in order to be able to choose the Java methods with the appropriate
signature. In this particular case all primitive datatypes are (ooRexx/C) strings in the dynamically
typed language such that at runtime each argument needs to be checked whether they contain valid
values for conversion to primitive datatypes in the signature of a Java method candidate.

In the dynamically typed language there is no means available for casting that would be needed to
help solve the presented problem (there is optionally a box()-routine in the bridge that allows the
ooRexx programmer to explicitly determine which primitive type is to be used for picking an
appropriate Java method).

The new code in the bridge (had to rewrite that part from scratch because of Java 9 and later) uses
java.lang.reflect to pick an appropriate Constructor, Method or Field object. For Java 1.6 and 1.7
(although java.lang.invoke/MethodHandle got introduced with 1.7 I found cases where inaccessible
types did not have accessible ancestors in the JRE) java.lang.reflect invocation will be carried
out, for Java 1.8 and later java.lang.invoke/MethodHandle invocations gets employed.

Comparing the speed between invocations using java.lang.reflect and java.lang.invoke,
java.lang.invoke wins, but by a quite small margin.

Upon further testunit tests the rewritten bridge using java.lang.invoke would execute all ooRexx
programs like java.lang.reflect since Java 1.1 with one single exception so far which I reported
here, where invocation via java.lang.reflect behaves differently to java.lang.invoke.

Because of this discrepancy the question would be: is this considered a bug that will be corrected?
Would there be otherwise a solution possible in the bridge that could take care of situations like
this, where the bridge has no context-information other than referring to Constructor, Method or
Field objects with the supplied arguments?

A few coarse ideas to address this:

* if the unreflect() method gets used then MH.invoke() should behave like in the java.lang.reflect
case; this means that the same assumptions should govern MH.invoke(): reason being that if a
java.lang.reflect object gets unreflected, also the established java.lang.reflect rules should
keep working in this case for the MH.invoke* in order to remain fully compatible with each other,
o if a MH gets created without unreflect() then this would not be necessary
* the bridge could restrain itself to only use java.lang.reflect for invocations on Java 8 and
higher instead of java.lang.invoke
o clearly, the current Java development efforts in this corner are concentrated on
java.lang.invoke, so it would be desirable to switch from java.lang.reflect to
java.lang.invoke which is not possible as long as this problem (different behaviour in j.l.r
vs. j.l.i) persists
* do not fix this, but document this as an incompatibility with the ramification that existing
programs in dynamically typed languages need to be rewritten (e.g. in this case creating an
Array object from the List and supply that instead)
* ... ?

---rony

P.S.: It would be great if Java implemented reflective invocations for dynamically typed languages
as this would solve the problem for all such languages once and forever in a standard manner, rather
than have every bridge implementor create his own implementation, which is challenging,  effortful
and possibly error prone. In my case the assumptions that governed the original implementation for
reflective Constructor, Method and Field access for over fifteen years got broken by Java 9, because
j.l.r (notable 'setAccessible') changed in a fundamental aspect. If Java could be emplyoed instead,
the Java supplied implementation would be able to adopt changes in the inner workings accordingly
thereby insulating bridges from such details and problems.
I think what you found is a discrepancy between handling of varargs methods using reflection vs.
method handle's .invokeWithArguments().
Reflection basically ignores the fact that some methods are varargs methods. It treats them
exactly the same as if they had an array parameter (not a varargs parameter).
    public static <T> List<T> asList(T ... elements)
    public static <T> List<T> asList(T[] elements)
That's because varargs were introduced to Java in Java 5 as a kind of compilation sugar,
implemented as an array parameter, while reflection is an older beast and wasn't updated to behave
differently with varargs methods.
MethodHandle(s) came later, in Java 7, and took into consideration varargs methods.
        Method asListMethod = Arrays.class.getMethod("asList", Object[].class);
        String[] elements = {"a", "b", "c"};
        System.out.println(
            asListMethod.invoke(null, elements)
        );
You get: IllegalArgumentException: wrong number of arguments
    public Object invoke(Object obj, Object... args)
... so java compiler (in order to be backwards source compatible with pre-varargs methods) treats
this invocation in a way that just passes the String[] elements to the invoke method via the args
parameter without any conversion (because String[] is a subtype of Object[]). Mathod.invoke then
treats elements of the args[] array as individual verbatim parameters to be passed to the method
when invoking it. As reflection treats Arrays.asList() as a normal no-varargs method which accepts
a single Objet[] parameter and args[] contains 3 elements to be passed to a method with 3
parameters, exception occurs.
        Method asListMethod = Arrays.class.getMethod("asList", Object[].class);
        MethodHandle asListMH = MethodHandles.lookup().unreflect(asListMethod);
        String[] elements = {"a", "b", "c"};
        System.out.println(
            asListMH.invokeWithArguments(elements)
        );
    public Object invokeWithArguments(Object... arguments)
... so java compiler (in order to be backwards source compatible with pre-varargs methods) treats
this invocation in a way that just passes the String[] elements to the invokeWithArguments method
via the single 'arguments' parameter without any conversion (because String[] is a subtype of
Object[]). MethodHandle.invokeWithArguments therefore takes 3 array elements and tries to invoke
the underlying asList method with them. It observes that Arrays.asList is a varargs method, so it
wraps the 3 "wannabe parameters" with the Object[] and passes the array to the asList method as
    [a, b, c]
If you want MethodHandle.invokeWithArguments() to treat "wannabe parameters" passed to it as
verbatim parameters regardless of the variable/fixed arity of the method to be invoked, then you
        Method asListMethod = Arrays.class.getMethod("asList", Object[].class);
        String[] elements = {"a", "b", "c"};
        System.out.println(
            asListMethod.invoke(null, (Object) elements)
        );
        MethodHandle asListMH = MethodHandles.lookup().unreflect(asListMethod);
        asListMH = asListMH.asFixedArity();
        System.out.println(
            asListMH.invokeWithArguments((Object) elements)
        );
    [a, b, c]
    [a, b, c]
    public Object invokeWithArguments(Object... arguments)
... so java compiler this time wraps the '(Object) elements' value with an Object[] because the
value is of Object type (which is not a subtype of Object[]) - no backwards compatibility needed
here, varargs conversion kicks in in the javac. MethodHandle.invokeWithArguments therefore takes
an array with 1 element (the element being 'elements' array) and tries to invoke the underlying
asList method with it. This time it observes that the method is not a varargs method, (because
MethodHandle was transformed to fixed arity method handle) so it passes the single 'elements'
wannabee parameter to the Arrays.asList single Object[] parameter - this invocation works because
fixed arity asList() takes Object[] and 'elements' is a String[]...
Hope this helps you understand what's going on.
Regards, Peter
Post by Rony G. Flatscher
Post by Rony G. Flatscher
Well, still trying to find out what the reason is, that core reflection's invoke behaves
differently to MethodHandle's invokeWithArguments in one single case so far (using the method
java.utli.Arrays.asList(...)).
Here is a little Java program that excercises reflective access to
"java.util.Arrays.asList​(T... a)" using core reflection, i.e. "Method.invoke​(Object obj,
import java.util.*;
import java.lang.reflect.*;
import java.lang.invoke.*;
class DemoAsListProblem
{
public static void main (String args[])
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[1] (main) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");
List listNames=Arrays.asList(arrNames);
System.out.println("[2] (main) after invoking Arrays.asList(arrNames), listNames=\""+listNames+"\"");
System.out.println("\ninvoking testReflective() ...\n");
testReflective();
}
public static void testReflective()
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[3] (testReflective) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");
Method methAsList=null;
List listNames=null;
try {
Class paramTypes[]=new Class[] { (new Object[0]).getClass() };
methAsList=Arrays.class.getDeclaredMethod("asList", paramTypes);
System.out.println("--- (core reflection) Method object asList: "+methAsList);
System.out.println("\n--- (core reflection) now invoking Arrays.asList() via Method.invoke(...):");
listNames=(List) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ); // static method
System.out.println("[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) methAsList.invoke( null, (Object) new Object[]{arrNames} ); // static method
System.out.println("[4b] --- (CR) methAsList.invoke( null, (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
// listNames=(List) methAsList.invoke( null, (Object[]) arrNames ); // static method
// System.out.println("[5a] --- (CR) methAsList.invoke( null, (Object[]) arrNames ) -> listNames=\""+listNames+"\"");
listNames=(List) methAsList.invoke( null, (Object) arrNames ); // static method
System.out.println("[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames=\""+listNames+"\"");
}
catch (Throwable t)
{
System.err.println("oops #1: "+t);
t.printStackTrace();
System.exit(-1);
}
System.out.println("\n--- (MH) now invoking Arrays.asList() via MethodHandle.invokeWithArguments(...):");
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle mh=null;
try {
mh=lookup.unreflect(methAsList);
System.out.println("--- (MH) unreflected MethodHandle mh: "+mh);
listNames=(List) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} );
System.out.println("[6a] --- (MH) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object) new Object[]{arrNames} );
System.out.println("[6b] --- (MH) mh.invokeWithArguments( (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object[]) arrNames );
System.out.println("[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object) arrNames );
System.out.println("[7b] --- (MH) mh.invokeWithArguments( (Object) arrNames ) -> listNames=\""+listNames+"\"");
}
catch (Throwable t)
{
System.err.println("oops #2: "+t);
t.printStackTrace();
System.exit(-2);
}
}
}
[2] (main) after invoking Arrays.asList(arrNames), listNames="[anne, bert, celine]"
invoking testReflective() ...
--- (core reflection) Method object asList: public static java.util.List java.util.Arrays.asList(java.lang.Object[])
[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames="[anne, bert, celine]"
[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames="[anne, bert, celine]"
--- (MH) unreflected MethodHandle mh: MethodHandle(Object[])List
[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames="[anne, bert, celine]"
So a String array is turned into a List using Arrays.asList(strArray). Doing it with core
reflection yields different results to doing it with invokeWithArguments().
I would have expected that [4a] and [6a] would behave the same.
Using reflective invoke() and invokeWithArguments() has been working for my bridge for all the
test units (using literally the same arguments in both cases) interchangeably, the one exception
is Arrays.asList().
Maybe I am not seeing the obvious (having done too much "close-up tests" for quite some time
now). So any hint, insight, help would be really appreciated!
---rony
As a few months have gone by without any follow-ups, I have been wondering whether there is a
solution at all, if this is a problem rooted in the current implementation of MethodHandle
methods not being 100% compatible with reflective invoke (which may imply that one needs to stick
to use reflective invoke and forgo MethodHandle invokes for good).
---rony
Peter Levart
2018-06-10 15:04:31 UTC
Permalink
Hi Rony,

I showed you some code samples in Java language, so I had to explain
what java compiler is doing too, for you to fully understand what's
going on. You see, when using Java and HM.invokeWithArguments(),
conversions (varargs included) are taking place at two levels. The 1st
conversion is performed by javac generated bytecode (when invoking
.invokeWithArguments, which is a varargs method) and the 2nd is the
conversion performed by the HM.invokeWithArguments() method itself when
it invokes the target method. The 1st is specified by JLS, while the 2nd
is specified in the javadoc of .invokeWithArguments.

If we skip the 1st conversion, since you probably know what compiler is
doing, you still have to be aware of the 2nd conversion, performed by
MH.invokeWithArguments() (and MH.invoke()). First thing you have to know
is the following:

/Method handles produced by lookups or constant loads from methods or
constructors with the variable arity modifier bit (0x0080) have a
corresponding variable arity, as if they were defined with the help of
asVarargsCollector.//
/
This part does not mention the MethodHandles.Lookup.unreflect(Method)
way of producing MethodHandle(s), but its all the same. You have to be
aware that some method handles returned to you have a special property:
they are varargs collectors. When invoking those methods, they accept
any number of trailing positional arguments and collect them into an
array argument.

This does not happen when you invoke such methods using reflection.

So if you want HM.invokeWithArguments() to behave the same as
Method.invoke(), you have to transform the method handles obtained from
lookup / unreflect with MH.asFixedArity() method. If the method handle
is already a fixed arity method handle, this method will just return it,
else it will return the equivalent method handle that doesn't perform
collection of trailing positional arguments into an array argument.
Using MH.asFixedArity() on each and every method handle you obtain by
unreflect should give you the same behavior of MH.invokeWithArguments()
as using Method.invoke().

Regards, Peter
Post by Rony G. Flatscher
thank you very much for your thorough and extensive analysis taking
the evolvement of Java into account!
You use the Java compiler and its behaviour (which adds syntax sugar
to the Java language and is known to stick strictly to the Java
language specification) for explanations and as reference, which is
also what I have been doing in the past to counter-check the
implemented behaviour of the bridge.
The Java compiler has a very different outset: *at compilation time*
it knows everything it needs to know for compiling. If something is
missing or wrong at compilation time it is able to stop compilation
raising appropriate errors, thereby inhibiting the successful creation
of Java classes as long as the Java programmer does not correct
erroneous code or supplies missing vital information. Java being a
strictly typed language allows one to cast arguments to force the
compiler to pick the method the Java programmer had in mind in the
case that the compiler could choose one from a set of a signatures.
By contrast a bridge for an interpreted, dynamically typed language
(like ooRexx) needs to reflect and infer at *runtime* in order to be
able to choose the Java methods with the appropriate signature. In
this particular case all primitive datatypes are (ooRexx/C) strings in
the dynamically typed language such that at runtime each argument
needs to be checked whether they contain valid values for conversion
to primitive datatypes in the signature of a Java method candidate.
In the dynamically typed language there is no means available for
casting that would be needed to help solve the presented problem
(there is optionally a box()-routine in the bridge that allows the
ooRexx programmer to explicitly determine which primitive type is to
be used for picking an appropriate Java method).
The new code in the bridge (had to rewrite that part from scratch
because of Java 9 and later) uses java.lang.reflect to pick an
appropriate Constructor, Method or Field object. For Java 1.6 and 1.7
(although java.lang.invoke/MethodHandle got introduced with 1.7 I
found cases where inaccessible types did not have accessible ancestors
in the JRE) java.lang.reflect invocation will be carried out, for Java
1.8 and later java.lang.invoke/MethodHandle invocations gets employed.
Comparing the speed between invocations using java.lang.reflect and
java.lang.invoke, java.lang.invoke wins, but by a quite small margin.
Upon further testunit tests the rewritten bridge using
java.lang.invoke would execute all ooRexx programs like
java.lang.reflect since Java 1.1 with one single exception so far
which I reported here, where invocation via java.lang.reflect behaves
differently to java.lang.invoke.
Because of this discrepancy the question would be: is this considered
a bug that will be corrected? Would there be otherwise a solution
possible in the bridge that could take care of situations like this,
where the bridge has no context-information other than referring to
Constructor, Method or Field objects with the supplied arguments?
* if the unreflect() method gets used then MH.invoke() should behave
like in the java.lang.reflect case; this means that the same
assumptions should govern MH.invoke(): reason being that if a
java.lang.reflect object gets unreflected, also the established
java.lang.reflect rules should keep working in this case for the
MH.invoke* in order to remain fully compatible with each other,
o if a MH gets created without unreflect() then this would not be necessary
* the bridge could restrain itself to only use java.lang.reflect for
invocations on Java 8 and higher instead of java.lang.invoke
o clearly, the current Java development efforts in this corner
are concentrated on java.lang.invoke, so it would be desirable
to switch from java.lang.reflect to java.lang.invoke which is
not possible as long as this problem (different behaviour in
j.l.r vs. j.l.i) persists
* do not fix this, but document this as an incompatibility with the
ramification that existing programs in dynamically typed languages
need to be rewritten (e.g. in this case creating an Array object
from the List and supply that instead)
* ... ?
---rony
P.S.: It would be great if Java implemented reflective invocations for
dynamically typed languages as this would solve the problem for all
such languages once and forever in a standard manner, rather than have
every bridge implementor create his own implementation, which is
challenging,  effortful and possibly error prone. In my case the
assumptions that governed the original implementation for reflective
Constructor, Method and Field access for over fifteen years got broken
by Java 9, because j.l.r (notable 'setAccessible') changed in a
fundamental aspect. If Java could be emplyoed instead, the Java
supplied implementation would be able to adopt changes in the inner
workings accordingly thereby insulating bridges from such details and
problems.
Post by Peter Levart
I think what you found is a discrepancy between handling of varargs
methods using reflection vs. method handle's .invokeWithArguments().
Reflection basically ignores the fact that some methods are varargs
methods. It treats them exactly the same as if they had an array
parameter (not a varargs parameter).
    public static <T> List<T> asList(T ... elements)
    public static <T> List<T> asList(T[] elements)
That's because varargs were introduced to Java in Java 5 as a kind of
compilation sugar, implemented as an array parameter, while
reflection is an older beast and wasn't updated to behave differently
with varargs methods.
MethodHandle(s) came later, in Java 7, and took into consideration varargs methods.
        Method asListMethod = Arrays.class.getMethod("asList",
Object[].class);
        String[] elements = {"a", "b", "c"};
        System.out.println(
            asListMethod.invoke(null, elements)
        );
You get: IllegalArgumentException: wrong number of arguments
    public Object invoke(Object obj, Object... args)
... so java compiler (in order to be backwards source compatible with
pre-varargs methods) treats this invocation in a way that just passes
the String[] elements to the invoke method via the args parameter
without any conversion (because String[] is a subtype of Object[]).
Mathod.invoke then treats elements of the args[] array as individual
verbatim parameters to be passed to the method when invoking it. As
reflection treats Arrays.asList() as a normal no-varargs method which
accepts a single Objet[] parameter and args[] contains 3 elements to
be passed to a method with 3 parameters, exception occurs.
        Method asListMethod = Arrays.class.getMethod("asList",
Object[].class);
        MethodHandle asListMH =
MethodHandles.lookup().unreflect(asListMethod);
        String[] elements = {"a", "b", "c"};
        System.out.println(
            asListMH.invokeWithArguments(elements)
        );
    public Object invokeWithArguments(Object... arguments)
... so java compiler (in order to be backwards source compatible with
pre-varargs methods) treats this invocation in a way that just passes
the String[] elements to the invokeWithArguments method via the
single 'arguments' parameter without any conversion (because String[]
is a subtype of Object[]). MethodHandle.invokeWithArguments therefore
takes 3 array elements and tries to invoke the underlying asList
method with them. It observes that Arrays.asList is a varargs method,
so it wraps the 3 "wannabe parameters" with the Object[] and passes
the array to the asList method as single parameter. The result of
    [a, b, c]
If you want MethodHandle.invokeWithArguments() to treat "wannabe
parameters" passed to it as verbatim parameters regardless of the
variable/fixed arity of the method to be invoked, then you can
        Method asListMethod = Arrays.class.getMethod("asList",
Object[].class);
        String[] elements = {"a", "b", "c"};
        System.out.println(
            asListMethod.invoke(null, (Object) elements)
        );
        MethodHandle asListMH =
MethodHandles.lookup().unreflect(asListMethod);
        asListMH = asListMH.asFixedArity();
        System.out.println(
            asListMH.invokeWithArguments((Object) elements)
        );
    [a, b, c]
    [a, b, c]
What happens here is the following (will only describe the MH case -
    public Object invokeWithArguments(Object... arguments)
... so java compiler this time wraps the '(Object) elements' value
with an Object[] because the value is of Object type (which is not a
subtype of Object[]) - no backwards compatibility needed here,
varargs conversion kicks in in the javac.
MethodHandle.invokeWithArguments therefore takes an array with 1
element (the element being 'elements' array) and tries to invoke the
underlying asList method with it. This time it observes that the
method is not a varargs method, (because MethodHandle was transformed
to fixed arity method handle) so it passes the single 'elements'
wannabee parameter to the Arrays.asList single Object[] parameter -
this invocation works because fixed arity asList() takes Object[] and
'elements' is a String[]...
Hope this helps you understand what's going on.
Regards, Peter
Post by Rony G. Flatscher
Post by Rony G. Flatscher
Well, still trying to find out what the reason is, that core
reflection's invoke behaves differently to MethodHandle's
invokeWithArguments in one single case so far (using the method
java.utli.Arrays.asList(...)).
Here is a little Java program that excercises reflective access to
"java.util.Arrays.asList​(T... a)" using core reflection, i.e.
"Method.invoke​(Object obj, Object... args)" and
import java.util.*;
import java.lang.reflect.*;
import java.lang.invoke.*;
class DemoAsListProblem
{
public static void main (String args[])
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[1] (main) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");
List listNames=Arrays.asList(arrNames);
System.out.println("[2] (main) after invoking Arrays.asList(arrNames), listNames=\""+listNames+"\"");
System.out.println("\ninvoking testReflective() ...\n");
testReflective();
}
public static void testReflective()
{
String arrNames[]=new String[] { "anne", "bert", "celine"};
System.out.println("[3] (testReflective) arrNames=\""+arrNames+"\", .toString()=\""+arrNames.toString()+"\"");
Method methAsList=null;
List listNames=null;
try {
Class paramTypes[]=new Class[] { (new Object[0]).getClass() };
methAsList=Arrays.class.getDeclaredMethod("asList", paramTypes);
System.out.println("--- (core reflection) Method object asList: "+methAsList);
System.out.println("\n--- (core reflection) now invoking Arrays.asList() via Method.invoke(...):");
listNames=(List) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ); // static method
System.out.println("[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) methAsList.invoke( null, (Object) new Object[]{arrNames} ); // static method
System.out.println("[4b] --- (CR) methAsList.invoke( null, (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
// listNames=(List) methAsList.invoke( null, (Object[]) arrNames ); // static method
// System.out.println("[5a] --- (CR) methAsList.invoke( null, (Object[]) arrNames ) -> listNames=\""+listNames+"\"");
listNames=(List) methAsList.invoke( null, (Object) arrNames ); // static method
System.out.println("[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames=\""+listNames+"\"");
}
catch (Throwable t)
{
System.err.println("oops #1: "+t);
t.printStackTrace();
System.exit(-1);
}
System.out.println("\n--- (MH) now invoking Arrays.asList() via MethodHandle.invokeWithArguments(...):");
MethodHandles.Lookup lookup=MethodHandles.lookup();
MethodHandle mh=null;
try {
mh=lookup.unreflect(methAsList);
System.out.println("--- (MH) unreflected MethodHandle mh: "+mh);
listNames=(List) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} );
System.out.println("[6a] --- (MH) mh.invokeWithArguments( (Object[]) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object) new Object[]{arrNames} );
System.out.println("[6b] --- (MH) mh.invokeWithArguments( (Object) new Object[]{arrNames} ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object[]) arrNames );
System.out.println("[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames=\""+listNames+"\"");
listNames=(List) mh.invokeWithArguments( (Object) arrNames );
System.out.println("[7b] --- (MH) mh.invokeWithArguments( (Object) arrNames ) -> listNames=\""+listNames+"\"");
}
catch (Throwable t)
{
System.err.println("oops #2: "+t);
t.printStackTrace();
System.exit(-2);
}
}
}
[2] (main) after invoking Arrays.asList(arrNames), listNames="[anne, bert, celine]"
invoking testReflective() ...
--- (core reflection) Method object asList: public static java.util.List java.util.Arrays.asList(java.lang.Object[])
[4a] --- (CR) methAsList.invoke( null, (Object[]) new Object[]{arrNames} ) -> listNames="[anne, bert, celine]"
[5b] --- (CR) methAsList.invoke( null, (Object) arrNames ) -> listNames="[anne, bert, celine]"
--- (MH) unreflected MethodHandle mh: MethodHandle(Object[])List
[7a] --- (MH) mh.invokeWithArguments( (Object[]) arrNames ) -> listNames="[anne, bert, celine]"
So a String array is turned into a List using
Arrays.asList(strArray). Doing it with core reflection yields
different results to doing it with invokeWithArguments().
I would have expected that [4a] and [6a] would behave the same.
Using reflective invoke() and invokeWithArguments() has been
working for my bridge for all the test units (using literally the
same arguments in both cases) interchangeably, the one exception is
Arrays.asList().
Maybe I am not seeing the obvious (having done too much "close-up
tests" for quite some time now). So any hint, insight, help would
be really appreciated!
---rony
As a few months have gone by without any follow-ups, I have been
wondering whether there is a solution at all, if this is a problem
rooted in the current implementation of MethodHandle methods not
being 100% compatible with reflective invoke (which may imply that
one needs to stick to use reflective invoke and forgo MethodHandle
invokes for good).
---rony
Rony G. Flatscher
2018-06-11 14:37:15 UTC
Permalink
Hi Peter,

once more: thank you very much for your explanations, efforts and patience! :)
I showed you some code samples in Java language, so I had to explain what java compiler is doing
too, for you to fully understand what's going on. You see, when using Java and
HM.invokeWithArguments(), conversions (varargs included) are taking place at two levels. The 1st
conversion is performed by javac generated bytecode (when invoking .invokeWithArguments, which is
a varargs method) and the 2nd is the conversion performed by the HM.invokeWithArguments() method
itself when it invokes the target method. The 1st is specified by JLS, while the 2nd is specified
in the javadoc of .invokeWithArguments.
If we skip the 1st conversion, since you probably know what compiler is doing, you still have to
be aware of the 2nd conversion, performed by MH.invokeWithArguments() (and MH.invoke()). First
/Method handles produced by lookups or constant loads from methods or constructors with the
variable arity modifier bit (0x0080) have a corresponding variable arity, as if they were defined
with the help of asVarargsCollector.//
/
This part does not mention the MethodHandles.Lookup.unreflect(Method) way of producing
MethodHandle(s), but its all the same. You have to be aware that some method handles returned to
you have a special property: they are varargs collectors. When invoking those methods, they accept
any number of trailing positional arguments and collect them into an array argument.
This does not happen when you invoke such methods using reflection.
So if you want HM.invokeWithArguments() to behave the same as Method.invoke(), you have to
transform the method handles obtained from lookup / unreflect with MH.asFixedArity() method. If
the method handle is already a fixed arity method handle, this method will just return it, else it
will return the equivalent method handle that doesn't perform collection of trailing positional
arguments into an array argument. Using MH.asFixedArity() on each and every method handle you
obtain by unreflect should give you the same behavior of MH.invokeWithArguments() as using
Method.invoke().
Indeed, this seems to solve the problem, which is really great as this problem can be put to rest
and it makes it safe to use MH invocations in the bridge from Java 8 on!

Thank you *very* much again!
(And now, "after the fact" of your explanations I have become able to understand the cause of the
problem and its solution. I simply was not aware that by default a varargs collector would be
employed for the last (in this case single) argument for a varargs argument.)

---

Now, I would have another request in this context: are there any tutorials that would introduce one
into the world of MethodHandle(s) in the sort of "for dummies"? The reason I ask is as follows: in
the past years I have followed the introduction of MethodHandle(s) with Java 7 and from time to time
skimmed over articles demonstrating the basics of taking advantage of MH. Yet when starting to use
them for rewriting the reflective invocation part I encountered a world that is documented, but
maybe too briefly (and for MH experts). It took me quite a long time to learn about the most
important methods and how to employ them. (But obviously, there is too much I am still not aware of.)

My take is that probably a little bit of more documentation and/or brief tutorials that stress
different aspects of MH programming (for the uninitiated, newbies) would help considerably. Maybe
brief ("nutshell") tutorials each explaining and demonstrating the different kinds of MH methods
(what problem do they solve, how would one program a problem solution).

So if anyone could point to such brief tutorials/articles "for newbies/dummies" covering and
demonstrating all the MH methods, this would be really great!

---rony
Rony G. Flatscher
2018-06-12 08:27:35 UTC
Permalink
On 10.06.2018 17:04, Peter Levart wrote:
... cut ...
So if you want HM.invokeWithArguments() to behave the same as Method.invoke(), you have to
transform the method handles obtained from lookup / unreflect with MH.asFixedArity() method. If
the method handle is already a fixed arity method handle, this method will just return it, else it
will return the equivalent method handle that doesn't perform collection of trailing positional
arguments into an array argument. Using MH.asFixedArity() on each and every method handle you
obtain by unreflect should give you the same behavior of MH.invokeWithArguments() as using
Method.invoke().
... cut ...


On 11.06.2018 16:37, Rony G. Flatscher wrote:
... cut ...
Indeed, this seems to solve the problem, which is really great as this problem can be put to rest
and it makes it safe to use MH invocations in the bridge from Java 8 on!
Thank you *very* much again!
(And now, "after the fact" of your explanations I have become able to understand the cause of the
problem and its solution. I simply was not aware that by default a varargs collector would be
employed for the last (in this case single) argument for a varargs argument.)
... cut ...


On 10.06.2018 14:16, Rony G. Flatscher wrote:
... cut ...
* if the unreflect() method gets used then MH.invoke() should behave like in the
java.lang.reflect case; this means that the same assumptions should govern MH.invoke(): reason
being that if a java.lang.reflect object gets unreflected, also the established
java.lang.reflect rules should keep working in this case for the MH.invoke* in order to remain
fully compatible with each other,
o if a MH gets created without unreflect() then this would not be necessary
... cut ...

The suggestion would be to have unreflect() apply MH.asFixedArity() such that the MH behaves as
c.l.r invoke.

---rony

Loading...