Skip to content

stepancheg/mh-lang

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mh-lang: DSL for constructing Java MethodHandles

Reflection is great, but slow. Single reflection operation can be fast, but when invoking a sequence of operations, a user has to pay for reflective invocation overhead in each operation, which is:

  • type checking of all arguments
  • accessibility
  • boxing/unboxing

Consider this use case: check that none of the class fields are not null. Pseudo code:

void checkAllNotNull(T t) {
  for (Field f in T.fields) {
    Object v = f.get(t);
    if (v == null) {
      throw new IllegalStateException();
    }
  }
}

Field.get is invoked multiple times, each time it performs a type check.

Java MethodHandles allow glueing multiple reflective operation together. Each reflective operation (field get/set, method call, constructor) is a MethodHandle, and MethodHandles class contains a lot of combinators to glue all of the operations together.

Type-checking happens only once, when combined MethodHandle is constructed, and only once when glued MethodHandles is invoked.

The example above would be written as:

MethodHandle checkAllNotNull = ...;

void checkAllNotNull(T t) {
  checkAllNotNull.invokeExact(t);
}

The question is how to construct resulting MethodHandle. Raw combine operations (e. g. collectArguments) are very hard in my experience, and writing stateful code

  • when a computation result need to be used in multiple operations
  • with the order of operations is important
  • with runtime-conditional invocations

is very hard to a regular human.

mh-lang library provides a DSL for writing method handles in a friendly semi-functional style.

The mh described above could be written like this:

MhBuilder b = new MhBuilder();
Var<?> t = b.addParameter(tClass);
for (Field field : tClass.getDeclaredFields()) {
  Closure<?> fieldExpr = Closure.getField(field, t);
  b.assign(Closure.ifThen(
    fieldExpr.isNull(),
    Closure.throwException(void.class, Closure.supplier(IllegalStateException.class, () -> new IllegalStateException()));
  ));
}
checkAllNotNull = b.buildReturnVoid();

Examples

Examples live in examples folder, and the most demonstrative one is an implementation of struct of arrays.

Struct of arrays

Until Java gets proper value types, efficient memory usage is an issue.

One possible way to deal with this issue it to store lists of objects in structs of arrays: for each field allocate an array, and store each struct fields in own arrays.

As a demo for this library I have written to implementation of struct of arrays array lists:

MH version is twenty times faster than reflective version.

What about bytecode generation?

Bytecode generation is equally hard for a regular user and a small project, but additionally, bytecode cannot easily (without using JVM hacks) perform restricted operatins like accessing private fields or invoking private methods.

Method handles do not have this limitation.

Maven and other feedback

This library is not published in Maven or elsewhere. Feel free to just copy sources to your project or to you private storage. If you think that it need to be published on Maven, open an issue in GitHub issue tracker and I'll try to do something about it.

Any other feedback? Open an issue in GitHub issue tracker.

Thanks!

About

DSL for constructing MethodHandles

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages