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> 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 > ASF Member, Apache Lucene PMC / Committer > Bremen, Germany > http://lucene.apache.org/ > >