Hi Attila,

 

thanks for the info. We are currently targeting Java 8.

 

The proposal made here was more about making the API in the MethodHandles class 
“feature complete”, because you have the array element getters/setters there, 
but not the length. This is pure inconsistent, because for countedLoop (which 
is new in Java 9), you coulduse it. From a user perspective this is also easier 
to understand.

 

In our code we stay for now with the code as described. I was just hoping that 
Java 9 would make the API more consistent and easy to use. The new 
MethodHandles.countedLoop really cries for support to natively and 
straigtforward way to get the array length – because this is the “normal way to 
process arrays with for-loops. And this is not natively possible unless you use 
my hack or java.lang.reflect.Array#getLength. For now, I added this as a “hack 
some private inner class like ArrayAccessor in the JDK”, but I would like to 
get rid of it once we are targetting Java 9.

 

This is why I started the thread here. My request is just to make the public 
MethodHandles API easy to use and consistent, which it isn’t. And addition of 
that should be easy and could be done in the same JEP like the new loop stuff 
in MethodHandles. I’d make a proposal, but I have no idea what the process 
would be.

 

FYI, we also have a DynamicCallSite in Painless that uses polymorphic caching, 
up to 5 different types using MethodHandles.guardWithTest: https://goo.gl/GhWmTS

 

Uwe

 

-----

Uwe Schindler

uschind...@apache.org 

ASF Member, Apache Lucene PMC / Committer

Bremen, Germany

http://lucene.apache.org/

 

From: Attila Szegedi [mailto:szege...@gmail.com] 
Sent: Thursday, May 12, 2016 11:37 AM
To: Uwe Schindler <uschind...@apache.org>
Cc: Core-Libs-Dev <core-libs-dev@openjdk.java.net>
Subject: Re: MethodHandle for array length

 

Hi Uwe,

 

If you’re targetting JDK 9 only, you could also consider basing your language’s 
dynamic operations on Dynalink; it’s integrated into JDK 9 and is specifically 
meant as a way to ease implementation of languages that have dynamic types. It 
happens to have a predefined GET_LENGTH[1] operation that does exactly what you 
want when BeansLinker[2] applies it to Java arrays. 

 

The documentation can be found at 
<http://download.java.net/java/jdk9/docs/jdk/api/dynalink 
<http://download.java.net/java/jdk9/docs/jdk/api/dynalink/jdk/dynalink/StandardOperation.html#GET_LENGTH>
 >; the jdk.dynalink package description is a good starting point (for some 
reason the main package is listed in a separate “Other Packages” section on the 
overview page… probably a Javadoc glitch w/regard to Jigsaw)

 

Attila.

 

[1] 
http://download.java.net/java/jdk9/docs/jdk/api/dynalink//jdk/dynalink/StandardOperation.html#GET_LENGTH
 
<http://download.java.net/java/jdk9/docs/jdk/api/dynalink/jdk/dynalink/StandardOperation.html#GET_LENGTH>
 

[2] 
http://download.java.net/java/jdk9/docs/jdk/api/dynalink/jdk/dynalink/beans/BeansLinker.html

 

On 11 May 2016, at 21:35, Uwe Schindler <uschind...@apache.org 
<mailto:uschind...@apache.org> > wrote:

 

Hi,

while working and trying the new JDK9 MethodHandles features like 
MethodHandles#countedLoop, I recognized a API inconsistency problem with it.

