Agent to change retention of `@JavaScriptBody` and `@JavaScriptResource`
To support use of HTML/Java API in GraalVM's native-image tool:
- the
@JavaScriptBodyand@JavaScriptResourceannotation need to be available in runtime - however they have
RetentionType.CLASS - let's patch the bytecode then
It is necessary to change the bytecode of:
- all classes using these annotations need to duplicate their usage into
runtimeVisibleAnnotationclass file attribute - all these annotations need to be patched to declare
RetentionType.RUNTIME
With such changes we should be able to use the JVM agent of HTML/Java to make these annotation available at GraalVM's native image build time.
- [x] Write a test to verify behavior of the
-agentliboption - [x] Update documentation
This PR properly implements the ideas discussed at https://github.com/oracle/graal/issues/8177#issuecomment-2376103806
net.java.html.boot-2.0-SNAPSHOT.jar.gz JAR (after gzip -d) contains the Agent-Class attribute in its manifest:
/graalvm-21/bin/java \
-javaagent:net.java.html.boot-2.0-SNAPSHOT.jar \
-cp net.java.html.boot-2.0-SNAPSHOT.jar \
App.java
can be used to change the retention of the @JavaScriptBody & co. annotations as following App.java sample code demonstrates:
import net.java.html.js.JavaScriptBody;
class App {
@JavaScriptBody(args={}, body="")
public static native void hello();
public static void main(String... args) throws Exception {
var hello = App.class.getMethod("hello");
System.err.println("method: " + hello);
System.err.println("ann: " + hello.getAnnotation(JavaScriptBody.class));
}
}
Executing the program should print: ann: [JavaScriptBody](method: @net.java.html.js.JavaScriptBody(wait4js=true, wait4java=true, javacall=false, keepAlive=true, args={}, body="")).
Testing with Native Image
The primary goal of this change is to allow integration with GraalVM. Let's do a small test. Take GraalVM repository at release/graalvm-25.0 branch, in particular commit bb84380ba769081088ed5a95c2a4f3df9f1ac652:
graal/web-image$ mx build
graal/web-image$ graal/web-image$ mx web-image
Error: Please specify class (or <module>/<mainclass>) containing the main entry point method. (see --help)
With the mx web-image tool functioning. Let's create netbeans-html4j-test.sh file with the following content:
#!/bin/bash
set -e
mkdir -p hi
V=2.0-SNAPSHOT
CP=$1
if ! [ -e "$CP" ]; then
echo Usage $0 '<path_to_net.java.html.boot.jar>'
echo - typical path may be: $HOME/.m2/repository/org/netbeans/html/net.java.html.boot/$V/net.java.html.boot-$V.jar
exit 1
fi
cat >hi/Hi.java <<KONEC
class Hi {
@net.java.html.js.JavaScriptBody(args="txt", body="""
console.log(txt);
return "...returning back from JavaScript...";
""")
private static native String hello(String txt);
public static void main(String... args) {
System.out.println("In JVM");
var hi = hello("Hi from JavaScript");
System.out.println(hi);
System.out.println("Back in JVM");
}
}
KONEC
javac -cp $CP hi/Hi.java -d hi
mx web-image -H:+UnlockExperimentalVMOptions -o hi -H:Path=hi \
-H:+SILENT_COMPILE -H:-ClosureCompiler -H:+DumpPreClosure \
-H:+VerificationPhases -H:JSRuntime=Generic -H:Class=Hi \
-cp hi \
-J-javaagent:$CP
node hi/hi.js
and then just launch it with
net.java.html.boot-2.0-SNAPSHOT.jar.gz after uncompressing it with gzip -d:
graal/web-image$ ./netbeans-html4j-test.sh net.java.html.boot-2.0-SNAPSHOT.jar
======================
GraalVM Native Image: Generating 'hi' (executable)...
======================
...
In JVM
Hi from JavaScript
...returning back from JavaScript...
Back in JVM
Heuréka. CCing @pziegler, @toni-epple, @fniephaus - everything seems to be working. The next version of NetBeans/HTML API will easily generate the right code with mx web-image tool (with the help of -javaagent:net.java.html.boot.jar)!