uschindler opened a new pull request, #12873:
URL: https://github.com/apache/lucene/pull/12873

   This PR rewrites the JavascriptCompiler for the main branch to use more 
modern JVM features to be easier configurable and does not need a separate 
classloader per expression (uses hidden classes).
   
   Hidden classes are a new feature of Java 15: It allows to define a hidden 
class with byte code without using a classloader using the access constraints 
of a given private lookup. This is the same technique used by lambdas and other 
invokeDynamic features and replaces the old Unsafe API that Lucene never used. 
The downsides with a separate on-class classloader are described in #8933. 
Mainly with many expressions compiled in parallel there's a lock in parent 
classloader while looking up methods/fields and loading classes.
   
   The new Java 15 hidden classes are unloaded by the JVM if the get out of 
scope, so the workaround with a separate classloader is no longer needed.
   
   A second reason why separate classloaders were needed is to allow code 
access functions form other classloaders. This stopped me doing the whole 
optimization at first until I rewrote the whole function lookup to be more 
modern:
   
   - The function array is now a `Map<String, MethodHandle>`. This allows to 
create arbitrary functions anyhere in code like Elasticsearch/Opensearch (the 
code may even be private or in another classloader). The access to those method 
is no longer the deal of the compiler, if the MethodHandle is there, it can be 
used. Access is the deal of the user of the expressions API. In addition you 
can also use "synthetic" methodhandles like those generated by 
`MethodHandles#filterArguments` or you can use non-static methods as long as 
you bind the receiver to a fixed instance. Some examples were added to the 
tests.
   - The problem with MethodHandles is that they should be `static final` 
constants in the class or must appear in the constant pool of the class. This 
is now the second trick used here: Since Java 11 there is the feature of 
dynamic constants: Basically the LDC instruction can load a MethodHandle 
constant (which is treated as the "perfect" MethodHandle for Hotspot) using a 
bootstrap method (invokedynamic v2). The Javascript compiler now implements a 
bootstrap method that looks up the function dynamically from the above 
`Map<String,MethodHandle>`.
   - Java 16 brought another feature that allows to add `classData` to hidden 
classes (Java 15). This classData is saved in the private bootstrapper `Lookup` 
and is accessible during bootstrapping constants. When loading the hidden class 
we pass our function map as classdata to the lookup and this allows the 
constant bootstrapping to return the MethodHandle.
   - I have to do more benchmarking, but generally the dynamic constants and 
using LDC and `MethodHandle.invokeExact()` should be enough to call ALL 
functions. For now I added a shortcut: If the `MethodHandle` in our map links 
directly to a static Method in a public class in same classloader, the bytecode 
generator for now uses the `MethodHandleInfo` to generate a conventional 
`invokestatic`. I plan to remove this "optimization" after more benchmarking. 
You can easily disable the optimization by setting `directFunctions` to an 
empty Map.
   
   There is one downside in the hidden class feature that I added a workaround 
for: Hidden classes never appear in stack traces so my initial version did not 
print the actual JS function in stack trace. I worked around this by adding 
"exception patching" in the base class. It catches all Throwables throws by the 
generated code in the subclass and changes the stack trace to include the 
source code and hidden class name. This allows same user experience before.
   
   The current code is not backward compatible and needs an entry in 
`MIGRATE.txt`. I will investiagte how to add some helper methods for those 
people using `java.lang.reflect.Method` maps. Those can be easily converted to 
`MethodHandle` with some wrapper method. Support for separate classloader was 
removed. This is not a security issue, because the methods available for 
calling are defined externally and the API user is still responsible to only 
supply methods which should be accessible by users. The access checking is done 
by the provider of function map. For safety you can still let run whole of 
Lucene or the expressions module in a separate module/classloader.
   
   @rjernst, @jdconrad: Maybe this is also an idea for Painless.
   
   This PR replaces the outdated 
https://github.com/apache/lucene-solr/pull/1837. This closes #8933.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscr...@lucene.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscr...@lucene.apache.org
For additional commands, e-mail: issues-h...@lucene.apache.org

Reply via email to