We also found this while implementing the "Painless" scripting language for 
Elasticsearch (see https://goo.gl/DbOzjC)! Painless is a very limited scripting 
language with a very limited subset of classes to access and close integration 
into Elasticsearch (similar to Lucene's Expressions Module). Both compile 
script code to anonymous classes and Elasticsearch uses invokedynamic for the 
untyped stuff. It is much faster than Groovy or Nashorn, because it compiles 
most of the code completely static with known types (like Lucene expressions), 
but also supports a Groovy-like "def" type. The latter one uses invokedynamic, 
everything else is exactly the same speed as native Java code.

During implementing array getters and setters for untyped stuff with 
invokedynamic we recognized the following problem (Java 8 code, see 
https://github.com/elastic/elasticsearch/pull/18232):

There is MethodHandles.arrayElementGetter and MethodHandles.arrayElementSetter, 
so we can have full speed and full dynamic flexibility for array loads and 
stores - the speed is great, same as native array access! But there is one 
factory method missing in Java 8: MethodHandles.arrayLengthGetter (or similar 
name). As "length" is not a field on the array class, it is impossible to 
create a "simple" MethodHandle as field access to "array.class#length" to 
access it (it must invoke the "arraylength" bytecode, there is no field). I 
think the "length" field is just syntactic sugar of javac compiler.

I know that Nashorn used to use java.lang.reflect.Array#getLength (not sure if 
this is still the case), but this makes usage inconsistent. You have to 
explicitly create a MethodHandle using the reflective array class to use it: 
And it is not strongly typed (it accepts Object). A real MethodHandle would be 
created for a specific type of array class and would return its length without 
runtime type checks. I know that at least Array.getLength() is optimized by 
Hotspot (in contrast to the other get/set methods, which are like 20 times 
slower than a method access, so MethodHandles.arrayElementGetter/Setter is very 
useful), but I would really like to suggest to fix this!

With Java 9 this gets a bit worse: There is no "easy way" with the 
MethodHanldes class to generate a MethodHandles.countedLoop() on all elements 
of an array:

public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle 
init, MethodHandle  body) [new in Java 9]

With a full-featured API one could write:

Class<?> type = arraytype, e.g. long[].class or String[].class
MethodHandle body = some code that uses MethodHandles.arrayElementGetter(type) 
for further processing
MethodHandles.countedLoop(MethodHandles.arrayLengthGetter(type), 
MethodHandles.constant(int.class, 0), body);

As you see, for the first parameter (count), you would need to use the 
reflective part in java.lang.reflect.Array if the method is still missing in 
Java 9. This is not bad here, because it is not called all the time, but for 
our scripting language, the reflective class was slower.

We implemented our own version of "arrayLengthGetter":

public  class ArrayLengthHelper {
 private ArrayLengthHelper() {}

 private static final Lookup PRIV_LOOKUP = MethodHandles.lookup();
 private static final Map<Class<?>,MethodHandle> ARRAY_TYPE_MH_MAPPING = 
Collections.unmodifiableMap(
   Stream.of(boolean[].class, byte[].class, short[].class, int[].class, 
long[].class, char[].class, float[].class, double[].class, Object[].class)
     .collect(Collectors.toMap(Function.identity(), type -> {
       try {
         return PRIV_LOOKUP.findStatic(PRIV_LOOKUP.lookupClass(), 
"getArrayLength", MethodType.methodType(int.class, type));
       } catch (ReflectiveOperationException e) {
         throw new AssertionError(e);
       }
     }))
 );
 private static final MethodHandle OBJECT_ARRAY_MH = 
ARRAY_TYPE_MH_MAPPING.get(Object[].class);

 static int getArrayLength(boolean[] array) { return array.length; }
 static int getArrayLength(byte[] array) { return array.length; }
 static int getArrayLength(short[] array) { return array.length; }
 static int getArrayLength(int[] array) { return array.length; }
 static int getArrayLength(long[] array) { return array.length; }
 static int getArrayLength(char[] array) { return array.length; }
 static int getArrayLength(float[] array) { return array.length; }
 static int getArrayLength(double[] array) { return array.length; }
 static int getArrayLength(Object[] array) { return array.length; }

 public static MethodHandle arrayLengthGetter(Class<?> arrayType) {
   if (!arrayType.isArray()) {
     throw new IllegalArgumentException("type must be an array");
   }
   return (ARRAY_TYPE_MH_MAPPING.containsKey(arrayType)) ?
       ARRAY_TYPE_MH_MAPPING.get(arrayType) :
       OBJECT_ARRAY_MH.asType(OBJECT_ARRAY_MH.type().changeParameterType(0, 
arrayType));
 }
}

Interestingly I later found out that MethodHandles.arrayElementGetter/Setter 
uses the same "trick" behind the scenes! See MethodHandleImpl.ArrayAccessor: 
http://goo.gl/94f6OB

I would suggest to add the missing Method to MethodHandles class and implement 
it together with the getters and setters in ArrayAccessor, similar to our 
example code (it is just a few lines more). In addition this one could then 
also use the extra intrinsic improvement that our class cannot use: 
makeIntrinsic(getAccessor(Object[].class, false), Intrinsic.ARRAY_LOAD);
[with another intrinsic added: Intrinsic.ARRAY_LENGTH for the arraylength 
bytecode]

What would be the process to propose such a change? Bug/Issue/JEP?
I could quickly create a patch, but fixing the Intrinsic/LambdaForm stuff is 
way too complicated for me, so I would suggest to take this as an inspiration 
how to do it

Uwe

-----
Uwe Schindler
uschind...@apache.org <mailto:uschind...@apache.org>  
ASF Member, Apache Lucene PMC / Committer
Bremen, Germany
http://lucene.apache.org/



 

Reply via email to