diff --git a/native-image/spring-boot-webserver/Dockerfile.debian-slim.uber-jar b/native-image/spring-boot-webserver/Dockerfile.debian-slim.uber-jar new file mode 100644 index 0000000..c9b837f --- /dev/null +++ b/native-image/spring-boot-webserver/Dockerfile.debian-slim.uber-jar @@ -0,0 +1,9 @@ +FROM container-registry.oracle.com/graalvm/jdk:23 AS build +COPY . /webserver +WORKDIR /webserver +RUN ./mvnw clean package + +FROM gcr.io/distroless/java21-debian12 +COPY --from=build /webserver/target/webserver-0.0.1-SNAPSHOT.jar webserver-0.0.1-SNAPSHOT.jar +EXPOSE 8080 +ENTRYPOINT ["java", "-jar", "webserver-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-base.mostly b/native-image/spring-boot-webserver/Dockerfile.distroless-base.mostly new file mode 100644 index 0000000..8791fa0 --- /dev/null +++ b/native-image/spring-boot-webserver/Dockerfile.distroless-base.mostly @@ -0,0 +1,4 @@ +FROM gcr.io/distroless/base-debian12 +COPY target/webserver.mostly-static / +EXPOSE 8080 +ENTRYPOINT ["/webserver.mostly-static"] \ No newline at end of file diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic new file mode 100644 index 0000000..4c726d7 --- /dev/null +++ b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic @@ -0,0 +1,4 @@ +FROM gcr.io/distroless/java-base-debian12 +COPY target/webserver / +EXPOSE 8080 +ENTRYPOINT ["/webserver"] \ No newline at end of file diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-optimized b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-optimized new file mode 100644 index 0000000..141d92c --- /dev/null +++ b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.dynamic-optimized @@ -0,0 +1,4 @@ +FROM gcr.io/distroless/java-base-debian12 +COPY target/webserver.dynamic-optimized / +EXPOSE 8080 +ENTRYPOINT ["/webserver.dynamic-optimized"] \ No newline at end of file diff --git a/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.jlink b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.jlink new file mode 100644 index 0000000..e7b9790 --- /dev/null +++ b/native-image/spring-boot-webserver/Dockerfile.distroless-java-base.jlink @@ -0,0 +1,22 @@ +FROM container-registry.oracle.com/graalvm/jdk:23 AS build +COPY . /webserver +WORKDIR /webserver +RUN ./mvnw clean package +RUN ./mvnw dependency:build-classpath -Dmdep.outputFile=cp.txt +RUN jdeps --ignore-missing-deps -q --recursive --multi-release 23 --print-module-deps --class-path $(cat cp.txt) target/webserver-0.0.1-SNAPSHOT.jar +RUN jlink \ + --module-path ${JAVA_HOME}/jmods \ + --add-modules java.base,java.compiler,java.desktop,java.instrument,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.sql,jdk.jfr,jdk.unsupported,org.graalvm.nativeimage \ + --verbose \ + --strip-debug \ + --compress zip-9 \ + --no-header-files \ + --no-man-pages \ + --strip-java-debug-attributes \ + --output jlink-jre + +FROM gcr.io/distroless/java-base-debian12 +COPY --from=build /webserver/target/webserver-0.0.1-SNAPSHOT.jar webserver-0.0.1-SNAPSHOT.jar +COPY --from=build /webserver/jlink-jre jlink-jre +EXPOSE 8080 +ENTRYPOINT ["jlink-jre/bin/java", "-jar", "webserver-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/native-image/spring-boot-webserver/Dockerfile.scratch.static b/native-image/spring-boot-webserver/Dockerfile.scratch.static new file mode 100644 index 0000000..5b52e12 --- /dev/null +++ b/native-image/spring-boot-webserver/Dockerfile.scratch.static @@ -0,0 +1,5 @@ +FROM scratch +WORKDIR /tmp +COPY target/webserver.static / +EXPOSE 8080 +ENTRYPOINT ["/webserver.static"] \ No newline at end of file diff --git a/native-image/spring-boot-webserver/Dockerfile.scratch.static-upx b/native-image/spring-boot-webserver/Dockerfile.scratch.static-upx new file mode 100644 index 0000000..131d926 --- /dev/null +++ b/native-image/spring-boot-webserver/Dockerfile.scratch.static-upx @@ -0,0 +1,5 @@ +FROM scratch +WORKDIR /tmp +COPY target/webserver.static-upx / +EXPOSE 8080 +ENTRYPOINT ["/webserver.static-upx"] \ No newline at end of file diff --git a/native-image/spring-boot-webserver/README.md b/native-image/spring-boot-webserver/README.md new file mode 100644 index 0000000..a4f476f --- /dev/null +++ b/native-image/spring-boot-webserver/README.md @@ -0,0 +1,585 @@ +# Multi-Cloud Apps with GraalVM - Up and Running + +This workshop is for developers looking to understand better how to **build size-optimized cloud native Java applications** using [GraalVM Native Image](https://www.graalvm.org/jdk21/reference-manual/native-image/). You are going to discover ways to minimize application footprint by taking advantage of different Native Image linking options and packaging into various base containers. + +For the demo part, you will run a Spring Boot web server application, hosting the GraalVM website. +Spring Boot 3 has integrated support for GraalVM Native Image, making it easier to set up and configure a project. +Compiling a Spring Boot application ahead of time can significantly boost the performance and reduce its footprint. + +### Workshop Objectives + +In this workshop you will: + +- Learn how to compile a Spring Boot application ahead-of-time into a native executable and optimize it for file size. +- See how to use the [GraalVM Native Image Maven Plugin](https://graalvm.github.io/native-build-tools/latest/maven-plugin.html). +- Create native executables and run them inside different Docker containers. +- Shrink a container image size by taking advantage of different Native Image containerisation and linking options. +- Compare the deployed container images sizes. +- See how to use GitHub Actions to automate the build of native executables as part of a CI/CD pipeline. + +### Prerequisites + +* x86 Linux +* `musl` toolchain +* Container runtime such as [Rancher Desktop](https://docs.rancherdesktop.io/getting-started/installation/) or [Docker](https://www.docker.com/gettingstarted/) installed and running +* [GraalVM for JDK 23](https://www.graalvm.org/downloads/) or later. We recommend using [SDKMAN!](https://sdkman.io/). (For other download options, see [GraalVM Downloads](https://www.graalvm.org/downloads/).) + ```bash + sdk install java 23-graal + ``` + +## Setup + +Clone this repository with Git and enter the application directory: +```bash +git clone https://github.com/olyagpl/spring-boot-webserver.git +``` +```bash +cd spring-boot-webserver +``` + +## **STEP 1**: Compile and Run the Application from a JAR File Inside a Container + +Start by compiling and running the application from a JAR file inside a Docker container. +It requires a container image with a full JDK and runtime libraries. + +### Explanation + +The Dockerfile, provided for this step, _Dockerfile.distroless-base.uber-jar_, uses [Oracle GraalVM for JDK 23 container image](https://docs.oracle.com/en/graalvm/jdk/23/docs/getting-started/container-images/) for the builder, and [Debian Slim Linux image](https://github.com/linuxcontainers/debian-slim) for the runtime. +The entrypoint for this image is equivalent to `java -jar`, so only a path to a JAR file is specified in `CMD`. + +### Action + +1. Run the _build-jar.sh_ script from the application directory: + ```bash + ./build-jar.sh + ``` + +2. Once the script finishes, a container image _debian-slim.jar_ should be available. Start the application using `docker run`: + ```bash + docker run --rm -p8080:8080 webserver:debian-slim.jar + ``` + The container started in hundreds of milliseconds, **1.135 seconds**. + +3. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served. + +4. Return to the terminal and stop the running container by clicking CTRL+C. (The Docker runs in an attached mode.) + +5. Check the container size: + ```bash + docker images + ``` + The expected size is ~240MB. + Note that the website pages added **44MB** to the container size. + ``` + REPOSITORY TAG IMAGE ID CREATED SIZE + webserver debian-slim.jar 5c69f06a3972 About an hour ago 238MB + ``` + +## **STEP 2**: Build and Run a Jlink Custom Runtime Image Inside a Container + +In this step, you will create a custom runtime of this Spring Boot web server with Jlink and run it inside a container image. +See how much reduction in size you can gain. + +### Explanation + +Jlink, or `jlink`, is a tool that generates a custom Java runtime image that contains only the platform modules that are required for your application. This is one of the approaches to create cloud native applications introduced in Java 11. + +The script _build-jlink.sh_ that runs `docker build` using the _Dockerfile.distroless-java-base.jlink_. +The Dockerfile contains a multistage build: first it generates a Jlink custom runtime on a full JDK; then copies the runtime image folder along with static website pages into a Java base container image, and sets the entrypoint. + +The application does not have to be modular, but you need to figure out which modules the application depends on to be able to `jlink` it. +In the builder stage, running on a full JDK, after compiling the project, Docker generates a file _cp.txt_ containing the classpath with all the dependencies: +``` +RUN ./mvnw dependency:build-classpath -Dmdep.outputFile=cp.txt +``` +Then, Docker runs the `jdeps` command with the classpath to check required modules for this Spring Boot application: +```bash +RUN jdeps --ignore-missing-deps -q --recursive --multi-release 23 --print-module-deps --class-path $(cat cp.txt) target/webserver-0.0.1-SNAPSHOT.jar +``` +Finally, Docker runs `jlink` to create a custom runtime in the specified output directory _jlink-jre_. +The `ENTRYPOINT` for the application would be `java` from the custom runtime. + +### Action + +1. Run the script: + ``` + ./build-jlink.sh + ``` + +2. Once the script finishes, a container image _distroless-java-base.jlink_ should be available. Run it, mapping the ports: + ```bash + docker run --rm -p8080:8080 webserver:distroless-java-base.jlink + ``` + + The container started in **1.224 seconds**. + +3. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served. + +4. Return to the terminal and stop the running container by clicking CTRL+C. + +5. Compare the file size of container images: + ```bash + docker images + ``` + The expected output is: + ``` + REPOSITORY TAG IMAGE ID CREATED SIZE + webserver distroless-java-base.jlink 687f7683ad58 16 minutes ago 192MB + webserver debian-slim.jar 5c69f06a3972 2 hours ago 238MB + ``` + Jlink shrinked the container by **46MB**. + +## **STEP 3**: Build and Run a Native Image Inside a Container Using Paketo Buildpacks + +In this step, you will compile this Spring Boot application ahead of time with GraalVM Native Image and run it using Paketo Buildpacks container images. + +### Explanation + +Spring Boot supports building a native image in a container using the [Paketo Buildpack for Oracle](https://github.com/paketo-buildpacks/oracle) which provides GraalVM Native Image. + +The mechanism is that the Paketo builder pulls the [Jammy Tiny Stack image](https://github.com/paketo-buildpacks/builder-jammy-tiny) (Ubuntu distroless-like image) which contains no buildpacks. +Then you point the **builder** image to the **creator** image. +For this workshop, you point to the [Paketo Buildpack for Oracle](https://github.com/paketo-buildpacks/oracle) explicitly requesting the Native Image tool. + +If you open the _pom.xml_ file, you see the `spring-boot-maven-plugin` declaration added for you: +```xml + + + paketobuildpacks/builder-jammy-buildpackless-tiny + + paketobuildpacks/oracle + paketobuildpacks/java-native-image + + + +``` +When `java-native-image` is requested, the buildpack downloads Oracle GraalVM, which includes Native Image. +The [Paketo documentation provides several examples](https://paketo.io/docs/howto/java/#build-an-app-as-a-graalvm-native-image-application) that show you how to build applications with Native Image using buildpacks. + +> Note that if you do not specify Oracle's buildpack, it will pull the default buildpack, which can result in reduced performance. + +### Action + +1. Build a native executable for this Spring application using the Paketo buildpack: + ```bash + ./mvnw -Pnative spring-boot:build-image + ``` + +2. Once the build completes, a container image _0.0.1-SNAPSHOT_ should be available. Run it, mapping the ports: + ```bash + docker run --rm -p8080:8080 docker.io/library/webserver:0.0.1-SNAPSHOT + ``` + + The application is running from the native image inside a container. The container started in just **0.031 seconds**! + +3. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served. + +4. Return to the terminal and stop the running container by clicking CTRL+C. + +5. Check the size of this container image: + ```bash + docker images + ``` + The expected output is: + ``` + REPOSITORY TAG IMAGE ID CREATED SIZE + webserver distroless-java-base.jlink 687f7683ad58 31 minutes ago 192MB + webserver debian-slim.jar 5c69f06a3972 2 hours ago 238MB + webserver 0.0.1-SNAPSHOT 0660806da4a2 44 years ago 163MB + ``` + The new container, tagged as _0.0.1-SNAPSHOT_, is much smaller now **163MB**. + +## **STEP 4**: Build a Native Image Locally and Run Inside a Container (Default Configuration) + +In this step, you will create a native image with the default configuration on a host machine, and only run it inside a container. + +### Explanation + +Spring Boot 3 has integrated support for GraalVM Native Image, making it easier to set up and configure your project. +[Native Build Tools](https://graalvm.github.io/native-build-tools/latest/index.html) project, maintained by the GraalVM team, provide Maven and Gradle plugins for building native images. +The project configuration already contains all necessary plugins, including [Native Image Maven plugin](https://graalvm.github.io/native-build-tools/latest/index.html): +```xml + + org.graalvm.buildtools + native-maven-plugin + +``` + +You can build this web server ahead of time into a native executable, on your host machine, just like this: `./mvnw -Pnative native:compile`. The command will compile the application and create a fully dynamically linked native image, `webserver`, in the _target/_ directory. + +However, there is a script _build-dynamic-image.sh_, for your convenience, that does that and packages this native image in a distroless base container image with just enough to run the application. No Java Runtime Environment (JRE) is required! + +**Distroless container images** contain only your application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution. +Learn more in ["Distroless" Container Images](https://github.com/GoogleContainerTools/distroless). + +### Action + +1. Run the script: + ``` + ./build-dynamic-image.sh + ``` + +2. Once the build completes, a container image _distroless-java-base.dynamic_ should be available. Run it, mapping the ports: + ```bash + docker run --rm -p8080:8080 webserver:distroless-java-base.dynamic + ``` + + The application is running from the native image inside a container. The container started in **0.033 seconds**. + +3. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served. + +4. Return to the terminal and stop the running container by clicking CTRL+C. + +5. Check the size of this container image: + ```bash + docker images + ``` + The expected output is: + ```bash + REPOSITORY TAG IMAGE ID CREATED SIZE + webserver distroless-java-base.dynamic 5a5de47579ef 2 minutes ago 164MB + webserver distroless-java-base.jlink 687f7683ad58 37 minutes ago 192MB + webserver debian-slim.jar 5c69f06a3972 2 hours ago 238MB + webserver 0.0.1-SNAPSHOT 0660806da4a2 44 years ago 163MB + ``` + The new container image size **164MB** it almost matches the size of the container built with Paketo Buildpacks. + It is expected because it is the same native executable packaged into a different base container. + +6. Check what is the size of the native executable itself: + ```bash + ls -lh target/webserver + ``` + The expected size is **125M**. + Note that the static resources are "baked" into this native executable and added 44M to its size. + +## **STEP 5**: Build a Size-Optimized Native Image Locally and Run Inside a Container + +_This is where the fun begins._ + +In this step, you will build a fully dynamically linked native image **with the file size optimization on**, on a host machine, and run it inside a container. + +### Explanation + +GraalVM Native Image provides the option `-Os` which optimizes the resulting native image for file size. +`-Os` enables `-O2` optimizations except those that can increase code or executable size significantly. Learn more in [the Native Image documentation](https://www.graalvm.org/jdk23/reference-manual/native-image/optimizations-and-performance/#optimization-levels). + +For that, a separate Maven profile is provided to differentiate this run from the default build, and giving a different name for the output file: +```xml + + dynamic-size-optimized + + + + org.graalvm.buildtools + native-maven-plugin + + webserver.dynamic + + -Os + + + + + + +``` + +The script _build-dynamic-image.sh_, available in this repository for your convenience, creates a native image with fully dynamically linked shared libraries, **optimized for size**, and then packages it in a distroless base container image with just enough to run the application. No Java Runtime Environment (JRE) is required. + +> The `-Os` optimization will be on for all the subsequent builds. + +### Action + +1. Run the script to build a size-optimized native executable and package it into a container: + ```bash + ./build-dynamic-image-optimized.sh + ``` + +2. Once the build completes, a container image _distroless-java-base.dynamic-optimized_ should be available. Run it, mapping the ports: + ```bash + docker run --rm -p8080:8080 webserver:distroless-java-base.dynamic-optimized + ``` + + The application is running from the native image inside a container. The container started in **0.035 seconds**. + +3. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served. + +4. Return to the terminal and stop the running container by clicking CTRL+C. + +5. Check the size of this container image: + ```bash + docker images + ``` + The expected output is: + ```bash + REPOSITORY TAG IMAGE ID CREATED SIZE + webserver distroless-java-base.dynamic-optimized 0bed48dadd11 About a minute ago 130MB + webserver distroless-java-base.dynamic 5a5de47579ef 16 minutes ago 164MB + webserver distroless-java-base.jlink 687f7683ad58 51 minutes ago 192MB + webserver debian-slim.jar 5c69f06a3972 2 hours ago 238MB + webserver 0.0.1-SNAPSHOT 0660806da4a2 44 years ago 163MB + ``` + The size of `distroless-java-base.dynamic-optimized` container is cut down from **164MB** to **130MB**. + This is because the native executable reduced in size. + +6. Check what is the size of the native executable now: + ```bash + ls -lh target/webserver + ``` + The size decreased from **125M** to **92M** by applying the file size optimization. + ``` + -rwxr-xr-x. 1 opc opc 125M Aug 29 10:51 target/webserver + -rw-r--r--. 1 opc opc 45M Aug 29 10:39 target/webserver-0.0.1-SNAPSHOT.jar + -rw-r--r--. 1 opc opc 25M Aug 29 10:39 target/webserver-0.0.1-SNAPSHOT.jar.original + -rwxr-xr-x. 1 opc opc 92M Aug 29 11:06 target/webserver.dynamic-optimized + ``` + +## **STEP 6**: Build a Size-Optimized Mostly Static Native Image Locally and Run Inside a Container + +In this step, you will build a **mostly static** native image, with the file size optimization on, on a host machine, then package it into a container image that provides `glibc`, and run. + +### Explanation + +A **mostly static** native image links all the shared libraries on which it relies (`zlib`, JDK-shared static libraries) except the standard C library, `libc`. This type of native image is useful for deployment on a distroless base container image. +You can build a mostly statically linked image by passing the `--static-nolibc` option at build time. + +A separate Maven profile exists for this step: +```xml + + mostly-static + + + + org.graalvm.buildtools + native-maven-plugin + + webserver.mostly-static + + --static-nolibc + -Os + + + + + + +``` + +### Action + +1. Run the script: + ```bash + ./build-mostly-static-image.sh + ``` + +2. Once the build completes, a container image _distroless-base.mostly-static_ should be available. Run it, mapping the ports: + ```bash + docker run --rm -p8080:8080 webserver:distroless-base.mostly-static + ``` + + The application is running from the mostly static native image inside a container. The container started in **0.036 seconds**. + +3. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served. + +4. Return to the terminal and stop the running container by clicking CTRL+C. + +5. Check the size of this container image: + ```bash + docker images + ``` + The expected output is: + ```bash + REPOSITORY TAG IMAGE ID CREATED SIZE + webserver distroless-base.mostly-static b49eec5bdfa6 About a minute ago 117MB + webserver distroless-java-base.dynamic-optimized 0bed48dadd11 9 minutes ago 130MB + webserver distroless-java-base.dynamic 5a5de47579ef 24 minutes ago 164MB + webserver distroless-java-base.jlink 687f7683ad58 About an hour ago 192MB + webserver debian-slim.jar 5c69f06a3972 2 hours ago 238MB + webserver 0.0.1-SNAPSHOT 0660806da4a2 44 years ago 163MB + ``` + + The size of the new _distroless-base.mostly-static_ container is **117MB**. + The reduction in size is related to the fact that a smaller base image was pulled: **gcr.io/distroless/base-debian12**. + [Distroless images](https://github.com/GoogleContainerTools/distroless) are very small, and the one used is only **48.3 MB**. + That's about 50% of the size of **java-base-debian12**(124 MB) used before, and 3 times less than **java21-debian12** (192 MB) containing a full JDK. + + The size of the mostly static native image has not changed, and is **92MB**. + +## **STEP 7**: Build a Size-Optimized Fully Static Native Image Locally and Run Inside a Container + +In this step, you will build a **fully static** native image, with the file size optimization on, on a host machine, then package it into a _scratch_ container. + +### Explanation + +A **fully static** native image is a statically linked binary that you can use without any additional library dependencies. +You can create a static native image by statically linking it against `musl-libc`, a lightweight, fast, and simple `libc` implementation. +To build a fully static executable, pass the `--static --libc=musl` options at build time. + +This static native image is easy to deploy on a slim or distroless container, even a [_scratch_ container](https://hub.docker.com/_/scratch). +A _scratch_ container is a [Docker official image](https://hub.docker.com/_/scratch), useful for building super minimal images. + +A separate Maven profile exists for this step: +```xml + + fully-static + + + + org.graalvm.buildtools + native-maven-plugin + + webserver.static + + --static --libc=musl + -Os + + + + + + +``` + +### Action + +1. This step requires the `musl` toolchain with `zlib`. Run the following script to download and configure the `musl` toolchain, and install `zlib` into the toolchain: + ```bash + ./setup-musl.sh + ``` + +2. Run the script to build a fully static native executable and package it into a _scratch_ container: + ```bash + ./build-static-image.sh + ``` + +3. Once the build completes, a container image _scratch.static_ should be available. Run it, mapping the ports: + ```bash + docker run --rm -p8080:8080 webserver:scratch.static + ``` + The container started in **0.035 seconds**. + As a result you get a tiny container with a fully functional and deployable server application! + +4. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served. + +5. Return to the terminal and stop the running container by clicking CTRL+C. + +6. Double-check if the native executable that was just created is indeed fully static with `ldd`: + ```bash + ldd target/webserver.static + ``` + You should see "not a dynamic executable" for the response. + Which means that the **image does not rely on any libraries in the operating system environment** and can be packaged in the tiniest container! + +7. Now check the size of this container image: + ```bash + docker images + ``` + The expected output is: + ``` + REPOSITORY TAG IMAGE ID CREATED SIZE + webserver scratch.static 6e8b25dacca6 7 minutes ago 96.4MB + webserver distroless-base.mostly-static b49eec5bdfa6 25 minutes ago 117MB + webserver distroless-java-base.dynamic-optimized 0bed48dadd11 32 minutes ago 130MB + webserver distroless-java-base.dynamic 5a5de47579ef 48 minutes ago 164MB + webserver distroless-java-base.jlink 687f7683ad58 About an hour ago 192MB + webserver debian-slim.jar 5c69f06a3972 3 hours ago 238MB + webserver 0.0.1-SNAPSHOT 0660806da4a2 44 years ago 163MB + ``` + The container size shrinked to **96.4MB**! A _scratch_ container weights only **14.5MB**. + +## **STEP 8**: Compress a Static Native Image with UPX and Run Inside a Container + +_Not convincing? What can you do next to reduce the size even more?_ + +You can compress your fully static native image with UPX, then package into the same _scratch_ container, and run. + +### Explanation + +[UPX](https://upx.github.io/) - an advanced executable file compressor. +It can significantly reduce the executable size, but note, that UPX loads the executable into the memory, unpackages it, and then recompresses. + +1. Download and install UPX: + ```bash + ./setup-upx.sh + ``` + +2. Run the script to compress the fully static executable, created at the previous step, and package it into a _scratch_ container. + ```bash + ./build-static-upx-image.sh + ``` + +3. Once the build completes, a container image _scratch.static-upx_ should be available. Run it, mapping the ports: + + ```bash + docker run --rm -p8080:8080 webserver:scratch.static-upx + ``` + The container started in **0.035 seconds**. + +4. Open a browser and navigate to [localhost:8080/](http://localhost:8080/). You see the GraalVM documentation pages served. + +5. Return to the terminal and stop the running container by clicking CTRL+C. + +6. Now check how much `upx` compressed the static native image: + ```bash + ls -lh target/webserver* + ``` + The expected output is: + ``` + -rwxr-xr-x. 1 opc opc 125M Aug 29 10:51 target/webserver + -rw-r--r--. 1 opc opc 45M Aug 29 11:30 target/webserver-0.0.1-SNAPSHOT.jar + -rwxr-xr-x. 1 opc opc 92M Aug 29 11:06 target/webserver.dynamic-optimized + -rwxr-xr-x. 1 opc opc 92M Aug 29 11:14 target/webserver.mostly-static + -rwxr-xr-x. 1 opc opc 92M Aug 29 11:32 target/webserver.static + -rwxr-xr-x. 1 opc opc 35M Aug 29 11:32 target/webserver.static-upx + ``` + The **webserver.static-upx** is only **35MB**! + The `upx` compressed executable by **57MB** from the "uncompressed" one. + +7. Lastly, check the size of all container images: + ```bash + docker images + ``` + The expected output is: + ``` + REPOSITORY TAG IMAGE ID CREATED SIZE + webserver scratch.static-upx c87bfe44c7fb 6 seconds ago 36.2MB + webserver scratch.static 6e8b25dacca6 7 minutes ago 96.4MB + webserver distroless-base.mostly-static b49eec5bdfa6 25 minutes ago 117MB + webserver distroless-java-base.dynamic-optimized 0bed48dadd11 32 minutes ago 130MB + webserver distroless-java-base.dynamic 5a5de47579ef 48 minutes ago 164MB + webserver distroless-java-base.jlink 687f7683ad58 About an hour ago 192MB + webserver debian-slim.jar 5c69f06a3972 3 hours ago 238MB + webserver 0.0.1-SNAPSHOT 0660806da4a2 44 years ago 163MB + ``` + The container size reduced dramatically to just **36.2MB**. + The application and container image's size were now shrinked to the minimum. + +## **STEP 9**: Clean up (Optional) + +To clean up all images, run the `./clean.sh` script provided for that purpose. + +## Conclusions + +A fully functional and, at the same time, minimal, Java application was compiled into a native Linux executable and packaged into base, distroless, and scratch containers thanks to GraalVM Native Image's support for various linking options. +All the versions of this Spring Boot application are functionally equivalent. + +Sorted by size, it is clear that the fully static native image, compressed with `upx`, and then packaged on the _scratch_ container is the smallest at just **36.2MB**. Note that the website static pages added 44M to the container images size. Static resources are "baked” into native images. + +| Container | Size of a build artefact
(JAR, Jlink runtime, native executable) | Base image | Container | +|----------------------------------------|-----------------------------------------------------------------------|------------|-----------| +| debian-slim.jar | webserver-0.0.1-SNAPSHOT.jar **44MB** | 192 MB | 249MB | +| distroless-java-base.jlink | jlink-jre custom runtime **68MB** | 128 MB | 202MB | +| distroless-java-base.dynamic | webserver **125MB** | 128 MB | 164MB | +| 0.0.1-SNAPSHOT | | | 163MB | +| distroless-java-base.dynamic-optimized | webserver-optimized **92MB** | 128 MB | 130MB | +| distroless-base.mostly-static | webserver.mostly-static **92MB** | 48.3 MB | 117MB | +| scratch.static | webserver.scratch.static **92MB** | 14.5 MB | 96.4MB | +| scratch.static-upx | webserver.scratch.static-upx **35MB** | 14.5 MB | 36.2MB | + +## Learn More + +- ["Distroless" Container Images](https://github.com/GoogleContainerTools/distroless) +- [Paketo Buildpacks](https://paketo.io/docs/) +- [Native Build Tools](https://graalvm.github.io/native-build-tools/latest/index.html) +- [Static and Mostly Static Images](https://www.graalvm.org/jdk23/reference-manual/native-image/guides/build-static-executables/) +- [Tiny Java Containers by Shaun Smith at DevoxxUK 2022](https://youtu.be/6wYrAtngIVo) \ No newline at end of file diff --git a/native-image/spring-boot-webserver/build-all.sh b/native-image/spring-boot-webserver/build-all.sh new file mode 100755 index 0000000..af1b039 --- /dev/null +++ b/native-image/spring-boot-webserver/build-all.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +./build-jar.sh +./build-jlink.sh +./build-dynamic-image.sh +./build-mostly-static-image.sh +./build-static-image.sh +./build-static-upx-image.sh + +echo "Generated Executables" +ls -lh target/webserver* + +echo "Generated Docker Container Images" +docker images webserver \ No newline at end of file diff --git a/native-image/spring-boot-webserver/build-dynamic-image-optimized.sh b/native-image/spring-boot-webserver/build-dynamic-image-optimized.sh new file mode 100755 index 0000000..a4c6a6b --- /dev/null +++ b/native-image/spring-boot-webserver/build-dynamic-image-optimized.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Compile with fully dynamically linked shared libraries; optimize for size +./mvnw -Dmaven.test.skip=true -Pdynamic-size-optimized native:compile + +# Distroless Java Base-provides glibc and other libraries needed by the JDK +docker build . -f Dockerfile.distroless-java-base.dynamic-optimized -t webserver:distroless-java-base.dynamic-optimized \ No newline at end of file diff --git a/native-image/spring-boot-webserver/build-dynamic-image.sh b/native-image/spring-boot-webserver/build-dynamic-image.sh new file mode 100755 index 0000000..1a7997b --- /dev/null +++ b/native-image/spring-boot-webserver/build-dynamic-image.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Compile with fully dynamically linked shared libraries +./mvnw -Dmaven.test.skip=true native:compile + +# Distroless Java Base-provides glibc and other libraries needed by the JDK +docker build . -f Dockerfile.distroless-java-base.dynamic -t webserver:distroless-java-base.dynamic \ No newline at end of file diff --git a/native-image/spring-boot-webserver/build-jar.sh b/native-image/spring-boot-webserver/build-jar.sh new file mode 100755 index 0000000..fc43acf --- /dev/null +++ b/native-image/spring-boot-webserver/build-jar.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +# Distroless Java Base-provides glibc and other libraries needed by the JDK +docker build . -f Dockerfile.debian-slim.uber-jar -t webserver:debian-slim.jar \ No newline at end of file diff --git a/native-image/spring-boot-webserver/build-jlink.sh b/native-image/spring-boot-webserver/build-jlink.sh new file mode 100755 index 0000000..abf2032 --- /dev/null +++ b/native-image/spring-boot-webserver/build-jlink.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +rm -rf jlink-jre cp.txt + +# Distroless Java Base-provides glibc and other libraries needed by the JDK +docker build . -f Dockerfile.distroless-java-base.jlink -t webserver:distroless-java-base.jlink \ No newline at end of file diff --git a/native-image/spring-boot-webserver/build-mostly-static-image.sh b/native-image/spring-boot-webserver/build-mostly-static-image.sh new file mode 100755 index 0000000..b65b9f8 --- /dev/null +++ b/native-image/spring-boot-webserver/build-mostly-static-image.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +# Compile linking zlib and JDK shared libraries except the standard C library (libc); optimize for size +./mvnw -Dmaven.test.skip=true -Pnative,mostly-static native:compile + +# Distroless Base (provides glibc) +docker build . -f Dockerfile.distroless-base.mostly -t webserver:distroless-base.mostly-static \ No newline at end of file diff --git a/native-image/spring-boot-webserver/build-static-image.sh b/native-image/spring-boot-webserver/build-static-image.sh new file mode 100755 index 0000000..52ac253 --- /dev/null +++ b/native-image/spring-boot-webserver/build-static-image.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +TOOLCHAIN_DIR=${SCRIPT_DIR}/musl-toolchain +PATH=${TOOLCHAIN_DIR}/bin:${PATH} + +# Create a statically linked binary that can be used without any additional library dependencies; optimize for size +./mvnw -Dmaven.test.skip=true -Pnative,fully-static native:compile + +# Scratch-nothing +docker build . -f Dockerfile.scratch.static -t webserver:scratch.static \ No newline at end of file diff --git a/native-image/spring-boot-webserver/build-static-upx-image.sh b/native-image/spring-boot-webserver/build-static-upx-image.sh new file mode 100755 index 0000000..8405282 --- /dev/null +++ b/native-image/spring-boot-webserver/build-static-upx-image.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +rm -f target/webserver.static-upx + +# Compress with UPX +./upx --lzma --best -o target/webserver.static-upx target/webserver.static + +# Scratch--fully static and compressed +docker build . -f Dockerfile.scratch.static-upx -t webserver:scratch.static-upx \ No newline at end of file diff --git a/native-image/spring-boot-webserver/clean.sh b/native-image/spring-boot-webserver/clean.sh new file mode 100755 index 0000000..e3c4656 --- /dev/null +++ b/native-image/spring-boot-webserver/clean.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set +e + +./mvnw clean +rm -rf jlink-jre cp.txt +docker images webserver -q | grep -v TAG | awk '{print($1)}' | xargs docker rmi \ No newline at end of file diff --git a/native-image/spring-boot-webserver/mvnw b/native-image/spring-boot-webserver/mvnw new file mode 100755 index 0000000..8d937f4 --- /dev/null +++ b/native-image/spring-boot-webserver/mvnw @@ -0,0 +1,308 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "$(uname)" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then + if $darwin ; then + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" + else + javaExecutable="$(readlink -f "\"$javaExecutable\"")" + fi + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=$(cd "$wdir/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +fi + +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$JAVA_HOME" ] && + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") + [ -n "$CLASSPATH" ] && + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/native-image/spring-boot-webserver/mvnw.cmd b/native-image/spring-boot-webserver/mvnw.cmd new file mode 100644 index 0000000..f80fbad --- /dev/null +++ b/native-image/spring-boot-webserver/mvnw.cmd @@ -0,0 +1,205 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/native-image/spring-boot-webserver/pom.xml b/native-image/spring-boot-webserver/pom.xml new file mode 100644 index 0000000..ade3bfd --- /dev/null +++ b/native-image/spring-boot-webserver/pom.xml @@ -0,0 +1,144 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.2 + + + com.example + webserver + 0.0.1-SNAPSHOT + webserver + Demo project for Spring Boot + + 17 + com.example.webserver.WebserverApplication + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.graalvm.buildtools + native-maven-plugin + + + org.springframework.boot + spring-boot-maven-plugin + + com.example.webserver.WebserverApplication + + paketobuildpacks/builder-jammy-buildpackless-tiny + + paketobuildpacks/oracle + paketobuildpacks/java-native-image + + + + + + + repackage + + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + dynamic-size-optimized + + + + org.graalvm.buildtools + native-maven-plugin + + webserver.dynamic-optimized + + -Os + + + + + + + + + mostly-static + + + + org.graalvm.buildtools + native-maven-plugin + + webserver.mostly-static + + --static-nolibc + -Os + + + + + + + + + fully-static + + + + org.graalvm.buildtools + native-maven-plugin + + webserver.static + + --static + --libc=musl + -Os + + + + + + + + + \ No newline at end of file diff --git a/native-image/spring-boot-webserver/setup-musl.sh b/native-image/spring-boot-webserver/setup-musl.sh new file mode 100755 index 0000000..71aebb7 --- /dev/null +++ b/native-image/spring-boot-webserver/setup-musl.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -e + +# Specify an installation directory for musl: +export MUSL_HOME=$PWD/musl-toolchain + +# Download musl and zlib sources: +curl -O https://musl.libc.org/releases/musl-1.2.4.tar.gz +curl -O https://zlib.net/fossils/zlib-1.2.13.tar.gz + +# Build musl from source +tar -xzvf musl-1.2.4.tar.gz +rm -rf musl-1.2.4.tar.gz +pushd musl-1.2.4 +./configure --prefix=$MUSL_HOME --static +# The next operation may require privileged access to system resources, so use sudo +sudo make && make install +popd + +# Install a symlink for use by native-image +ln -s $MUSL_HOME/bin/musl-gcc $MUSL_HOME/bin/x86_64-linux-musl-gcc + +# Extend the system path and confirm that musl is available by printing its version +export PATH="$MUSL_HOME/bin:$PATH" +x86_64-linux-musl-gcc --version + +# Build zlib with musl from source and install into the MUSL_HOME directory +tar -xzvf zlib-1.2.13.tar.gz +rm -rf zlib-1.2.13.tar.gz +pushd zlib-1.2.13 +CC=musl-gcc ./configure --prefix=$MUSL_HOME --static +make && make install +popd \ No newline at end of file diff --git a/native-image/spring-boot-webserver/setup-upx.sh b/native-image/spring-boot-webserver/setup-upx.sh new file mode 100755 index 0000000..d12a82c --- /dev/null +++ b/native-image/spring-boot-webserver/setup-upx.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +wget -q https://github.com/upx/upx/releases/download/v4.2.4/upx-4.2.4-amd64_linux.tar.xz +tar -xJf upx-4.2.4-amd64_linux.tar.xz +rm -rf upx-4.2.4-amd64_linux.tar.xz +mv upx-4.2.4-amd64_linux/upx . +rm -rf upx-4.2.4--amd64_linux \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/java/com/example/webserver/WebserverApplication.java b/native-image/spring-boot-webserver/src/main/java/com/example/webserver/WebserverApplication.java new file mode 100644 index 0000000..2c0c5f7 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/java/com/example/webserver/WebserverApplication.java @@ -0,0 +1,13 @@ +package com.example.webserver; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class WebserverApplication { + + public static void main(String[] args) { + SpringApplication.run(WebserverApplication.class, args); + } + +} diff --git a/native-image/spring-boot-webserver/src/main/resources/application.properties b/native-image/spring-boot-webserver/src/main/resources/application.properties new file mode 100644 index 0000000..2109a44 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/application.properties @@ -0,0 +1 @@ +spring.application.name=demo diff --git a/native-image/spring-boot-webserver/src/main/resources/static/assets/main.css b/native-image/spring-boot-webserver/src/main/resources/static/assets/main.css new file mode 100644 index 0000000..1006351 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/assets/main.css @@ -0,0 +1,12708 @@ +@charset "UTF-8"; +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors + * Copyright 2011-2020 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +:root { + --blue: #007bff; + --indigo: #6610f2; + --purple: #6f42c1; + --pink: #e83e8c; + --red: #dc3545; + --orange: #fd7e14; + --yellow: #ffc107; + --green: #28a745; + --teal: #20c997; + --cyan: #17a2b8; + --white: #fff; + --gray: #6c757d; + --gray-dark: #343a40; + --primary: #007bff; + --secondary: #6c757d; + --success: #28a745; + --info: #17a2b8; + --warning: #ffc107; + --danger: #dc3545; + --light: #f8f9fa; + --dark: #343a40; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } + +*, +*::before, +*::after { + box-sizing: border-box; } + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } + +article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { + display: block; } + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #212529; + text-align: left; + background-color: #fff; } + +[tabindex="-1"]:focus:not(:focus-visible) { + outline: 0 !important; } + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; } + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0.5rem; } + +p { + margin-top: 0; + margin-bottom: 1rem; } + +abbr[title], +abbr[data-original-title] { + text-decoration: underline; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; + text-decoration-skip-ink: none; } + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; } + +ol, +ul, +dl { + margin-top: 0; + margin-bottom: 1rem; } + +ol ol, +ul ul, +ol ul, +ul ol { + margin-bottom: 0; } + +dt { + font-weight: 700; } + +dd { + margin-bottom: .5rem; + margin-left: 0; } + +blockquote { + margin: 0 0 1rem; } + +b, +strong { + font-weight: bolder; } + +small { + font-size: 80%; } + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; } + +sub { + bottom: -.25em; } + +sup { + top: -.5em; } + +a { + color: #007bff; + text-decoration: none; + background-color: transparent; } + a:hover { + color: #0056b3; + text-decoration: underline; } + +a:not([href]) { + color: inherit; + text-decoration: none; } + a:not([href]):hover { + color: inherit; + text-decoration: none; } + +pre, +code, +kbd, +samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 1em; } + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + -ms-overflow-style: scrollbar; } + +figure { + margin: 0 0 1rem; } + +img { + vertical-align: middle; + border-style: none; } + +svg { + overflow: hidden; + vertical-align: middle; } + +table { + border-collapse: collapse; } + +caption { + padding-top: 0.75rem; + padding-bottom: 0.75rem; + color: #6c757d; + text-align: left; + caption-side: bottom; } + +th { + text-align: inherit; } + +label { + display: inline-block; + margin-bottom: 0.5rem; } + +button { + border-radius: 0; } + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; } + +input, +button, +select, +optgroup, +textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; } + +button, +input { + overflow: visible; } + +button, +select { + text-transform: none; } + +[role="button"] { + cursor: pointer; } + +select { + word-wrap: normal; } + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; } + +button:not(:disabled), +[type="button"]:not(:disabled), +[type="reset"]:not(:disabled), +[type="submit"]:not(:disabled) { + cursor: pointer; } + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + padding: 0; + border-style: none; } + +input[type="radio"], +input[type="checkbox"] { + box-sizing: border-box; + padding: 0; } + +textarea { + overflow: auto; + resize: vertical; } + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; } + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: .5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal; } + +progress { + vertical-align: baseline; } + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; } + +[type="search"] { + outline-offset: -2px; + -webkit-appearance: none; } + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; } + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button; } + +output { + display: inline-block; } + +summary { + display: list-item; + cursor: pointer; } + +template { + display: none; } + +[hidden] { + display: none !important; } + +h1, h2, h3, h4, h5, h6, +.h1, .h2, .h3, .h4, .h5, .h6 { + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; } + +h1, .h1 { + font-size: 2.5rem; } + +h2, .h2 { + font-size: 2rem; } + +h3, .h3 { + font-size: 1.75rem; } + +h4, .h4 { + font-size: 1.5rem; } + +h5, .h5 { + font-size: 1.25rem; } + +h6, .h6 { + font-size: 1rem; } + +.lead { + font-size: 1.25rem; + font-weight: 300; } + +.display-1 { + font-size: 6rem; + font-weight: 300; + line-height: 1.2; } + +.display-2 { + font-size: 5.5rem; + font-weight: 300; + line-height: 1.2; } + +.display-3 { + font-size: 4.5rem; + font-weight: 300; + line-height: 1.2; } + +.display-4 { + font-size: 3.5rem; + font-weight: 300; + line-height: 1.2; } + +hr { + margin-top: 1rem; + margin-bottom: 1rem; + border: 0; + border-top: 1px solid rgba(0, 0, 0, 0.1); } + +small, +.small { + font-size: 80%; + font-weight: 400; } + +mark, +.mark { + padding: 0.2em; + background-color: #fcf8e3; } + +.list-unstyled { + padding-left: 0; + list-style: none; } + +.list-inline { + padding-left: 0; + list-style: none; } + +.list-inline-item { + display: inline-block; } + .list-inline-item:not(:last-child) { + margin-right: 0.5rem; } + +.initialism { + font-size: 90%; + text-transform: uppercase; } + +.blockquote { + margin-bottom: 1rem; + font-size: 1.25rem; } + +.blockquote-footer { + display: block; + font-size: 80%; + color: #6c757d; } + .blockquote-footer::before { + content: "\2014\00A0"; } + +.img-fluid { + max-width: 100%; + height: auto; } + +.img-thumbnail { + padding: 0.25rem; + background-color: #fff; + border: 1px solid #dee2e6; + border-radius: 0.25rem; + max-width: 100%; + height: auto; } + +.figure { + display: inline-block; } + +.figure-img { + margin-bottom: 0.5rem; + line-height: 1; } + +.figure-caption { + font-size: 90%; + color: #6c757d; } + +code { + font-size: 87.5%; + color: #e83e8c; + word-wrap: break-word; } + a > code { + color: inherit; } + +kbd { + padding: 0.2rem 0.4rem; + font-size: 87.5%; + color: #fff; + background-color: #212529; + border-radius: 0.2rem; } + kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 700; } + +pre { + display: block; + font-size: 87.5%; + color: #212529; } + pre code { + font-size: inherit; + color: inherit; + word-break: normal; } + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; } + +.container { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; } + @media (min-width: 576px) { + .container { + max-width: 540px; } } + @media (min-width: 768px) { + .container { + max-width: 720px; } } + @media (min-width: 992px) { + .container { + max-width: 960px; } } + @media (min-width: 1200px) { + .container { + max-width: 1140px; } } + +.container-fluid, .container-sm, .container-md, .container-lg, .container-xl { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; } + +@media (min-width: 576px) { + .container, .container-sm { + max-width: 540px; } } +@media (min-width: 768px) { + .container, .container-sm, .container-md { + max-width: 720px; } } +@media (min-width: 992px) { + .container, .container-sm, .container-md, .container-lg { + max-width: 960px; } } +@media (min-width: 1200px) { + .container, .container-sm, .container-md, .container-lg, .container-xl { + max-width: 1140px; } } +.row { + display: flex; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px; } + +.no-gutters { + margin-right: 0; + margin-left: 0; } + .no-gutters > .col, + .no-gutters > [class*="col-"] { + padding-right: 0; + padding-left: 0; } + +.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, +.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, +.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, +.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, +.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, +.col-xl-auto { + position: relative; + width: 100%; + padding-right: 15px; + padding-left: 15px; } + +.col { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + +.row-cols-1 > * { + flex: 0 0 100%; + max-width: 100%; } + +.row-cols-2 > * { + flex: 0 0 50%; + max-width: 50%; } + +.row-cols-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + +.row-cols-4 > * { + flex: 0 0 25%; + max-width: 25%; } + +.row-cols-5 > * { + flex: 0 0 20%; + max-width: 20%; } + +.row-cols-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + +.col-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + +.col-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + +.col-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + +.col-3 { + flex: 0 0 25%; + max-width: 25%; } + +.col-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + +.col-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + +.col-6 { + flex: 0 0 50%; + max-width: 50%; } + +.col-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + +.col-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + +.col-9 { + flex: 0 0 75%; + max-width: 75%; } + +.col-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + +.col-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + +.col-12 { + flex: 0 0 100%; + max-width: 100%; } + +.order-first { + order: -1; } + +.order-last { + order: 13; } + +.order-0 { + order: 0; } + +.order-1 { + order: 1; } + +.order-2 { + order: 2; } + +.order-3 { + order: 3; } + +.order-4 { + order: 4; } + +.order-5 { + order: 5; } + +.order-6 { + order: 6; } + +.order-7 { + order: 7; } + +.order-8 { + order: 8; } + +.order-9 { + order: 9; } + +.order-10 { + order: 10; } + +.order-11 { + order: 11; } + +.order-12 { + order: 12; } + +.offset-1 { + margin-left: 8.3333333333%; } + +.offset-2 { + margin-left: 16.6666666667%; } + +.offset-3 { + margin-left: 25%; } + +.offset-4 { + margin-left: 33.3333333333%; } + +.offset-5 { + margin-left: 41.6666666667%; } + +.offset-6 { + margin-left: 50%; } + +.offset-7 { + margin-left: 58.3333333333%; } + +.offset-8 { + margin-left: 66.6666666667%; } + +.offset-9 { + margin-left: 75%; } + +.offset-10 { + margin-left: 83.3333333333%; } + +.offset-11 { + margin-left: 91.6666666667%; } + +@media (min-width: 576px) { + .col-sm { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + + .row-cols-sm-1 > * { + flex: 0 0 100%; + max-width: 100%; } + + .row-cols-sm-2 > * { + flex: 0 0 50%; + max-width: 50%; } + + .row-cols-sm-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .row-cols-sm-4 > * { + flex: 0 0 25%; + max-width: 25%; } + + .row-cols-sm-5 > * { + flex: 0 0 20%; + max-width: 20%; } + + .row-cols-sm-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-sm-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + + .col-sm-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + + .col-sm-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-sm-3 { + flex: 0 0 25%; + max-width: 25%; } + + .col-sm-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .col-sm-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + + .col-sm-6 { + flex: 0 0 50%; + max-width: 50%; } + + .col-sm-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + + .col-sm-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + + .col-sm-9 { + flex: 0 0 75%; + max-width: 75%; } + + .col-sm-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + + .col-sm-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + + .col-sm-12 { + flex: 0 0 100%; + max-width: 100%; } + + .order-sm-first { + order: -1; } + + .order-sm-last { + order: 13; } + + .order-sm-0 { + order: 0; } + + .order-sm-1 { + order: 1; } + + .order-sm-2 { + order: 2; } + + .order-sm-3 { + order: 3; } + + .order-sm-4 { + order: 4; } + + .order-sm-5 { + order: 5; } + + .order-sm-6 { + order: 6; } + + .order-sm-7 { + order: 7; } + + .order-sm-8 { + order: 8; } + + .order-sm-9 { + order: 9; } + + .order-sm-10 { + order: 10; } + + .order-sm-11 { + order: 11; } + + .order-sm-12 { + order: 12; } + + .offset-sm-0 { + margin-left: 0; } + + .offset-sm-1 { + margin-left: 8.3333333333%; } + + .offset-sm-2 { + margin-left: 16.6666666667%; } + + .offset-sm-3 { + margin-left: 25%; } + + .offset-sm-4 { + margin-left: 33.3333333333%; } + + .offset-sm-5 { + margin-left: 41.6666666667%; } + + .offset-sm-6 { + margin-left: 50%; } + + .offset-sm-7 { + margin-left: 58.3333333333%; } + + .offset-sm-8 { + margin-left: 66.6666666667%; } + + .offset-sm-9 { + margin-left: 75%; } + + .offset-sm-10 { + margin-left: 83.3333333333%; } + + .offset-sm-11 { + margin-left: 91.6666666667%; } } +@media (min-width: 768px) { + .col-md { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + + .row-cols-md-1 > * { + flex: 0 0 100%; + max-width: 100%; } + + .row-cols-md-2 > * { + flex: 0 0 50%; + max-width: 50%; } + + .row-cols-md-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .row-cols-md-4 > * { + flex: 0 0 25%; + max-width: 25%; } + + .row-cols-md-5 > * { + flex: 0 0 20%; + max-width: 20%; } + + .row-cols-md-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-md-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + + .col-md-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + + .col-md-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-md-3 { + flex: 0 0 25%; + max-width: 25%; } + + .col-md-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .col-md-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + + .col-md-6 { + flex: 0 0 50%; + max-width: 50%; } + + .col-md-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + + .col-md-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + + .col-md-9 { + flex: 0 0 75%; + max-width: 75%; } + + .col-md-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + + .col-md-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + + .col-md-12 { + flex: 0 0 100%; + max-width: 100%; } + + .order-md-first { + order: -1; } + + .order-md-last { + order: 13; } + + .order-md-0 { + order: 0; } + + .order-md-1 { + order: 1; } + + .order-md-2 { + order: 2; } + + .order-md-3 { + order: 3; } + + .order-md-4 { + order: 4; } + + .order-md-5 { + order: 5; } + + .order-md-6 { + order: 6; } + + .order-md-7 { + order: 7; } + + .order-md-8 { + order: 8; } + + .order-md-9 { + order: 9; } + + .order-md-10 { + order: 10; } + + .order-md-11 { + order: 11; } + + .order-md-12 { + order: 12; } + + .offset-md-0 { + margin-left: 0; } + + .offset-md-1 { + margin-left: 8.3333333333%; } + + .offset-md-2 { + margin-left: 16.6666666667%; } + + .offset-md-3 { + margin-left: 25%; } + + .offset-md-4 { + margin-left: 33.3333333333%; } + + .offset-md-5 { + margin-left: 41.6666666667%; } + + .offset-md-6 { + margin-left: 50%; } + + .offset-md-7 { + margin-left: 58.3333333333%; } + + .offset-md-8 { + margin-left: 66.6666666667%; } + + .offset-md-9 { + margin-left: 75%; } + + .offset-md-10 { + margin-left: 83.3333333333%; } + + .offset-md-11 { + margin-left: 91.6666666667%; } } +@media (min-width: 992px) { + .col-lg { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + + .row-cols-lg-1 > * { + flex: 0 0 100%; + max-width: 100%; } + + .row-cols-lg-2 > * { + flex: 0 0 50%; + max-width: 50%; } + + .row-cols-lg-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .row-cols-lg-4 > * { + flex: 0 0 25%; + max-width: 25%; } + + .row-cols-lg-5 > * { + flex: 0 0 20%; + max-width: 20%; } + + .row-cols-lg-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-lg-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + + .col-lg-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + + .col-lg-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-lg-3 { + flex: 0 0 25%; + max-width: 25%; } + + .col-lg-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .col-lg-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + + .col-lg-6 { + flex: 0 0 50%; + max-width: 50%; } + + .col-lg-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + + .col-lg-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + + .col-lg-9 { + flex: 0 0 75%; + max-width: 75%; } + + .col-lg-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + + .col-lg-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + + .col-lg-12 { + flex: 0 0 100%; + max-width: 100%; } + + .order-lg-first { + order: -1; } + + .order-lg-last { + order: 13; } + + .order-lg-0 { + order: 0; } + + .order-lg-1 { + order: 1; } + + .order-lg-2 { + order: 2; } + + .order-lg-3 { + order: 3; } + + .order-lg-4 { + order: 4; } + + .order-lg-5 { + order: 5; } + + .order-lg-6 { + order: 6; } + + .order-lg-7 { + order: 7; } + + .order-lg-8 { + order: 8; } + + .order-lg-9 { + order: 9; } + + .order-lg-10 { + order: 10; } + + .order-lg-11 { + order: 11; } + + .order-lg-12 { + order: 12; } + + .offset-lg-0 { + margin-left: 0; } + + .offset-lg-1 { + margin-left: 8.3333333333%; } + + .offset-lg-2 { + margin-left: 16.6666666667%; } + + .offset-lg-3 { + margin-left: 25%; } + + .offset-lg-4 { + margin-left: 33.3333333333%; } + + .offset-lg-5 { + margin-left: 41.6666666667%; } + + .offset-lg-6 { + margin-left: 50%; } + + .offset-lg-7 { + margin-left: 58.3333333333%; } + + .offset-lg-8 { + margin-left: 66.6666666667%; } + + .offset-lg-9 { + margin-left: 75%; } + + .offset-lg-10 { + margin-left: 83.3333333333%; } + + .offset-lg-11 { + margin-left: 91.6666666667%; } } +@media (min-width: 1200px) { + .col-xl { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + + .row-cols-xl-1 > * { + flex: 0 0 100%; + max-width: 100%; } + + .row-cols-xl-2 > * { + flex: 0 0 50%; + max-width: 50%; } + + .row-cols-xl-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .row-cols-xl-4 > * { + flex: 0 0 25%; + max-width: 25%; } + + .row-cols-xl-5 > * { + flex: 0 0 20%; + max-width: 20%; } + + .row-cols-xl-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-xl-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + + .col-xl-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + + .col-xl-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-xl-3 { + flex: 0 0 25%; + max-width: 25%; } + + .col-xl-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .col-xl-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + + .col-xl-6 { + flex: 0 0 50%; + max-width: 50%; } + + .col-xl-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + + .col-xl-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + + .col-xl-9 { + flex: 0 0 75%; + max-width: 75%; } + + .col-xl-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + + .col-xl-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + + .col-xl-12 { + flex: 0 0 100%; + max-width: 100%; } + + .order-xl-first { + order: -1; } + + .order-xl-last { + order: 13; } + + .order-xl-0 { + order: 0; } + + .order-xl-1 { + order: 1; } + + .order-xl-2 { + order: 2; } + + .order-xl-3 { + order: 3; } + + .order-xl-4 { + order: 4; } + + .order-xl-5 { + order: 5; } + + .order-xl-6 { + order: 6; } + + .order-xl-7 { + order: 7; } + + .order-xl-8 { + order: 8; } + + .order-xl-9 { + order: 9; } + + .order-xl-10 { + order: 10; } + + .order-xl-11 { + order: 11; } + + .order-xl-12 { + order: 12; } + + .offset-xl-0 { + margin-left: 0; } + + .offset-xl-1 { + margin-left: 8.3333333333%; } + + .offset-xl-2 { + margin-left: 16.6666666667%; } + + .offset-xl-3 { + margin-left: 25%; } + + .offset-xl-4 { + margin-left: 33.3333333333%; } + + .offset-xl-5 { + margin-left: 41.6666666667%; } + + .offset-xl-6 { + margin-left: 50%; } + + .offset-xl-7 { + margin-left: 58.3333333333%; } + + .offset-xl-8 { + margin-left: 66.6666666667%; } + + .offset-xl-9 { + margin-left: 75%; } + + .offset-xl-10 { + margin-left: 83.3333333333%; } + + .offset-xl-11 { + margin-left: 91.6666666667%; } } +.table { + width: 100%; + margin-bottom: 1rem; + color: #212529; } + .table th, + .table td { + padding: 0.75rem; + vertical-align: top; + border-top: 1px solid #dee2e6; } + .table thead th { + vertical-align: bottom; + border-bottom: 2px solid #dee2e6; } + .table tbody + tbody { + border-top: 2px solid #dee2e6; } + +.table-sm th, +.table-sm td { + padding: 0.3rem; } + +.table-bordered { + border: 1px solid #dee2e6; } + .table-bordered th, + .table-bordered td { + border: 1px solid #dee2e6; } + .table-bordered thead th, + .table-bordered thead td { + border-bottom-width: 2px; } + +.table-borderless th, +.table-borderless td, +.table-borderless thead th, +.table-borderless tbody + tbody { + border: 0; } + +.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(0, 0, 0, 0.05); } + +.table-hover tbody tr:hover { + color: #212529; + background-color: rgba(0, 0, 0, 0.075); } + +.table-primary, +.table-primary > th, +.table-primary > td { + background-color: #b8daff; } +.table-primary th, +.table-primary td, +.table-primary thead th, +.table-primary tbody + tbody { + border-color: #7abaff; } + +.table-hover .table-primary:hover { + background-color: #9fcdff; } + .table-hover .table-primary:hover > td, + .table-hover .table-primary:hover > th { + background-color: #9fcdff; } + +.table-secondary, +.table-secondary > th, +.table-secondary > td { + background-color: #d6d8db; } +.table-secondary th, +.table-secondary td, +.table-secondary thead th, +.table-secondary tbody + tbody { + border-color: #b3b7bb; } + +.table-hover .table-secondary:hover { + background-color: #c8cbcf; } + .table-hover .table-secondary:hover > td, + .table-hover .table-secondary:hover > th { + background-color: #c8cbcf; } + +.table-success, +.table-success > th, +.table-success > td { + background-color: #c3e6cb; } +.table-success th, +.table-success td, +.table-success thead th, +.table-success tbody + tbody { + border-color: #8fd19e; } + +.table-hover .table-success:hover { + background-color: #b1dfbb; } + .table-hover .table-success:hover > td, + .table-hover .table-success:hover > th { + background-color: #b1dfbb; } + +.table-info, +.table-info > th, +.table-info > td { + background-color: #bee5eb; } +.table-info th, +.table-info td, +.table-info thead th, +.table-info tbody + tbody { + border-color: #86cfda; } + +.table-hover .table-info:hover { + background-color: #abdde5; } + .table-hover .table-info:hover > td, + .table-hover .table-info:hover > th { + background-color: #abdde5; } + +.table-warning, +.table-warning > th, +.table-warning > td { + background-color: #ffeeba; } +.table-warning th, +.table-warning td, +.table-warning thead th, +.table-warning tbody + tbody { + border-color: #ffdf7e; } + +.table-hover .table-warning:hover { + background-color: #ffe8a1; } + .table-hover .table-warning:hover > td, + .table-hover .table-warning:hover > th { + background-color: #ffe8a1; } + +.table-danger, +.table-danger > th, +.table-danger > td { + background-color: #f5c6cb; } +.table-danger th, +.table-danger td, +.table-danger thead th, +.table-danger tbody + tbody { + border-color: #ed969e; } + +.table-hover .table-danger:hover { + background-color: #f1b0b7; } + .table-hover .table-danger:hover > td, + .table-hover .table-danger:hover > th { + background-color: #f1b0b7; } + +.table-light, +.table-light > th, +.table-light > td { + background-color: #fdfdfe; } +.table-light th, +.table-light td, +.table-light thead th, +.table-light tbody + tbody { + border-color: #fbfcfc; } + +.table-hover .table-light:hover { + background-color: #ececf6; } + .table-hover .table-light:hover > td, + .table-hover .table-light:hover > th { + background-color: #ececf6; } + +.table-dark, +.table-dark > th, +.table-dark > td { + background-color: #c6c8ca; } +.table-dark th, +.table-dark td, +.table-dark thead th, +.table-dark tbody + tbody { + border-color: #95999c; } + +.table-hover .table-dark:hover { + background-color: #b9bbbe; } + .table-hover .table-dark:hover > td, + .table-hover .table-dark:hover > th { + background-color: #b9bbbe; } + +.table-active, +.table-active > th, +.table-active > td { + background-color: rgba(0, 0, 0, 0.075); } + +.table-hover .table-active:hover { + background-color: rgba(0, 0, 0, 0.075); } + .table-hover .table-active:hover > td, + .table-hover .table-active:hover > th { + background-color: rgba(0, 0, 0, 0.075); } + +.table .thead-dark th { + color: #fff; + background-color: #343a40; + border-color: #454d55; } +.table .thead-light th { + color: #495057; + background-color: #e9ecef; + border-color: #dee2e6; } + +.table-dark { + color: #fff; + background-color: #343a40; } + .table-dark th, + .table-dark td, + .table-dark thead th { + border-color: #454d55; } + .table-dark.table-bordered { + border: 0; } + .table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, 0.05); } + .table-dark.table-hover tbody tr:hover { + color: #fff; + background-color: rgba(255, 255, 255, 0.075); } + +@media (max-width: 575.98px) { + .table-responsive-sm { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive-sm > .table-bordered { + border: 0; } } +@media (max-width: 767.98px) { + .table-responsive-md { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive-md > .table-bordered { + border: 0; } } +@media (max-width: 991.98px) { + .table-responsive-lg { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive-lg > .table-bordered { + border: 0; } } +@media (max-width: 1199.98px) { + .table-responsive-xl { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive-xl > .table-bordered { + border: 0; } } +.table-responsive { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; } + .table-responsive > .table-bordered { + border: 0; } + +.form-control { + display: block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .form-control { + transition: none; } } + .form-control::-ms-expand { + background-color: transparent; + border: 0; } + .form-control:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; } + .form-control:focus { + color: #495057; + background-color: #fff; + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .form-control::placeholder { + color: #6c757d; + opacity: 1; } + .form-control:disabled, .form-control[readonly] { + background-color: #e9ecef; + opacity: 1; } + +input[type="date"].form-control, +input[type="time"].form-control, +input[type="datetime-local"].form-control, +input[type="month"].form-control { + appearance: none; } + +select.form-control:focus::-ms-value { + color: #495057; + background-color: #fff; } + +.form-control-file, +.form-control-range { + display: block; + width: 100%; } + +.col-form-label { + padding-top: calc(0.375rem + 1px); + padding-bottom: calc(0.375rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; } + +.col-form-label-lg { + padding-top: calc(0.5rem + 1px); + padding-bottom: calc(0.5rem + 1px); + font-size: 1.25rem; + line-height: 1.5; } + +.col-form-label-sm { + padding-top: calc(0.25rem + 1px); + padding-bottom: calc(0.25rem + 1px); + font-size: 0.875rem; + line-height: 1.5; } + +.form-control-plaintext { + display: block; + width: 100%; + padding: 0.375rem 0; + margin-bottom: 0; + font-size: 1rem; + line-height: 1.5; + color: #212529; + background-color: transparent; + border: solid transparent; + border-width: 1px 0; } + .form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg { + padding-right: 0; + padding-left: 0; } + +.form-control-sm { + height: calc(1.5em + 0.5rem + 2px); + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; } + +.form-control-lg { + height: calc(1.5em + 1rem + 2px); + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; } + +select.form-control[size], select.form-control[multiple] { + height: auto; } + +textarea.form-control { + height: auto; } + +.form-group { + margin-bottom: 1rem; } + +.form-text { + display: block; + margin-top: 0.25rem; } + +.form-row { + display: flex; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px; } + .form-row > .col, + .form-row > [class*="col-"] { + padding-right: 5px; + padding-left: 5px; } + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem; } + +.form-check-input { + position: absolute; + margin-top: 0.3rem; + margin-left: -1.25rem; } + .form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label { + color: #6c757d; } + +.form-check-label { + margin-bottom: 0; } + +.form-check-inline { + display: inline-flex; + align-items: center; + padding-left: 0; + margin-right: 0.75rem; } + .form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: 0.3125rem; + margin-left: 0; } + +.valid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #28a745; } + +.valid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(40, 167, 69, 0.9); + border-radius: 0.25rem; } + +.was-validated :valid ~ .valid-feedback, +.was-validated :valid ~ .valid-tooltip, +.is-valid ~ .valid-feedback, +.is-valid ~ .valid-tooltip { + display: block; } + +.was-validated .form-control:valid, .form-control.is-valid { + border-color: #28a745; + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-control:valid:focus, .form-control.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } + +.was-validated textarea.form-control:valid, textarea.form-control.is-valid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } + +.was-validated .custom-select:valid, .custom-select.is-valid { + border-color: #28a745; + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .custom-select:valid:focus, .custom-select.is-valid:focus { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } + +.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { + color: #28a745; } +.was-validated .form-check-input:valid ~ .valid-feedback, +.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback, +.form-check-input.is-valid ~ .valid-tooltip { + display: block; } + +.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label { + color: #28a745; } + .was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before { + border-color: #28a745; } +.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before { + border-color: #34ce57; + background-color: #34ce57; } +.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } +.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #28a745; } + +.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label { + border-color: #28a745; } +.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label { + border-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); } + +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #dc3545; } + +.invalid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.875rem; + line-height: 1.5; + color: #fff; + background-color: rgba(220, 53, 69, 0.9); + border-radius: 0.25rem; } + +.was-validated :invalid ~ .invalid-feedback, +.was-validated :invalid ~ .invalid-tooltip, +.is-invalid ~ .invalid-feedback, +.is-invalid ~ .invalid-tooltip { + display: block; } + +.was-validated .form-control:invalid, .form-control.is-invalid { + border-color: #dc3545; + padding-right: calc(1.5em + 0.75rem); + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); + background-repeat: no-repeat; + background-position: right calc(0.375em + 0.1875rem) center; + background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } + +.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { + padding-right: calc(1.5em + 0.75rem); + background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem); } + +.was-validated .custom-select:invalid, .custom-select.is-invalid { + border-color: #dc3545; + padding-right: calc(0.75em + 2.3125rem); + background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } + .was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } + +.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { + color: #dc3545; } +.was-validated .form-check-input:invalid ~ .invalid-feedback, +.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback, +.form-check-input.is-invalid ~ .invalid-tooltip { + display: block; } + +.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label { + color: #dc3545; } + .was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before { + border-color: #dc3545; } +.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before { + border-color: #e4606d; + background-color: #e4606d; } +.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } +.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before { + border-color: #dc3545; } + +.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label { + border-color: #dc3545; } +.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label { + border-color: #dc3545; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25); } + +.form-inline { + display: flex; + flex-flow: row wrap; + align-items: center; } + .form-inline .form-check { + width: 100%; } + @media (min-width: 576px) { + .form-inline label { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0; } + .form-inline .form-group { + display: flex; + flex: 0 0 auto; + flex-flow: row wrap; + align-items: center; + margin-bottom: 0; } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; } + .form-inline .form-control-plaintext { + display: inline-block; } + .form-inline .input-group, + .form-inline .custom-select { + width: auto; } + .form-inline .form-check { + display: flex; + align-items: center; + justify-content: center; + width: auto; + padding-left: 0; } + .form-inline .form-check-input { + position: relative; + flex-shrink: 0; + margin-top: 0; + margin-right: 0.25rem; + margin-left: 0; } + .form-inline .custom-control { + align-items: center; + justify-content: center; } + .form-inline .custom-control-label { + margin-bottom: 0; } } + +.btn { + display: inline-block; + font-weight: 400; + color: #212529; + text-align: center; + vertical-align: middle; + user-select: none; + background-color: transparent; + border: 1px solid transparent; + padding: 0.375rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .btn { + transition: none; } } + .btn:hover { + color: #212529; + text-decoration: none; } + .btn:focus, .btn.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .btn.disabled, .btn:disabled { + opacity: 0.65; } + .btn:not(:disabled):not(.disabled) { + cursor: pointer; } + +a.btn.disabled, +fieldset:disabled a.btn { + pointer-events: none; } + +.btn-primary { + color: #fff; + background-color: #007bff; + border-color: #007bff; } + .btn-primary:hover { + color: #fff; + background-color: #0069d9; + border-color: #0062cc; } + .btn-primary:focus, .btn-primary.focus { + color: #fff; + background-color: #0069d9; + border-color: #0062cc; + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); } + .btn-primary.disabled, .btn-primary:disabled { + color: #fff; + background-color: #007bff; + border-color: #007bff; } + .btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active, .show > .btn-primary.dropdown-toggle { + color: #fff; + background-color: #0062cc; + border-color: #005cbf; } + .btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5); } + +.btn-secondary { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; } + .btn-secondary:hover { + color: #fff; + background-color: #5a6268; + border-color: #545b62; } + .btn-secondary:focus, .btn-secondary.focus { + color: #fff; + background-color: #5a6268; + border-color: #545b62; + box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); } + .btn-secondary.disabled, .btn-secondary:disabled { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; } + .btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active, .show > .btn-secondary.dropdown-toggle { + color: #fff; + background-color: #545b62; + border-color: #4e555b; } + .btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5); } + +.btn-success { + color: #fff; + background-color: #28a745; + border-color: #28a745; } + .btn-success:hover { + color: #fff; + background-color: #218838; + border-color: #1e7e34; } + .btn-success:focus, .btn-success.focus { + color: #fff; + background-color: #218838; + border-color: #1e7e34; + box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); } + .btn-success.disabled, .btn-success:disabled { + color: #fff; + background-color: #28a745; + border-color: #28a745; } + .btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active, .show > .btn-success.dropdown-toggle { + color: #fff; + background-color: #1e7e34; + border-color: #1c7430; } + .btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus, .show > .btn-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5); } + +.btn-info { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; } + .btn-info:hover { + color: #fff; + background-color: #138496; + border-color: #117a8b; } + .btn-info:focus, .btn-info.focus { + color: #fff; + background-color: #138496; + border-color: #117a8b; + box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); } + .btn-info.disabled, .btn-info:disabled { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; } + .btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active, .show > .btn-info.dropdown-toggle { + color: #fff; + background-color: #117a8b; + border-color: #10707f; } + .btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus, .show > .btn-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5); } + +.btn-warning { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; } + .btn-warning:hover { + color: #212529; + background-color: #e0a800; + border-color: #d39e00; } + .btn-warning:focus, .btn-warning.focus { + color: #212529; + background-color: #e0a800; + border-color: #d39e00; + box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); } + .btn-warning.disabled, .btn-warning:disabled { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; } + .btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active, .show > .btn-warning.dropdown-toggle { + color: #212529; + background-color: #d39e00; + border-color: #c69500; } + .btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5); } + +.btn-danger { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; } + .btn-danger:hover { + color: #fff; + background-color: #c82333; + border-color: #bd2130; } + .btn-danger:focus, .btn-danger.focus { + color: #fff; + background-color: #c82333; + border-color: #bd2130; + box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); } + .btn-danger.disabled, .btn-danger:disabled { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; } + .btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active, .show > .btn-danger.dropdown-toggle { + color: #fff; + background-color: #bd2130; + border-color: #b21f2d; } + .btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5); } + +.btn-light { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; } + .btn-light:hover { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; } + .btn-light:focus, .btn-light.focus { + color: #212529; + background-color: #e2e6ea; + border-color: #dae0e5; + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); } + .btn-light.disabled, .btn-light:disabled { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; } + .btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active, .show > .btn-light.dropdown-toggle { + color: #212529; + background-color: #dae0e5; + border-color: #d3d9df; } + .btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus, .show > .btn-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5); } + +.btn-dark { + color: #fff; + background-color: #343a40; + border-color: #343a40; } + .btn-dark:hover { + color: #fff; + background-color: #23272b; + border-color: #1d2124; } + .btn-dark:focus, .btn-dark.focus { + color: #fff; + background-color: #23272b; + border-color: #1d2124; + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); } + .btn-dark.disabled, .btn-dark:disabled { + color: #fff; + background-color: #343a40; + border-color: #343a40; } + .btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active, .show > .btn-dark.dropdown-toggle { + color: #fff; + background-color: #1d2124; + border-color: #171a1d; } + .btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5); } + +.btn-outline-primary { + color: #007bff; + border-color: #007bff; } + .btn-outline-primary:hover { + color: #fff; + background-color: #007bff; + border-color: #007bff; } + .btn-outline-primary:focus, .btn-outline-primary.focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } + .btn-outline-primary.disabled, .btn-outline-primary:disabled { + color: #007bff; + background-color: transparent; } + .btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active, .show > .btn-outline-primary.dropdown-toggle { + color: #fff; + background-color: #007bff; + border-color: #007bff; } + .btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } + +.btn-outline-secondary { + color: #6c757d; + border-color: #6c757d; } + .btn-outline-secondary:hover { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; } + .btn-outline-secondary:focus, .btn-outline-secondary.focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } + .btn-outline-secondary.disabled, .btn-outline-secondary:disabled { + color: #6c757d; + background-color: transparent; } + .btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active, .show > .btn-outline-secondary.dropdown-toggle { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; } + .btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } + +.btn-outline-success { + color: #28a745; + border-color: #28a745; } + .btn-outline-success:hover { + color: #fff; + background-color: #28a745; + border-color: #28a745; } + .btn-outline-success:focus, .btn-outline-success.focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } + .btn-outline-success.disabled, .btn-outline-success:disabled { + color: #28a745; + background-color: transparent; } + .btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active, .show > .btn-outline-success.dropdown-toggle { + color: #fff; + background-color: #28a745; + border-color: #28a745; } + .btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } + +.btn-outline-info { + color: #17a2b8; + border-color: #17a2b8; } + .btn-outline-info:hover { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; } + .btn-outline-info:focus, .btn-outline-info.focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } + .btn-outline-info.disabled, .btn-outline-info:disabled { + color: #17a2b8; + background-color: transparent; } + .btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active, .show > .btn-outline-info.dropdown-toggle { + color: #fff; + background-color: #17a2b8; + border-color: #17a2b8; } + .btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } + +.btn-outline-warning { + color: #ffc107; + border-color: #ffc107; } + .btn-outline-warning:hover { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; } + .btn-outline-warning:focus, .btn-outline-warning.focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } + .btn-outline-warning.disabled, .btn-outline-warning:disabled { + color: #ffc107; + background-color: transparent; } + .btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active, .show > .btn-outline-warning.dropdown-toggle { + color: #212529; + background-color: #ffc107; + border-color: #ffc107; } + .btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } + +.btn-outline-danger { + color: #dc3545; + border-color: #dc3545; } + .btn-outline-danger:hover { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; } + .btn-outline-danger:focus, .btn-outline-danger.focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } + .btn-outline-danger.disabled, .btn-outline-danger:disabled { + color: #dc3545; + background-color: transparent; } + .btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active, .show > .btn-outline-danger.dropdown-toggle { + color: #fff; + background-color: #dc3545; + border-color: #dc3545; } + .btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } + +.btn-outline-light { + color: #f8f9fa; + border-color: #f8f9fa; } + .btn-outline-light:hover { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; } + .btn-outline-light:focus, .btn-outline-light.focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } + .btn-outline-light.disabled, .btn-outline-light:disabled { + color: #f8f9fa; + background-color: transparent; } + .btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active, .show > .btn-outline-light.dropdown-toggle { + color: #212529; + background-color: #f8f9fa; + border-color: #f8f9fa; } + .btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } + +.btn-outline-dark { + color: #343a40; + border-color: #343a40; } + .btn-outline-dark:hover { + color: #fff; + background-color: #343a40; + border-color: #343a40; } + .btn-outline-dark:focus, .btn-outline-dark.focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } + .btn-outline-dark.disabled, .btn-outline-dark:disabled { + color: #343a40; + background-color: transparent; } + .btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active, .show > .btn-outline-dark.dropdown-toggle { + color: #fff; + background-color: #343a40; + border-color: #343a40; } + .btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus, .show > .btn-outline-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } + +.btn-link { + font-weight: 400; + color: #007bff; + text-decoration: none; } + .btn-link:hover { + color: #0056b3; + text-decoration: underline; } + .btn-link:focus, .btn-link.focus { + text-decoration: underline; } + .btn-link:disabled, .btn-link.disabled { + color: #6c757d; + pointer-events: none; } + +.btn-lg, .btn-group-lg > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; } + +.btn-sm, .btn-group-sm > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; } + +.btn-block { + display: block; + width: 100%; } + .btn-block + .btn-block { + margin-top: 0.5rem; } + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; } + +.fade { + transition: opacity 0.15s linear; } + @media (prefers-reduced-motion: reduce) { + .fade { + transition: none; } } + .fade:not(.show) { + opacity: 0; } + +.collapse:not(.show) { + display: none; } + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + transition: height 0.35s ease; } + @media (prefers-reduced-motion: reduce) { + .collapsing { + transition: none; } } + +.dropup, +.dropright, +.dropdown, +.dropleft { + position: relative; } + +.dropdown-toggle { + white-space: nowrap; } + .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; } + .dropdown-toggle:empty::after { + margin-left: 0; } + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 10rem; + padding: 0.5rem 0; + margin: 0.125rem 0 0; + font-size: 1rem; + color: #212529; + text-align: left; + list-style: none; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0.25rem; } + +.dropdown-menu-left { + right: auto; + left: 0; } + +.dropdown-menu-right { + right: 0; + left: auto; } + +@media (min-width: 576px) { + .dropdown-menu-sm-left { + right: auto; + left: 0; } + + .dropdown-menu-sm-right { + right: 0; + left: auto; } } +@media (min-width: 768px) { + .dropdown-menu-md-left { + right: auto; + left: 0; } + + .dropdown-menu-md-right { + right: 0; + left: auto; } } +@media (min-width: 992px) { + .dropdown-menu-lg-left { + right: auto; + left: 0; } + + .dropdown-menu-lg-right { + right: 0; + left: auto; } } +@media (min-width: 1200px) { + .dropdown-menu-xl-left { + right: auto; + left: 0; } + + .dropdown-menu-xl-right { + right: 0; + left: auto; } } +.dropup .dropdown-menu { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: 0.125rem; } +.dropup .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-right: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-left: 0.3em solid transparent; } +.dropup .dropdown-toggle:empty::after { + margin-left: 0; } + +.dropright .dropdown-menu { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: 0.125rem; } +.dropright .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0; + border-bottom: 0.3em solid transparent; + border-left: 0.3em solid; } +.dropright .dropdown-toggle:empty::after { + margin-left: 0; } +.dropright .dropdown-toggle::after { + vertical-align: 0; } + +.dropleft .dropdown-menu { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: 0.125rem; } +.dropleft .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; } +.dropleft .dropdown-toggle::after { + display: none; } +.dropleft .dropdown-toggle::before { + display: inline-block; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0.3em solid; + border-bottom: 0.3em solid transparent; } +.dropleft .dropdown-toggle:empty::after { + margin-left: 0; } +.dropleft .dropdown-toggle::before { + vertical-align: 0; } + +.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] { + right: auto; + bottom: auto; } + +.dropdown-divider { + height: 0; + margin: 0.5rem 0; + overflow: hidden; + border-top: 1px solid #e9ecef; } + +.dropdown-item { + display: block; + width: 100%; + padding: 0.25rem 1.5rem; + clear: both; + font-weight: 400; + color: #212529; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0; } + .dropdown-item:hover, .dropdown-item:focus { + color: #16181b; + text-decoration: none; + background-color: #f8f9fa; } + .dropdown-item.active, .dropdown-item:active { + color: #fff; + text-decoration: none; + background-color: #007bff; } + .dropdown-item.disabled, .dropdown-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: transparent; } + +.dropdown-menu.show { + display: block; } + +.dropdown-header { + display: block; + padding: 0.5rem 1.5rem; + margin-bottom: 0; + font-size: 0.875rem; + color: #6c757d; + white-space: nowrap; } + +.dropdown-item-text { + display: block; + padding: 0.25rem 1.5rem; + color: #212529; } + +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-flex; + vertical-align: middle; } + .btn-group > .btn, + .btn-group-vertical > .btn { + position: relative; + flex: 1 1 auto; } + .btn-group > .btn:hover, + .btn-group-vertical > .btn:hover { + z-index: 1; } + .btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active, + .btn-group-vertical > .btn:focus, + .btn-group-vertical > .btn:active, + .btn-group-vertical > .btn.active { + z-index: 1; } + +.btn-toolbar { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; } + .btn-toolbar .input-group { + width: auto; } + +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) { + margin-left: -1px; } +.btn-group > .btn:not(:last-child):not(.dropdown-toggle), +.btn-group > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } +.btn-group > .btn:not(:first-child), +.btn-group > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + +.dropdown-toggle-split { + padding-right: 0.5625rem; + padding-left: 0.5625rem; } + .dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropright .dropdown-toggle-split::after { + margin-left: 0; } + .dropleft .dropdown-toggle-split::before { + margin-right: 0; } + +.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split { + padding-right: 0.375rem; + padding-left: 0.375rem; } + +.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split { + padding-right: 0.75rem; + padding-left: 0.75rem; } + +.btn-group-vertical { + flex-direction: column; + align-items: flex-start; + justify-content: center; } + .btn-group-vertical > .btn, + .btn-group-vertical > .btn-group { + width: 100%; } + .btn-group-vertical > .btn:not(:first-child), + .btn-group-vertical > .btn-group:not(:first-child) { + margin-top: -1px; } + .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), + .btn-group-vertical > .btn-group:not(:last-child) > .btn { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; } + .btn-group-vertical > .btn:not(:first-child), + .btn-group-vertical > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.btn-group-toggle > .btn, +.btn-group-toggle > .btn-group > .btn { + margin-bottom: 0; } + .btn-group-toggle > .btn input[type="radio"], + .btn-group-toggle > .btn input[type="checkbox"], + .btn-group-toggle > .btn-group > .btn input[type="radio"], + .btn-group-toggle > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; } + +.input-group { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: stretch; + width: 100%; } + .input-group > .form-control, + .input-group > .form-control-plaintext, + .input-group > .custom-select, + .input-group > .custom-file { + position: relative; + flex: 1 1 auto; + width: 1%; + min-width: 0; + margin-bottom: 0; } + .input-group > .form-control + .form-control, + .input-group > .form-control + .custom-select, + .input-group > .form-control + .custom-file, + .input-group > .form-control-plaintext + .form-control, + .input-group > .form-control-plaintext + .custom-select, + .input-group > .form-control-plaintext + .custom-file, + .input-group > .custom-select + .form-control, + .input-group > .custom-select + .custom-select, + .input-group > .custom-select + .custom-file, + .input-group > .custom-file + .form-control, + .input-group > .custom-file + .custom-select, + .input-group > .custom-file + .custom-file { + margin-left: -1px; } + .input-group > .form-control:focus, + .input-group > .custom-select:focus, + .input-group > .custom-file .custom-file-input:focus ~ .custom-file-label { + z-index: 3; } + .input-group > .custom-file .custom-file-input:focus { + z-index: 4; } + .input-group > .form-control:not(:last-child), + .input-group > .custom-select:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .input-group > .form-control:not(:first-child), + .input-group > .custom-select:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .input-group > .custom-file { + display: flex; + align-items: center; } + .input-group > .custom-file:not(:last-child) .custom-file-label, .input-group > .custom-file:not(:last-child) .custom-file-label::after { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .input-group > .custom-file:not(:first-child) .custom-file-label { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + +.input-group-prepend, +.input-group-append { + display: flex; } + .input-group-prepend .btn, + .input-group-append .btn { + position: relative; + z-index: 2; } + .input-group-prepend .btn:focus, + .input-group-append .btn:focus { + z-index: 3; } + .input-group-prepend .btn + .btn, + .input-group-prepend .btn + .input-group-text, + .input-group-prepend .input-group-text + .input-group-text, + .input-group-prepend .input-group-text + .btn, + .input-group-append .btn + .btn, + .input-group-append .btn + .input-group-text, + .input-group-append .input-group-text + .input-group-text, + .input-group-append .input-group-text + .btn { + margin-left: -1px; } + +.input-group-prepend { + margin-right: -1px; } + +.input-group-append { + margin-left: -1px; } + +.input-group-text { + display: flex; + align-items: center; + padding: 0.375rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + text-align: center; + white-space: nowrap; + background-color: #e9ecef; + border: 1px solid #ced4da; + border-radius: 0.25rem; } + .input-group-text input[type="radio"], + .input-group-text input[type="checkbox"] { + margin-top: 0; } + +.input-group-lg > .form-control:not(textarea), +.input-group-lg > .custom-select { + height: calc(1.5em + 1rem + 2px); } + +.input-group-lg > .form-control, +.input-group-lg > .custom-select, +.input-group-lg > .input-group-prepend > .input-group-text, +.input-group-lg > .input-group-append > .input-group-text, +.input-group-lg > .input-group-prepend > .btn, +.input-group-lg > .input-group-append > .btn { + padding: 0.5rem 1rem; + font-size: 1.25rem; + line-height: 1.5; + border-radius: 0.3rem; } + +.input-group-sm > .form-control:not(textarea), +.input-group-sm > .custom-select { + height: calc(1.5em + 0.5rem + 2px); } + +.input-group-sm > .form-control, +.input-group-sm > .custom-select, +.input-group-sm > .input-group-prepend > .input-group-text, +.input-group-sm > .input-group-append > .input-group-text, +.input-group-sm > .input-group-prepend > .btn, +.input-group-sm > .input-group-append > .btn { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; + border-radius: 0.2rem; } + +.input-group-lg > .custom-select, +.input-group-sm > .custom-select { + padding-right: 1.75rem; } + +.input-group > .input-group-prepend > .btn, +.input-group > .input-group-prepend > .input-group-text, +.input-group > .input-group-append:not(:last-child) > .btn, +.input-group > .input-group-append:not(:last-child) > .input-group-text, +.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + +.input-group > .input-group-append > .btn, +.input-group > .input-group-append > .input-group-text, +.input-group > .input-group-prepend:not(:first-child) > .btn, +.input-group > .input-group-prepend:not(:first-child) > .input-group-text, +.input-group > .input-group-prepend:first-child > .btn:not(:first-child), +.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + +.custom-control { + position: relative; + display: block; + min-height: 1.5rem; + padding-left: 1.5rem; } + +.custom-control-inline { + display: inline-flex; + margin-right: 1rem; } + +.custom-control-input { + position: absolute; + left: 0; + z-index: -1; + width: 1rem; + height: 1.25rem; + opacity: 0; } + .custom-control-input:checked ~ .custom-control-label::before { + color: #fff; + border-color: #007bff; + background-color: #007bff; } + .custom-control-input:focus ~ .custom-control-label::before { + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-control-input:focus:not(:checked) ~ .custom-control-label::before { + border-color: #80bdff; } + .custom-control-input:not(:disabled):active ~ .custom-control-label::before { + color: #fff; + background-color: #b3d7ff; + border-color: #b3d7ff; } + .custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label { + color: #6c757d; } + .custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before { + background-color: #e9ecef; } + +.custom-control-label { + position: relative; + margin-bottom: 0; + vertical-align: top; } + .custom-control-label::before { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + pointer-events: none; + content: ""; + background-color: #fff; + border: #adb5bd solid 1px; } + .custom-control-label::after { + position: absolute; + top: 0.25rem; + left: -1.5rem; + display: block; + width: 1rem; + height: 1rem; + content: ""; + background: no-repeat 50% / 50% 50%; } + +.custom-checkbox .custom-control-label::before { + border-radius: 0.25rem; } +.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e"); } +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before { + border-color: #007bff; + background-color: #007bff; } +.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e"); } +.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); } +.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); } + +.custom-radio .custom-control-label::before { + border-radius: 50%; } +.custom-radio .custom-control-input:checked ~ .custom-control-label::after { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } +.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); } + +.custom-switch { + padding-left: 2.25rem; } + .custom-switch .custom-control-label::before { + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: 0.5rem; } + .custom-switch .custom-control-label::after { + top: calc(0.25rem + 2px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #adb5bd; + border-radius: 0.5rem; + transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .custom-switch .custom-control-label::after { + transition: none; } } + .custom-switch .custom-control-input:checked ~ .custom-control-label::after { + background-color: #fff; + transform: translateX(0.75rem); } + .custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before { + background-color: rgba(0, 123, 255, 0.5); } + +.custom-select { + display: inline-block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 1.75rem 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + vertical-align: middle; + background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px; + border: 1px solid #ced4da; + border-radius: 0.25rem; + appearance: none; } + .custom-select:focus { + border-color: #80bdff; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-select:focus::-ms-value { + color: #495057; + background-color: #fff; } + .custom-select[multiple], .custom-select[size]:not([size="1"]) { + height: auto; + padding-right: 0.75rem; + background-image: none; } + .custom-select:disabled { + color: #6c757d; + background-color: #e9ecef; } + .custom-select::-ms-expand { + display: none; } + .custom-select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #495057; } + +.custom-select-sm { + height: calc(1.5em + 0.5rem + 2px); + padding-top: 0.25rem; + padding-bottom: 0.25rem; + padding-left: 0.5rem; + font-size: 0.875rem; } + +.custom-select-lg { + height: calc(1.5em + 1rem + 2px); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 1rem; + font-size: 1.25rem; } + +.custom-file { + position: relative; + display: inline-block; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + margin-bottom: 0; } + +.custom-file-input { + position: relative; + z-index: 2; + width: 100%; + height: calc(1.5em + 0.75rem + 2px); + margin: 0; + opacity: 0; } + .custom-file-input:focus ~ .custom-file-label { + border-color: #80bdff; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-file-input[disabled] ~ .custom-file-label, .custom-file-input:disabled ~ .custom-file-label { + background-color: #e9ecef; } + .custom-file-input:lang(en) ~ .custom-file-label::after { + content: "Browse"; } + .custom-file-input ~ .custom-file-label[data-browse]::after { + content: attr(data-browse); } + +.custom-file-label { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 1; + height: calc(1.5em + 0.75rem + 2px); + padding: 0.375rem 0.75rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: #fff; + border: 1px solid #ced4da; + border-radius: 0.25rem; } + .custom-file-label::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 3; + display: block; + height: calc(1.5em + 0.75rem); + padding: 0.375rem 0.75rem; + line-height: 1.5; + color: #495057; + content: "Browse"; + background-color: #e9ecef; + border-left: inherit; + border-radius: 0 0.25rem 0.25rem 0; } + +.custom-range { + width: 100%; + height: 1.4rem; + padding: 0; + background-color: transparent; + appearance: none; } + .custom-range:focus { + outline: none; } + .custom-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-range:focus::-ms-thumb { + box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + .custom-range::-moz-focus-outer { + border: 0; } + .custom-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; } + @media (prefers-reduced-motion: reduce) { + .custom-range::-webkit-slider-thumb { + transition: none; } } + .custom-range::-webkit-slider-thumb:active { + background-color: #b3d7ff; } + .custom-range::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; } + .custom-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; } + @media (prefers-reduced-motion: reduce) { + .custom-range::-moz-range-thumb { + transition: none; } } + .custom-range::-moz-range-thumb:active { + background-color: #b3d7ff; } + .custom-range::-moz-range-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #dee2e6; + border-color: transparent; + border-radius: 1rem; } + .custom-range::-ms-thumb { + width: 1rem; + height: 1rem; + margin-top: 0; + margin-right: 0.2rem; + margin-left: 0.2rem; + background-color: #007bff; + border: 0; + border-radius: 1rem; + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; } + @media (prefers-reduced-motion: reduce) { + .custom-range::-ms-thumb { + transition: none; } } + .custom-range::-ms-thumb:active { + background-color: #b3d7ff; } + .custom-range::-ms-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: transparent; + border-color: transparent; + border-width: 0.5rem; } + .custom-range::-ms-fill-lower { + background-color: #dee2e6; + border-radius: 1rem; } + .custom-range::-ms-fill-upper { + margin-right: 15px; + background-color: #dee2e6; + border-radius: 1rem; } + .custom-range:disabled::-webkit-slider-thumb { + background-color: #adb5bd; } + .custom-range:disabled::-webkit-slider-runnable-track { + cursor: default; } + .custom-range:disabled::-moz-range-thumb { + background-color: #adb5bd; } + .custom-range:disabled::-moz-range-track { + cursor: default; } + .custom-range:disabled::-ms-thumb { + background-color: #adb5bd; } + +.custom-control-label::before, +.custom-file-label, +.custom-select { + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .custom-control-label::before, + .custom-file-label, + .custom-select { + transition: none; } } + +.nav { + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; } + +.nav-link { + display: block; + padding: 0.5rem 1rem; } + .nav-link:hover, .nav-link:focus { + text-decoration: none; } + .nav-link.disabled { + color: #6c757d; + pointer-events: none; + cursor: default; } + +.nav-tabs { + border-bottom: 1px solid #dee2e6; } + .nav-tabs .nav-item { + margin-bottom: -1px; } + .nav-tabs .nav-link { + border: 1px solid transparent; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; } + .nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus { + border-color: #e9ecef #e9ecef #dee2e6; } + .nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent; } + .nav-tabs .nav-link.active, + .nav-tabs .nav-item.show .nav-link { + color: #495057; + background-color: #fff; + border-color: #dee2e6 #dee2e6 #fff; } + .nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; } + +.nav-pills .nav-link { + border-radius: 0.25rem; } +.nav-pills .nav-link.active, +.nav-pills .show > .nav-link { + color: #fff; + background-color: #007bff; } + +.nav-fill .nav-item { + flex: 1 1 auto; + text-align: center; } + +.nav-justified .nav-item { + flex-basis: 0; + flex-grow: 1; + text-align: center; } + +.tab-content > .tab-pane { + display: none; } +.tab-content > .active { + display: block; } + +.navbar { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 0.5rem 1rem; } + .navbar .container, + .navbar .container-fluid, + .navbar .container-sm, + .navbar .container-md, + .navbar .container-lg, + .navbar .container-xl { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; } + +.navbar-brand { + display: inline-block; + padding-top: 0.3125rem; + padding-bottom: 0.3125rem; + margin-right: 1rem; + font-size: 1.25rem; + line-height: inherit; + white-space: nowrap; } + .navbar-brand:hover, .navbar-brand:focus { + text-decoration: none; } + +.navbar-nav { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; } + .navbar-nav .nav-link { + padding-right: 0; + padding-left: 0; } + .navbar-nav .dropdown-menu { + position: static; + float: none; } + +.navbar-text { + display: inline-block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; } + +.navbar-collapse { + flex-basis: 100%; + flex-grow: 1; + align-items: center; } + +.navbar-toggler { + padding: 0.25rem 0.75rem; + font-size: 1.25rem; + line-height: 1; + background-color: transparent; + border: 1px solid transparent; + border-radius: 0.25rem; } + .navbar-toggler:hover, .navbar-toggler:focus { + text-decoration: none; } + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: no-repeat center center; + background-size: 100% 100%; } + +@media (max-width: 575.98px) { + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid, + .navbar-expand-sm > .container-sm, + .navbar-expand-sm > .container-md, + .navbar-expand-sm > .container-lg, + .navbar-expand-sm > .container-xl { + padding-right: 0; + padding-left: 0; } } +@media (min-width: 576px) { + .navbar-expand-sm { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand-sm .navbar-nav { + flex-direction: row; } + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand-sm > .container, + .navbar-expand-sm > .container-fluid, + .navbar-expand-sm > .container-sm, + .navbar-expand-sm > .container-md, + .navbar-expand-sm > .container-lg, + .navbar-expand-sm > .container-xl { + flex-wrap: nowrap; } + .navbar-expand-sm .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand-sm .navbar-toggler { + display: none; } } +@media (max-width: 767.98px) { + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid, + .navbar-expand-md > .container-sm, + .navbar-expand-md > .container-md, + .navbar-expand-md > .container-lg, + .navbar-expand-md > .container-xl { + padding-right: 0; + padding-left: 0; } } +@media (min-width: 768px) { + .navbar-expand-md { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand-md .navbar-nav { + flex-direction: row; } + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand-md .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand-md > .container, + .navbar-expand-md > .container-fluid, + .navbar-expand-md > .container-sm, + .navbar-expand-md > .container-md, + .navbar-expand-md > .container-lg, + .navbar-expand-md > .container-xl { + flex-wrap: nowrap; } + .navbar-expand-md .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand-md .navbar-toggler { + display: none; } } +@media (max-width: 991.98px) { + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid, + .navbar-expand-lg > .container-sm, + .navbar-expand-lg > .container-md, + .navbar-expand-lg > .container-lg, + .navbar-expand-lg > .container-xl { + padding-right: 0; + padding-left: 0; } } +@media (min-width: 992px) { + .navbar-expand-lg { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand-lg .navbar-nav { + flex-direction: row; } + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand-lg > .container, + .navbar-expand-lg > .container-fluid, + .navbar-expand-lg > .container-sm, + .navbar-expand-lg > .container-md, + .navbar-expand-lg > .container-lg, + .navbar-expand-lg > .container-xl { + flex-wrap: nowrap; } + .navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand-lg .navbar-toggler { + display: none; } } +@media (max-width: 1199.98px) { + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid, + .navbar-expand-xl > .container-sm, + .navbar-expand-xl > .container-md, + .navbar-expand-xl > .container-lg, + .navbar-expand-xl > .container-xl { + padding-right: 0; + padding-left: 0; } } +@media (min-width: 1200px) { + .navbar-expand-xl { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand-xl .navbar-nav { + flex-direction: row; } + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand-xl > .container, + .navbar-expand-xl > .container-fluid, + .navbar-expand-xl > .container-sm, + .navbar-expand-xl > .container-md, + .navbar-expand-xl > .container-lg, + .navbar-expand-xl > .container-xl { + flex-wrap: nowrap; } + .navbar-expand-xl .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand-xl .navbar-toggler { + display: none; } } +.navbar-expand { + flex-flow: row nowrap; + justify-content: flex-start; } + .navbar-expand > .container, + .navbar-expand > .container-fluid, + .navbar-expand > .container-sm, + .navbar-expand > .container-md, + .navbar-expand > .container-lg, + .navbar-expand > .container-xl { + padding-right: 0; + padding-left: 0; } + .navbar-expand .navbar-nav { + flex-direction: row; } + .navbar-expand .navbar-nav .dropdown-menu { + position: absolute; } + .navbar-expand .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; } + .navbar-expand > .container, + .navbar-expand > .container-fluid, + .navbar-expand > .container-sm, + .navbar-expand > .container-md, + .navbar-expand > .container-lg, + .navbar-expand > .container-xl { + flex-wrap: nowrap; } + .navbar-expand .navbar-collapse { + display: flex !important; + flex-basis: auto; } + .navbar-expand .navbar-toggler { + display: none; } + +.navbar-light .navbar-brand { + color: rgba(0, 0, 0, 0.9); } + .navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus { + color: rgba(0, 0, 0, 0.9); } +.navbar-light .navbar-nav .nav-link { + color: rgba(0, 0, 0, 0.5); } + .navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus { + color: rgba(0, 0, 0, 0.7); } + .navbar-light .navbar-nav .nav-link.disabled { + color: rgba(0, 0, 0, 0.3); } +.navbar-light .navbar-nav .show > .nav-link, +.navbar-light .navbar-nav .active > .nav-link, +.navbar-light .navbar-nav .nav-link.show, +.navbar-light .navbar-nav .nav-link.active { + color: rgba(0, 0, 0, 0.9); } +.navbar-light .navbar-toggler { + color: rgba(0, 0, 0, 0.5); + border-color: rgba(0, 0, 0, 0.1); } +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } +.navbar-light .navbar-text { + color: rgba(0, 0, 0, 0.5); } + .navbar-light .navbar-text a { + color: rgba(0, 0, 0, 0.9); } + .navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus { + color: rgba(0, 0, 0, 0.9); } + +.navbar-dark .navbar-brand { + color: #fff; } + .navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus { + color: #fff; } +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.5); } + .navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus { + color: rgba(255, 255, 255, 0.75); } + .navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, 0.25); } +.navbar-dark .navbar-nav .show > .nav-link, +.navbar-dark .navbar-nav .active > .nav-link, +.navbar-dark .navbar-nav .nav-link.show, +.navbar-dark .navbar-nav .nav-link.active { + color: #fff; } +.navbar-dark .navbar-toggler { + color: rgba(255, 255, 255, 0.5); + border-color: rgba(255, 255, 255, 0.1); } +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } +.navbar-dark .navbar-text { + color: rgba(255, 255, 255, 0.5); } + .navbar-dark .navbar-text a { + color: #fff; } + .navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus { + color: #fff; } + +.card { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #fff; + background-clip: border-box; + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; } + .card > hr { + margin-right: 0; + margin-left: 0; } + .card > .list-group { + border-top: inherit; + border-bottom: inherit; } + .card > .list-group:first-child { + border-top-width: 0; + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); } + .card > .list-group:last-child { + border-bottom-width: 0; + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); } + +.card-body { + flex: 1 1 auto; + min-height: 1px; + padding: 1.25rem; } + +.card-title { + margin-bottom: 0.75rem; } + +.card-subtitle { + margin-top: -0.375rem; + margin-bottom: 0; } + +.card-text:last-child { + margin-bottom: 0; } + +.card-link:hover { + text-decoration: none; } +.card-link + .card-link { + margin-left: 1.25rem; } + +.card-header { + padding: 0.75rem 1.25rem; + margin-bottom: 0; + background-color: rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.125); } + .card-header:first-child { + border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0; } + .card-header + .list-group .list-group-item:first-child { + border-top: 0; } + +.card-footer { + padding: 0.75rem 1.25rem; + background-color: rgba(0, 0, 0, 0.03); + border-top: 1px solid rgba(0, 0, 0, 0.125); } + .card-footer:last-child { + border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px); } + +.card-header-tabs { + margin-right: -0.625rem; + margin-bottom: -0.75rem; + margin-left: -0.625rem; + border-bottom: 0; } + +.card-header-pills { + margin-right: -0.625rem; + margin-left: -0.625rem; } + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem; } + +.card-img, +.card-img-top, +.card-img-bottom { + flex-shrink: 0; + width: 100%; } + +.card-img, +.card-img-top { + border-top-left-radius: calc(0.25rem - 1px); + border-top-right-radius: calc(0.25rem - 1px); } + +.card-img, +.card-img-bottom { + border-bottom-right-radius: calc(0.25rem - 1px); + border-bottom-left-radius: calc(0.25rem - 1px); } + +.card-deck .card { + margin-bottom: 15px; } +@media (min-width: 576px) { + .card-deck { + display: flex; + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; } + .card-deck .card { + flex: 1 0 0%; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; } } + +.card-group > .card { + margin-bottom: 15px; } +@media (min-width: 576px) { + .card-group { + display: flex; + flex-flow: row wrap; } + .card-group > .card { + flex: 1 0 0%; + margin-bottom: 0; } + .card-group > .card + .card { + margin-left: 0; + border-left: 0; } + .card-group > .card:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-top, + .card-group > .card:not(:last-child) .card-header { + border-top-right-radius: 0; } + .card-group > .card:not(:last-child) .card-img-bottom, + .card-group > .card:not(:last-child) .card-footer { + border-bottom-right-radius: 0; } + .card-group > .card:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-top, + .card-group > .card:not(:first-child) .card-header { + border-top-left-radius: 0; } + .card-group > .card:not(:first-child) .card-img-bottom, + .card-group > .card:not(:first-child) .card-footer { + border-bottom-left-radius: 0; } } + +.card-columns .card { + margin-bottom: 0.75rem; } +@media (min-width: 576px) { + .card-columns { + column-count: 3; + column-gap: 1.25rem; + orphans: 1; + widows: 1; } + .card-columns .card { + display: inline-block; + width: 100%; } } + +.accordion > .card { + overflow: hidden; } + .accordion > .card:not(:last-of-type) { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; } + .accordion > .card:not(:first-of-type) { + border-top-left-radius: 0; + border-top-right-radius: 0; } + .accordion > .card > .card-header { + border-radius: 0; + margin-bottom: -1px; } + +.breadcrumb { + display: flex; + flex-wrap: wrap; + padding: 0.75rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: #e9ecef; + border-radius: 0.25rem; } + +.breadcrumb-item { + display: flex; } + .breadcrumb-item + .breadcrumb-item { + padding-left: 0.5rem; } + .breadcrumb-item + .breadcrumb-item::before { + display: inline-block; + padding-right: 0.5rem; + color: #6c757d; + content: "/"; } + .breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: underline; } + .breadcrumb-item + .breadcrumb-item:hover::before { + text-decoration: none; } + .breadcrumb-item.active { + color: #6c757d; } + +.pagination { + display: flex; + padding-left: 0; + list-style: none; + border-radius: 0.25rem; } + +.page-link { + position: relative; + display: block; + padding: 0.5rem 0.75rem; + margin-left: -1px; + line-height: 1.25; + color: #007bff; + background-color: #fff; + border: 1px solid #dee2e6; } + .page-link:hover { + z-index: 2; + color: #0056b3; + text-decoration: none; + background-color: #e9ecef; + border-color: #dee2e6; } + .page-link:focus { + z-index: 3; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25); } + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; } +.page-item:last-child .page-link { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; } +.page-item.active .page-link { + z-index: 3; + color: #fff; + background-color: #007bff; + border-color: #007bff; } +.page-item.disabled .page-link { + color: #6c757d; + pointer-events: none; + cursor: auto; + background-color: #fff; + border-color: #dee2e6; } + +.pagination-lg .page-link { + padding: 0.75rem 1.5rem; + font-size: 1.25rem; + line-height: 1.5; } +.pagination-lg .page-item:first-child .page-link { + border-top-left-radius: 0.3rem; + border-bottom-left-radius: 0.3rem; } +.pagination-lg .page-item:last-child .page-link { + border-top-right-radius: 0.3rem; + border-bottom-right-radius: 0.3rem; } + +.pagination-sm .page-link { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; + line-height: 1.5; } +.pagination-sm .page-item:first-child .page-link { + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; } +.pagination-sm .page-item:last-child .page-link { + border-top-right-radius: 0.2rem; + border-bottom-right-radius: 0.2rem; } + +.badge { + display: inline-block; + padding: 0.25em 0.4em; + font-size: 75%; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .badge { + transition: none; } } + a.badge:hover, a.badge:focus { + text-decoration: none; } + .badge:empty { + display: none; } + +.btn .badge { + position: relative; + top: -1px; } + +.badge-pill { + padding-right: 0.6em; + padding-left: 0.6em; + border-radius: 10rem; } + +.badge-primary { + color: #fff; + background-color: #007bff; } + a.badge-primary:hover, a.badge-primary:focus { + color: #fff; + background-color: #0062cc; } + a.badge-primary:focus, a.badge-primary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5); } + +.badge-secondary { + color: #fff; + background-color: #6c757d; } + a.badge-secondary:hover, a.badge-secondary:focus { + color: #fff; + background-color: #545b62; } + a.badge-secondary:focus, a.badge-secondary.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5); } + +.badge-success { + color: #fff; + background-color: #28a745; } + a.badge-success:hover, a.badge-success:focus { + color: #fff; + background-color: #1e7e34; } + a.badge-success:focus, a.badge-success.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5); } + +.badge-info { + color: #fff; + background-color: #17a2b8; } + a.badge-info:hover, a.badge-info:focus { + color: #fff; + background-color: #117a8b; } + a.badge-info:focus, a.badge-info.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); } + +.badge-warning { + color: #212529; + background-color: #ffc107; } + a.badge-warning:hover, a.badge-warning:focus { + color: #212529; + background-color: #d39e00; } + a.badge-warning:focus, a.badge-warning.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5); } + +.badge-danger { + color: #fff; + background-color: #dc3545; } + a.badge-danger:hover, a.badge-danger:focus { + color: #fff; + background-color: #bd2130; } + a.badge-danger:focus, a.badge-danger.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5); } + +.badge-light { + color: #212529; + background-color: #f8f9fa; } + a.badge-light:hover, a.badge-light:focus { + color: #212529; + background-color: #dae0e5; } + a.badge-light:focus, a.badge-light.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5); } + +.badge-dark { + color: #fff; + background-color: #343a40; } + a.badge-dark:hover, a.badge-dark:focus { + color: #fff; + background-color: #1d2124; } + a.badge-dark:focus, a.badge-dark.focus { + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5); } + +.jumbotron { + padding: 2rem 1rem; + margin-bottom: 2rem; + background-color: #e9ecef; + border-radius: 0.3rem; } + @media (min-width: 576px) { + .jumbotron { + padding: 4rem 2rem; } } + +.jumbotron-fluid { + padding-right: 0; + padding-left: 0; + border-radius: 0; } + +.alert { + position: relative; + padding: 0.75rem 1.25rem; + margin-bottom: 1rem; + border: 1px solid transparent; + border-radius: 0.25rem; } + +.alert-heading { + color: inherit; } + +.alert-link { + font-weight: 700; } + +.alert-dismissible { + padding-right: 4rem; } + .alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + padding: 0.75rem 1.25rem; + color: inherit; } + +.alert-primary { + color: #004085; + background-color: #cce5ff; + border-color: #b8daff; } + .alert-primary hr { + border-top-color: #9fcdff; } + .alert-primary .alert-link { + color: #002752; } + +.alert-secondary { + color: #383d41; + background-color: #e2e3e5; + border-color: #d6d8db; } + .alert-secondary hr { + border-top-color: #c8cbcf; } + .alert-secondary .alert-link { + color: #202326; } + +.alert-success { + color: #155724; + background-color: #d4edda; + border-color: #c3e6cb; } + .alert-success hr { + border-top-color: #b1dfbb; } + .alert-success .alert-link { + color: #0b2e13; } + +.alert-info { + color: #0c5460; + background-color: #d1ecf1; + border-color: #bee5eb; } + .alert-info hr { + border-top-color: #abdde5; } + .alert-info .alert-link { + color: #062c33; } + +.alert-warning { + color: #856404; + background-color: #fff3cd; + border-color: #ffeeba; } + .alert-warning hr { + border-top-color: #ffe8a1; } + .alert-warning .alert-link { + color: #533f03; } + +.alert-danger { + color: #721c24; + background-color: #f8d7da; + border-color: #f5c6cb; } + .alert-danger hr { + border-top-color: #f1b0b7; } + .alert-danger .alert-link { + color: #491217; } + +.alert-light { + color: #818182; + background-color: #fefefe; + border-color: #fdfdfe; } + .alert-light hr { + border-top-color: #ececf6; } + .alert-light .alert-link { + color: #686868; } + +.alert-dark { + color: #1b1e21; + background-color: #d6d8d9; + border-color: #c6c8ca; } + .alert-dark hr { + border-top-color: #b9bbbe; } + .alert-dark .alert-link { + color: #040505; } + +@keyframes progress-bar-stripes { + from { + background-position: 1rem 0; } + to { + background-position: 0 0; } } +.progress { + display: flex; + height: 1rem; + overflow: hidden; + line-height: 0; + font-size: 0.75rem; + background-color: #e9ecef; + border-radius: 0.25rem; } + +.progress-bar { + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; + color: #fff; + text-align: center; + white-space: nowrap; + background-color: #007bff; + transition: width 0.6s ease; } + @media (prefers-reduced-motion: reduce) { + .progress-bar { + transition: none; } } + +.progress-bar-striped { + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: 1rem 1rem; } + +.progress-bar-animated { + animation: progress-bar-stripes 1s linear infinite; } + @media (prefers-reduced-motion: reduce) { + .progress-bar-animated { + animation: none; } } + +.media { + display: flex; + align-items: flex-start; } + +.media-body { + flex: 1; } + +.list-group { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + border-radius: 0.25rem; } + +.list-group-item-action { + width: 100%; + color: #495057; + text-align: inherit; } + .list-group-item-action:hover, .list-group-item-action:focus { + z-index: 1; + color: #495057; + text-decoration: none; + background-color: #f8f9fa; } + .list-group-item-action:active { + color: #212529; + background-color: #e9ecef; } + +.list-group-item { + position: relative; + display: block; + padding: 0.75rem 1.25rem; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.125); } + .list-group-item:first-child { + border-top-left-radius: inherit; + border-top-right-radius: inherit; } + .list-group-item:last-child { + border-bottom-right-radius: inherit; + border-bottom-left-radius: inherit; } + .list-group-item.disabled, .list-group-item:disabled { + color: #6c757d; + pointer-events: none; + background-color: #fff; } + .list-group-item.active { + z-index: 2; + color: #fff; + background-color: #007bff; + border-color: #007bff; } + .list-group-item + .list-group-item { + border-top-width: 0; } + .list-group-item + .list-group-item.active { + margin-top: -1px; + border-top-width: 1px; } + +.list-group-horizontal { + flex-direction: row; } + .list-group-horizontal > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } + +@media (min-width: 576px) { + .list-group-horizontal-sm { + flex-direction: row; } + .list-group-horizontal-sm > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-sm > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-sm > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-sm > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-sm > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } +@media (min-width: 768px) { + .list-group-horizontal-md { + flex-direction: row; } + .list-group-horizontal-md > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-md > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-md > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-md > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-md > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } +@media (min-width: 992px) { + .list-group-horizontal-lg { + flex-direction: row; } + .list-group-horizontal-lg > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-lg > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-lg > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-lg > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-lg > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } +@media (min-width: 1200px) { + .list-group-horizontal-xl { + flex-direction: row; } + .list-group-horizontal-xl > .list-group-item:first-child { + border-bottom-left-radius: 0.25rem; + border-top-right-radius: 0; } + .list-group-horizontal-xl > .list-group-item:last-child { + border-top-right-radius: 0.25rem; + border-bottom-left-radius: 0; } + .list-group-horizontal-xl > .list-group-item.active { + margin-top: 0; } + .list-group-horizontal-xl > .list-group-item + .list-group-item { + border-top-width: 1px; + border-left-width: 0; } + .list-group-horizontal-xl > .list-group-item + .list-group-item.active { + margin-left: -1px; + border-left-width: 1px; } } +.list-group-flush { + border-radius: 0; } + .list-group-flush > .list-group-item { + border-width: 0 0 1px; } + .list-group-flush > .list-group-item:last-child { + border-bottom-width: 0; } + +.list-group-item-primary { + color: #004085; + background-color: #b8daff; } + .list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { + color: #004085; + background-color: #9fcdff; } + .list-group-item-primary.list-group-item-action.active { + color: #fff; + background-color: #004085; + border-color: #004085; } + +.list-group-item-secondary { + color: #383d41; + background-color: #d6d8db; } + .list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { + color: #383d41; + background-color: #c8cbcf; } + .list-group-item-secondary.list-group-item-action.active { + color: #fff; + background-color: #383d41; + border-color: #383d41; } + +.list-group-item-success { + color: #155724; + background-color: #c3e6cb; } + .list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { + color: #155724; + background-color: #b1dfbb; } + .list-group-item-success.list-group-item-action.active { + color: #fff; + background-color: #155724; + border-color: #155724; } + +.list-group-item-info { + color: #0c5460; + background-color: #bee5eb; } + .list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { + color: #0c5460; + background-color: #abdde5; } + .list-group-item-info.list-group-item-action.active { + color: #fff; + background-color: #0c5460; + border-color: #0c5460; } + +.list-group-item-warning { + color: #856404; + background-color: #ffeeba; } + .list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { + color: #856404; + background-color: #ffe8a1; } + .list-group-item-warning.list-group-item-action.active { + color: #fff; + background-color: #856404; + border-color: #856404; } + +.list-group-item-danger { + color: #721c24; + background-color: #f5c6cb; } + .list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { + color: #721c24; + background-color: #f1b0b7; } + .list-group-item-danger.list-group-item-action.active { + color: #fff; + background-color: #721c24; + border-color: #721c24; } + +.list-group-item-light { + color: #818182; + background-color: #fdfdfe; } + .list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { + color: #818182; + background-color: #ececf6; } + .list-group-item-light.list-group-item-action.active { + color: #fff; + background-color: #818182; + border-color: #818182; } + +.list-group-item-dark { + color: #1b1e21; + background-color: #c6c8ca; } + .list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { + color: #1b1e21; + background-color: #b9bbbe; } + .list-group-item-dark.list-group-item-action.active { + color: #fff; + background-color: #1b1e21; + border-color: #1b1e21; } + +.close { + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5; } + .close:hover { + color: #000; + text-decoration: none; } + .close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus { + opacity: .75; } + +button.close { + padding: 0; + background-color: transparent; + border: 0; } + +a.close.disabled { + pointer-events: none; } + +.toast { + max-width: 350px; + overflow: hidden; + font-size: 0.875rem; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1); + backdrop-filter: blur(10px); + opacity: 0; + border-radius: 0.25rem; } + .toast:not(:last-child) { + margin-bottom: 0.75rem; } + .toast.showing { + opacity: 1; } + .toast.show { + display: block; + opacity: 1; } + .toast.hide { + display: none; } + +.toast-header { + display: flex; + align-items: center; + padding: 0.25rem 0.75rem; + color: #6c757d; + background-color: rgba(255, 255, 255, 0.85); + background-clip: padding-box; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); } + +.toast-body { + padding: 0.75rem; } + +.modal-open { + overflow: hidden; } + .modal-open .modal { + overflow-x: hidden; + overflow-y: auto; } + +.modal { + position: fixed; + top: 0; + left: 0; + z-index: 1050; + display: none; + width: 100%; + height: 100%; + overflow: hidden; + outline: 0; } + +.modal-dialog { + position: relative; + width: auto; + margin: 0.5rem; + pointer-events: none; } + .modal.fade .modal-dialog { + transition: transform 0.3s ease-out; + transform: translate(0, -50px); } + @media (prefers-reduced-motion: reduce) { + .modal.fade .modal-dialog { + transition: none; } } + .modal.show .modal-dialog { + transform: none; } + .modal.modal-static .modal-dialog { + transform: scale(1.02); } + +.modal-dialog-scrollable { + display: flex; + max-height: calc(100% - 1rem); } + .modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 1rem); + overflow: hidden; } + .modal-dialog-scrollable .modal-header, + .modal-dialog-scrollable .modal-footer { + flex-shrink: 0; } + .modal-dialog-scrollable .modal-body { + overflow-y: auto; } + +.modal-dialog-centered { + display: flex; + align-items: center; + min-height: calc(100% - 1rem); } + .modal-dialog-centered::before { + display: block; + height: calc(100vh - 1rem); + height: min-content; + content: ""; } + .modal-dialog-centered.modal-dialog-scrollable { + flex-direction: column; + justify-content: center; + height: 100%; } + .modal-dialog-centered.modal-dialog-scrollable .modal-content { + max-height: none; } + .modal-dialog-centered.modal-dialog-scrollable::before { + content: none; } + +.modal-content { + position: relative; + display: flex; + flex-direction: column; + width: 100%; + pointer-events: auto; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; + outline: 0; } + +.modal-backdrop { + position: fixed; + top: 0; + left: 0; + z-index: 1040; + width: 100vw; + height: 100vh; + background-color: #000; } + .modal-backdrop.fade { + opacity: 0; } + .modal-backdrop.show { + opacity: 0.5; } + +.modal-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: 1rem 1rem; + border-bottom: 1px solid #dee2e6; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); } + .modal-header .close { + padding: 1rem 1rem; + margin: -1rem -1rem -1rem auto; } + +.modal-title { + margin-bottom: 0; + line-height: 1.5; } + +.modal-body { + position: relative; + flex: 1 1 auto; + padding: 1rem; } + +.modal-footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + padding: 0.75rem; + border-top: 1px solid #dee2e6; + border-bottom-right-radius: calc(0.3rem - 1px); + border-bottom-left-radius: calc(0.3rem - 1px); } + .modal-footer > * { + margin: 0.25rem; } + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; } + +@media (min-width: 576px) { + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto; } + + .modal-dialog-scrollable { + max-height: calc(100% - 3.5rem); } + .modal-dialog-scrollable .modal-content { + max-height: calc(100vh - 3.5rem); } + + .modal-dialog-centered { + min-height: calc(100% - 3.5rem); } + .modal-dialog-centered::before { + height: calc(100vh - 3.5rem); + height: min-content; } + + .modal-sm { + max-width: 300px; } } +@media (min-width: 992px) { + .modal-lg, + .modal-xl { + max-width: 800px; } } +@media (min-width: 1200px) { + .modal-xl { + max-width: 1140px; } } +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + opacity: 0; } + .tooltip.show { + opacity: 0.9; } + .tooltip .arrow { + position: absolute; + display: block; + width: 0.8rem; + height: 0.4rem; } + .tooltip .arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; } + +.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] { + padding: 0.4rem 0; } + .bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow { + bottom: 0; } + .bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before { + top: 0; + border-width: 0.4rem 0.4rem 0; + border-top-color: #000; } + +.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] { + padding: 0 0.4rem; } + .bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; } + .bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before { + right: 0; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #000; } + +.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] { + padding: 0.4rem 0; } + .bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow { + top: 0; } + .bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before { + bottom: 0; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #000; } + +.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] { + padding: 0 0.4rem; } + .bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; } + .bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before { + left: 0; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #000; } + +.tooltip-inner { + max-width: 200px; + padding: 0.25rem 0.5rem; + color: #fff; + text-align: center; + background-color: #000; + border-radius: 0.25rem; } + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: block; + max-width: 276px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.875rem; + word-wrap: break-word; + background-color: #fff; + background-clip: padding-box; + border: 1px solid rgba(0, 0, 0, 0.2); + border-radius: 0.3rem; } + .popover .arrow { + position: absolute; + display: block; + width: 1rem; + height: 0.5rem; + margin: 0 0.3rem; } + .popover .arrow::before, .popover .arrow::after { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; } + +.bs-popover-top, .bs-popover-auto[x-placement^="top"] { + margin-bottom: 0.5rem; } + .bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow { + bottom: calc(-0.5rem - 1px); } + .bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before { + bottom: 0; + border-width: 0.5rem 0.5rem 0; + border-top-color: rgba(0, 0, 0, 0.25); } + .bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^="top"] > .arrow::after { + bottom: 1px; + border-width: 0.5rem 0.5rem 0; + border-top-color: #fff; } + +.bs-popover-right, .bs-popover-auto[x-placement^="right"] { + margin-left: 0.5rem; } + .bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow { + left: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; } + .bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^="right"] > .arrow::before { + left: 0; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: rgba(0, 0, 0, 0.25); } + .bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^="right"] > .arrow::after { + left: 1px; + border-width: 0.5rem 0.5rem 0.5rem 0; + border-right-color: #fff; } + +.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] { + margin-top: 0.5rem; } + .bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow { + top: calc(-0.5rem - 1px); } + .bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before { + top: 0; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: rgba(0, 0, 0, 0.25); } + .bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^="bottom"] > .arrow::after { + top: 1px; + border-width: 0 0.5rem 0.5rem 0.5rem; + border-bottom-color: #fff; } + .bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1rem; + margin-left: -0.5rem; + content: ""; + border-bottom: 1px solid #f7f7f7; } + +.bs-popover-left, .bs-popover-auto[x-placement^="left"] { + margin-right: 0.5rem; } + .bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow { + right: calc(-0.5rem - 1px); + width: 0.5rem; + height: 1rem; + margin: 0.3rem 0; } + .bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^="left"] > .arrow::before { + right: 0; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: rgba(0, 0, 0, 0.25); } + .bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^="left"] > .arrow::after { + right: 1px; + border-width: 0.5rem 0 0.5rem 0.5rem; + border-left-color: #fff; } + +.popover-header { + padding: 0.5rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); } + .popover-header:empty { + display: none; } + +.popover-body { + padding: 0.5rem 0.75rem; + color: #212529; } + +.carousel { + position: relative; } + +.carousel.pointer-event { + touch-action: pan-y; } + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; } + .carousel-inner::after { + display: block; + clear: both; + content: ""; } + +.carousel-item { + position: relative; + display: none; + float: left; + width: 100%; + margin-right: -100%; + backface-visibility: hidden; + transition: transform 0.6s ease-in-out; } + @media (prefers-reduced-motion: reduce) { + .carousel-item { + transition: none; } } + +.carousel-item.active, +.carousel-item-next, +.carousel-item-prev { + display: block; } + +.carousel-item-next:not(.carousel-item-left), +.active.carousel-item-right { + transform: translateX(100%); } + +.carousel-item-prev:not(.carousel-item-right), +.active.carousel-item-left { + transform: translateX(-100%); } + +.carousel-fade .carousel-item { + opacity: 0; + transition-property: opacity; + transform: none; } +.carousel-fade .carousel-item.active, +.carousel-fade .carousel-item-next.carousel-item-left, +.carousel-fade .carousel-item-prev.carousel-item-right { + z-index: 1; + opacity: 1; } +.carousel-fade .active.carousel-item-left, +.carousel-fade .active.carousel-item-right { + z-index: 0; + opacity: 0; + transition: opacity 0s 0.6s; } + @media (prefers-reduced-motion: reduce) { + .carousel-fade .active.carousel-item-left, + .carousel-fade .active.carousel-item-right { + transition: none; } } + +.carousel-control-prev, +.carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + width: 15%; + color: #fff; + text-align: center; + opacity: 0.5; + transition: opacity 0.15s ease; } + @media (prefers-reduced-motion: reduce) { + .carousel-control-prev, + .carousel-control-next { + transition: none; } } + .carousel-control-prev:hover, .carousel-control-prev:focus, + .carousel-control-next:hover, + .carousel-control-next:focus { + color: #fff; + text-decoration: none; + outline: 0; + opacity: 0.9; } + +.carousel-control-prev { + left: 0; } + +.carousel-control-next { + right: 0; } + +.carousel-control-prev-icon, +.carousel-control-next-icon { + display: inline-block; + width: 20px; + height: 20px; + background: no-repeat 50% / 100% 100%; } + +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e"); } + +.carousel-control-next-icon { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e"); } + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 0; + left: 0; + z-index: 15; + display: flex; + justify-content: center; + padding-left: 0; + margin-right: 15%; + margin-left: 15%; + list-style: none; } + .carousel-indicators li { + box-sizing: content-box; + flex: 0 1 auto; + width: 30px; + height: 3px; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: #fff; + background-clip: padding-box; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: .5; + transition: opacity 0.6s ease; } + @media (prefers-reduced-motion: reduce) { + .carousel-indicators li { + transition: none; } } + .carousel-indicators .active { + opacity: 1; } + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; } + +@keyframes spinner-border { + to { + transform: rotate(360deg); } } +.spinner-border { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + border: 0.25em solid currentColor; + border-right-color: transparent; + border-radius: 50%; + animation: spinner-border .75s linear infinite; } + +.spinner-border-sm { + width: 1rem; + height: 1rem; + border-width: 0.2em; } + +@keyframes spinner-grow { + 0% { + transform: scale(0); } + 50% { + opacity: 1; + transform: none; } } +.spinner-grow { + display: inline-block; + width: 2rem; + height: 2rem; + vertical-align: text-bottom; + background-color: currentColor; + border-radius: 50%; + opacity: 0; + animation: spinner-grow .75s linear infinite; } + +.spinner-grow-sm { + width: 1rem; + height: 1rem; } + +.align-baseline { + vertical-align: baseline !important; } + +.align-top { + vertical-align: top !important; } + +.align-middle { + vertical-align: middle !important; } + +.align-bottom { + vertical-align: bottom !important; } + +.align-text-bottom { + vertical-align: text-bottom !important; } + +.align-text-top { + vertical-align: text-top !important; } + +.bg-primary { + background-color: #007bff !important; } + +a.bg-primary:hover, a.bg-primary:focus, +button.bg-primary:hover, +button.bg-primary:focus { + background-color: #0062cc !important; } + +.bg-secondary { + background-color: #6c757d !important; } + +a.bg-secondary:hover, a.bg-secondary:focus, +button.bg-secondary:hover, +button.bg-secondary:focus { + background-color: #545b62 !important; } + +.bg-success { + background-color: #28a745 !important; } + +a.bg-success:hover, a.bg-success:focus, +button.bg-success:hover, +button.bg-success:focus { + background-color: #1e7e34 !important; } + +.bg-info { + background-color: #17a2b8 !important; } + +a.bg-info:hover, a.bg-info:focus, +button.bg-info:hover, +button.bg-info:focus { + background-color: #117a8b !important; } + +.bg-warning { + background-color: #ffc107 !important; } + +a.bg-warning:hover, a.bg-warning:focus, +button.bg-warning:hover, +button.bg-warning:focus { + background-color: #d39e00 !important; } + +.bg-danger { + background-color: #dc3545 !important; } + +a.bg-danger:hover, a.bg-danger:focus, +button.bg-danger:hover, +button.bg-danger:focus { + background-color: #bd2130 !important; } + +.bg-light { + background-color: #f8f9fa !important; } + +a.bg-light:hover, a.bg-light:focus, +button.bg-light:hover, +button.bg-light:focus { + background-color: #dae0e5 !important; } + +.bg-dark { + background-color: #343a40 !important; } + +a.bg-dark:hover, a.bg-dark:focus, +button.bg-dark:hover, +button.bg-dark:focus { + background-color: #1d2124 !important; } + +.bg-white { + background-color: #fff !important; } + +.bg-transparent { + background-color: transparent !important; } + +.border { + border: 1px solid #dee2e6 !important; } + +.border-top { + border-top: 1px solid #dee2e6 !important; } + +.border-right { + border-right: 1px solid #dee2e6 !important; } + +.border-bottom { + border-bottom: 1px solid #dee2e6 !important; } + +.border-left { + border-left: 1px solid #dee2e6 !important; } + +.border-0 { + border: 0 !important; } + +.border-top-0 { + border-top: 0 !important; } + +.border-right-0 { + border-right: 0 !important; } + +.border-bottom-0 { + border-bottom: 0 !important; } + +.border-left-0 { + border-left: 0 !important; } + +.border-primary { + border-color: #007bff !important; } + +.border-secondary { + border-color: #6c757d !important; } + +.border-success { + border-color: #28a745 !important; } + +.border-info { + border-color: #17a2b8 !important; } + +.border-warning { + border-color: #ffc107 !important; } + +.border-danger { + border-color: #dc3545 !important; } + +.border-light { + border-color: #f8f9fa !important; } + +.border-dark { + border-color: #343a40 !important; } + +.border-white { + border-color: #fff !important; } + +.rounded-sm { + border-radius: 0.2rem !important; } + +.rounded { + border-radius: 0.25rem !important; } + +.rounded-top { + border-top-left-radius: 0.25rem !important; + border-top-right-radius: 0.25rem !important; } + +.rounded-right { + border-top-right-radius: 0.25rem !important; + border-bottom-right-radius: 0.25rem !important; } + +.rounded-bottom { + border-bottom-right-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; } + +.rounded-left { + border-top-left-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; } + +.rounded-lg { + border-radius: 0.3rem !important; } + +.rounded-circle { + border-radius: 50% !important; } + +.rounded-pill { + border-radius: 50rem !important; } + +.rounded-0 { + border-radius: 0 !important; } + +.clearfix::after { + display: block; + clear: both; + content: ""; } + +.d-none { + display: none !important; } + +.d-inline { + display: inline !important; } + +.d-inline-block { + display: inline-block !important; } + +.d-block { + display: block !important; } + +.d-table { + display: table !important; } + +.d-table-row { + display: table-row !important; } + +.d-table-cell { + display: table-cell !important; } + +.d-flex { + display: flex !important; } + +.d-inline-flex { + display: inline-flex !important; } + +@media (min-width: 576px) { + .d-sm-none { + display: none !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-block { + display: block !important; } + + .d-sm-table { + display: table !important; } + + .d-sm-table-row { + display: table-row !important; } + + .d-sm-table-cell { + display: table-cell !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline-flex { + display: inline-flex !important; } } +@media (min-width: 768px) { + .d-md-none { + display: none !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-block { + display: block !important; } + + .d-md-table { + display: table !important; } + + .d-md-table-row { + display: table-row !important; } + + .d-md-table-cell { + display: table-cell !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline-flex { + display: inline-flex !important; } } +@media (min-width: 992px) { + .d-lg-none { + display: none !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-block { + display: block !important; } + + .d-lg-table { + display: table !important; } + + .d-lg-table-row { + display: table-row !important; } + + .d-lg-table-cell { + display: table-cell !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline-flex { + display: inline-flex !important; } } +@media (min-width: 1200px) { + .d-xl-none { + display: none !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-block { + display: block !important; } + + .d-xl-table { + display: table !important; } + + .d-xl-table-row { + display: table-row !important; } + + .d-xl-table-cell { + display: table-cell !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline-flex { + display: inline-flex !important; } } +@media print { + .d-print-none { + display: none !important; } + + .d-print-inline { + display: inline !important; } + + .d-print-inline-block { + display: inline-block !important; } + + .d-print-block { + display: block !important; } + + .d-print-table { + display: table !important; } + + .d-print-table-row { + display: table-row !important; } + + .d-print-table-cell { + display: table-cell !important; } + + .d-print-flex { + display: flex !important; } + + .d-print-inline-flex { + display: inline-flex !important; } } +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden; } + .embed-responsive::before { + display: block; + content: ""; } + .embed-responsive .embed-responsive-item, + .embed-responsive iframe, + .embed-responsive embed, + .embed-responsive object, + .embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; } + +.embed-responsive-21by9::before { + padding-top: 42.8571428571%; } + +.embed-responsive-16by9::before { + padding-top: 56.25%; } + +.embed-responsive-4by3::before { + padding-top: 75%; } + +.embed-responsive-1by1::before { + padding-top: 100%; } + +.flex-row { + flex-direction: row !important; } + +.flex-column { + flex-direction: column !important; } + +.flex-row-reverse { + flex-direction: row-reverse !important; } + +.flex-column-reverse { + flex-direction: column-reverse !important; } + +.flex-wrap { + flex-wrap: wrap !important; } + +.flex-nowrap { + flex-wrap: nowrap !important; } + +.flex-wrap-reverse { + flex-wrap: wrap-reverse !important; } + +.flex-fill { + flex: 1 1 auto !important; } + +.flex-grow-0 { + flex-grow: 0 !important; } + +.flex-grow-1 { + flex-grow: 1 !important; } + +.flex-shrink-0 { + flex-shrink: 0 !important; } + +.flex-shrink-1 { + flex-shrink: 1 !important; } + +.justify-content-start { + justify-content: flex-start !important; } + +.justify-content-end { + justify-content: flex-end !important; } + +.justify-content-center { + justify-content: center !important; } + +.justify-content-between { + justify-content: space-between !important; } + +.justify-content-around { + justify-content: space-around !important; } + +.align-items-start { + align-items: flex-start !important; } + +.align-items-end { + align-items: flex-end !important; } + +.align-items-center { + align-items: center !important; } + +.align-items-baseline { + align-items: baseline !important; } + +.align-items-stretch { + align-items: stretch !important; } + +.align-content-start { + align-content: flex-start !important; } + +.align-content-end { + align-content: flex-end !important; } + +.align-content-center { + align-content: center !important; } + +.align-content-between { + align-content: space-between !important; } + +.align-content-around { + align-content: space-around !important; } + +.align-content-stretch { + align-content: stretch !important; } + +.align-self-auto { + align-self: auto !important; } + +.align-self-start { + align-self: flex-start !important; } + +.align-self-end { + align-self: flex-end !important; } + +.align-self-center { + align-self: center !important; } + +.align-self-baseline { + align-self: baseline !important; } + +.align-self-stretch { + align-self: stretch !important; } + +@media (min-width: 576px) { + .flex-sm-row { + flex-direction: row !important; } + + .flex-sm-column { + flex-direction: column !important; } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important; } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important; } + + .flex-sm-wrap { + flex-wrap: wrap !important; } + + .flex-sm-nowrap { + flex-wrap: nowrap !important; } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; } + + .flex-sm-fill { + flex: 1 1 auto !important; } + + .flex-sm-grow-0 { + flex-grow: 0 !important; } + + .flex-sm-grow-1 { + flex-grow: 1 !important; } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important; } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important; } + + .justify-content-sm-start { + justify-content: flex-start !important; } + + .justify-content-sm-end { + justify-content: flex-end !important; } + + .justify-content-sm-center { + justify-content: center !important; } + + .justify-content-sm-between { + justify-content: space-between !important; } + + .justify-content-sm-around { + justify-content: space-around !important; } + + .align-items-sm-start { + align-items: flex-start !important; } + + .align-items-sm-end { + align-items: flex-end !important; } + + .align-items-sm-center { + align-items: center !important; } + + .align-items-sm-baseline { + align-items: baseline !important; } + + .align-items-sm-stretch { + align-items: stretch !important; } + + .align-content-sm-start { + align-content: flex-start !important; } + + .align-content-sm-end { + align-content: flex-end !important; } + + .align-content-sm-center { + align-content: center !important; } + + .align-content-sm-between { + align-content: space-between !important; } + + .align-content-sm-around { + align-content: space-around !important; } + + .align-content-sm-stretch { + align-content: stretch !important; } + + .align-self-sm-auto { + align-self: auto !important; } + + .align-self-sm-start { + align-self: flex-start !important; } + + .align-self-sm-end { + align-self: flex-end !important; } + + .align-self-sm-center { + align-self: center !important; } + + .align-self-sm-baseline { + align-self: baseline !important; } + + .align-self-sm-stretch { + align-self: stretch !important; } } +@media (min-width: 768px) { + .flex-md-row { + flex-direction: row !important; } + + .flex-md-column { + flex-direction: column !important; } + + .flex-md-row-reverse { + flex-direction: row-reverse !important; } + + .flex-md-column-reverse { + flex-direction: column-reverse !important; } + + .flex-md-wrap { + flex-wrap: wrap !important; } + + .flex-md-nowrap { + flex-wrap: nowrap !important; } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; } + + .flex-md-fill { + flex: 1 1 auto !important; } + + .flex-md-grow-0 { + flex-grow: 0 !important; } + + .flex-md-grow-1 { + flex-grow: 1 !important; } + + .flex-md-shrink-0 { + flex-shrink: 0 !important; } + + .flex-md-shrink-1 { + flex-shrink: 1 !important; } + + .justify-content-md-start { + justify-content: flex-start !important; } + + .justify-content-md-end { + justify-content: flex-end !important; } + + .justify-content-md-center { + justify-content: center !important; } + + .justify-content-md-between { + justify-content: space-between !important; } + + .justify-content-md-around { + justify-content: space-around !important; } + + .align-items-md-start { + align-items: flex-start !important; } + + .align-items-md-end { + align-items: flex-end !important; } + + .align-items-md-center { + align-items: center !important; } + + .align-items-md-baseline { + align-items: baseline !important; } + + .align-items-md-stretch { + align-items: stretch !important; } + + .align-content-md-start { + align-content: flex-start !important; } + + .align-content-md-end { + align-content: flex-end !important; } + + .align-content-md-center { + align-content: center !important; } + + .align-content-md-between { + align-content: space-between !important; } + + .align-content-md-around { + align-content: space-around !important; } + + .align-content-md-stretch { + align-content: stretch !important; } + + .align-self-md-auto { + align-self: auto !important; } + + .align-self-md-start { + align-self: flex-start !important; } + + .align-self-md-end { + align-self: flex-end !important; } + + .align-self-md-center { + align-self: center !important; } + + .align-self-md-baseline { + align-self: baseline !important; } + + .align-self-md-stretch { + align-self: stretch !important; } } +@media (min-width: 992px) { + .flex-lg-row { + flex-direction: row !important; } + + .flex-lg-column { + flex-direction: column !important; } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important; } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important; } + + .flex-lg-wrap { + flex-wrap: wrap !important; } + + .flex-lg-nowrap { + flex-wrap: nowrap !important; } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; } + + .flex-lg-fill { + flex: 1 1 auto !important; } + + .flex-lg-grow-0 { + flex-grow: 0 !important; } + + .flex-lg-grow-1 { + flex-grow: 1 !important; } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important; } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important; } + + .justify-content-lg-start { + justify-content: flex-start !important; } + + .justify-content-lg-end { + justify-content: flex-end !important; } + + .justify-content-lg-center { + justify-content: center !important; } + + .justify-content-lg-between { + justify-content: space-between !important; } + + .justify-content-lg-around { + justify-content: space-around !important; } + + .align-items-lg-start { + align-items: flex-start !important; } + + .align-items-lg-end { + align-items: flex-end !important; } + + .align-items-lg-center { + align-items: center !important; } + + .align-items-lg-baseline { + align-items: baseline !important; } + + .align-items-lg-stretch { + align-items: stretch !important; } + + .align-content-lg-start { + align-content: flex-start !important; } + + .align-content-lg-end { + align-content: flex-end !important; } + + .align-content-lg-center { + align-content: center !important; } + + .align-content-lg-between { + align-content: space-between !important; } + + .align-content-lg-around { + align-content: space-around !important; } + + .align-content-lg-stretch { + align-content: stretch !important; } + + .align-self-lg-auto { + align-self: auto !important; } + + .align-self-lg-start { + align-self: flex-start !important; } + + .align-self-lg-end { + align-self: flex-end !important; } + + .align-self-lg-center { + align-self: center !important; } + + .align-self-lg-baseline { + align-self: baseline !important; } + + .align-self-lg-stretch { + align-self: stretch !important; } } +@media (min-width: 1200px) { + .flex-xl-row { + flex-direction: row !important; } + + .flex-xl-column { + flex-direction: column !important; } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important; } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important; } + + .flex-xl-wrap { + flex-wrap: wrap !important; } + + .flex-xl-nowrap { + flex-wrap: nowrap !important; } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; } + + .flex-xl-fill { + flex: 1 1 auto !important; } + + .flex-xl-grow-0 { + flex-grow: 0 !important; } + + .flex-xl-grow-1 { + flex-grow: 1 !important; } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important; } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important; } + + .justify-content-xl-start { + justify-content: flex-start !important; } + + .justify-content-xl-end { + justify-content: flex-end !important; } + + .justify-content-xl-center { + justify-content: center !important; } + + .justify-content-xl-between { + justify-content: space-between !important; } + + .justify-content-xl-around { + justify-content: space-around !important; } + + .align-items-xl-start { + align-items: flex-start !important; } + + .align-items-xl-end { + align-items: flex-end !important; } + + .align-items-xl-center { + align-items: center !important; } + + .align-items-xl-baseline { + align-items: baseline !important; } + + .align-items-xl-stretch { + align-items: stretch !important; } + + .align-content-xl-start { + align-content: flex-start !important; } + + .align-content-xl-end { + align-content: flex-end !important; } + + .align-content-xl-center { + align-content: center !important; } + + .align-content-xl-between { + align-content: space-between !important; } + + .align-content-xl-around { + align-content: space-around !important; } + + .align-content-xl-stretch { + align-content: stretch !important; } + + .align-self-xl-auto { + align-self: auto !important; } + + .align-self-xl-start { + align-self: flex-start !important; } + + .align-self-xl-end { + align-self: flex-end !important; } + + .align-self-xl-center { + align-self: center !important; } + + .align-self-xl-baseline { + align-self: baseline !important; } + + .align-self-xl-stretch { + align-self: stretch !important; } } +.float-left { + float: left !important; } + +.float-right { + float: right !important; } + +.float-none { + float: none !important; } + +@media (min-width: 576px) { + .float-sm-left { + float: left !important; } + + .float-sm-right { + float: right !important; } + + .float-sm-none { + float: none !important; } } +@media (min-width: 768px) { + .float-md-left { + float: left !important; } + + .float-md-right { + float: right !important; } + + .float-md-none { + float: none !important; } } +@media (min-width: 992px) { + .float-lg-left { + float: left !important; } + + .float-lg-right { + float: right !important; } + + .float-lg-none { + float: none !important; } } +@media (min-width: 1200px) { + .float-xl-left { + float: left !important; } + + .float-xl-right { + float: right !important; } + + .float-xl-none { + float: none !important; } } +.user-select-all { + user-select: all !important; } + +.user-select-auto { + user-select: auto !important; } + +.user-select-none { + user-select: none !important; } + +.overflow-auto { + overflow: auto !important; } + +.overflow-hidden { + overflow: hidden !important; } + +.position-static { + position: static !important; } + +.position-relative { + position: relative !important; } + +.position-absolute { + position: absolute !important; } + +.position-fixed { + position: fixed !important; } + +.position-sticky { + position: sticky !important; } + +.fixed-top { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; } + +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; } + +@supports (position: sticky) { + .sticky-top { + position: sticky; + top: 0; + z-index: 1020; } } + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; } + +.sr-only-focusable:active, .sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal; } + +.shadow-sm { + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important; } + +.shadow { + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } + +.shadow-lg { + box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important; } + +.shadow-none { + box-shadow: none !important; } + +.w-25 { + width: 25% !important; } + +.w-50 { + width: 50% !important; } + +.w-75 { + width: 75% !important; } + +.w-100 { + width: 100% !important; } + +.w-auto { + width: auto !important; } + +.h-25 { + height: 25% !important; } + +.h-50 { + height: 50% !important; } + +.h-75 { + height: 75% !important; } + +.h-100 { + height: 100% !important; } + +.h-auto { + height: auto !important; } + +.mw-100 { + max-width: 100% !important; } + +.mh-100 { + max-height: 100% !important; } + +.min-vw-100 { + min-width: 100vw !important; } + +.min-vh-100 { + min-height: 100vh !important; } + +.vw-100 { + width: 100vw !important; } + +.vh-100 { + height: 100vh !important; } + +.m-0 { + margin: 0 !important; } + +.mt-0, +.my-0 { + margin-top: 0 !important; } + +.mr-0, +.mx-0 { + margin-right: 0 !important; } + +.mb-0, +.my-0 { + margin-bottom: 0 !important; } + +.ml-0, +.mx-0 { + margin-left: 0 !important; } + +.m-1 { + margin: 0.25rem !important; } + +.mt-1, +.my-1 { + margin-top: 0.25rem !important; } + +.mr-1, +.mx-1 { + margin-right: 0.25rem !important; } + +.mb-1, +.my-1 { + margin-bottom: 0.25rem !important; } + +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; } + +.m-2 { + margin: 0.5rem !important; } + +.mt-2, +.my-2 { + margin-top: 0.5rem !important; } + +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; } + +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; } + +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; } + +.m-3 { + margin: 1rem !important; } + +.mt-3, +.my-3 { + margin-top: 1rem !important; } + +.mr-3, +.mx-3 { + margin-right: 1rem !important; } + +.mb-3, +.my-3 { + margin-bottom: 1rem !important; } + +.ml-3, +.mx-3 { + margin-left: 1rem !important; } + +.m-4 { + margin: 1.5rem !important; } + +.mt-4, +.my-4 { + margin-top: 1.5rem !important; } + +.mr-4, +.mx-4 { + margin-right: 1.5rem !important; } + +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; } + +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; } + +.m-5 { + margin: 3rem !important; } + +.mt-5, +.my-5 { + margin-top: 3rem !important; } + +.mr-5, +.mx-5 { + margin-right: 3rem !important; } + +.mb-5, +.my-5 { + margin-bottom: 3rem !important; } + +.ml-5, +.mx-5 { + margin-left: 3rem !important; } + +.p-0 { + padding: 0 !important; } + +.pt-0, +.py-0 { + padding-top: 0 !important; } + +.pr-0, +.px-0 { + padding-right: 0 !important; } + +.pb-0, +.py-0 { + padding-bottom: 0 !important; } + +.pl-0, +.px-0 { + padding-left: 0 !important; } + +.p-1 { + padding: 0.25rem !important; } + +.pt-1, +.py-1 { + padding-top: 0.25rem !important; } + +.pr-1, +.px-1 { + padding-right: 0.25rem !important; } + +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; } + +.pl-1, +.px-1 { + padding-left: 0.25rem !important; } + +.p-2 { + padding: 0.5rem !important; } + +.pt-2, +.py-2 { + padding-top: 0.5rem !important; } + +.pr-2, +.px-2 { + padding-right: 0.5rem !important; } + +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; } + +.pl-2, +.px-2 { + padding-left: 0.5rem !important; } + +.p-3 { + padding: 1rem !important; } + +.pt-3, +.py-3 { + padding-top: 1rem !important; } + +.pr-3, +.px-3 { + padding-right: 1rem !important; } + +.pb-3, +.py-3 { + padding-bottom: 1rem !important; } + +.pl-3, +.px-3 { + padding-left: 1rem !important; } + +.p-4 { + padding: 1.5rem !important; } + +.pt-4, +.py-4 { + padding-top: 1.5rem !important; } + +.pr-4, +.px-4 { + padding-right: 1.5rem !important; } + +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; } + +.pl-4, +.px-4 { + padding-left: 1.5rem !important; } + +.p-5 { + padding: 3rem !important; } + +.pt-5, +.py-5 { + padding-top: 3rem !important; } + +.pr-5, +.px-5 { + padding-right: 3rem !important; } + +.pb-5, +.py-5 { + padding-bottom: 3rem !important; } + +.pl-5, +.px-5 { + padding-left: 3rem !important; } + +.m-n1 { + margin: -0.25rem !important; } + +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; } + +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; } + +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; } + +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; } + +.m-n2 { + margin: -0.5rem !important; } + +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; } + +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; } + +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; } + +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; } + +.m-n3 { + margin: -1rem !important; } + +.mt-n3, +.my-n3 { + margin-top: -1rem !important; } + +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; } + +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; } + +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; } + +.m-n4 { + margin: -1.5rem !important; } + +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; } + +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; } + +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; } + +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; } + +.m-n5 { + margin: -3rem !important; } + +.mt-n5, +.my-n5 { + margin-top: -3rem !important; } + +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; } + +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; } + +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; } + +.m-auto { + margin: auto !important; } + +.mt-auto, +.my-auto { + margin-top: auto !important; } + +.mr-auto, +.mx-auto { + margin-right: auto !important; } + +.mb-auto, +.my-auto { + margin-bottom: auto !important; } + +.ml-auto, +.mx-auto { + margin-left: auto !important; } + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important; } + + .mt-sm-0, + .my-sm-0 { + margin-top: 0 !important; } + + .mr-sm-0, + .mx-sm-0 { + margin-right: 0 !important; } + + .mb-sm-0, + .my-sm-0 { + margin-bottom: 0 !important; } + + .ml-sm-0, + .mx-sm-0 { + margin-left: 0 !important; } + + .m-sm-1 { + margin: 0.25rem !important; } + + .mt-sm-1, + .my-sm-1 { + margin-top: 0.25rem !important; } + + .mr-sm-1, + .mx-sm-1 { + margin-right: 0.25rem !important; } + + .mb-sm-1, + .my-sm-1 { + margin-bottom: 0.25rem !important; } + + .ml-sm-1, + .mx-sm-1 { + margin-left: 0.25rem !important; } + + .m-sm-2 { + margin: 0.5rem !important; } + + .mt-sm-2, + .my-sm-2 { + margin-top: 0.5rem !important; } + + .mr-sm-2, + .mx-sm-2 { + margin-right: 0.5rem !important; } + + .mb-sm-2, + .my-sm-2 { + margin-bottom: 0.5rem !important; } + + .ml-sm-2, + .mx-sm-2 { + margin-left: 0.5rem !important; } + + .m-sm-3 { + margin: 1rem !important; } + + .mt-sm-3, + .my-sm-3 { + margin-top: 1rem !important; } + + .mr-sm-3, + .mx-sm-3 { + margin-right: 1rem !important; } + + .mb-sm-3, + .my-sm-3 { + margin-bottom: 1rem !important; } + + .ml-sm-3, + .mx-sm-3 { + margin-left: 1rem !important; } + + .m-sm-4 { + margin: 1.5rem !important; } + + .mt-sm-4, + .my-sm-4 { + margin-top: 1.5rem !important; } + + .mr-sm-4, + .mx-sm-4 { + margin-right: 1.5rem !important; } + + .mb-sm-4, + .my-sm-4 { + margin-bottom: 1.5rem !important; } + + .ml-sm-4, + .mx-sm-4 { + margin-left: 1.5rem !important; } + + .m-sm-5 { + margin: 3rem !important; } + + .mt-sm-5, + .my-sm-5 { + margin-top: 3rem !important; } + + .mr-sm-5, + .mx-sm-5 { + margin-right: 3rem !important; } + + .mb-sm-5, + .my-sm-5 { + margin-bottom: 3rem !important; } + + .ml-sm-5, + .mx-sm-5 { + margin-left: 3rem !important; } + + .p-sm-0 { + padding: 0 !important; } + + .pt-sm-0, + .py-sm-0 { + padding-top: 0 !important; } + + .pr-sm-0, + .px-sm-0 { + padding-right: 0 !important; } + + .pb-sm-0, + .py-sm-0 { + padding-bottom: 0 !important; } + + .pl-sm-0, + .px-sm-0 { + padding-left: 0 !important; } + + .p-sm-1 { + padding: 0.25rem !important; } + + .pt-sm-1, + .py-sm-1 { + padding-top: 0.25rem !important; } + + .pr-sm-1, + .px-sm-1 { + padding-right: 0.25rem !important; } + + .pb-sm-1, + .py-sm-1 { + padding-bottom: 0.25rem !important; } + + .pl-sm-1, + .px-sm-1 { + padding-left: 0.25rem !important; } + + .p-sm-2 { + padding: 0.5rem !important; } + + .pt-sm-2, + .py-sm-2 { + padding-top: 0.5rem !important; } + + .pr-sm-2, + .px-sm-2 { + padding-right: 0.5rem !important; } + + .pb-sm-2, + .py-sm-2 { + padding-bottom: 0.5rem !important; } + + .pl-sm-2, + .px-sm-2 { + padding-left: 0.5rem !important; } + + .p-sm-3 { + padding: 1rem !important; } + + .pt-sm-3, + .py-sm-3 { + padding-top: 1rem !important; } + + .pr-sm-3, + .px-sm-3 { + padding-right: 1rem !important; } + + .pb-sm-3, + .py-sm-3 { + padding-bottom: 1rem !important; } + + .pl-sm-3, + .px-sm-3 { + padding-left: 1rem !important; } + + .p-sm-4 { + padding: 1.5rem !important; } + + .pt-sm-4, + .py-sm-4 { + padding-top: 1.5rem !important; } + + .pr-sm-4, + .px-sm-4 { + padding-right: 1.5rem !important; } + + .pb-sm-4, + .py-sm-4 { + padding-bottom: 1.5rem !important; } + + .pl-sm-4, + .px-sm-4 { + padding-left: 1.5rem !important; } + + .p-sm-5 { + padding: 3rem !important; } + + .pt-sm-5, + .py-sm-5 { + padding-top: 3rem !important; } + + .pr-sm-5, + .px-sm-5 { + padding-right: 3rem !important; } + + .pb-sm-5, + .py-sm-5 { + padding-bottom: 3rem !important; } + + .pl-sm-5, + .px-sm-5 { + padding-left: 3rem !important; } + + .m-sm-n1 { + margin: -0.25rem !important; } + + .mt-sm-n1, + .my-sm-n1 { + margin-top: -0.25rem !important; } + + .mr-sm-n1, + .mx-sm-n1 { + margin-right: -0.25rem !important; } + + .mb-sm-n1, + .my-sm-n1 { + margin-bottom: -0.25rem !important; } + + .ml-sm-n1, + .mx-sm-n1 { + margin-left: -0.25rem !important; } + + .m-sm-n2 { + margin: -0.5rem !important; } + + .mt-sm-n2, + .my-sm-n2 { + margin-top: -0.5rem !important; } + + .mr-sm-n2, + .mx-sm-n2 { + margin-right: -0.5rem !important; } + + .mb-sm-n2, + .my-sm-n2 { + margin-bottom: -0.5rem !important; } + + .ml-sm-n2, + .mx-sm-n2 { + margin-left: -0.5rem !important; } + + .m-sm-n3 { + margin: -1rem !important; } + + .mt-sm-n3, + .my-sm-n3 { + margin-top: -1rem !important; } + + .mr-sm-n3, + .mx-sm-n3 { + margin-right: -1rem !important; } + + .mb-sm-n3, + .my-sm-n3 { + margin-bottom: -1rem !important; } + + .ml-sm-n3, + .mx-sm-n3 { + margin-left: -1rem !important; } + + .m-sm-n4 { + margin: -1.5rem !important; } + + .mt-sm-n4, + .my-sm-n4 { + margin-top: -1.5rem !important; } + + .mr-sm-n4, + .mx-sm-n4 { + margin-right: -1.5rem !important; } + + .mb-sm-n4, + .my-sm-n4 { + margin-bottom: -1.5rem !important; } + + .ml-sm-n4, + .mx-sm-n4 { + margin-left: -1.5rem !important; } + + .m-sm-n5 { + margin: -3rem !important; } + + .mt-sm-n5, + .my-sm-n5 { + margin-top: -3rem !important; } + + .mr-sm-n5, + .mx-sm-n5 { + margin-right: -3rem !important; } + + .mb-sm-n5, + .my-sm-n5 { + margin-bottom: -3rem !important; } + + .ml-sm-n5, + .mx-sm-n5 { + margin-left: -3rem !important; } + + .m-sm-auto { + margin: auto !important; } + + .mt-sm-auto, + .my-sm-auto { + margin-top: auto !important; } + + .mr-sm-auto, + .mx-sm-auto { + margin-right: auto !important; } + + .mb-sm-auto, + .my-sm-auto { + margin-bottom: auto !important; } + + .ml-sm-auto, + .mx-sm-auto { + margin-left: auto !important; } } +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important; } + + .mt-md-0, + .my-md-0 { + margin-top: 0 !important; } + + .mr-md-0, + .mx-md-0 { + margin-right: 0 !important; } + + .mb-md-0, + .my-md-0 { + margin-bottom: 0 !important; } + + .ml-md-0, + .mx-md-0 { + margin-left: 0 !important; } + + .m-md-1 { + margin: 0.25rem !important; } + + .mt-md-1, + .my-md-1 { + margin-top: 0.25rem !important; } + + .mr-md-1, + .mx-md-1 { + margin-right: 0.25rem !important; } + + .mb-md-1, + .my-md-1 { + margin-bottom: 0.25rem !important; } + + .ml-md-1, + .mx-md-1 { + margin-left: 0.25rem !important; } + + .m-md-2 { + margin: 0.5rem !important; } + + .mt-md-2, + .my-md-2 { + margin-top: 0.5rem !important; } + + .mr-md-2, + .mx-md-2 { + margin-right: 0.5rem !important; } + + .mb-md-2, + .my-md-2 { + margin-bottom: 0.5rem !important; } + + .ml-md-2, + .mx-md-2 { + margin-left: 0.5rem !important; } + + .m-md-3 { + margin: 1rem !important; } + + .mt-md-3, + .my-md-3 { + margin-top: 1rem !important; } + + .mr-md-3, + .mx-md-3 { + margin-right: 1rem !important; } + + .mb-md-3, + .my-md-3 { + margin-bottom: 1rem !important; } + + .ml-md-3, + .mx-md-3 { + margin-left: 1rem !important; } + + .m-md-4 { + margin: 1.5rem !important; } + + .mt-md-4, + .my-md-4 { + margin-top: 1.5rem !important; } + + .mr-md-4, + .mx-md-4 { + margin-right: 1.5rem !important; } + + .mb-md-4, + .my-md-4 { + margin-bottom: 1.5rem !important; } + + .ml-md-4, + .mx-md-4 { + margin-left: 1.5rem !important; } + + .m-md-5 { + margin: 3rem !important; } + + .mt-md-5, + .my-md-5 { + margin-top: 3rem !important; } + + .mr-md-5, + .mx-md-5 { + margin-right: 3rem !important; } + + .mb-md-5, + .my-md-5 { + margin-bottom: 3rem !important; } + + .ml-md-5, + .mx-md-5 { + margin-left: 3rem !important; } + + .p-md-0 { + padding: 0 !important; } + + .pt-md-0, + .py-md-0 { + padding-top: 0 !important; } + + .pr-md-0, + .px-md-0 { + padding-right: 0 !important; } + + .pb-md-0, + .py-md-0 { + padding-bottom: 0 !important; } + + .pl-md-0, + .px-md-0 { + padding-left: 0 !important; } + + .p-md-1 { + padding: 0.25rem !important; } + + .pt-md-1, + .py-md-1 { + padding-top: 0.25rem !important; } + + .pr-md-1, + .px-md-1 { + padding-right: 0.25rem !important; } + + .pb-md-1, + .py-md-1 { + padding-bottom: 0.25rem !important; } + + .pl-md-1, + .px-md-1 { + padding-left: 0.25rem !important; } + + .p-md-2 { + padding: 0.5rem !important; } + + .pt-md-2, + .py-md-2 { + padding-top: 0.5rem !important; } + + .pr-md-2, + .px-md-2 { + padding-right: 0.5rem !important; } + + .pb-md-2, + .py-md-2 { + padding-bottom: 0.5rem !important; } + + .pl-md-2, + .px-md-2 { + padding-left: 0.5rem !important; } + + .p-md-3 { + padding: 1rem !important; } + + .pt-md-3, + .py-md-3 { + padding-top: 1rem !important; } + + .pr-md-3, + .px-md-3 { + padding-right: 1rem !important; } + + .pb-md-3, + .py-md-3 { + padding-bottom: 1rem !important; } + + .pl-md-3, + .px-md-3 { + padding-left: 1rem !important; } + + .p-md-4 { + padding: 1.5rem !important; } + + .pt-md-4, + .py-md-4 { + padding-top: 1.5rem !important; } + + .pr-md-4, + .px-md-4 { + padding-right: 1.5rem !important; } + + .pb-md-4, + .py-md-4 { + padding-bottom: 1.5rem !important; } + + .pl-md-4, + .px-md-4 { + padding-left: 1.5rem !important; } + + .p-md-5 { + padding: 3rem !important; } + + .pt-md-5, + .py-md-5 { + padding-top: 3rem !important; } + + .pr-md-5, + .px-md-5 { + padding-right: 3rem !important; } + + .pb-md-5, + .py-md-5 { + padding-bottom: 3rem !important; } + + .pl-md-5, + .px-md-5 { + padding-left: 3rem !important; } + + .m-md-n1 { + margin: -0.25rem !important; } + + .mt-md-n1, + .my-md-n1 { + margin-top: -0.25rem !important; } + + .mr-md-n1, + .mx-md-n1 { + margin-right: -0.25rem !important; } + + .mb-md-n1, + .my-md-n1 { + margin-bottom: -0.25rem !important; } + + .ml-md-n1, + .mx-md-n1 { + margin-left: -0.25rem !important; } + + .m-md-n2 { + margin: -0.5rem !important; } + + .mt-md-n2, + .my-md-n2 { + margin-top: -0.5rem !important; } + + .mr-md-n2, + .mx-md-n2 { + margin-right: -0.5rem !important; } + + .mb-md-n2, + .my-md-n2 { + margin-bottom: -0.5rem !important; } + + .ml-md-n2, + .mx-md-n2 { + margin-left: -0.5rem !important; } + + .m-md-n3 { + margin: -1rem !important; } + + .mt-md-n3, + .my-md-n3 { + margin-top: -1rem !important; } + + .mr-md-n3, + .mx-md-n3 { + margin-right: -1rem !important; } + + .mb-md-n3, + .my-md-n3 { + margin-bottom: -1rem !important; } + + .ml-md-n3, + .mx-md-n3 { + margin-left: -1rem !important; } + + .m-md-n4 { + margin: -1.5rem !important; } + + .mt-md-n4, + .my-md-n4 { + margin-top: -1.5rem !important; } + + .mr-md-n4, + .mx-md-n4 { + margin-right: -1.5rem !important; } + + .mb-md-n4, + .my-md-n4 { + margin-bottom: -1.5rem !important; } + + .ml-md-n4, + .mx-md-n4 { + margin-left: -1.5rem !important; } + + .m-md-n5 { + margin: -3rem !important; } + + .mt-md-n5, + .my-md-n5 { + margin-top: -3rem !important; } + + .mr-md-n5, + .mx-md-n5 { + margin-right: -3rem !important; } + + .mb-md-n5, + .my-md-n5 { + margin-bottom: -3rem !important; } + + .ml-md-n5, + .mx-md-n5 { + margin-left: -3rem !important; } + + .m-md-auto { + margin: auto !important; } + + .mt-md-auto, + .my-md-auto { + margin-top: auto !important; } + + .mr-md-auto, + .mx-md-auto { + margin-right: auto !important; } + + .mb-md-auto, + .my-md-auto { + margin-bottom: auto !important; } + + .ml-md-auto, + .mx-md-auto { + margin-left: auto !important; } } +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important; } + + .mt-lg-0, + .my-lg-0 { + margin-top: 0 !important; } + + .mr-lg-0, + .mx-lg-0 { + margin-right: 0 !important; } + + .mb-lg-0, + .my-lg-0 { + margin-bottom: 0 !important; } + + .ml-lg-0, + .mx-lg-0 { + margin-left: 0 !important; } + + .m-lg-1 { + margin: 0.25rem !important; } + + .mt-lg-1, + .my-lg-1 { + margin-top: 0.25rem !important; } + + .mr-lg-1, + .mx-lg-1 { + margin-right: 0.25rem !important; } + + .mb-lg-1, + .my-lg-1 { + margin-bottom: 0.25rem !important; } + + .ml-lg-1, + .mx-lg-1 { + margin-left: 0.25rem !important; } + + .m-lg-2 { + margin: 0.5rem !important; } + + .mt-lg-2, + .my-lg-2 { + margin-top: 0.5rem !important; } + + .mr-lg-2, + .mx-lg-2 { + margin-right: 0.5rem !important; } + + .mb-lg-2, + .my-lg-2 { + margin-bottom: 0.5rem !important; } + + .ml-lg-2, + .mx-lg-2 { + margin-left: 0.5rem !important; } + + .m-lg-3 { + margin: 1rem !important; } + + .mt-lg-3, + .my-lg-3 { + margin-top: 1rem !important; } + + .mr-lg-3, + .mx-lg-3 { + margin-right: 1rem !important; } + + .mb-lg-3, + .my-lg-3 { + margin-bottom: 1rem !important; } + + .ml-lg-3, + .mx-lg-3 { + margin-left: 1rem !important; } + + .m-lg-4 { + margin: 1.5rem !important; } + + .mt-lg-4, + .my-lg-4 { + margin-top: 1.5rem !important; } + + .mr-lg-4, + .mx-lg-4 { + margin-right: 1.5rem !important; } + + .mb-lg-4, + .my-lg-4 { + margin-bottom: 1.5rem !important; } + + .ml-lg-4, + .mx-lg-4 { + margin-left: 1.5rem !important; } + + .m-lg-5 { + margin: 3rem !important; } + + .mt-lg-5, + .my-lg-5 { + margin-top: 3rem !important; } + + .mr-lg-5, + .mx-lg-5 { + margin-right: 3rem !important; } + + .mb-lg-5, + .my-lg-5 { + margin-bottom: 3rem !important; } + + .ml-lg-5, + .mx-lg-5 { + margin-left: 3rem !important; } + + .p-lg-0 { + padding: 0 !important; } + + .pt-lg-0, + .py-lg-0 { + padding-top: 0 !important; } + + .pr-lg-0, + .px-lg-0 { + padding-right: 0 !important; } + + .pb-lg-0, + .py-lg-0 { + padding-bottom: 0 !important; } + + .pl-lg-0, + .px-lg-0 { + padding-left: 0 !important; } + + .p-lg-1 { + padding: 0.25rem !important; } + + .pt-lg-1, + .py-lg-1 { + padding-top: 0.25rem !important; } + + .pr-lg-1, + .px-lg-1 { + padding-right: 0.25rem !important; } + + .pb-lg-1, + .py-lg-1 { + padding-bottom: 0.25rem !important; } + + .pl-lg-1, + .px-lg-1 { + padding-left: 0.25rem !important; } + + .p-lg-2 { + padding: 0.5rem !important; } + + .pt-lg-2, + .py-lg-2 { + padding-top: 0.5rem !important; } + + .pr-lg-2, + .px-lg-2 { + padding-right: 0.5rem !important; } + + .pb-lg-2, + .py-lg-2 { + padding-bottom: 0.5rem !important; } + + .pl-lg-2, + .px-lg-2 { + padding-left: 0.5rem !important; } + + .p-lg-3 { + padding: 1rem !important; } + + .pt-lg-3, + .py-lg-3 { + padding-top: 1rem !important; } + + .pr-lg-3, + .px-lg-3 { + padding-right: 1rem !important; } + + .pb-lg-3, + .py-lg-3 { + padding-bottom: 1rem !important; } + + .pl-lg-3, + .px-lg-3 { + padding-left: 1rem !important; } + + .p-lg-4 { + padding: 1.5rem !important; } + + .pt-lg-4, + .py-lg-4 { + padding-top: 1.5rem !important; } + + .pr-lg-4, + .px-lg-4 { + padding-right: 1.5rem !important; } + + .pb-lg-4, + .py-lg-4 { + padding-bottom: 1.5rem !important; } + + .pl-lg-4, + .px-lg-4 { + padding-left: 1.5rem !important; } + + .p-lg-5 { + padding: 3rem !important; } + + .pt-lg-5, + .py-lg-5 { + padding-top: 3rem !important; } + + .pr-lg-5, + .px-lg-5 { + padding-right: 3rem !important; } + + .pb-lg-5, + .py-lg-5 { + padding-bottom: 3rem !important; } + + .pl-lg-5, + .px-lg-5 { + padding-left: 3rem !important; } + + .m-lg-n1 { + margin: -0.25rem !important; } + + .mt-lg-n1, + .my-lg-n1 { + margin-top: -0.25rem !important; } + + .mr-lg-n1, + .mx-lg-n1 { + margin-right: -0.25rem !important; } + + .mb-lg-n1, + .my-lg-n1 { + margin-bottom: -0.25rem !important; } + + .ml-lg-n1, + .mx-lg-n1 { + margin-left: -0.25rem !important; } + + .m-lg-n2 { + margin: -0.5rem !important; } + + .mt-lg-n2, + .my-lg-n2 { + margin-top: -0.5rem !important; } + + .mr-lg-n2, + .mx-lg-n2 { + margin-right: -0.5rem !important; } + + .mb-lg-n2, + .my-lg-n2 { + margin-bottom: -0.5rem !important; } + + .ml-lg-n2, + .mx-lg-n2 { + margin-left: -0.5rem !important; } + + .m-lg-n3 { + margin: -1rem !important; } + + .mt-lg-n3, + .my-lg-n3 { + margin-top: -1rem !important; } + + .mr-lg-n3, + .mx-lg-n3 { + margin-right: -1rem !important; } + + .mb-lg-n3, + .my-lg-n3 { + margin-bottom: -1rem !important; } + + .ml-lg-n3, + .mx-lg-n3 { + margin-left: -1rem !important; } + + .m-lg-n4 { + margin: -1.5rem !important; } + + .mt-lg-n4, + .my-lg-n4 { + margin-top: -1.5rem !important; } + + .mr-lg-n4, + .mx-lg-n4 { + margin-right: -1.5rem !important; } + + .mb-lg-n4, + .my-lg-n4 { + margin-bottom: -1.5rem !important; } + + .ml-lg-n4, + .mx-lg-n4 { + margin-left: -1.5rem !important; } + + .m-lg-n5 { + margin: -3rem !important; } + + .mt-lg-n5, + .my-lg-n5 { + margin-top: -3rem !important; } + + .mr-lg-n5, + .mx-lg-n5 { + margin-right: -3rem !important; } + + .mb-lg-n5, + .my-lg-n5 { + margin-bottom: -3rem !important; } + + .ml-lg-n5, + .mx-lg-n5 { + margin-left: -3rem !important; } + + .m-lg-auto { + margin: auto !important; } + + .mt-lg-auto, + .my-lg-auto { + margin-top: auto !important; } + + .mr-lg-auto, + .mx-lg-auto { + margin-right: auto !important; } + + .mb-lg-auto, + .my-lg-auto { + margin-bottom: auto !important; } + + .ml-lg-auto, + .mx-lg-auto { + margin-left: auto !important; } } +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important; } + + .mt-xl-0, + .my-xl-0 { + margin-top: 0 !important; } + + .mr-xl-0, + .mx-xl-0 { + margin-right: 0 !important; } + + .mb-xl-0, + .my-xl-0 { + margin-bottom: 0 !important; } + + .ml-xl-0, + .mx-xl-0 { + margin-left: 0 !important; } + + .m-xl-1 { + margin: 0.25rem !important; } + + .mt-xl-1, + .my-xl-1 { + margin-top: 0.25rem !important; } + + .mr-xl-1, + .mx-xl-1 { + margin-right: 0.25rem !important; } + + .mb-xl-1, + .my-xl-1 { + margin-bottom: 0.25rem !important; } + + .ml-xl-1, + .mx-xl-1 { + margin-left: 0.25rem !important; } + + .m-xl-2 { + margin: 0.5rem !important; } + + .mt-xl-2, + .my-xl-2 { + margin-top: 0.5rem !important; } + + .mr-xl-2, + .mx-xl-2 { + margin-right: 0.5rem !important; } + + .mb-xl-2, + .my-xl-2 { + margin-bottom: 0.5rem !important; } + + .ml-xl-2, + .mx-xl-2 { + margin-left: 0.5rem !important; } + + .m-xl-3 { + margin: 1rem !important; } + + .mt-xl-3, + .my-xl-3 { + margin-top: 1rem !important; } + + .mr-xl-3, + .mx-xl-3 { + margin-right: 1rem !important; } + + .mb-xl-3, + .my-xl-3 { + margin-bottom: 1rem !important; } + + .ml-xl-3, + .mx-xl-3 { + margin-left: 1rem !important; } + + .m-xl-4 { + margin: 1.5rem !important; } + + .mt-xl-4, + .my-xl-4 { + margin-top: 1.5rem !important; } + + .mr-xl-4, + .mx-xl-4 { + margin-right: 1.5rem !important; } + + .mb-xl-4, + .my-xl-4 { + margin-bottom: 1.5rem !important; } + + .ml-xl-4, + .mx-xl-4 { + margin-left: 1.5rem !important; } + + .m-xl-5 { + margin: 3rem !important; } + + .mt-xl-5, + .my-xl-5 { + margin-top: 3rem !important; } + + .mr-xl-5, + .mx-xl-5 { + margin-right: 3rem !important; } + + .mb-xl-5, + .my-xl-5 { + margin-bottom: 3rem !important; } + + .ml-xl-5, + .mx-xl-5 { + margin-left: 3rem !important; } + + .p-xl-0 { + padding: 0 !important; } + + .pt-xl-0, + .py-xl-0 { + padding-top: 0 !important; } + + .pr-xl-0, + .px-xl-0 { + padding-right: 0 !important; } + + .pb-xl-0, + .py-xl-0 { + padding-bottom: 0 !important; } + + .pl-xl-0, + .px-xl-0 { + padding-left: 0 !important; } + + .p-xl-1 { + padding: 0.25rem !important; } + + .pt-xl-1, + .py-xl-1 { + padding-top: 0.25rem !important; } + + .pr-xl-1, + .px-xl-1 { + padding-right: 0.25rem !important; } + + .pb-xl-1, + .py-xl-1 { + padding-bottom: 0.25rem !important; } + + .pl-xl-1, + .px-xl-1 { + padding-left: 0.25rem !important; } + + .p-xl-2 { + padding: 0.5rem !important; } + + .pt-xl-2, + .py-xl-2 { + padding-top: 0.5rem !important; } + + .pr-xl-2, + .px-xl-2 { + padding-right: 0.5rem !important; } + + .pb-xl-2, + .py-xl-2 { + padding-bottom: 0.5rem !important; } + + .pl-xl-2, + .px-xl-2 { + padding-left: 0.5rem !important; } + + .p-xl-3 { + padding: 1rem !important; } + + .pt-xl-3, + .py-xl-3 { + padding-top: 1rem !important; } + + .pr-xl-3, + .px-xl-3 { + padding-right: 1rem !important; } + + .pb-xl-3, + .py-xl-3 { + padding-bottom: 1rem !important; } + + .pl-xl-3, + .px-xl-3 { + padding-left: 1rem !important; } + + .p-xl-4 { + padding: 1.5rem !important; } + + .pt-xl-4, + .py-xl-4 { + padding-top: 1.5rem !important; } + + .pr-xl-4, + .px-xl-4 { + padding-right: 1.5rem !important; } + + .pb-xl-4, + .py-xl-4 { + padding-bottom: 1.5rem !important; } + + .pl-xl-4, + .px-xl-4 { + padding-left: 1.5rem !important; } + + .p-xl-5 { + padding: 3rem !important; } + + .pt-xl-5, + .py-xl-5 { + padding-top: 3rem !important; } + + .pr-xl-5, + .px-xl-5 { + padding-right: 3rem !important; } + + .pb-xl-5, + .py-xl-5 { + padding-bottom: 3rem !important; } + + .pl-xl-5, + .px-xl-5 { + padding-left: 3rem !important; } + + .m-xl-n1 { + margin: -0.25rem !important; } + + .mt-xl-n1, + .my-xl-n1 { + margin-top: -0.25rem !important; } + + .mr-xl-n1, + .mx-xl-n1 { + margin-right: -0.25rem !important; } + + .mb-xl-n1, + .my-xl-n1 { + margin-bottom: -0.25rem !important; } + + .ml-xl-n1, + .mx-xl-n1 { + margin-left: -0.25rem !important; } + + .m-xl-n2 { + margin: -0.5rem !important; } + + .mt-xl-n2, + .my-xl-n2 { + margin-top: -0.5rem !important; } + + .mr-xl-n2, + .mx-xl-n2 { + margin-right: -0.5rem !important; } + + .mb-xl-n2, + .my-xl-n2 { + margin-bottom: -0.5rem !important; } + + .ml-xl-n2, + .mx-xl-n2 { + margin-left: -0.5rem !important; } + + .m-xl-n3 { + margin: -1rem !important; } + + .mt-xl-n3, + .my-xl-n3 { + margin-top: -1rem !important; } + + .mr-xl-n3, + .mx-xl-n3 { + margin-right: -1rem !important; } + + .mb-xl-n3, + .my-xl-n3 { + margin-bottom: -1rem !important; } + + .ml-xl-n3, + .mx-xl-n3 { + margin-left: -1rem !important; } + + .m-xl-n4 { + margin: -1.5rem !important; } + + .mt-xl-n4, + .my-xl-n4 { + margin-top: -1.5rem !important; } + + .mr-xl-n4, + .mx-xl-n4 { + margin-right: -1.5rem !important; } + + .mb-xl-n4, + .my-xl-n4 { + margin-bottom: -1.5rem !important; } + + .ml-xl-n4, + .mx-xl-n4 { + margin-left: -1.5rem !important; } + + .m-xl-n5 { + margin: -3rem !important; } + + .mt-xl-n5, + .my-xl-n5 { + margin-top: -3rem !important; } + + .mr-xl-n5, + .mx-xl-n5 { + margin-right: -3rem !important; } + + .mb-xl-n5, + .my-xl-n5 { + margin-bottom: -3rem !important; } + + .ml-xl-n5, + .mx-xl-n5 { + margin-left: -3rem !important; } + + .m-xl-auto { + margin: auto !important; } + + .mt-xl-auto, + .my-xl-auto { + margin-top: auto !important; } + + .mr-xl-auto, + .mx-xl-auto { + margin-right: auto !important; } + + .mb-xl-auto, + .my-xl-auto { + margin-bottom: auto !important; } + + .ml-xl-auto, + .mx-xl-auto { + margin-left: auto !important; } } +.stretched-link::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + pointer-events: auto; + content: ""; + background-color: rgba(0, 0, 0, 0); } + +.text-monospace { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important; } + +.text-justify { + text-align: justify !important; } + +.text-wrap { + white-space: normal !important; } + +.text-nowrap { + white-space: nowrap !important; } + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } + +.text-left { + text-align: left !important; } + +.text-right { + text-align: right !important; } + +.text-center { + text-align: center !important; } + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important; } + + .text-sm-right { + text-align: right !important; } + + .text-sm-center { + text-align: center !important; } } +@media (min-width: 768px) { + .text-md-left { + text-align: left !important; } + + .text-md-right { + text-align: right !important; } + + .text-md-center { + text-align: center !important; } } +@media (min-width: 992px) { + .text-lg-left { + text-align: left !important; } + + .text-lg-right { + text-align: right !important; } + + .text-lg-center { + text-align: center !important; } } +@media (min-width: 1200px) { + .text-xl-left { + text-align: left !important; } + + .text-xl-right { + text-align: right !important; } + + .text-xl-center { + text-align: center !important; } } +.text-lowercase { + text-transform: lowercase !important; } + +.text-uppercase { + text-transform: uppercase !important; } + +.text-capitalize { + text-transform: capitalize !important; } + +.font-weight-light { + font-weight: 300 !important; } + +.font-weight-lighter { + font-weight: lighter !important; } + +.font-weight-normal { + font-weight: 400 !important; } + +.font-weight-bold { + font-weight: 700 !important; } + +.font-weight-bolder { + font-weight: bolder !important; } + +.font-italic { + font-style: italic !important; } + +.text-white { + color: #fff !important; } + +.text-primary { + color: #007bff !important; } + +a.text-primary:hover, a.text-primary:focus { + color: #0056b3 !important; } + +.text-secondary { + color: #6c757d !important; } + +a.text-secondary:hover, a.text-secondary:focus { + color: #494f54 !important; } + +.text-success { + color: #28a745 !important; } + +a.text-success:hover, a.text-success:focus { + color: #19692c !important; } + +.text-info { + color: #17a2b8 !important; } + +a.text-info:hover, a.text-info:focus { + color: #0f6674 !important; } + +.text-warning { + color: #ffc107 !important; } + +a.text-warning:hover, a.text-warning:focus { + color: #ba8b00 !important; } + +.text-danger { + color: #dc3545 !important; } + +a.text-danger:hover, a.text-danger:focus { + color: #a71d2a !important; } + +.text-light { + color: #f8f9fa !important; } + +a.text-light:hover, a.text-light:focus { + color: #cbd3da !important; } + +.text-dark { + color: #343a40 !important; } + +a.text-dark:hover, a.text-dark:focus { + color: #121416 !important; } + +.text-body { + color: #212529 !important; } + +.text-muted { + color: #6c757d !important; } + +.text-black-50 { + color: rgba(0, 0, 0, 0.5) !important; } + +.text-white-50 { + color: rgba(255, 255, 255, 0.5) !important; } + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; } + +.text-decoration-none { + text-decoration: none !important; } + +.text-break { + word-wrap: break-word !important; } + +.text-reset { + color: inherit !important; } + +.visible { + visibility: visible !important; } + +.invisible { + visibility: hidden !important; } + +@media print { + *, + *::before, + *::after { + text-shadow: none !important; + box-shadow: none !important; } + + a:not(.btn) { + text-decoration: underline; } + + abbr[title]::after { + content: " (" attr(title) ")"; } + + pre { + white-space: pre-wrap !important; } + + pre, + blockquote { + border: 1px solid #adb5bd; + page-break-inside: avoid; } + + thead { + display: table-header-group; } + + tr, + img { + page-break-inside: avoid; } + + p, + h2, + h3 { + orphans: 3; + widows: 3; } + + h2, + h3 { + page-break-after: avoid; } + + @page { + size: a3; } + body { + min-width: 992px !important; } + + .container { + min-width: 992px !important; } + + .navbar { + display: none; } + + .badge { + border: 1px solid #000; } + + .table { + border-collapse: collapse !important; } + .table td, + .table th { + background-color: #fff !important; } + + .table-bordered th, + .table-bordered td { + border: 1px solid #dee2e6 !important; } + + .table-dark { + color: inherit; } + .table-dark th, + .table-dark td, + .table-dark thead th, + .table-dark tbody + tbody { + border-color: #dee2e6; } + + .table .thead-dark th { + color: inherit; + border-color: #dee2e6; } } +/*! + * Bootstrap Grid v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors + * Copyright 2011-2020 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +html { + box-sizing: border-box; + -ms-overflow-style: scrollbar; } + +*, +*::before, +*::after { + box-sizing: inherit; } + +.container { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; } + @media (min-width: 576px) { + .container { + max-width: 540px; } } + @media (min-width: 768px) { + .container { + max-width: 720px; } } + @media (min-width: 992px) { + .container { + max-width: 960px; } } + @media (min-width: 1200px) { + .container { + max-width: 1140px; } } + +.container-fluid, .container-sm, .container-md, .container-lg, .container-xl { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; } + +@media (min-width: 576px) { + .container, .container-sm { + max-width: 540px; } } +@media (min-width: 768px) { + .container, .container-sm, .container-md { + max-width: 720px; } } +@media (min-width: 992px) { + .container, .container-sm, .container-md, .container-lg { + max-width: 960px; } } +@media (min-width: 1200px) { + .container, .container-sm, .container-md, .container-lg, .container-xl { + max-width: 1140px; } } +.row { + display: flex; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px; } + +.no-gutters { + margin-right: 0; + margin-left: 0; } + .no-gutters > .col, + .no-gutters > [class*="col-"] { + padding-right: 0; + padding-left: 0; } + +.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, +.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, +.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, +.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, +.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, +.col-xl-auto { + position: relative; + width: 100%; + padding-right: 15px; + padding-left: 15px; } + +.col { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + +.row-cols-1 > * { + flex: 0 0 100%; + max-width: 100%; } + +.row-cols-2 > * { + flex: 0 0 50%; + max-width: 50%; } + +.row-cols-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + +.row-cols-4 > * { + flex: 0 0 25%; + max-width: 25%; } + +.row-cols-5 > * { + flex: 0 0 20%; + max-width: 20%; } + +.row-cols-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + +.col-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + +.col-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + +.col-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + +.col-3 { + flex: 0 0 25%; + max-width: 25%; } + +.col-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + +.col-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + +.col-6 { + flex: 0 0 50%; + max-width: 50%; } + +.col-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + +.col-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + +.col-9 { + flex: 0 0 75%; + max-width: 75%; } + +.col-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + +.col-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + +.col-12 { + flex: 0 0 100%; + max-width: 100%; } + +.order-first { + order: -1; } + +.order-last { + order: 13; } + +.order-0 { + order: 0; } + +.order-1 { + order: 1; } + +.order-2 { + order: 2; } + +.order-3 { + order: 3; } + +.order-4 { + order: 4; } + +.order-5 { + order: 5; } + +.order-6 { + order: 6; } + +.order-7 { + order: 7; } + +.order-8 { + order: 8; } + +.order-9 { + order: 9; } + +.order-10 { + order: 10; } + +.order-11 { + order: 11; } + +.order-12 { + order: 12; } + +.offset-1 { + margin-left: 8.3333333333%; } + +.offset-2 { + margin-left: 16.6666666667%; } + +.offset-3 { + margin-left: 25%; } + +.offset-4 { + margin-left: 33.3333333333%; } + +.offset-5 { + margin-left: 41.6666666667%; } + +.offset-6 { + margin-left: 50%; } + +.offset-7 { + margin-left: 58.3333333333%; } + +.offset-8 { + margin-left: 66.6666666667%; } + +.offset-9 { + margin-left: 75%; } + +.offset-10 { + margin-left: 83.3333333333%; } + +.offset-11 { + margin-left: 91.6666666667%; } + +@media (min-width: 576px) { + .col-sm { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + + .row-cols-sm-1 > * { + flex: 0 0 100%; + max-width: 100%; } + + .row-cols-sm-2 > * { + flex: 0 0 50%; + max-width: 50%; } + + .row-cols-sm-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .row-cols-sm-4 > * { + flex: 0 0 25%; + max-width: 25%; } + + .row-cols-sm-5 > * { + flex: 0 0 20%; + max-width: 20%; } + + .row-cols-sm-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-sm-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + + .col-sm-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + + .col-sm-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-sm-3 { + flex: 0 0 25%; + max-width: 25%; } + + .col-sm-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .col-sm-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + + .col-sm-6 { + flex: 0 0 50%; + max-width: 50%; } + + .col-sm-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + + .col-sm-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + + .col-sm-9 { + flex: 0 0 75%; + max-width: 75%; } + + .col-sm-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + + .col-sm-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + + .col-sm-12 { + flex: 0 0 100%; + max-width: 100%; } + + .order-sm-first { + order: -1; } + + .order-sm-last { + order: 13; } + + .order-sm-0 { + order: 0; } + + .order-sm-1 { + order: 1; } + + .order-sm-2 { + order: 2; } + + .order-sm-3 { + order: 3; } + + .order-sm-4 { + order: 4; } + + .order-sm-5 { + order: 5; } + + .order-sm-6 { + order: 6; } + + .order-sm-7 { + order: 7; } + + .order-sm-8 { + order: 8; } + + .order-sm-9 { + order: 9; } + + .order-sm-10 { + order: 10; } + + .order-sm-11 { + order: 11; } + + .order-sm-12 { + order: 12; } + + .offset-sm-0 { + margin-left: 0; } + + .offset-sm-1 { + margin-left: 8.3333333333%; } + + .offset-sm-2 { + margin-left: 16.6666666667%; } + + .offset-sm-3 { + margin-left: 25%; } + + .offset-sm-4 { + margin-left: 33.3333333333%; } + + .offset-sm-5 { + margin-left: 41.6666666667%; } + + .offset-sm-6 { + margin-left: 50%; } + + .offset-sm-7 { + margin-left: 58.3333333333%; } + + .offset-sm-8 { + margin-left: 66.6666666667%; } + + .offset-sm-9 { + margin-left: 75%; } + + .offset-sm-10 { + margin-left: 83.3333333333%; } + + .offset-sm-11 { + margin-left: 91.6666666667%; } } +@media (min-width: 768px) { + .col-md { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + + .row-cols-md-1 > * { + flex: 0 0 100%; + max-width: 100%; } + + .row-cols-md-2 > * { + flex: 0 0 50%; + max-width: 50%; } + + .row-cols-md-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .row-cols-md-4 > * { + flex: 0 0 25%; + max-width: 25%; } + + .row-cols-md-5 > * { + flex: 0 0 20%; + max-width: 20%; } + + .row-cols-md-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-md-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + + .col-md-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + + .col-md-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-md-3 { + flex: 0 0 25%; + max-width: 25%; } + + .col-md-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .col-md-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + + .col-md-6 { + flex: 0 0 50%; + max-width: 50%; } + + .col-md-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + + .col-md-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + + .col-md-9 { + flex: 0 0 75%; + max-width: 75%; } + + .col-md-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + + .col-md-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + + .col-md-12 { + flex: 0 0 100%; + max-width: 100%; } + + .order-md-first { + order: -1; } + + .order-md-last { + order: 13; } + + .order-md-0 { + order: 0; } + + .order-md-1 { + order: 1; } + + .order-md-2 { + order: 2; } + + .order-md-3 { + order: 3; } + + .order-md-4 { + order: 4; } + + .order-md-5 { + order: 5; } + + .order-md-6 { + order: 6; } + + .order-md-7 { + order: 7; } + + .order-md-8 { + order: 8; } + + .order-md-9 { + order: 9; } + + .order-md-10 { + order: 10; } + + .order-md-11 { + order: 11; } + + .order-md-12 { + order: 12; } + + .offset-md-0 { + margin-left: 0; } + + .offset-md-1 { + margin-left: 8.3333333333%; } + + .offset-md-2 { + margin-left: 16.6666666667%; } + + .offset-md-3 { + margin-left: 25%; } + + .offset-md-4 { + margin-left: 33.3333333333%; } + + .offset-md-5 { + margin-left: 41.6666666667%; } + + .offset-md-6 { + margin-left: 50%; } + + .offset-md-7 { + margin-left: 58.3333333333%; } + + .offset-md-8 { + margin-left: 66.6666666667%; } + + .offset-md-9 { + margin-left: 75%; } + + .offset-md-10 { + margin-left: 83.3333333333%; } + + .offset-md-11 { + margin-left: 91.6666666667%; } } +@media (min-width: 992px) { + .col-lg { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + + .row-cols-lg-1 > * { + flex: 0 0 100%; + max-width: 100%; } + + .row-cols-lg-2 > * { + flex: 0 0 50%; + max-width: 50%; } + + .row-cols-lg-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .row-cols-lg-4 > * { + flex: 0 0 25%; + max-width: 25%; } + + .row-cols-lg-5 > * { + flex: 0 0 20%; + max-width: 20%; } + + .row-cols-lg-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-lg-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + + .col-lg-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + + .col-lg-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-lg-3 { + flex: 0 0 25%; + max-width: 25%; } + + .col-lg-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .col-lg-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + + .col-lg-6 { + flex: 0 0 50%; + max-width: 50%; } + + .col-lg-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + + .col-lg-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + + .col-lg-9 { + flex: 0 0 75%; + max-width: 75%; } + + .col-lg-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + + .col-lg-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + + .col-lg-12 { + flex: 0 0 100%; + max-width: 100%; } + + .order-lg-first { + order: -1; } + + .order-lg-last { + order: 13; } + + .order-lg-0 { + order: 0; } + + .order-lg-1 { + order: 1; } + + .order-lg-2 { + order: 2; } + + .order-lg-3 { + order: 3; } + + .order-lg-4 { + order: 4; } + + .order-lg-5 { + order: 5; } + + .order-lg-6 { + order: 6; } + + .order-lg-7 { + order: 7; } + + .order-lg-8 { + order: 8; } + + .order-lg-9 { + order: 9; } + + .order-lg-10 { + order: 10; } + + .order-lg-11 { + order: 11; } + + .order-lg-12 { + order: 12; } + + .offset-lg-0 { + margin-left: 0; } + + .offset-lg-1 { + margin-left: 8.3333333333%; } + + .offset-lg-2 { + margin-left: 16.6666666667%; } + + .offset-lg-3 { + margin-left: 25%; } + + .offset-lg-4 { + margin-left: 33.3333333333%; } + + .offset-lg-5 { + margin-left: 41.6666666667%; } + + .offset-lg-6 { + margin-left: 50%; } + + .offset-lg-7 { + margin-left: 58.3333333333%; } + + .offset-lg-8 { + margin-left: 66.6666666667%; } + + .offset-lg-9 { + margin-left: 75%; } + + .offset-lg-10 { + margin-left: 83.3333333333%; } + + .offset-lg-11 { + margin-left: 91.6666666667%; } } +@media (min-width: 1200px) { + .col-xl { + flex-basis: 0; + flex-grow: 1; + min-width: 0; + max-width: 100%; } + + .row-cols-xl-1 > * { + flex: 0 0 100%; + max-width: 100%; } + + .row-cols-xl-2 > * { + flex: 0 0 50%; + max-width: 50%; } + + .row-cols-xl-3 > * { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .row-cols-xl-4 > * { + flex: 0 0 25%; + max-width: 25%; } + + .row-cols-xl-5 > * { + flex: 0 0 20%; + max-width: 20%; } + + .row-cols-xl-6 > * { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-xl-auto { + flex: 0 0 auto; + width: auto; + max-width: 100%; } + + .col-xl-1 { + flex: 0 0 8.3333333333%; + max-width: 8.3333333333%; } + + .col-xl-2 { + flex: 0 0 16.6666666667%; + max-width: 16.6666666667%; } + + .col-xl-3 { + flex: 0 0 25%; + max-width: 25%; } + + .col-xl-4 { + flex: 0 0 33.3333333333%; + max-width: 33.3333333333%; } + + .col-xl-5 { + flex: 0 0 41.6666666667%; + max-width: 41.6666666667%; } + + .col-xl-6 { + flex: 0 0 50%; + max-width: 50%; } + + .col-xl-7 { + flex: 0 0 58.3333333333%; + max-width: 58.3333333333%; } + + .col-xl-8 { + flex: 0 0 66.6666666667%; + max-width: 66.6666666667%; } + + .col-xl-9 { + flex: 0 0 75%; + max-width: 75%; } + + .col-xl-10 { + flex: 0 0 83.3333333333%; + max-width: 83.3333333333%; } + + .col-xl-11 { + flex: 0 0 91.6666666667%; + max-width: 91.6666666667%; } + + .col-xl-12 { + flex: 0 0 100%; + max-width: 100%; } + + .order-xl-first { + order: -1; } + + .order-xl-last { + order: 13; } + + .order-xl-0 { + order: 0; } + + .order-xl-1 { + order: 1; } + + .order-xl-2 { + order: 2; } + + .order-xl-3 { + order: 3; } + + .order-xl-4 { + order: 4; } + + .order-xl-5 { + order: 5; } + + .order-xl-6 { + order: 6; } + + .order-xl-7 { + order: 7; } + + .order-xl-8 { + order: 8; } + + .order-xl-9 { + order: 9; } + + .order-xl-10 { + order: 10; } + + .order-xl-11 { + order: 11; } + + .order-xl-12 { + order: 12; } + + .offset-xl-0 { + margin-left: 0; } + + .offset-xl-1 { + margin-left: 8.3333333333%; } + + .offset-xl-2 { + margin-left: 16.6666666667%; } + + .offset-xl-3 { + margin-left: 25%; } + + .offset-xl-4 { + margin-left: 33.3333333333%; } + + .offset-xl-5 { + margin-left: 41.6666666667%; } + + .offset-xl-6 { + margin-left: 50%; } + + .offset-xl-7 { + margin-left: 58.3333333333%; } + + .offset-xl-8 { + margin-left: 66.6666666667%; } + + .offset-xl-9 { + margin-left: 75%; } + + .offset-xl-10 { + margin-left: 83.3333333333%; } + + .offset-xl-11 { + margin-left: 91.6666666667%; } } +.d-none { + display: none !important; } + +.d-inline { + display: inline !important; } + +.d-inline-block { + display: inline-block !important; } + +.d-block { + display: block !important; } + +.d-table { + display: table !important; } + +.d-table-row { + display: table-row !important; } + +.d-table-cell { + display: table-cell !important; } + +.d-flex { + display: flex !important; } + +.d-inline-flex { + display: inline-flex !important; } + +@media (min-width: 576px) { + .d-sm-none { + display: none !important; } + + .d-sm-inline { + display: inline !important; } + + .d-sm-inline-block { + display: inline-block !important; } + + .d-sm-block { + display: block !important; } + + .d-sm-table { + display: table !important; } + + .d-sm-table-row { + display: table-row !important; } + + .d-sm-table-cell { + display: table-cell !important; } + + .d-sm-flex { + display: flex !important; } + + .d-sm-inline-flex { + display: inline-flex !important; } } +@media (min-width: 768px) { + .d-md-none { + display: none !important; } + + .d-md-inline { + display: inline !important; } + + .d-md-inline-block { + display: inline-block !important; } + + .d-md-block { + display: block !important; } + + .d-md-table { + display: table !important; } + + .d-md-table-row { + display: table-row !important; } + + .d-md-table-cell { + display: table-cell !important; } + + .d-md-flex { + display: flex !important; } + + .d-md-inline-flex { + display: inline-flex !important; } } +@media (min-width: 992px) { + .d-lg-none { + display: none !important; } + + .d-lg-inline { + display: inline !important; } + + .d-lg-inline-block { + display: inline-block !important; } + + .d-lg-block { + display: block !important; } + + .d-lg-table { + display: table !important; } + + .d-lg-table-row { + display: table-row !important; } + + .d-lg-table-cell { + display: table-cell !important; } + + .d-lg-flex { + display: flex !important; } + + .d-lg-inline-flex { + display: inline-flex !important; } } +@media (min-width: 1200px) { + .d-xl-none { + display: none !important; } + + .d-xl-inline { + display: inline !important; } + + .d-xl-inline-block { + display: inline-block !important; } + + .d-xl-block { + display: block !important; } + + .d-xl-table { + display: table !important; } + + .d-xl-table-row { + display: table-row !important; } + + .d-xl-table-cell { + display: table-cell !important; } + + .d-xl-flex { + display: flex !important; } + + .d-xl-inline-flex { + display: inline-flex !important; } } +@media print { + .d-print-none { + display: none !important; } + + .d-print-inline { + display: inline !important; } + + .d-print-inline-block { + display: inline-block !important; } + + .d-print-block { + display: block !important; } + + .d-print-table { + display: table !important; } + + .d-print-table-row { + display: table-row !important; } + + .d-print-table-cell { + display: table-cell !important; } + + .d-print-flex { + display: flex !important; } + + .d-print-inline-flex { + display: inline-flex !important; } } +.flex-row { + flex-direction: row !important; } + +.flex-column { + flex-direction: column !important; } + +.flex-row-reverse { + flex-direction: row-reverse !important; } + +.flex-column-reverse { + flex-direction: column-reverse !important; } + +.flex-wrap { + flex-wrap: wrap !important; } + +.flex-nowrap { + flex-wrap: nowrap !important; } + +.flex-wrap-reverse { + flex-wrap: wrap-reverse !important; } + +.flex-fill { + flex: 1 1 auto !important; } + +.flex-grow-0 { + flex-grow: 0 !important; } + +.flex-grow-1 { + flex-grow: 1 !important; } + +.flex-shrink-0 { + flex-shrink: 0 !important; } + +.flex-shrink-1 { + flex-shrink: 1 !important; } + +.justify-content-start { + justify-content: flex-start !important; } + +.justify-content-end { + justify-content: flex-end !important; } + +.justify-content-center { + justify-content: center !important; } + +.justify-content-between { + justify-content: space-between !important; } + +.justify-content-around { + justify-content: space-around !important; } + +.align-items-start { + align-items: flex-start !important; } + +.align-items-end { + align-items: flex-end !important; } + +.align-items-center { + align-items: center !important; } + +.align-items-baseline { + align-items: baseline !important; } + +.align-items-stretch { + align-items: stretch !important; } + +.align-content-start { + align-content: flex-start !important; } + +.align-content-end { + align-content: flex-end !important; } + +.align-content-center { + align-content: center !important; } + +.align-content-between { + align-content: space-between !important; } + +.align-content-around { + align-content: space-around !important; } + +.align-content-stretch { + align-content: stretch !important; } + +.align-self-auto { + align-self: auto !important; } + +.align-self-start { + align-self: flex-start !important; } + +.align-self-end { + align-self: flex-end !important; } + +.align-self-center { + align-self: center !important; } + +.align-self-baseline { + align-self: baseline !important; } + +.align-self-stretch { + align-self: stretch !important; } + +@media (min-width: 576px) { + .flex-sm-row { + flex-direction: row !important; } + + .flex-sm-column { + flex-direction: column !important; } + + .flex-sm-row-reverse { + flex-direction: row-reverse !important; } + + .flex-sm-column-reverse { + flex-direction: column-reverse !important; } + + .flex-sm-wrap { + flex-wrap: wrap !important; } + + .flex-sm-nowrap { + flex-wrap: nowrap !important; } + + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; } + + .flex-sm-fill { + flex: 1 1 auto !important; } + + .flex-sm-grow-0 { + flex-grow: 0 !important; } + + .flex-sm-grow-1 { + flex-grow: 1 !important; } + + .flex-sm-shrink-0 { + flex-shrink: 0 !important; } + + .flex-sm-shrink-1 { + flex-shrink: 1 !important; } + + .justify-content-sm-start { + justify-content: flex-start !important; } + + .justify-content-sm-end { + justify-content: flex-end !important; } + + .justify-content-sm-center { + justify-content: center !important; } + + .justify-content-sm-between { + justify-content: space-between !important; } + + .justify-content-sm-around { + justify-content: space-around !important; } + + .align-items-sm-start { + align-items: flex-start !important; } + + .align-items-sm-end { + align-items: flex-end !important; } + + .align-items-sm-center { + align-items: center !important; } + + .align-items-sm-baseline { + align-items: baseline !important; } + + .align-items-sm-stretch { + align-items: stretch !important; } + + .align-content-sm-start { + align-content: flex-start !important; } + + .align-content-sm-end { + align-content: flex-end !important; } + + .align-content-sm-center { + align-content: center !important; } + + .align-content-sm-between { + align-content: space-between !important; } + + .align-content-sm-around { + align-content: space-around !important; } + + .align-content-sm-stretch { + align-content: stretch !important; } + + .align-self-sm-auto { + align-self: auto !important; } + + .align-self-sm-start { + align-self: flex-start !important; } + + .align-self-sm-end { + align-self: flex-end !important; } + + .align-self-sm-center { + align-self: center !important; } + + .align-self-sm-baseline { + align-self: baseline !important; } + + .align-self-sm-stretch { + align-self: stretch !important; } } +@media (min-width: 768px) { + .flex-md-row { + flex-direction: row !important; } + + .flex-md-column { + flex-direction: column !important; } + + .flex-md-row-reverse { + flex-direction: row-reverse !important; } + + .flex-md-column-reverse { + flex-direction: column-reverse !important; } + + .flex-md-wrap { + flex-wrap: wrap !important; } + + .flex-md-nowrap { + flex-wrap: nowrap !important; } + + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; } + + .flex-md-fill { + flex: 1 1 auto !important; } + + .flex-md-grow-0 { + flex-grow: 0 !important; } + + .flex-md-grow-1 { + flex-grow: 1 !important; } + + .flex-md-shrink-0 { + flex-shrink: 0 !important; } + + .flex-md-shrink-1 { + flex-shrink: 1 !important; } + + .justify-content-md-start { + justify-content: flex-start !important; } + + .justify-content-md-end { + justify-content: flex-end !important; } + + .justify-content-md-center { + justify-content: center !important; } + + .justify-content-md-between { + justify-content: space-between !important; } + + .justify-content-md-around { + justify-content: space-around !important; } + + .align-items-md-start { + align-items: flex-start !important; } + + .align-items-md-end { + align-items: flex-end !important; } + + .align-items-md-center { + align-items: center !important; } + + .align-items-md-baseline { + align-items: baseline !important; } + + .align-items-md-stretch { + align-items: stretch !important; } + + .align-content-md-start { + align-content: flex-start !important; } + + .align-content-md-end { + align-content: flex-end !important; } + + .align-content-md-center { + align-content: center !important; } + + .align-content-md-between { + align-content: space-between !important; } + + .align-content-md-around { + align-content: space-around !important; } + + .align-content-md-stretch { + align-content: stretch !important; } + + .align-self-md-auto { + align-self: auto !important; } + + .align-self-md-start { + align-self: flex-start !important; } + + .align-self-md-end { + align-self: flex-end !important; } + + .align-self-md-center { + align-self: center !important; } + + .align-self-md-baseline { + align-self: baseline !important; } + + .align-self-md-stretch { + align-self: stretch !important; } } +@media (min-width: 992px) { + .flex-lg-row { + flex-direction: row !important; } + + .flex-lg-column { + flex-direction: column !important; } + + .flex-lg-row-reverse { + flex-direction: row-reverse !important; } + + .flex-lg-column-reverse { + flex-direction: column-reverse !important; } + + .flex-lg-wrap { + flex-wrap: wrap !important; } + + .flex-lg-nowrap { + flex-wrap: nowrap !important; } + + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; } + + .flex-lg-fill { + flex: 1 1 auto !important; } + + .flex-lg-grow-0 { + flex-grow: 0 !important; } + + .flex-lg-grow-1 { + flex-grow: 1 !important; } + + .flex-lg-shrink-0 { + flex-shrink: 0 !important; } + + .flex-lg-shrink-1 { + flex-shrink: 1 !important; } + + .justify-content-lg-start { + justify-content: flex-start !important; } + + .justify-content-lg-end { + justify-content: flex-end !important; } + + .justify-content-lg-center { + justify-content: center !important; } + + .justify-content-lg-between { + justify-content: space-between !important; } + + .justify-content-lg-around { + justify-content: space-around !important; } + + .align-items-lg-start { + align-items: flex-start !important; } + + .align-items-lg-end { + align-items: flex-end !important; } + + .align-items-lg-center { + align-items: center !important; } + + .align-items-lg-baseline { + align-items: baseline !important; } + + .align-items-lg-stretch { + align-items: stretch !important; } + + .align-content-lg-start { + align-content: flex-start !important; } + + .align-content-lg-end { + align-content: flex-end !important; } + + .align-content-lg-center { + align-content: center !important; } + + .align-content-lg-between { + align-content: space-between !important; } + + .align-content-lg-around { + align-content: space-around !important; } + + .align-content-lg-stretch { + align-content: stretch !important; } + + .align-self-lg-auto { + align-self: auto !important; } + + .align-self-lg-start { + align-self: flex-start !important; } + + .align-self-lg-end { + align-self: flex-end !important; } + + .align-self-lg-center { + align-self: center !important; } + + .align-self-lg-baseline { + align-self: baseline !important; } + + .align-self-lg-stretch { + align-self: stretch !important; } } +@media (min-width: 1200px) { + .flex-xl-row { + flex-direction: row !important; } + + .flex-xl-column { + flex-direction: column !important; } + + .flex-xl-row-reverse { + flex-direction: row-reverse !important; } + + .flex-xl-column-reverse { + flex-direction: column-reverse !important; } + + .flex-xl-wrap { + flex-wrap: wrap !important; } + + .flex-xl-nowrap { + flex-wrap: nowrap !important; } + + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; } + + .flex-xl-fill { + flex: 1 1 auto !important; } + + .flex-xl-grow-0 { + flex-grow: 0 !important; } + + .flex-xl-grow-1 { + flex-grow: 1 !important; } + + .flex-xl-shrink-0 { + flex-shrink: 0 !important; } + + .flex-xl-shrink-1 { + flex-shrink: 1 !important; } + + .justify-content-xl-start { + justify-content: flex-start !important; } + + .justify-content-xl-end { + justify-content: flex-end !important; } + + .justify-content-xl-center { + justify-content: center !important; } + + .justify-content-xl-between { + justify-content: space-between !important; } + + .justify-content-xl-around { + justify-content: space-around !important; } + + .align-items-xl-start { + align-items: flex-start !important; } + + .align-items-xl-end { + align-items: flex-end !important; } + + .align-items-xl-center { + align-items: center !important; } + + .align-items-xl-baseline { + align-items: baseline !important; } + + .align-items-xl-stretch { + align-items: stretch !important; } + + .align-content-xl-start { + align-content: flex-start !important; } + + .align-content-xl-end { + align-content: flex-end !important; } + + .align-content-xl-center { + align-content: center !important; } + + .align-content-xl-between { + align-content: space-between !important; } + + .align-content-xl-around { + align-content: space-around !important; } + + .align-content-xl-stretch { + align-content: stretch !important; } + + .align-self-xl-auto { + align-self: auto !important; } + + .align-self-xl-start { + align-self: flex-start !important; } + + .align-self-xl-end { + align-self: flex-end !important; } + + .align-self-xl-center { + align-self: center !important; } + + .align-self-xl-baseline { + align-self: baseline !important; } + + .align-self-xl-stretch { + align-self: stretch !important; } } +.m-0 { + margin: 0 !important; } + +.mt-0, +.my-0 { + margin-top: 0 !important; } + +.mr-0, +.mx-0 { + margin-right: 0 !important; } + +.mb-0, +.my-0 { + margin-bottom: 0 !important; } + +.ml-0, +.mx-0 { + margin-left: 0 !important; } + +.m-1 { + margin: 0.25rem !important; } + +.mt-1, +.my-1 { + margin-top: 0.25rem !important; } + +.mr-1, +.mx-1 { + margin-right: 0.25rem !important; } + +.mb-1, +.my-1 { + margin-bottom: 0.25rem !important; } + +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; } + +.m-2 { + margin: 0.5rem !important; } + +.mt-2, +.my-2 { + margin-top: 0.5rem !important; } + +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; } + +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; } + +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; } + +.m-3 { + margin: 1rem !important; } + +.mt-3, +.my-3 { + margin-top: 1rem !important; } + +.mr-3, +.mx-3 { + margin-right: 1rem !important; } + +.mb-3, +.my-3 { + margin-bottom: 1rem !important; } + +.ml-3, +.mx-3 { + margin-left: 1rem !important; } + +.m-4 { + margin: 1.5rem !important; } + +.mt-4, +.my-4 { + margin-top: 1.5rem !important; } + +.mr-4, +.mx-4 { + margin-right: 1.5rem !important; } + +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; } + +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; } + +.m-5 { + margin: 3rem !important; } + +.mt-5, +.my-5 { + margin-top: 3rem !important; } + +.mr-5, +.mx-5 { + margin-right: 3rem !important; } + +.mb-5, +.my-5 { + margin-bottom: 3rem !important; } + +.ml-5, +.mx-5 { + margin-left: 3rem !important; } + +.p-0 { + padding: 0 !important; } + +.pt-0, +.py-0 { + padding-top: 0 !important; } + +.pr-0, +.px-0 { + padding-right: 0 !important; } + +.pb-0, +.py-0 { + padding-bottom: 0 !important; } + +.pl-0, +.px-0 { + padding-left: 0 !important; } + +.p-1 { + padding: 0.25rem !important; } + +.pt-1, +.py-1 { + padding-top: 0.25rem !important; } + +.pr-1, +.px-1 { + padding-right: 0.25rem !important; } + +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; } + +.pl-1, +.px-1 { + padding-left: 0.25rem !important; } + +.p-2 { + padding: 0.5rem !important; } + +.pt-2, +.py-2 { + padding-top: 0.5rem !important; } + +.pr-2, +.px-2 { + padding-right: 0.5rem !important; } + +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; } + +.pl-2, +.px-2 { + padding-left: 0.5rem !important; } + +.p-3 { + padding: 1rem !important; } + +.pt-3, +.py-3 { + padding-top: 1rem !important; } + +.pr-3, +.px-3 { + padding-right: 1rem !important; } + +.pb-3, +.py-3 { + padding-bottom: 1rem !important; } + +.pl-3, +.px-3 { + padding-left: 1rem !important; } + +.p-4 { + padding: 1.5rem !important; } + +.pt-4, +.py-4 { + padding-top: 1.5rem !important; } + +.pr-4, +.px-4 { + padding-right: 1.5rem !important; } + +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; } + +.pl-4, +.px-4 { + padding-left: 1.5rem !important; } + +.p-5 { + padding: 3rem !important; } + +.pt-5, +.py-5 { + padding-top: 3rem !important; } + +.pr-5, +.px-5 { + padding-right: 3rem !important; } + +.pb-5, +.py-5 { + padding-bottom: 3rem !important; } + +.pl-5, +.px-5 { + padding-left: 3rem !important; } + +.m-n1 { + margin: -0.25rem !important; } + +.mt-n1, +.my-n1 { + margin-top: -0.25rem !important; } + +.mr-n1, +.mx-n1 { + margin-right: -0.25rem !important; } + +.mb-n1, +.my-n1 { + margin-bottom: -0.25rem !important; } + +.ml-n1, +.mx-n1 { + margin-left: -0.25rem !important; } + +.m-n2 { + margin: -0.5rem !important; } + +.mt-n2, +.my-n2 { + margin-top: -0.5rem !important; } + +.mr-n2, +.mx-n2 { + margin-right: -0.5rem !important; } + +.mb-n2, +.my-n2 { + margin-bottom: -0.5rem !important; } + +.ml-n2, +.mx-n2 { + margin-left: -0.5rem !important; } + +.m-n3 { + margin: -1rem !important; } + +.mt-n3, +.my-n3 { + margin-top: -1rem !important; } + +.mr-n3, +.mx-n3 { + margin-right: -1rem !important; } + +.mb-n3, +.my-n3 { + margin-bottom: -1rem !important; } + +.ml-n3, +.mx-n3 { + margin-left: -1rem !important; } + +.m-n4 { + margin: -1.5rem !important; } + +.mt-n4, +.my-n4 { + margin-top: -1.5rem !important; } + +.mr-n4, +.mx-n4 { + margin-right: -1.5rem !important; } + +.mb-n4, +.my-n4 { + margin-bottom: -1.5rem !important; } + +.ml-n4, +.mx-n4 { + margin-left: -1.5rem !important; } + +.m-n5 { + margin: -3rem !important; } + +.mt-n5, +.my-n5 { + margin-top: -3rem !important; } + +.mr-n5, +.mx-n5 { + margin-right: -3rem !important; } + +.mb-n5, +.my-n5 { + margin-bottom: -3rem !important; } + +.ml-n5, +.mx-n5 { + margin-left: -3rem !important; } + +.m-auto { + margin: auto !important; } + +.mt-auto, +.my-auto { + margin-top: auto !important; } + +.mr-auto, +.mx-auto { + margin-right: auto !important; } + +.mb-auto, +.my-auto { + margin-bottom: auto !important; } + +.ml-auto, +.mx-auto { + margin-left: auto !important; } + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important; } + + .mt-sm-0, + .my-sm-0 { + margin-top: 0 !important; } + + .mr-sm-0, + .mx-sm-0 { + margin-right: 0 !important; } + + .mb-sm-0, + .my-sm-0 { + margin-bottom: 0 !important; } + + .ml-sm-0, + .mx-sm-0 { + margin-left: 0 !important; } + + .m-sm-1 { + margin: 0.25rem !important; } + + .mt-sm-1, + .my-sm-1 { + margin-top: 0.25rem !important; } + + .mr-sm-1, + .mx-sm-1 { + margin-right: 0.25rem !important; } + + .mb-sm-1, + .my-sm-1 { + margin-bottom: 0.25rem !important; } + + .ml-sm-1, + .mx-sm-1 { + margin-left: 0.25rem !important; } + + .m-sm-2 { + margin: 0.5rem !important; } + + .mt-sm-2, + .my-sm-2 { + margin-top: 0.5rem !important; } + + .mr-sm-2, + .mx-sm-2 { + margin-right: 0.5rem !important; } + + .mb-sm-2, + .my-sm-2 { + margin-bottom: 0.5rem !important; } + + .ml-sm-2, + .mx-sm-2 { + margin-left: 0.5rem !important; } + + .m-sm-3 { + margin: 1rem !important; } + + .mt-sm-3, + .my-sm-3 { + margin-top: 1rem !important; } + + .mr-sm-3, + .mx-sm-3 { + margin-right: 1rem !important; } + + .mb-sm-3, + .my-sm-3 { + margin-bottom: 1rem !important; } + + .ml-sm-3, + .mx-sm-3 { + margin-left: 1rem !important; } + + .m-sm-4 { + margin: 1.5rem !important; } + + .mt-sm-4, + .my-sm-4 { + margin-top: 1.5rem !important; } + + .mr-sm-4, + .mx-sm-4 { + margin-right: 1.5rem !important; } + + .mb-sm-4, + .my-sm-4 { + margin-bottom: 1.5rem !important; } + + .ml-sm-4, + .mx-sm-4 { + margin-left: 1.5rem !important; } + + .m-sm-5 { + margin: 3rem !important; } + + .mt-sm-5, + .my-sm-5 { + margin-top: 3rem !important; } + + .mr-sm-5, + .mx-sm-5 { + margin-right: 3rem !important; } + + .mb-sm-5, + .my-sm-5 { + margin-bottom: 3rem !important; } + + .ml-sm-5, + .mx-sm-5 { + margin-left: 3rem !important; } + + .p-sm-0 { + padding: 0 !important; } + + .pt-sm-0, + .py-sm-0 { + padding-top: 0 !important; } + + .pr-sm-0, + .px-sm-0 { + padding-right: 0 !important; } + + .pb-sm-0, + .py-sm-0 { + padding-bottom: 0 !important; } + + .pl-sm-0, + .px-sm-0 { + padding-left: 0 !important; } + + .p-sm-1 { + padding: 0.25rem !important; } + + .pt-sm-1, + .py-sm-1 { + padding-top: 0.25rem !important; } + + .pr-sm-1, + .px-sm-1 { + padding-right: 0.25rem !important; } + + .pb-sm-1, + .py-sm-1 { + padding-bottom: 0.25rem !important; } + + .pl-sm-1, + .px-sm-1 { + padding-left: 0.25rem !important; } + + .p-sm-2 { + padding: 0.5rem !important; } + + .pt-sm-2, + .py-sm-2 { + padding-top: 0.5rem !important; } + + .pr-sm-2, + .px-sm-2 { + padding-right: 0.5rem !important; } + + .pb-sm-2, + .py-sm-2 { + padding-bottom: 0.5rem !important; } + + .pl-sm-2, + .px-sm-2 { + padding-left: 0.5rem !important; } + + .p-sm-3 { + padding: 1rem !important; } + + .pt-sm-3, + .py-sm-3 { + padding-top: 1rem !important; } + + .pr-sm-3, + .px-sm-3 { + padding-right: 1rem !important; } + + .pb-sm-3, + .py-sm-3 { + padding-bottom: 1rem !important; } + + .pl-sm-3, + .px-sm-3 { + padding-left: 1rem !important; } + + .p-sm-4 { + padding: 1.5rem !important; } + + .pt-sm-4, + .py-sm-4 { + padding-top: 1.5rem !important; } + + .pr-sm-4, + .px-sm-4 { + padding-right: 1.5rem !important; } + + .pb-sm-4, + .py-sm-4 { + padding-bottom: 1.5rem !important; } + + .pl-sm-4, + .px-sm-4 { + padding-left: 1.5rem !important; } + + .p-sm-5 { + padding: 3rem !important; } + + .pt-sm-5, + .py-sm-5 { + padding-top: 3rem !important; } + + .pr-sm-5, + .px-sm-5 { + padding-right: 3rem !important; } + + .pb-sm-5, + .py-sm-5 { + padding-bottom: 3rem !important; } + + .pl-sm-5, + .px-sm-5 { + padding-left: 3rem !important; } + + .m-sm-n1 { + margin: -0.25rem !important; } + + .mt-sm-n1, + .my-sm-n1 { + margin-top: -0.25rem !important; } + + .mr-sm-n1, + .mx-sm-n1 { + margin-right: -0.25rem !important; } + + .mb-sm-n1, + .my-sm-n1 { + margin-bottom: -0.25rem !important; } + + .ml-sm-n1, + .mx-sm-n1 { + margin-left: -0.25rem !important; } + + .m-sm-n2 { + margin: -0.5rem !important; } + + .mt-sm-n2, + .my-sm-n2 { + margin-top: -0.5rem !important; } + + .mr-sm-n2, + .mx-sm-n2 { + margin-right: -0.5rem !important; } + + .mb-sm-n2, + .my-sm-n2 { + margin-bottom: -0.5rem !important; } + + .ml-sm-n2, + .mx-sm-n2 { + margin-left: -0.5rem !important; } + + .m-sm-n3 { + margin: -1rem !important; } + + .mt-sm-n3, + .my-sm-n3 { + margin-top: -1rem !important; } + + .mr-sm-n3, + .mx-sm-n3 { + margin-right: -1rem !important; } + + .mb-sm-n3, + .my-sm-n3 { + margin-bottom: -1rem !important; } + + .ml-sm-n3, + .mx-sm-n3 { + margin-left: -1rem !important; } + + .m-sm-n4 { + margin: -1.5rem !important; } + + .mt-sm-n4, + .my-sm-n4 { + margin-top: -1.5rem !important; } + + .mr-sm-n4, + .mx-sm-n4 { + margin-right: -1.5rem !important; } + + .mb-sm-n4, + .my-sm-n4 { + margin-bottom: -1.5rem !important; } + + .ml-sm-n4, + .mx-sm-n4 { + margin-left: -1.5rem !important; } + + .m-sm-n5 { + margin: -3rem !important; } + + .mt-sm-n5, + .my-sm-n5 { + margin-top: -3rem !important; } + + .mr-sm-n5, + .mx-sm-n5 { + margin-right: -3rem !important; } + + .mb-sm-n5, + .my-sm-n5 { + margin-bottom: -3rem !important; } + + .ml-sm-n5, + .mx-sm-n5 { + margin-left: -3rem !important; } + + .m-sm-auto { + margin: auto !important; } + + .mt-sm-auto, + .my-sm-auto { + margin-top: auto !important; } + + .mr-sm-auto, + .mx-sm-auto { + margin-right: auto !important; } + + .mb-sm-auto, + .my-sm-auto { + margin-bottom: auto !important; } + + .ml-sm-auto, + .mx-sm-auto { + margin-left: auto !important; } } +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important; } + + .mt-md-0, + .my-md-0 { + margin-top: 0 !important; } + + .mr-md-0, + .mx-md-0 { + margin-right: 0 !important; } + + .mb-md-0, + .my-md-0 { + margin-bottom: 0 !important; } + + .ml-md-0, + .mx-md-0 { + margin-left: 0 !important; } + + .m-md-1 { + margin: 0.25rem !important; } + + .mt-md-1, + .my-md-1 { + margin-top: 0.25rem !important; } + + .mr-md-1, + .mx-md-1 { + margin-right: 0.25rem !important; } + + .mb-md-1, + .my-md-1 { + margin-bottom: 0.25rem !important; } + + .ml-md-1, + .mx-md-1 { + margin-left: 0.25rem !important; } + + .m-md-2 { + margin: 0.5rem !important; } + + .mt-md-2, + .my-md-2 { + margin-top: 0.5rem !important; } + + .mr-md-2, + .mx-md-2 { + margin-right: 0.5rem !important; } + + .mb-md-2, + .my-md-2 { + margin-bottom: 0.5rem !important; } + + .ml-md-2, + .mx-md-2 { + margin-left: 0.5rem !important; } + + .m-md-3 { + margin: 1rem !important; } + + .mt-md-3, + .my-md-3 { + margin-top: 1rem !important; } + + .mr-md-3, + .mx-md-3 { + margin-right: 1rem !important; } + + .mb-md-3, + .my-md-3 { + margin-bottom: 1rem !important; } + + .ml-md-3, + .mx-md-3 { + margin-left: 1rem !important; } + + .m-md-4 { + margin: 1.5rem !important; } + + .mt-md-4, + .my-md-4 { + margin-top: 1.5rem !important; } + + .mr-md-4, + .mx-md-4 { + margin-right: 1.5rem !important; } + + .mb-md-4, + .my-md-4 { + margin-bottom: 1.5rem !important; } + + .ml-md-4, + .mx-md-4 { + margin-left: 1.5rem !important; } + + .m-md-5 { + margin: 3rem !important; } + + .mt-md-5, + .my-md-5 { + margin-top: 3rem !important; } + + .mr-md-5, + .mx-md-5 { + margin-right: 3rem !important; } + + .mb-md-5, + .my-md-5 { + margin-bottom: 3rem !important; } + + .ml-md-5, + .mx-md-5 { + margin-left: 3rem !important; } + + .p-md-0 { + padding: 0 !important; } + + .pt-md-0, + .py-md-0 { + padding-top: 0 !important; } + + .pr-md-0, + .px-md-0 { + padding-right: 0 !important; } + + .pb-md-0, + .py-md-0 { + padding-bottom: 0 !important; } + + .pl-md-0, + .px-md-0 { + padding-left: 0 !important; } + + .p-md-1 { + padding: 0.25rem !important; } + + .pt-md-1, + .py-md-1 { + padding-top: 0.25rem !important; } + + .pr-md-1, + .px-md-1 { + padding-right: 0.25rem !important; } + + .pb-md-1, + .py-md-1 { + padding-bottom: 0.25rem !important; } + + .pl-md-1, + .px-md-1 { + padding-left: 0.25rem !important; } + + .p-md-2 { + padding: 0.5rem !important; } + + .pt-md-2, + .py-md-2 { + padding-top: 0.5rem !important; } + + .pr-md-2, + .px-md-2 { + padding-right: 0.5rem !important; } + + .pb-md-2, + .py-md-2 { + padding-bottom: 0.5rem !important; } + + .pl-md-2, + .px-md-2 { + padding-left: 0.5rem !important; } + + .p-md-3 { + padding: 1rem !important; } + + .pt-md-3, + .py-md-3 { + padding-top: 1rem !important; } + + .pr-md-3, + .px-md-3 { + padding-right: 1rem !important; } + + .pb-md-3, + .py-md-3 { + padding-bottom: 1rem !important; } + + .pl-md-3, + .px-md-3 { + padding-left: 1rem !important; } + + .p-md-4 { + padding: 1.5rem !important; } + + .pt-md-4, + .py-md-4 { + padding-top: 1.5rem !important; } + + .pr-md-4, + .px-md-4 { + padding-right: 1.5rem !important; } + + .pb-md-4, + .py-md-4 { + padding-bottom: 1.5rem !important; } + + .pl-md-4, + .px-md-4 { + padding-left: 1.5rem !important; } + + .p-md-5 { + padding: 3rem !important; } + + .pt-md-5, + .py-md-5 { + padding-top: 3rem !important; } + + .pr-md-5, + .px-md-5 { + padding-right: 3rem !important; } + + .pb-md-5, + .py-md-5 { + padding-bottom: 3rem !important; } + + .pl-md-5, + .px-md-5 { + padding-left: 3rem !important; } + + .m-md-n1 { + margin: -0.25rem !important; } + + .mt-md-n1, + .my-md-n1 { + margin-top: -0.25rem !important; } + + .mr-md-n1, + .mx-md-n1 { + margin-right: -0.25rem !important; } + + .mb-md-n1, + .my-md-n1 { + margin-bottom: -0.25rem !important; } + + .ml-md-n1, + .mx-md-n1 { + margin-left: -0.25rem !important; } + + .m-md-n2 { + margin: -0.5rem !important; } + + .mt-md-n2, + .my-md-n2 { + margin-top: -0.5rem !important; } + + .mr-md-n2, + .mx-md-n2 { + margin-right: -0.5rem !important; } + + .mb-md-n2, + .my-md-n2 { + margin-bottom: -0.5rem !important; } + + .ml-md-n2, + .mx-md-n2 { + margin-left: -0.5rem !important; } + + .m-md-n3 { + margin: -1rem !important; } + + .mt-md-n3, + .my-md-n3 { + margin-top: -1rem !important; } + + .mr-md-n3, + .mx-md-n3 { + margin-right: -1rem !important; } + + .mb-md-n3, + .my-md-n3 { + margin-bottom: -1rem !important; } + + .ml-md-n3, + .mx-md-n3 { + margin-left: -1rem !important; } + + .m-md-n4 { + margin: -1.5rem !important; } + + .mt-md-n4, + .my-md-n4 { + margin-top: -1.5rem !important; } + + .mr-md-n4, + .mx-md-n4 { + margin-right: -1.5rem !important; } + + .mb-md-n4, + .my-md-n4 { + margin-bottom: -1.5rem !important; } + + .ml-md-n4, + .mx-md-n4 { + margin-left: -1.5rem !important; } + + .m-md-n5 { + margin: -3rem !important; } + + .mt-md-n5, + .my-md-n5 { + margin-top: -3rem !important; } + + .mr-md-n5, + .mx-md-n5 { + margin-right: -3rem !important; } + + .mb-md-n5, + .my-md-n5 { + margin-bottom: -3rem !important; } + + .ml-md-n5, + .mx-md-n5 { + margin-left: -3rem !important; } + + .m-md-auto { + margin: auto !important; } + + .mt-md-auto, + .my-md-auto { + margin-top: auto !important; } + + .mr-md-auto, + .mx-md-auto { + margin-right: auto !important; } + + .mb-md-auto, + .my-md-auto { + margin-bottom: auto !important; } + + .ml-md-auto, + .mx-md-auto { + margin-left: auto !important; } } +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important; } + + .mt-lg-0, + .my-lg-0 { + margin-top: 0 !important; } + + .mr-lg-0, + .mx-lg-0 { + margin-right: 0 !important; } + + .mb-lg-0, + .my-lg-0 { + margin-bottom: 0 !important; } + + .ml-lg-0, + .mx-lg-0 { + margin-left: 0 !important; } + + .m-lg-1 { + margin: 0.25rem !important; } + + .mt-lg-1, + .my-lg-1 { + margin-top: 0.25rem !important; } + + .mr-lg-1, + .mx-lg-1 { + margin-right: 0.25rem !important; } + + .mb-lg-1, + .my-lg-1 { + margin-bottom: 0.25rem !important; } + + .ml-lg-1, + .mx-lg-1 { + margin-left: 0.25rem !important; } + + .m-lg-2 { + margin: 0.5rem !important; } + + .mt-lg-2, + .my-lg-2 { + margin-top: 0.5rem !important; } + + .mr-lg-2, + .mx-lg-2 { + margin-right: 0.5rem !important; } + + .mb-lg-2, + .my-lg-2 { + margin-bottom: 0.5rem !important; } + + .ml-lg-2, + .mx-lg-2 { + margin-left: 0.5rem !important; } + + .m-lg-3 { + margin: 1rem !important; } + + .mt-lg-3, + .my-lg-3 { + margin-top: 1rem !important; } + + .mr-lg-3, + .mx-lg-3 { + margin-right: 1rem !important; } + + .mb-lg-3, + .my-lg-3 { + margin-bottom: 1rem !important; } + + .ml-lg-3, + .mx-lg-3 { + margin-left: 1rem !important; } + + .m-lg-4 { + margin: 1.5rem !important; } + + .mt-lg-4, + .my-lg-4 { + margin-top: 1.5rem !important; } + + .mr-lg-4, + .mx-lg-4 { + margin-right: 1.5rem !important; } + + .mb-lg-4, + .my-lg-4 { + margin-bottom: 1.5rem !important; } + + .ml-lg-4, + .mx-lg-4 { + margin-left: 1.5rem !important; } + + .m-lg-5 { + margin: 3rem !important; } + + .mt-lg-5, + .my-lg-5 { + margin-top: 3rem !important; } + + .mr-lg-5, + .mx-lg-5 { + margin-right: 3rem !important; } + + .mb-lg-5, + .my-lg-5 { + margin-bottom: 3rem !important; } + + .ml-lg-5, + .mx-lg-5 { + margin-left: 3rem !important; } + + .p-lg-0 { + padding: 0 !important; } + + .pt-lg-0, + .py-lg-0 { + padding-top: 0 !important; } + + .pr-lg-0, + .px-lg-0 { + padding-right: 0 !important; } + + .pb-lg-0, + .py-lg-0 { + padding-bottom: 0 !important; } + + .pl-lg-0, + .px-lg-0 { + padding-left: 0 !important; } + + .p-lg-1 { + padding: 0.25rem !important; } + + .pt-lg-1, + .py-lg-1 { + padding-top: 0.25rem !important; } + + .pr-lg-1, + .px-lg-1 { + padding-right: 0.25rem !important; } + + .pb-lg-1, + .py-lg-1 { + padding-bottom: 0.25rem !important; } + + .pl-lg-1, + .px-lg-1 { + padding-left: 0.25rem !important; } + + .p-lg-2 { + padding: 0.5rem !important; } + + .pt-lg-2, + .py-lg-2 { + padding-top: 0.5rem !important; } + + .pr-lg-2, + .px-lg-2 { + padding-right: 0.5rem !important; } + + .pb-lg-2, + .py-lg-2 { + padding-bottom: 0.5rem !important; } + + .pl-lg-2, + .px-lg-2 { + padding-left: 0.5rem !important; } + + .p-lg-3 { + padding: 1rem !important; } + + .pt-lg-3, + .py-lg-3 { + padding-top: 1rem !important; } + + .pr-lg-3, + .px-lg-3 { + padding-right: 1rem !important; } + + .pb-lg-3, + .py-lg-3 { + padding-bottom: 1rem !important; } + + .pl-lg-3, + .px-lg-3 { + padding-left: 1rem !important; } + + .p-lg-4 { + padding: 1.5rem !important; } + + .pt-lg-4, + .py-lg-4 { + padding-top: 1.5rem !important; } + + .pr-lg-4, + .px-lg-4 { + padding-right: 1.5rem !important; } + + .pb-lg-4, + .py-lg-4 { + padding-bottom: 1.5rem !important; } + + .pl-lg-4, + .px-lg-4 { + padding-left: 1.5rem !important; } + + .p-lg-5 { + padding: 3rem !important; } + + .pt-lg-5, + .py-lg-5 { + padding-top: 3rem !important; } + + .pr-lg-5, + .px-lg-5 { + padding-right: 3rem !important; } + + .pb-lg-5, + .py-lg-5 { + padding-bottom: 3rem !important; } + + .pl-lg-5, + .px-lg-5 { + padding-left: 3rem !important; } + + .m-lg-n1 { + margin: -0.25rem !important; } + + .mt-lg-n1, + .my-lg-n1 { + margin-top: -0.25rem !important; } + + .mr-lg-n1, + .mx-lg-n1 { + margin-right: -0.25rem !important; } + + .mb-lg-n1, + .my-lg-n1 { + margin-bottom: -0.25rem !important; } + + .ml-lg-n1, + .mx-lg-n1 { + margin-left: -0.25rem !important; } + + .m-lg-n2 { + margin: -0.5rem !important; } + + .mt-lg-n2, + .my-lg-n2 { + margin-top: -0.5rem !important; } + + .mr-lg-n2, + .mx-lg-n2 { + margin-right: -0.5rem !important; } + + .mb-lg-n2, + .my-lg-n2 { + margin-bottom: -0.5rem !important; } + + .ml-lg-n2, + .mx-lg-n2 { + margin-left: -0.5rem !important; } + + .m-lg-n3 { + margin: -1rem !important; } + + .mt-lg-n3, + .my-lg-n3 { + margin-top: -1rem !important; } + + .mr-lg-n3, + .mx-lg-n3 { + margin-right: -1rem !important; } + + .mb-lg-n3, + .my-lg-n3 { + margin-bottom: -1rem !important; } + + .ml-lg-n3, + .mx-lg-n3 { + margin-left: -1rem !important; } + + .m-lg-n4 { + margin: -1.5rem !important; } + + .mt-lg-n4, + .my-lg-n4 { + margin-top: -1.5rem !important; } + + .mr-lg-n4, + .mx-lg-n4 { + margin-right: -1.5rem !important; } + + .mb-lg-n4, + .my-lg-n4 { + margin-bottom: -1.5rem !important; } + + .ml-lg-n4, + .mx-lg-n4 { + margin-left: -1.5rem !important; } + + .m-lg-n5 { + margin: -3rem !important; } + + .mt-lg-n5, + .my-lg-n5 { + margin-top: -3rem !important; } + + .mr-lg-n5, + .mx-lg-n5 { + margin-right: -3rem !important; } + + .mb-lg-n5, + .my-lg-n5 { + margin-bottom: -3rem !important; } + + .ml-lg-n5, + .mx-lg-n5 { + margin-left: -3rem !important; } + + .m-lg-auto { + margin: auto !important; } + + .mt-lg-auto, + .my-lg-auto { + margin-top: auto !important; } + + .mr-lg-auto, + .mx-lg-auto { + margin-right: auto !important; } + + .mb-lg-auto, + .my-lg-auto { + margin-bottom: auto !important; } + + .ml-lg-auto, + .mx-lg-auto { + margin-left: auto !important; } } +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important; } + + .mt-xl-0, + .my-xl-0 { + margin-top: 0 !important; } + + .mr-xl-0, + .mx-xl-0 { + margin-right: 0 !important; } + + .mb-xl-0, + .my-xl-0 { + margin-bottom: 0 !important; } + + .ml-xl-0, + .mx-xl-0 { + margin-left: 0 !important; } + + .m-xl-1 { + margin: 0.25rem !important; } + + .mt-xl-1, + .my-xl-1 { + margin-top: 0.25rem !important; } + + .mr-xl-1, + .mx-xl-1 { + margin-right: 0.25rem !important; } + + .mb-xl-1, + .my-xl-1 { + margin-bottom: 0.25rem !important; } + + .ml-xl-1, + .mx-xl-1 { + margin-left: 0.25rem !important; } + + .m-xl-2 { + margin: 0.5rem !important; } + + .mt-xl-2, + .my-xl-2 { + margin-top: 0.5rem !important; } + + .mr-xl-2, + .mx-xl-2 { + margin-right: 0.5rem !important; } + + .mb-xl-2, + .my-xl-2 { + margin-bottom: 0.5rem !important; } + + .ml-xl-2, + .mx-xl-2 { + margin-left: 0.5rem !important; } + + .m-xl-3 { + margin: 1rem !important; } + + .mt-xl-3, + .my-xl-3 { + margin-top: 1rem !important; } + + .mr-xl-3, + .mx-xl-3 { + margin-right: 1rem !important; } + + .mb-xl-3, + .my-xl-3 { + margin-bottom: 1rem !important; } + + .ml-xl-3, + .mx-xl-3 { + margin-left: 1rem !important; } + + .m-xl-4 { + margin: 1.5rem !important; } + + .mt-xl-4, + .my-xl-4 { + margin-top: 1.5rem !important; } + + .mr-xl-4, + .mx-xl-4 { + margin-right: 1.5rem !important; } + + .mb-xl-4, + .my-xl-4 { + margin-bottom: 1.5rem !important; } + + .ml-xl-4, + .mx-xl-4 { + margin-left: 1.5rem !important; } + + .m-xl-5 { + margin: 3rem !important; } + + .mt-xl-5, + .my-xl-5 { + margin-top: 3rem !important; } + + .mr-xl-5, + .mx-xl-5 { + margin-right: 3rem !important; } + + .mb-xl-5, + .my-xl-5 { + margin-bottom: 3rem !important; } + + .ml-xl-5, + .mx-xl-5 { + margin-left: 3rem !important; } + + .p-xl-0 { + padding: 0 !important; } + + .pt-xl-0, + .py-xl-0 { + padding-top: 0 !important; } + + .pr-xl-0, + .px-xl-0 { + padding-right: 0 !important; } + + .pb-xl-0, + .py-xl-0 { + padding-bottom: 0 !important; } + + .pl-xl-0, + .px-xl-0 { + padding-left: 0 !important; } + + .p-xl-1 { + padding: 0.25rem !important; } + + .pt-xl-1, + .py-xl-1 { + padding-top: 0.25rem !important; } + + .pr-xl-1, + .px-xl-1 { + padding-right: 0.25rem !important; } + + .pb-xl-1, + .py-xl-1 { + padding-bottom: 0.25rem !important; } + + .pl-xl-1, + .px-xl-1 { + padding-left: 0.25rem !important; } + + .p-xl-2 { + padding: 0.5rem !important; } + + .pt-xl-2, + .py-xl-2 { + padding-top: 0.5rem !important; } + + .pr-xl-2, + .px-xl-2 { + padding-right: 0.5rem !important; } + + .pb-xl-2, + .py-xl-2 { + padding-bottom: 0.5rem !important; } + + .pl-xl-2, + .px-xl-2 { + padding-left: 0.5rem !important; } + + .p-xl-3 { + padding: 1rem !important; } + + .pt-xl-3, + .py-xl-3 { + padding-top: 1rem !important; } + + .pr-xl-3, + .px-xl-3 { + padding-right: 1rem !important; } + + .pb-xl-3, + .py-xl-3 { + padding-bottom: 1rem !important; } + + .pl-xl-3, + .px-xl-3 { + padding-left: 1rem !important; } + + .p-xl-4 { + padding: 1.5rem !important; } + + .pt-xl-4, + .py-xl-4 { + padding-top: 1.5rem !important; } + + .pr-xl-4, + .px-xl-4 { + padding-right: 1.5rem !important; } + + .pb-xl-4, + .py-xl-4 { + padding-bottom: 1.5rem !important; } + + .pl-xl-4, + .px-xl-4 { + padding-left: 1.5rem !important; } + + .p-xl-5 { + padding: 3rem !important; } + + .pt-xl-5, + .py-xl-5 { + padding-top: 3rem !important; } + + .pr-xl-5, + .px-xl-5 { + padding-right: 3rem !important; } + + .pb-xl-5, + .py-xl-5 { + padding-bottom: 3rem !important; } + + .pl-xl-5, + .px-xl-5 { + padding-left: 3rem !important; } + + .m-xl-n1 { + margin: -0.25rem !important; } + + .mt-xl-n1, + .my-xl-n1 { + margin-top: -0.25rem !important; } + + .mr-xl-n1, + .mx-xl-n1 { + margin-right: -0.25rem !important; } + + .mb-xl-n1, + .my-xl-n1 { + margin-bottom: -0.25rem !important; } + + .ml-xl-n1, + .mx-xl-n1 { + margin-left: -0.25rem !important; } + + .m-xl-n2 { + margin: -0.5rem !important; } + + .mt-xl-n2, + .my-xl-n2 { + margin-top: -0.5rem !important; } + + .mr-xl-n2, + .mx-xl-n2 { + margin-right: -0.5rem !important; } + + .mb-xl-n2, + .my-xl-n2 { + margin-bottom: -0.5rem !important; } + + .ml-xl-n2, + .mx-xl-n2 { + margin-left: -0.5rem !important; } + + .m-xl-n3 { + margin: -1rem !important; } + + .mt-xl-n3, + .my-xl-n3 { + margin-top: -1rem !important; } + + .mr-xl-n3, + .mx-xl-n3 { + margin-right: -1rem !important; } + + .mb-xl-n3, + .my-xl-n3 { + margin-bottom: -1rem !important; } + + .ml-xl-n3, + .mx-xl-n3 { + margin-left: -1rem !important; } + + .m-xl-n4 { + margin: -1.5rem !important; } + + .mt-xl-n4, + .my-xl-n4 { + margin-top: -1.5rem !important; } + + .mr-xl-n4, + .mx-xl-n4 { + margin-right: -1.5rem !important; } + + .mb-xl-n4, + .my-xl-n4 { + margin-bottom: -1.5rem !important; } + + .ml-xl-n4, + .mx-xl-n4 { + margin-left: -1.5rem !important; } + + .m-xl-n5 { + margin: -3rem !important; } + + .mt-xl-n5, + .my-xl-n5 { + margin-top: -3rem !important; } + + .mr-xl-n5, + .mx-xl-n5 { + margin-right: -3rem !important; } + + .mb-xl-n5, + .my-xl-n5 { + margin-bottom: -3rem !important; } + + .ml-xl-n5, + .mx-xl-n5 { + margin-left: -3rem !important; } + + .m-xl-auto { + margin: auto !important; } + + .mt-xl-auto, + .my-xl-auto { + margin-top: auto !important; } + + .mr-xl-auto, + .mx-xl-auto { + margin-right: auto !important; } + + .mb-xl-auto, + .my-xl-auto { + margin-bottom: auto !important; } + + .ml-xl-auto, + .mx-xl-auto { + margin-left: auto !important; } } +@font-face { + font-family: "OracleSans"; + src: url("../resources/fonts/oracle-sans/OracleSans_Bd.ttf"), url("../resources/fonts/oracle-sans/OracleSans_BdIt.ttf"), url("../resources/fonts/oracle-sans/OracleSans_It.ttf"), url("../resources/fonts/oracle-sans/OracleSans_Lt.ttf"), url("../resources/fonts/oracle-sans/OracleSans_LtIt.ttf"), url("../resources/fonts/oracle-sans/OracleSans_SBd.ttf"), url("../resources/fonts/oracle-sans/OracleSans_SBdIt.ttf"), url("../resources/fonts/oracle-sans/OracleSans_ULt.ttf"), url("../resources/fonts/oracle-sans/OracleSans_ULtIt.ttf"), url("../resources/fonts/oracle-sans/OracleSans_XBd.ttf"), url("../resources/fonts/oracle-sans/OracleSans_XBdIt.ttf"); } +body, h1, h2, h3, h4, h5, h6, +p, blockquote, pre, hr, +dl, dd, ol, ul, figure { + margin: 0; + padding: 0; } + +html { + height: 100%; } + +body { + font: 400 1rem/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #171F26; + background-color: #ffffff; + -webkit-text-size-adjust: 100%; + -webkit-font-feature-settings: "kern" 1; + -moz-font-feature-settings: "kern" 1; + -o-font-feature-settings: "kern" 1; + font-feature-settings: "kern" 1; + font-kerning: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } + @media (max-width: 767.98px) { + body { + font-size: 0.8rem; } } + body.overflow-hidden { + overflow: hidden; } + @media (min-width: 768px) { + body.overflow-hidden { + overflow: auto; } } + +*, +*:before, +*:after { + box-sizing: border-box; } + +img { + max-width: 100%; + vertical-align: middle; + height: auto; + -ms-interpolation-mode: bicubic; } + +figure > img { + display: block; } + +figcaption { + font-size: 80%; } + +.wrapper { + min-height: 100%; + height: 100%; } + +.icon > svg { + display: inline-block; + vertical-align: middle; } + .icon > svg path { + fill: #828282; } + +.social-media-list .icon { + padding-right: 5px; } +.social-media-list li + li { + padding-top: 5px; } + +.section-div { + position: absolute; + bottom: 0; + left: 0; + right: 0; + margin: 0 auto; + width: 100%; + max-width: 1140px; + height: 1px; + background-color: #ebecf0; } + +.overlay { + position: fixed; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + display: none; + top: 0; + z-index: 10; } + @media (min-width: 768px) { + .overlay { + display: none !important; } } + +.docs-content { + padding-top: 20px; + padding-bottom: 30px; } + @media (min-width: 768px) { + .docs-content--with-sidebar { + padding-left: 40px; } } + +.anchor { + display: block; + position: relative; + top: -70px; + visibility: hidden; } + +.unknown-module { + font-weight: 700; + font-style: italic; } + +.hide { + display: none; } + +.link-back { + color: #171F26; } + .link-back i { + font-style: normal; + margin-right: 5px; } + .link-back:hover { + color: #d9810c; } + +.btn:focus, .social-count:focus { + outline: -webkit-focus-ring-color auto 5px; } + +.docs-content pre > code { + margin-top: 0; + margin-bottom: 25px; + border-radius: 8px; + font-size: 15px; } + +.hljs { + display: block; + overflow-x: auto; + border: 0.4px solid #ffffff39; + background: #171923; + color: #ffffff; + padding: 0.5em 20px !important; + border-radius: 4px; } + +pre button { + appearance: none; + border: 0; + line-height: 1; + position: absolute; + top: 5px; + right: 5px; + font-size: 10px; + color: #171923; + background: #FFFFFF; + padding: 5px; + border-radius: 6px; } + +.docs-content pre > code { + margin-top: 0; + margin-bottom: 25px; + border-radius: 8px; + font-size: 15px; } + +h1, h2, h3, h4, h5, h6 { + font-weight: 400; + line-height: 1.3; + margin-bottom: 15px; + margin-top: 35px; } + +h1 { + font-size: 1.875em; } + +h2 { + font-size: 1.63em; } + +h3 { + font-size: 1.53em; } + +h4 { + font-size: 1.43em; } + +h5 { + font-size: 1.3em; } + +h6 { + font-size: 1em; } + +blockquote { + border-left: 2px solid #d2d5df; + padding-left: 25px; + margin-left: 0; + margin-bottom: 40px; } + blockquote > :last-child { + margin-bottom: 0; } + blockquote > :before { + content: none !important; } + +pre, +code { + font-size: 0.9375rem; } + +code { + background-color: #f2f4f9; + padding: 2px; } + .docs-content code { + padding: 2px 4px; + font-size: 15px; } + table code { + background-color: #f2f4f9; + padding: 2px; } + +table { + border-collapse: collapse; + border-spacing: 0; + border: 1px solid #d7dae1; + margin-bottom: 25px; } + table thead tr th:first-child { + border-radius: 4px 0 0; } + table thead tr th::last-child { + border-radius: 0 4px 0 0; } + @media (min-width: 768px) { + table th, + table td { + min-width: 215px; } } + table tr { + border-bottom: 1px solid #d7dae1; } + table th, table td { + font-size: 14px; + text-align: left; + padding: 4px 15px; + border-right: 1px solid #d7dae1; } + +pre { + margin-bottom: 0; + overflow-x: auto; + position: relative; } + pre > code { + border: 0; + padding-right: 0; + padding-left: 0; } + .docs-content pre > code { + margin-top: 6px; + margin-bottom: 25px; + border-radius: 4px; + font-size: 15px; } + .tab-content pre > code { + border-radius: 0 0 4px 4px; } + .content-section pre { + margin-bottom: 15px; + margin-top: 15px; + position: relative; + padding-top: 26px; + overflow-y: visible !important; + border: none; + box-shadow: 2px 16px 20px -9px rgba(0, 0, 0, 0.4); + border-radius: 0 0 8px 8px; } + .content-section pre:after { + position: absolute; + content: ""; + top: 0; + left: 0; + height: 26px; + width: 100%; + border-radius: 8px 8px 0 0; + background-image: linear-gradient(180deg, #e7e6e7 0%, #d1d0d1 100%); } + .content-section pre:before { + position: absolute; + content: ""; + top: 7px; + left: 13px; + width: 52px; + height: 12px; + background-image: url("/resources/img/system-icons.png"); + background-repeat: no-repeat; + background-size: cover; + z-index: 2; } + @media (min-width: 768px) { + .content-section pre { + margin-bottom: 0; + margin-top: 0; } } + +ul, ol { + margin: 0 0 20px; + padding-left: 30px; } + +ul li { + position: relative; + padding-left: 15px; + list-style: none; } + ul li:before { + content: ""; + position: absolute; + top: 11px; + left: 0; + width: 3px; + height: 3px; + border-radius: 50%; + background-color: #171F26; } + +li > ul, +li > ol { + margin-bottom: 0; } +.docs-content li { + font-size: 1em; + margin-bottom: 7px; } + .docs-content li:last-of-type { + margin-bottom: 0; } + +a { + color: #f29110; + text-decoration: none; + transition: color .2s; } + a:hover { + color: #d9810c; + text-decoration: none; } + .social-media-list a:hover { + text-decoration: none; } + .social-media-list a:hover .username { + text-decoration: none; } + +p { + font-size: 1em; + line-height: 1.8; + color: #171F26; + margin-bottom: 15px; } + @media (min-width: 768px) { + p { + margin-bottom: 25px; } } + p + pre, p + .snippet-container { + margin-top: -15px; } + +.visible { + display: block !important; } + +strong { + font-weight: 600; } + +.link-main { + display: inline-block; + margin-top: 5px; + padding: 3px 0; + color: #f29110; + font-size: 14px; + font-weight: 700; + text-transform: uppercase; } + .link-main--dark { + color: #171F26; } + .link-main--dark:hover { + color: #ffffff; } + +.text-md { + font-size: 1.13em; } + +blockquote { + background: #c3ebff; + border-left: 10px solid #8EC9E6; + margin: 1.5em 0px; + padding: 0.5em 10px; + quotes: "“" "”"; + font-style: italic; + border-radius: 8px; } + +button, +input[type='submit'] { + padding: 0; + appearance: none; + border: 0; + border-radius: 0; + line-height: 1; + cursor: pointer; } + button:focus, + input[type='submit']:focus { + outline: -webkit-focus-ring-color auto 5px; } + +.btn { + border: none; + display: inline-block; + background: none; + outline: none; + padding: 0; + vertical-align: middle; + user-select: none; + cursor: pointer; + -webkit-appearance: none; } + .btn:hover, .btn:active { + background-color: transparent; + text-decoration: none; + outline: none !important; } + .btn.disabled, .btn.disabled:hover, .btn.disabled:focus, .btn[disabled], .btn[disabled]:hover, .btn[disabled]:focus { + background-color: transparent; } + +.btn-try { + border-radius: 3px; + text-align: center; + font-size: 12px !important; } + @media (min-width: 768px) { + .btn-try { + padding: 8px 25px 9px !important; + display: inline-block; } } + @media (min-width: 768px) { + .btn-try { + display: none; } + .header--filled .btn-try, .header--content .btn-try { + display: block; } } + +.btn-primary, +.btn-secondary { + border-radius: 4px; + border: 1px solid #f29110; + padding: 16px 25px; + font-size: 14px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.44px; + transition: background-color .3s, color .3s; } + @media (min-width: 768px) { + .btn-primary, + .btn-secondary { + padding: 18px 30px; } } + +.btn-primary { + background-color: #f29110; + color: #ffffff; } + .btn-primary:hover, .btn-primary:focus { + background-color: #dd830c; + border-color: #dd830c; + color: #ffffff; } + .btn-primary--filled:hover, .btn-primary--filled:focus { + color: #ffffff; + background-color: #f5a740; + border-color: #f5a740; } + +.btn-secondary { + background-color: transparent; + color: #f29110; } + .btn-secondary:hover, .btn-secondary:focus { + color: #ffffff; + background-color: #f29110; } + +.btn--sm { + padding: 10px 12px; + font-size: 12px; } + +.btn-submit { + padding: 10px 12px; + border-radius: 2px; + background-color: #f29110; + color: #ffffff; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.38px; + text-transform: uppercase; + transition: background-color .3s; } + .btn-submit:hover, .btn-submit:active, .btn-submit:checked, .btn-submit:visited { + background-color: #dd830c; } + +.btn--more { + color: #171F26; } + +.btn-clear { + visibility: hidden; + position: absolute; + right: 7px; + top: 10px; + font-size: 15px; + color: #171F26; } + +.menu-btn { + position: absolute; + transition: background 750ms ease-in-out; + user-select: none; + cursor: pointer; } + @media (min-width: 768px) { + .menu-btn { + display: none; } } + .menu-btn--sidebar { + display: flex; + align-items: center; + justify-content: center; + position: fixed; + right: 15px; + top: auto; + bottom: 20px; + padding: 5px; + width: 56px; + height: 56px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25); + border-radius: 4px; + background-color: #f29110; + z-index: 999; } + .menu-btn--sidebar .hamburger::before, + .menu-btn--sidebar .hamburger::after, + .menu-btn--sidebar .hamburger .inner { + background: #ffffff; } + .menu-btn--sidebar.menu-open .hamburger::before, .menu-btn--sidebar.menu-open .hamburger::after, .menu-btn--sidebar.menu-open .hamburger .inner { + background: #ffffff; } + @media (min-width: 576px) { + .menu-btn--sidebar { + display: none; } } + +.hamburger { + position: relative; + width: 35px; + height: 30px; + transition: transform 750ms ease-in-out; + margin-top: 10px; + margin-left: 10px; } + +.menu-btn:active .hamburger { + transition: transform 50ms; + transform: scale(0.95); } + +.hamburger::before, +.hamburger::after, +.hamburger .inner { + content: ''; + position: absolute; + width: 25px; + height: 2px; + background: #ccc; + transform-origin: 100% 50%; + transition: all 750ms ease-in-out; } + +.hamburger::before { + top: -1px; } + +.hamburger .inner { + top: 9px; + transform-origin: 50% 50%; } + +.hamburger::after { + top: 19px; } + +.menu-open .hamburger { + transform: rotateY(-180deg); } + +.menu-open.menu-btn:active .hamburger { + transform: scale(0.95) rotateY(-180deg); } + +.menu-open .hamburger::before, +.menu-open .hamburger::after, +.menu-open .hamburger .inner { + background: #29252f; } + +.menu-open .hamburger::before { + transform: translate3d(-4px, 1px, 0) rotateZ(-45deg); } + +.menu-open .hamburger .inner { + transform: rotateY(-90deg); + transition: transform 375ms, background-color 750ms ease-in-out; } + +.menu-open .hamburger::after { + transform: translate3d(-4px, -1px, 0) rotateZ(45deg); } + +.menu-btn.menu-btn--menu.js-show-menu.menu-open.close:focus-visible { + outline: none; } + +.menu-btn.menu-btn--menu.js-show-menu:focus-visible { + outline: none; } + +.hamburger:focus-visible { + outline: none; } + +.button-group { + display: flex; + justify-content: center; + flex-direction: column; + text-align: center; } + @media (min-width: 576px) { + .button-group { + justify-content: flex-start; + flex-direction: row; } } + .button-group .btn + .btn { + margin-top: 15px; } + @media (min-width: 576px) { + .button-group .btn + .btn { + margin-left: 30px; + margin-top: 0; } } + +a.anchor_heading { + visibility: hidden; + color: #171F26; } + +h1:hover > a.anchor_heading, +h2:hover > a.anchor_heading, +h3:hover > a.anchor_heading, +h4:hover > a.anchor_heading, +h5:hover > a.anchor_heading, +h6:hover > a.anchor_heading { + visibility: visible; } + +a.enterprise { + padding: 0.2rem 0.4rem; + font-size: 1rem; + color: #212529; + background-color: #f7f8fb; + font-style: italic; + border-radius: 0.2rem; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 400; } + +.header { + position: relative; + margin: 0 auto; + width: 100%; + padding: 0px; + background-color: #171F26; + z-index: 50; } + +.header--home { + position: absolute; + top: 0; + left: 0; + margin: 0 auto; + width: 100%; + padding: 20px; + background-color: #171F26; + z-index: 50; } + +.header--content { + height: 60px; + overflow: auto; + position: fixed; + top: 0; + background-color: #171F26; + padding: 20px; } + +.header--content img { + display: none; } + +.footer { + background: url("/resources/img/home/grain.png") repeat, url("/resources/img/footer/metaimage-footer.svg") 50% 15%/1800px no-repeat, radial-gradient(79.99% 167.49% at 30.24% 129.2%, rgba(47, 79, 124, 0.8) 0%, rgba(38, 84, 107, 0) 100%), #161D24; + padding-top: 45px; } + @media (max-width: 480px) { + .footer { + display: none; } } + +.footer-list { + list-style: none; + margin-bottom: 30px; + padding-left: 0px; } + @media only screen and (min-width: 320px) and (max-width: 480px) { + .footer-list { + padding-left: 10px; } } + @media (min-width: 768px) { + .footer-list { + margin-bottom: 20px; } } + .footer-list__item { + padding-left: 0; + list-style: none; } + .footer-list__item:before { + display: none; } + .footer-list__item + .footer-list__item { + margin-top: 10px; } + .footer-list__item a { + color: #ffffff; + font-size: 14px; + line-height: 14px; + font-weight: normal; } + @media only screen and (min-width: 320px) and (max-width: 480px) { + .footer-list__item a { + font-size: 12px; + line-height: 12px; } } + @media only screen and (min-width: 414px) and (max-width: 736px) { + .footer-list__item a { + font-size: 12px; + line-height: 12px; } } + .footer-list__item a:hover { + color: #8EC9E6; } + +.row.footer-content { + margin-top: 0; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; } + +h6.title-footer { + color: white; + font-size: 22px; + line-height: 30px; + font-weight: bold; + margin-top: 0; + margin-bottom: 20px; } + @media only screen and (min-width: 320px) and (max-width: 480px) { + h6.title-footer { + background: url("/resources/img/docs-home/arrow-down.svg") 96% 50% no-repeat, #213242; + margin-top: 12px; + font-size: 16px; + margin-bottom: 8px; + height: 30px; + border-radius: 10px; + padding-left: 10px; + border: 0.4px solid #ffffff21; } } + @media only screen and (min-width: 481px) and (max-width: 736px) { + h6.title-footer { + margin-top: 20px; + font-size: 16px; + line-height: 18px; + margin-bottom: 10px; } } + @media only screen and (min-width: 768px) and (max-width: 1024px) { + h6.title-footer { + margin-bottom: 10px; + font-size: 18px; } } + +footer a.btn.btn-primary { + background: #F29111; + border-radius: 12px; + padding: 4px 28px 4px 28px; + text-transform: none; + font-style: normal; + font-weight: 400; + font-size: 16px; + line-height: 24px; } + +.copyright { + padding: 15px 15px; + border-top: 1px solid #ffffff; + color: #ffffff; + font-size: 13px; + line-height: 16px; + font-weight: 400; + text-align: center; + margin: 20px 0 0 0; } + @media only screen and (min-width: 320px) and (max-width: 480px) { + .copyright { + font-size: 10px; } } + @media only screen and (min-width: 414px) and (max-width: 736px) { + .copyright { + font-size: 10px; } } + +@media only screen and (min-width: 768px) and (max-width: 1024px) { + p.copyright { + font-size: 12px; } } + +.footer__columns { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: space-between; + min-width: 458px; + width: 60%; + padding: 0px 20px; } + @media only screen and (min-width: 320px) and (max-width: 852px) { + .footer__columns { + width: 100%; + padding: 16px; + justify-content: space-around; } } + @media only screen and (min-width: 320px) and (max-width: 480px) { + .footer__columns { + flex-direction: column; + padding: 16px; + min-width: 98%; } } + +@media (max-width: 480px) { + .footer__mobile { + background: url("/resources/img/home/grain.png") repeat, url("/resources/img/footer/metaimage-footer.svg") 50% 15%/1800px no-repeat, radial-gradient(79.99% 167.49% at 30.24% 129.2%, rgba(47, 79, 124, 0.8) 0%, rgba(38, 84, 107, 0) 100%), #161D24; + display: block; + position: relative; } + .footer__mobile .grow { + -moz-transition: height .5s; + -ms-transition: height .5s; + -o-transition: height .5s; + -webkit-transition: height .5s; + transition: height .5s; + height: 0; + overflow: hidden; } + .footer__mobile .arrow-footer.arrow-footer-down { + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-top: 6px solid #FFFFFF; + position: absolute; + left: 180px; + top: 15px; } } + @media only screen and (max-width: 480px) and (min-width: 320px) and (max-width: 480px) { + .footer__mobile .arrow-footer.arrow-footer-down { + top: 24px; + left: 344px; } } + @media only screen and (max-width: 480px) and (min-width: 414px) and (max-width: 736px) { + .footer__mobile .arrow-footer.arrow-footer-down { + top: 24px; + left: 344px; } } + +@media only screen and (min-width: 320px) and (max-width: 852px) { + .rubber-footer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin: 0px 0px 20px 0px; } } + +.col-bg:after { + position: absolute; + right: 0; + top: 0; + content: ""; + width: 400%; + height: 100%; + background-color: #f7f8fb; + z-index: -1; } + +.sidebar-wrap { + position: relative; } + @media (min-width: 576px) { + .sidebar-wrap { + min-height: calc(100vh - 70px); } } + +@media (max-width: 991.98px) { + .inner-wrapper-sticky { + transform: none !important; } } + +.sidebar { + position: fixed; + top: 0; + left: 0; + transform: translateX(-100%); + width: 100%; + height: 100vh; + overflow-y: auto; + background-color: #f7f8fb; + z-index: 99; + padding: 0 15px; + transition: transform .4s; } + .sidebar.open { + transform: translateX(0); } + @media (min-width: 576px) { + .sidebar { + position: relative; + top: auto; + height: auto; + padding: 0 0 10px; + background-color: #f7f8fb; + z-index: 2; + width: 100%; + left: auto; + transform: translate(0, 0); + /* For browsers don't support translate3d. */ + transform: translate3d(0, 0, 0); + will-change: position, transform; } } + @media (min-width: 576px) { + .sidebar--ecosysytem { + top: 60px; + position: sticky; + flex: 1; + max-width: 100%; + width: 100%; } } + +.content { + padding-top: 72px; + height: 100%; } + @media (min-width: 768px) { + .content { + padding-top: 60px; } } + .content--home { + padding-top: 60px; } + +.content-section { + position: relative; + padding: 20px 0; } + @media (min-width: 768px) { + .content-section { + padding: 40px 0; } } + .content-section--top-0 { + padding-top: 0; } + .content-section--gray { + background-color: #f9fafb; } + .content-section--blue { + background-color: #00758F; } + .content-section--blue p { + color: #fff; } + +.section-heading { + position: relative; + padding: 20px 0; + background-image: linear-gradient(173deg, #03687f 0%, #005b70 100%); + z-index: 1; } + .section-heading:after { + position: absolute; + content: ""; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-image: url("resources/img/heading-pattern.svg"); + background-repeat: no-repeat; + background-position: 70% 10%; + z-index: -1; } + @media (min-width: 768px) { + .section-heading { + padding: 36px 0; } } + +.section-downloads { + background-color: #f7f8fb; } + +.content-section-news { + background-color: #016b80; + padding: 60px 0; } + @media (min-width: 576px) { + .content-section-news { + padding: 60px 20px; } } + +.site-title { + font-size: 2.125rem; + font-weight: 400; + line-height: 3.9rem; + letter-spacing: -1px; + margin-bottom: 0; + margin-top: 1px; + float: left; + text-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2); } + .site-title, .site-title:visited { + transition: color 0.35s ease; + color: #005a80; } + .site-title:hover { + transition: color 0.35s ease; + text-decoration: none; + color: #0073a4; } + +.row--space { + margin-bottom: 30px; } + @media (min-width: 768px) { + .row--space { + margin-bottom: 115px; } } +.row--mb { + margin-bottom: 30px; } + @media (min-width: 768px) { + .row--mb { + margin-bottom: 75px; } } +@media (min-width: 768px) { + .row--lg-no-gutters { + margin-right: 0; + margin-left: 0; } + .row--lg-no-gutters > .col, + .row--lg-no-gutters > [class*="col-"] { + padding-right: 0; + padding-left: 0; } } + +.container-fluid--custom-sm { + max-width: 1140px; + margin: 0 auto; } + +@media (min-width: 768px) { + .col-sm-9.no-padding { + padding: 0; } } + +.docs-experimental h1 { + margin-top: 0; } + @media (min-width: 768px) { + .docs-experimental h1 { + margin-top: -50px; } } + +.label { + margin-left: -14px; + margin-top: -2px; + top: 60px; + /* Portrait and Landscape iPhones 4, 5, 5SE */ + /* Portrait and Landscape iPhones 6+, 7+ and 8+ */ } + @media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2) { + .label { + margin-top: 2px; } } + @media only screen and (min-device-width: 414px) and (max-device-width: 736px) and (-webkit-min-device-pixel-ratio: 3) { + .label { + margin-top: 2px; } } + +.label img { + width: 20%; } + @media (min-width: 576px) { + .label img { + width: 15%; } } + +.d-flex div[class*='col-'] { + margin-bottom: 20px; } + @media (min-width: 768px) { + .d-flex div[class*='col-'] { + margin-bottom: 30px; } } + .wrapper-content .d-flex div[class*='col-'] { + margin-bottom: 0; } + +h4.tagline { + color: #ffffff; + font-weight: 200; + margin-top: 20px; } + @media only screen and (min-width: 320px) and (max-width: 767px) { + h4.tagline { + text-align: center; } } + +a.btn.btn-primary.start { + font-family: Oracle Sans, sans-serif; + font-style: normal; + font-weight: 400; + font-size: 18px; + line-height: 24px; + color: #FFFFFF; + border-radius: 12px; + padding: 6px 0 6px 0; + text-transform: none; + margin: 0px 24px 40px 0px; + width: 180px; } + @media only screen and (min-width: 320px) and (max-width: 480px) { + a.btn.btn-primary.start { + margin: 0px 0px 40px 0px; + width: 250px; } } + +.title-section { + color: #171F26; + margin-bottom: 20px; + font-size: 2.63em; + margin-top: 0; + font-weight: 300; } + .title-section span { + display: block; + color: rgba(68, 80, 95, 0.5); + font-size: 18px; + font-weight: 400; } + @media (min-width: 768px) { + .title-section { + margin-bottom: 45px; } } + .title-section--white { + color: #fff; } + +.title-article { + color: #171F26; + font-size: 1.75em; + font-weight: 600; + line-height: 32px; + margin-bottom: 10px; + margin-top: 0; } + +.title-footer { + color: rgba(255, 255, 255, 0.8); + font-size: 14px; + font-weight: bold; + margin-top: 0; } + +.title-faq { + position: relative; + justify-content: space-between; + align-items: center; + font-size: 1.1em; + border-radius: 4px; + border: 1px solid #dce1e9; + padding: 20px; + display: flex; } + .title-faq:after { + content: ""; + width: 28px; + min-width: 28px; + flex-basis: 28px; + height: 28px; + border: 1px solid #dce1e9; + border-radius: 3px; + text-align: center; + cursor: pointer; } + .title-faq:before { + position: absolute; + content: ""; + top: 50%; + right: 31px; + width: 8px; + height: 8px; + margin-left: 5px; + border-top: 1px solid #171F26; + border-right: 1px solid #171F26; + transform: translateY(-50%) rotate(135deg); } + @media (min-width: 768px) { + .title-faq { + padding: 20px 35px; } + .title-faq:before { + right: 45px; } } + .title-faq--opened:before { + transform: translateY(-50%) rotate(-45deg); } + .title-faq:focus { + border-color: #f29110; + outline: none; } + +.title-table { + font-size: 1.3em; + margin-bottom: 10px; } + @media (min-width: 768px) { + .title-table { + position: relative; + left: auto; + right: auto; } } + +.copyright { + padding: 15px 0; + border-top: 1px solid #C4C4C4; + color: rgba(255, 255, 255, 0.6); + font-size: 13px; + font-weight: 400; + text-align: center; + margin: 20px 0 0 0; } + +.toc-bullets { + padding-top: 20px; + padding-left: 0; + margin-bottom: 0; } + .toc-bullets li { + font-size: 0.8rem; + list-style-type: none; + padding-left: 0; } + .toc-bullets li:before { + display: none; } + .toc-bullets li a { + text-decoration: none; + color: #757c8b; + padding: 4px 0; + display: block; + transition: color .2s; } + .toc-bullets li a:hover { + text-decoration: none; + color: #4a5366; } + .toc-bullets li.toc-bullets-focused { + color: #4a5366; + font-weight: 600; } + .toc-bullets li.toc-bullets-focused a { + color: #4a5366; + font-weight: 600; } + .toc-bullets li.toc-bullets-sub:first-of-type { + margin-top: 5px; } + .toc-bullets li.toc-bullets-sub a { + padding-left: 20px; } + .toc-bullets li.toc-bullets-sub-3 a { + padding-left: 40px; } + .toc-bullets li.toc-bullets-main.toc-bullets-highlight a { + color: #4a5366; + font-weight: 600; } + .toc-bullets li.toc-bullets-highlight a { + color: #4a5366; + font-weight: 600; } + .toc-bullets--horizontal { + position: relative; + padding: 5px 15px; + text-align: center; + border-bottom: 1px solid rgba(0, 0, 0, 0.15); + white-space: nowrap; + overflow-x: auto; } + @media (min-width: 768px) { + .toc-bullets--horizontal { + overflow: visible; } } + .toc-bullets--horizontal li { + border-bottom: 0; + display: inline-block; } + .toc-bullets--horizontal li + li { + margin-left: 30px; } + .toc-bullets--horizontal li a { + color: #8e99a8; + font-size: 15px; + line-height: 32px; } + .toc-bullets--horizontal li.toc-bullets-highlight a { + color: #171F26; + font-weight: 600; } + .toc-bullets--horizontal.toc-bullets--horizontal-stiky { + position: fixed; + left: 0; + top: 72px; + width: 100%; + padding-top: 0; + z-index: 10; + background-color: #ffffff; } + @media (min-width: 768px) { + .toc-bullets--horizontal.toc-bullets--horizontal-stiky { + top: 74px; } } + +section.content-section.toc { + padding-bottom: 50px; + /* Portrait and Landscape iPhones 4, 5, 5SE */ + /* Portrait and Landscape iPhones 6+, 7+ and 8+ */ } + @media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2) { + section.content-section.toc { + padding-bottom: 0; } } + @media only screen and (min-device-width: 414px) and (max-device-width: 736px) and (-webkit-min-device-pixel-ratio: 3) { + section.content-section.toc { + padding-bottom: 0; } } + +ul.tools-toc { + padding-left: 10px; + margin: 0; } + +.tools a.menu__link { + color: #193e47; + font-size: 1em; + line-height: 1.8em; + font-weight: 400; + cursor: pointer; } + .tools a.menu__link:hover { + color: #d9810c; } + +li.tools { + float: left; + padding: 0px 35px 0 10px; + /* Portrait and Landscape iPhones 4, 5, 5SE */ + /* Portrait and Landscape iPhones 6+, 7+ and 8+ */ } + li.tools::before { + content: none; } + @media only screen and (min-device-width: 320px) and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2) { + li.tools { + float: inherit; } } + @media only screen and (min-device-width: 414px) and (max-device-width: 736px) and (-webkit-min-device-pixel-ratio: 3) { + li.tools { + float: inherit; } } + +.page-heading { + color: #ffffff; + font-weight: 300; } + .page-heading__title { + font-size: 2.5em; + margin-bottom: 10px; + margin-top: 0; + font-weight: 300; } + .page-heading__description { + color: inherit; + font-size: 1.2em; } + +.tabs-below { + display: flex; + flex-direction: column; } + .tabs-below .tab-pane { + display: none; } + .tabs-below .tab-pane.active { + display: block; } + .tabs-below .nav-tabs { + order: -1; + display: flex; + padding: 7px 15px; + border-radius: 8px 8px 0 0; + background-image: linear-gradient(180deg, #e7e6e7 0%, #d1d0d1 100%); + margin-bottom: -7px; } + .tabs-below .nav-tabs li { + list-style: none; + padding-left: 0; + margin-bottom: 0; } + .tabs-below .nav-tabs li:before { + display: none; } + .tabs-below .nav-tabs li + li { + margin-left: 16px; } + .tabs-below .nav-tabs li a { + color: rgba(23, 31, 38, 0.9); + font-size: 14px; } + .tabs-below .nav-tabs li a:hover { + color: #f29110; } + .tabs-below .nav-tabs li.active a { + font-weight: 700; + color: #f29110; } + +.fade { + opacity: 1; + transition: opacity .3s; } + .fade.in { + opacity: 1; } + +table { + margin-top: 30px; + table-layout: fixed; + width: 100%; } + @media (max-width: 991.98px) { + table { + table-layout: auto !important; } } + @media (min-width: 768px) { + table { + margin-top: 0; } } + table thead th { + font-weight: 600; + font-size: 14px; + text-align: center; + background-color: rgba(242, 243, 249, 0.5); } + table td { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; } + +.results-table { + white-space: nowrap; + overflow-x: auto; + margin-bottom: 30px; } + +/* Tomorrow Night Eighties Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ +/* Tomorrow Comment */ +.hljs-comment, +.hljs-quote { + color: #999999; } + +/* Tomorrow Red */ +.hljs-variable, +.hljs-template-variable, +.hljs-tag, +.hljs-name, +.hljs-selector-id, +.hljs-selector-class, +.hljs-regexp, +.hljs-deletion { + color: #f2777a; } + +/* Tomorrow Orange */ +.hljs-number, +.hljs-built_in, +.hljs-builtin-name, +.hljs-literal, +.hljs-type, +.hljs-params, +.hljs-meta, +.hljs-link { + color: #f99157; } + +/* Tomorrow Yellow */ +.hljs-attribute { + color: #ffcc66; } + +/* Tomorrow Green */ +.hljs-string, +.hljs-symbol, +.hljs-bullet, +.hljs-addition { + color: #99cc99; } + +/* Tomorrow Blue */ +.hljs-title, +.hljs-section { + color: #6699cc; } + +/* Tomorrow Purple */ +.hljs-keyword, +.hljs-selector-tag { + color: #cc99cc; } + +@media (min-width: 768px) { + .hljs { + padding: 0.5em 12px !important; } } + +.hljs-emphasis { + font-style: italic; } + +.hljs-strong { + font-weight: bold; } + +/* Slider */ +.slick-slider { + position: relative; + display: block; + box-sizing: border-box; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -ms-touch-action: pan-y; + touch-action: pan-y; + -webkit-tap-highlight-color: transparent; } + +.slick-list { + position: relative; + overflow: hidden; + display: block; + margin: 0; + padding: 0; } + .slick-list.dragging { + cursor: pointer; + cursor: hand; } + +.slick-slider .slick-track, +.slick-slider .slick-list { + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); } + +.slick-track { + position: relative; + left: 0; + top: 0; + display: block; + margin-left: auto; + margin-right: auto; } + .slick-track:before, .slick-track:after { + content: ""; + display: table; } + .slick-track:after { + clear: both; } + .slick-loading .slick-track { + visibility: hidden; } + +.slick-slide { + float: left; + height: 100%; + min-height: 1px; + display: none; } + [dir="rtl"] .slick-slide { + float: right; } + .slick-slide img { + display: block; } + .slick-slide.slick-loading img { + display: none; } + .slick-slide.dragging img { + pointer-events: none; } + .slick-initialized .slick-slide { + display: block; } + .slick-loading .slick-slide { + visibility: hidden; } + .slick-vertical .slick-slide { + display: block; + height: auto; + border: 1px solid transparent; } + +.slick-arrow.slick-hidden { + display: none; } + +/* Slider */ +.slick-loading .slick-list { + background: #fff url("../resources/img/ajax-loader.gif") center center no-repeat; } + +/* Icons */ +@font-face { + font-family: "slick"; + src: url("../resources/fonts/slick-fonts/slick.eot"); + src: url("../resources/fonts/slick-fonts/slick.eot?#iefix") format("embedded-opentype"), url("../resources/fonts/slick-fonts/slick.woff") format("woff"), url("../resources/fonts/slick-fonts/slick.ttf") format("truetype"), url("../resources/fonts/slick-fonts/slick.svg#slick") format("svg"); + font-weight: normal; + font-style: normal; } +/* Arrows */ +.slick-prev, +.slick-next { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + height: 36px; + width: 36px; + line-height: 0; + font-size: 0; + cursor: pointer; + background: transparent; + color: transparent; + top: 50%; + transform: translate(0, -50%); + padding: 0; + border-radius: 4px; + border: 1px solid #dce1e9; + outline: none; + transition: border-color .3s; } + .slick-prev:hover, + .slick-next:hover { + outline: none; + background: transparent; + color: transparent; } + .slick-prev.slick-disabled:before, + .slick-next.slick-disabled:before { + opacity: 0.25; } + .slick-prev:before, + .slick-next:before { + content: ""; + width: 15px; + height: 2px; + margin: 0 auto; + position: absolute; + left: 0; + right: 0; + background-color: #dce1e9; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + transition: background-color .3s; } + .slick-prev:after, + .slick-next:after { + position: absolute; + content: ""; + top: 50%; + width: 10px; + height: 10px; + border-top: 2px solid #dce1e9; + border-left: 2px solid #dce1e9; + transition: border-color .3s; } + .slick-prev:hover, + .slick-next:hover { + border-color: #f29110; } + .slick-prev:hover:before, + .slick-next:hover:before { + background-color: #f29110; } + .slick-prev:hover:after, + .slick-next:hover:after { + border-color: #f29110; } + +.slick-prev { + left: -75px; } + [dir="rtl"] .slick-prev { + left: auto; + right: -25px; } + .slick-prev:after { + left: 9px; + transform: translateY(-50%) rotate(-45deg); } + +.slick-next { + right: -75px; } + [dir="rtl"] .slick-next { + left: -25px; + right: auto; } + .slick-next:after { + right: 9px; + transform: translateY(-50%) rotate(135deg); } + +/* Dots */ +.slick-dotted.slick-slider { + margin-bottom: 30px; } + +.slick-dots { + position: absolute; + bottom: -65px; + list-style: none; + display: block; + text-align: center; + padding: 0; + margin: 0; + width: 100%; } + .slick-dots li { + position: relative; + display: inline-block; + height: 20px; + width: 20px; + margin: 0 5px; + padding: 0; + padding-left: 0; + cursor: pointer; } + .slick-dots li:before { + display: none; } + .slick-dots li button { + border: 0; + background: transparent; + display: block; + height: 20px; + width: 20px; + outline: none; + line-height: 0px; + font-size: 0px; + color: transparent; + padding: 5px; + cursor: pointer; } + .slick-dots li button:hover { + outline: none; } + .slick-dots li button:hover:before { + background-color: #f29110; } + .slick-dots li button:before { + position: absolute; + top: 0; + left: 0; + content: ""; + width: 8px; + height: 8px; + line-height: 20px; + text-align: center; + background-color: #e2e7ee; + transition: background-color .3s; } + .slick-dots li.slick-active button:before { + background-color: #f29110; } + +.slick-dotted.slick-slider { + margin-bottom: 60px; } + +.slick-active:focus, +.slick-current:focus { + outline: -webkit-focus-ring-color auto 5px; } + +.preload * { + -webkit-transition: none !important; + -moz-transition: none !important; + -ms-transition: none !important; + -o-transition: none !important; + transition: none; } + +div.col-no-padding { + padding-left: 0; + padding-right: 0; } + +span.post-date { + font-size: 12px; } + +div.faded-line { + height: 1px; + background: linear-gradient(to right, rgba(30, 87, 153, 0) 0%, rgba(0, 0, 0, 0.2) 51%, rgba(125, 185, 232, 0) 100%); + margin-top: 3px; } + +a.see-more-button { + text-decoration: none; } + +a span.see-more-button, a:visited span.see-more-button { + position: relative; + left: 50%; + top: -14px; + font-family: Arial; + font-weight: bolder; + text-transform: uppercase; + font-size: 10px; + padding: 4px; + color: #999; + text-decoration: none; + transition: color 0.2s linear; + background-color: #dedede; + border: 2px solid #ccc; + border-radius: 4px; } + +a:hover span.see-more-button { + transition: all 0.2s linear; + color: #059; + background-color: #bcf; + border: 2px solid #059; + text-decoration: none; } + +div.flywheel { + margin-top: 80px; + margin-bottom: 80px; } + +div.main-description { + margin: auto; + width: 95%; + padding: 10px; + text-align: center; + font-size: 18px; } + +div.snippet-container { + position: relative; } + +div.snippet-container span.copy-button { + transition: opacity .1s; + cursor: pointer; + position: absolute; + display: inline; + float: right; + width: 25px; + height: 25px; + right: 12px; + top: 15px; + text-align: center; } + +div.snippet-container span.copy-button:hover img { + opacity: 0.7; } diff --git a/native-image/spring-boot-webserver/src/main/resources/static/ci.jsonnet b/native-image/spring-boot-webserver/src/main/resources/static/ci.jsonnet new file mode 100644 index 0000000..b26042e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/ci.jsonnet @@ -0,0 +1,95 @@ +{ + specVersion: "2", + local jekyll_common(version) = { + capabilities: ["linux", "amd64"], + # Necessary to use the precompiled nokogiri + docker: { + image: "phx.ocir.io/oraclelabs2/c_graal/buildslave:buildslave_ol7", + mount_modules: true, + }, + packages: { + ruby: "==2.6.6", + libffi: ">=3.2.1", + nodejs: ">=8.9.4", + git: ">=2.9.3", + }, + downloads: { + LIBGMP: {name: "libgmp", version: "6.1.0", platformspecific: true} + }, + environment: { + JEKYLL_ENV: "production", + BUNDLE_PATH: "$PWD/../bundle-path", + CPPFLAGS: "-I$LIBGMP/include", + LD_LIBRARY_PATH: "$LIBGMP/lib:$LD_LIBRARY_PATH", + LIBRARY_PATH: "$LIBGMP/lib:$LIBRARY_PATH", + CI: "true", + DOCS_VERSION: version + }, + timelimit: "20:00", + setup: [ + ["mkdir", "../gem-home"], + ["export", "GEM_HOME=$PWD/../gem-home"], + ["gem", "install", "--no-document", "bundler", "-v", "2.1.4"], + ["export", "PATH=$GEM_HOME/bin:$PATH"], + ["bundle", "install"], + ], + }, + + local jekyll_build(version) = jekyll_common(version) + { + run: [ + ["gem", "install", "nokogiri", "-v", "1.13.10"], + ["./_scripts/build.sh"], + ["head", version + "/index.html"], + ["echo", "Checking that top-level directory has no unexpected entries"], + ["ls", "-1", version, "|", "sort", ">", "_actual-sorted-output.txt"], + ["sort", "_scripts/expected-output.txt", ">", "_expected-sorted-output.txt"], + ["diff", "_actual-sorted-output.txt", "_expected-sorted-output.txt", "||", "exit", "23"], + ], + publishArtifacts: [ + { + name: "jekyll_build_artifact_" + version, + dir: version, + patterns: ["*"], + } + ] + }, + + local publish_staging(version) = { + capabilities: ["linux", "amd64"], + requireArtifacts: [ + { + name: "jekyll_build_artifact_" + version, + dir: version, + } + ], + run: [ + ["rsync", "-rlv", "-e", "ssh -p 2224 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no", "--exclude-from", "_rsync-excluded", version + "/", "graal@ol-graal-infra4.oraclecorp.com:/var/www/html/" + version] + ] + }, + + local publish_github(version) = { + capabilities: ["linux", "amd64"], + requireArtifacts: [ + { + name: "jekyll_build_artifact_" + version, + dir: version, + } + ], + run: [ + ["git", "clone", "ssh://git@ol-bitbucket.us.oracle.com:7999/g/graalvm-website.git"], + ["rsync", "-a", "--delete", "--exclude-from", "_rsync-excluded", "--filter", "P /*/javadoc/", version + "/", "graalvm-website/" + version], + ["git", "-C", "graalvm-website", "add", "."], + ["git", "-C", "graalvm-website", "status"], + ["git", "-C", "graalvm-website", "-c", "user.name=Web Publisher", "-c", "user.email=graalvm-dev@oss.oracle.com", "commit", "-m", "Update website"], + ["git", "-C", "graalvm-website", "push", "origin", "HEAD"], + ["git", "branch", "--force", "--no-track", "published"], + ["git", "push", "--force", "origin", "published"], + ] + }, + + builds: std.flattenArrays([[ + jekyll_build(version) + {name: "jekyll-build-"+version, targets: ["gate"]}, + publish_staging(version) + {name: "deploy-staging-"+version, targets: ["deploy"] }, # execute manually + publish_github(version) + {name: "deploy-production-"+version, targets: ["deploy"]}, # execute manually + ] for version in ["21.3", "22.0", "22.1", "22.2", "22.3", "jdk20"]]) +} \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/ci/run-spec.md b/native-image/spring-boot-webserver/src/main/resources/static/docs/ci/run-spec.md new file mode 100644 index 0000000..eb2db6c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/ci/run-spec.md @@ -0,0 +1,20 @@ +# CI Run Specification + +The CI run specification is a jsonnet library to declaratively express on which _platform_ (os/arch/jdk) +and at which _frequency_ (gate/daily/weekly/etc) to run a certain job. +The main idea is to have a single point that describes _what a task does_ (e.g. run `mx unittest`) that is decoupled (logically but not spatially) from +_where_ and _when_ to execute it. + +The entry point of the library is [`run-spec.jsonnet`](https://github.com/oracle/graal/blob/ci/ci_common/run-spec.jsonnet). +Examples and further details can be found in [`run-spec-demo.jsonnet`](https://github.com/oracle/graal/blob/ci/ci_common/run-spec-demo.jsonnet) +and [`run-spec-examples.jsonnet`](https://github.com/oracle/graal/blob/ci/ci_common/run-spec-examples.jsonnet). + +## Terminology + +* A `platform` is defined by an os, an architecture and a jdk. +* A `task` is the definition of something we want to execute without consideration of the platform. +* `variants` of task generate a set of tasks based on user slight alterations of a task. +* `batches` of a task splits a large task into N smaller tasks. +* A `frequency` defines how often a task must be executed: `gate`, `postmerge`, `daily`, `weekly`, `monthly`, `ondemand`. +* A `job` is the complete definition of a runnable CI job. It is defined by a task, a platform and a frequency. +* A `build` is the result of job execution within a specific CI system. It is identified with a unique number. diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/container-images/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/container-images/index.html new file mode 100644 index 0000000..58567e1 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/container-images/index.html @@ -0,0 +1,215 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

GraalVM Community Edition Container Images

+ +

To support container-based development, GraalVM Community Edition container images are published in the GitHub Container Registry.

+ +

Repositories

+ +

There are different GraalVM Community Edition container images provided depending on the architecture and the Java version. +The container image repositories for the latest GraalVM versions (GraalVM for JDK 17 and GraalVM for JDK 20) have a -community suffix. +These are: native-image-community, jdk-community, truffleruby-community, nodejs-community, and graalpy-community. +The images are multi-arch, for AMD64 and AArch64 processor architectures, with a choice of Oracle Linux versions 7, 8, or 9.

+ +

GraalVM is installed in /usr/lib64/graalvm/graalvm-java<$FeatureVersion> where <$FeatureVersion> is 17, 20, etc. +For instance, GraalVM for JDK 17 is installed in /usr/lib64/graalvm/graalvm-java17. +All binaries, including java, javac, native-image, and other binaries are available as global commands via the alternatives command.

+ +

See a full list of GraalVM Community Edition container images here.

+ +

Tags

+ +

Each repository provides multiple tags that let you choose the level of stability you need including the Java version, build number, and the Oracle Linux version. +Image tags use the following naming convention:

+ +
$version[-muslib(for native image only)][-$platform][-$buildnumber]
+
+ +

The following tags are listed from the most-specific tag (at the top) to the least-specific tag (at the bottom). +The most-specific tag is unique and always points to the same image, while the less-specific tags point to newer image variants over time.

+ +
17.0.8-ol9-20230725 
+17.0.8-ol9 
+17.0.8 
+17-ol9 
+17
+
+ +

Pulling Images

+ +
    +
  1. To pull the container image for GraalVM JDK for a specific JDK feature version, e.g, 17, run: +
     docker pull ghcr.io/graalvm/jdk-community:17
    +
    + +

    Alternatively, to use the container image as the base image in your Dockerfile, use:

    +
     FROM ghcr.io/graalvm/jdk-community:17
    +
    + +

    You have pulled a size compact GraalVM Community Edition container image with the GraalVM JDK and the Graal compiler pre-installed.

    +
  2. +
  3. To pull the container image with the native-image utility for a specific JDK feature version, e.g, 17, run: +
     docker pull ghcr.io/graalvm/native-image-community:17
    +
    + +

    Alternatively, to pull the container image with the native-image utility with the musl libc toolchain to create fully statically linked executables, use:

    +
     docker pull ghcr.io/graalvm/native-image-community:17-muslib
    +
    + +

    Alternatively, to use the container image as the base image in your Dockerfile, use:

    +
     FROM ghcr.io/graalvm/native-image-community:17-muslib
    +
    +
  4. +
  5. To verify, start the container and enter the Bash session: +
     docker run -it --rm ghcr.io/graalvm/native-image-community:17 bash
    +
    + +

    To check the version of GraalVM and its installed location, run the env command from the Bash prompt:

    +
     env
    +
    + +

    The output shows the environment variable JAVA_HOME pointing to the installed GraalVM version and location.

    + +

    To check the Java version, run:

    +
     java -version
    +
    + +

    To check the native-image version, run:

    +
     native-image --version
    +
    +
  6. +
  7. Calling docker pull without specifying a processor architecture pulls container images for the processor architecture that matches your Docker client. To pull container images for a different platform architecture, specify the desired platform architecture with the --platform option and either linux/amd64 or linux/aarch64 as follows: +
     docker pull --platform linux/aarch64 ghcr.io/graalvm/native-image-community:17
    +
    +
  8. +
+ +

If you are looking for Oracle GraalVM container images, they are published in the Oracle Container Registry.

+ +

Learn More

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/desktop_development_with_C.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/desktop_development_with_C.png new file mode 100644 index 0000000..e63eda9 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/desktop_development_with_C.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/open_vs_installer.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/open_vs_installer.png new file mode 100644 index 0000000..0224cce Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/open_vs_installer.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/visual-studio-installed-components.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/visual-studio-installed-components.png new file mode 100644 index 0000000..d7a58b3 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/visual-studio-installed-components.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/visual_studio_installer.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/visual_studio_installer.png new file mode 100644 index 0000000..7119da6 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/visual_studio_installer.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/windows-10-installed.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/windows-10-installed.png new file mode 100644 index 0000000..742bd87 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/img/windows-10-installed.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/index.html new file mode 100644 index 0000000..26baaed --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/index.html @@ -0,0 +1,195 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Getting Started with GraalVM

+ +

GraalVM compiles your Java applications ahead of time into standalone binaries that start instantly, provide peak performance with no warmup, and use fewer resources.

+ +

Here you will find information about installing GraalVM and running basic applications with it.

+ +

If you are new to GraalVM, we recommend starting with the introduction to GraalVM, where you will find information about GraalVM’s benefits, distributions available, supported platforms, features support, and licensing.

+ +

If you have GraalVM already installed and have experience using it, you can skip this page and proceed to the in-depth reference manuals.

+ +

Choose your operating system and proceed to the installation steps for your specific platform:

+ + + +

Start Running Applications

+ +

The core distribution of GraalVM includes the Java Development Kit (JDK), the just-in-time compiler (the Graal compiler), Native Image, and others. +You can use the GraalVM JDK just like any other JDK in your IDE, so having installed GraalVM, you can run any Java application unmodified.

+ +

The java launcher runs the JVM with Graal as the last-tier compiler. +Check the installed Java version:

+
$JAVA_HOME/bin/java -version
+
+ +

Using GraalVM Native Image you can compile Java bytecode into a platform-specific, self-contained native executable to achieve faster startup and a smaller footprint for your application.

+ +

Compile this simple HelloWorld.java application to bytecode and then build a native executable:

+
public class HelloWorld {
+  public static void main(String[] args) {
+    System.out.println("Hello, World!");
+  }
+}
+
+ +
javac HelloWorld.java
+
+
native-image HelloWorld
+
+ +

The last command generates an executable file named helloworld in the current working directory. +Invoking it runs the natively compiled code of the HelloWorld class as follows:

+
./helloworld
+Hello, World!
+
+ +
+

Note: For compilation native-image depends on the local toolchain. Make sure your system meets the prerequisites.

+
+ + + +

New Users

+ +

Continue to Native Image basics for more information about the technology. +For users who are familiar with GraalVM Native Image but may have little experience using it, proceed to User Guides.

+ +

For more information on the Graal compiler, see the compiler documentation. +Larger Java examples can be found in the GraalVM Demos repository on GitHub.

+ +

Advanced Users

+ +

Developers who are more experienced with GraalVM or want to do more with GraalVM can proceed directly to Reference Manuals for in-depth documentation.

+ +

You can find information on GraalVM’s security model in the Security Guide, and rich API documentation in the GraalVM SDK Javadoc and Truffle Javadoc.

+ +

We also recommend checking our GraalVM Team Blog.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/linux/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/linux/index.html new file mode 100644 index 0000000..7be1ef2 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/linux/index.html @@ -0,0 +1,159 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Installation on Linux Platforms

+ +

GraalVM is available for Linux on x64 and AArch64 architectures.

+ +

You can install GraalVM on Linux from an archive (.tar.gz) for the current user into any location, without affecting other JDK installations.

+ +

Follow these steps to install GraalVM:

+ +
    +
  1. +

    Navigate to the GraalVM Downloads page. Select 17 or 20 for the Java version, Linux for the operating system, x64 or aarch64 for the architecture, and download.

    +
  2. +
  3. +

    Change to directory where you want to install GraalVM, then move the .tar.gz file to that directory.

    +
  4. +
  5. Unzip the archive: +
     tar -xzf graalvm-jdk-<version>_linux-<architecture>.tar.gz
    +
    +
  6. +
  7. There can be multiple JDKs installed on the machine. The next step is to configure the runtime environment: +
      +
    • Set the value of the PATH environment variable to the GraalVM bin directory: +
       export PATH=/path/to/<graalvm>/bin:$PATH
      +
      +
    • +
    • Set the value of the JAVA_HOME environment variable to the installation directory: +
       export JAVA_HOME=/path/to/<graalvm>
      +
      +
    • +
    +
  8. +
  9. To confirm that the installation was successful, run the java -version command.
  10. +
+ +

Optionally, you can specify GraalVM as the default JRE or JDK installation in your Java IDE.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/macos/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/macos/index.html new file mode 100644 index 0000000..9b3529f --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/macos/index.html @@ -0,0 +1,175 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Installation on macOS Platforms

+ +

GraalVM is available for macOS on x64 and AArch64 architectures.

+ +

Note that on macOS the JDK installation path is: _/Library/Java/JavaVirtualMachines//Contents/Home_.

+ +

Follow these steps to install GraalVM:

+ +
    +
  1. +

    Navigate to the GraalVM Downloads page. Select 17 or 20 for the Java version, macOS for the operating system, x64 or aarch64 for the architecture, and download.

    +
  2. +
  3. Unzip the archive. +
     tar -xzf graalvm-jdk-<version>_macos-<architecture>.tar.gz
    +
    +

    Alternatively, open the file in Finder.

    +
    +

    Note: If you are using macOS Catalina and later you may need to remove the quarantine attribute:

    +
     sudo xattr -r -d com.apple.quarantine /path/to/graalvm
    +
    +
    +
  4. +
  5. Move the downloaded package to its proper location, the /Library/Java/JavaVirtualMachines directory. Since this is a system directory, sudo is required: +
    sudo mv graalvm-jdk-<version>_macos-<architecture> /Library/Java/JavaVirtualMachines
    +
    +

    To verify if the move is successful and to get a list of all installed JDKs, run /usr/libexec/java_home -V.

    +
  6. +
  7. There can be multiple JDKs installed on the machine. The next step is to configure the runtime environment: +
      +
    • Set the value of the PATH environment variable to the GraalVM bin directory: +
       export PATH=/Library/Java/JavaVirtualMachines/<graalvm>/Contents/Home/bin:$PATH
      +
      +
    • +
    • Set the JAVA_HOME environment variable to resolve to the GraalVM installation directory: +
       export JAVA_HOME=/Library/Java/JavaVirtualMachines/<graalvm>/Contents/Home
      +
      +
    • +
    +
  8. +
  9. To check whether the installation was successful, run the java -version command.
  10. +
+ +

Optionally, you can specify GraalVM as the default JRE or JDK installation in your Java IDE.

+ +

Installation Notes

+ +

On JAVA_HOME Command

+

The information property file, Info.plist, is in the top level Contents folder. +This means that GraalVM participates in the macOS-specific /usr/libexec/java_home mechanism. Depending on other JDK installation(s) available, it is now possible that /usr/libexec/java_home -v17 returns /Library/Java/JavaVirtualMachines/<graalvm>/Contents/Home. +You can run /usr/libexec/java_home -v17 -V to see the complete list of JDK 17 JVMs available to the java_home command. This command sorts the JVMs in decreasing version order and chooses the top one as the default for the specified version. +Within a specific version, the sort order appears to be stable but is unspecified.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/windows/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/windows/index.html new file mode 100644 index 0000000..bcdb0e4 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/getting-started/windows/index.html @@ -0,0 +1,235 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Installation on Windows Platforms

+ +

GraalVM Community Edition is available for Windows on the x64 architecture. +You can install it on Windows from an archive file (zip).

+ +

Follow these steps to install GraalVM:

+ +
    +
  1. +

    Navigate to the GraalVM Downloads page. Select 17 or 20 for the Java version, Windows for the operating system, and download.

    +
  2. +
  3. +

    Change to the directory where you want to install GraalVM, then move the .zip archive file to it.

    +
  4. +
  5. +

    Unzip the archive to your file system.

    +
  6. +
  7. There can be multiple JDKs installed on the machine. The next step is to configure the runtime environment. Setting environment variables via the command line will work the same way for Windows 8, 10, and 11. +
      +
    • Set the value of the PATH environment variable to the GraalVM bin directory: +
       setx /M PATH "C:\Progra~1\Java\<graalvm>\bin;%PATH%"
      +
      +
    • +
    • Set the JAVA_HOME environment variable to resolve to the GraalVM installation directory: +
       setx /M JAVA_HOME "C:\Progra~1\Java\<graalvm>"
      +
      +

      Note that the /M flag, equivalent to -m, requires elevated user privileges.

      +
    • +
    +
  8. +
  9. Restart Command Prompt to reload the environment variables. Then use the following command to check whether the variables were set correctly: +
     echo %PATH%
    + echo %JAVA_HOME%
    +
    +
  10. +
+ +

Alternatively, you can set up environment variables through a Windows GUI:

+ +
    +
  1. Go to Windows Start Menu, then Settings, then Advanced.
  2. +
  3. Click Environment Variables. In the section “System Variables” find the JAVA_HOME variable and select it.
  4. +
  5. Click Edit.
  6. +
  7. Click New.
  8. +
  9. Click Browse to find the directory to add. Confirm by clicking OK.
  10. +
  11. Restart the Command Prompt to reload the environment variables.
  12. +
+ +

Repeat the same for the PATH environment variable.

+ +

Prerequisites for Native Image on Windows

+ +

On Windows, Native Image requires Visual Studio and Microsoft Visual C++(MSVC). +You can use Visual Studio 2022 version 17.1.0 or later.

+ +

Install Visual Studio Build Tools and Windows SDK

+ +
    +
  1. +

    Download the Visual Studio Build Tools (C development environment) from visualstudio.microsoft.com.

    +
  2. +
  3. +

    Start the Visual Studio Build Tools installation by clicking on the .exe file, and then press Continue:

    + +

    Install Visual Studio Build Tools

    +
  4. +
  5. +

    Check the Desktop development with C++ box in the main window. Also, on the right side under Installation Details, choose Windows SDK, and click the Install button.

    + +

    Select Desktop development with C++

    +
  6. +
  7. +

    After the installation completes, reboot your system.

    +
  8. +
  9. +

    Next ensure Windows SDK is indeed available. Open the Visual Studio Installer:

    + +

    Open the Visual Studio Installer

    +
  10. +
  11. +

    Under the Installed tab, click Modify and choose Individual Components:

    + +

    Visual Studio Installed Components

    +
  12. +
  13. +

    Then scroll to the bottom and check if Windows SDK is installed and confirm the build tools are checked:

    + +

    Windows SDK Installed

    +
  14. +
+ +

Now that you have the Windows SDK and Visual Studio tooling installed, you can start using GraalVM Native Image.

+ +

Start Using Native Image

+ +

Native Image sets up build environments automatically if it can find an appropriate Visual Studio installation in a known location. +With the GraalVM JDK on your PATH, you can therefore now run the native-image utility in a Command Prompt (cmd) or a PowerShell (pwsh).

+ +

To check the native-image version, run:

+
C:\> native-image.cmd --version
+
+ +

To build a project using the Native Build Tools Maven plugin, run:

+
./mvnww.cmd native:compile
+
+ +

To build a project using the Native Build Tools Gradle plugin, run:

+
gradlew.bat nativeCompile
+
+ +

This guide was written for Windows 10, but should be valid for Windows 8 and 11.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-as-a-platform/implement-instrument/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-as-a-platform/implement-instrument/index.html new file mode 100644 index 0000000..0617988 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-as-a-platform/implement-instrument/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-as-a-platform/implement-language/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-as-a-platform/implement-language/index.html new file mode 100644 index 0000000..0625a10 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-as-a-platform/implement-language/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-as-a-platform/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-as-a-platform/index.html new file mode 100644 index 0000000..78344ce --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-as-a-platform/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-updater/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-updater/index.html new file mode 100644 index 0000000..1c25849 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/graalvm-updater/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/add-project-sdk.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/add-project-sdk.png new file mode 100644 index 0000000..32e7f7d Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/add-project-sdk.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/debug-jre-configuration.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/debug-jre-configuration.png new file mode 100644 index 0000000..cd8769b Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/debug-jre-configuration.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/generate-sources-maven.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/generate-sources-maven.png new file mode 100644 index 0000000..ab0b886 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/generate-sources-maven.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/open-heap-dump.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/open-heap-dump.png new file mode 100644 index 0000000..5bde433 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/open-heap-dump.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/run-jre-configurations.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/run-jre-configurations.png new file mode 100644 index 0000000..3ed89e2 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/run-jre-configurations.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/take-heap-dump.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/take-heap-dump.png new file mode 100644 index 0000000..92b3d06 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/take-heap-dump.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/visualvm.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/visualvm.png new file mode 100644 index 0000000..76dcc1c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/guides/img/visualvm.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/img/github-web-editor.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/img/github-web-editor.png new file mode 100644 index 0000000..03f2059 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/img/github-web-editor.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/img/graalvm_architecture_community.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/img/graalvm_architecture_community.png new file mode 100644 index 0000000..708abeb Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/img/graalvm_architecture_community.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/introduction/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/introduction/index.html new file mode 100644 index 0000000..a8e594a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/introduction/index.html @@ -0,0 +1,163 @@ + + + + + + + + + + + + + Overview + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

GraalVM Overview

+ +

GraalVM compiles your Java applications ahead of time into standalone binaries. +These binaries are smaller, start up to 100x faster, provide peak performance with no warmup, and use less memory and CPU than applications running on a Java Virtual Machine (JVM).

+ +

GraalVM reduces the attack surface of your application. +It excludes unused classes, methods, and fields from the application binary. +It restricts reflection and other dynamic Java language features to build time only. +It does not load any unknown code at run time.

+ +

Popular microservices frameworks such as Spring Boot, Micronaut, Helidon, and Quarkus, and cloud platforms such as Oracle Cloud Infrastructure, Amazon Web Services, Google Cloud Platform, and Microsoft Azure all support GraalVM.

+ +

With profile-guided optimization and the G1 (Garbage-First) garbage collector, you can get lower latency and on-par or better peak performance and throughput compared to applications running on a Java Virtual Machine (JVM).

+ +

You can use the GraalVM JDK just like any other Java Development Kit in your IDE.

+ +

Licensing and Support

+ +

Oracle GraalVM is licensed under GraalVM Free Terms and Conditions (GFTC) including License for Early Adopter Versions. +Subject to the conditions in the license, including the License for Early Adopter Versions, the GFTC is intended to permit use by any user including commercial and production use. Redistribution is permitted as long as it is not for a fee. +Oracle GraalVM is also free to use on Oracle Cloud Infrastructure. +For more information about Oracle GraalVM licensing, see the Oracle Java SE Licensing FAQ.

+ +

GraalVM Community Edition is open-source software built from the sources available on GitHub and distributed under version 2 of the GNU General Public License with the “Classpath” Exception, which are the same terms as for Java. +Check the licenses of individual GraalVM components which are generally derivative of the license of a particular language and may differ.

+ + + +

Start with installing GraalVM by following the installation guide.

+ +

Whether you are new to GraalVM Native Image or have little experience using it, continue to Getting Started.

+ +

After that we suggest you to take look at User Guides.

+ +

Developers who have experience using GraalVM and Native Image can proceed to the Reference Manuals for in-depth coverage.

+ +

To start coding with GraalVM APIs, check the GraalVM SDK Java API Reference.

+ +

If you cannot find the answer you need in the available documentation or have a troubleshooting query, you can ask for help in a Slack channel or submit a GitHub issue.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/polyglot-programming/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/polyglot-programming/index.html new file mode 100644 index 0000000..edc4ea9 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/polyglot-programming/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/img/reflect_config_file_example.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/img/reflect_config_file_example.png new file mode 100644 index 0000000..bd18e32 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/img/reflect_config_file_example.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/img/reflect_config_file_merged.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/img/reflect_config_file_merged.png new file mode 100644 index 0000000..d0b0de7 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/img/reflect_config_file_merged.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/index.html new file mode 100644 index 0000000..9f47d86 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/add-project-default-sdk.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/add-project-default-sdk.png new file mode 100644 index 0000000..d31fe98 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/add-project-default-sdk.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-1.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-1.png new file mode 100644 index 0000000..b028538 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-1.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-2.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-2.png new file mode 100644 index 0000000..28e822a Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-2.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-3.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-3.png new file mode 100644 index 0000000..a278529 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-3.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-4.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-4.png new file mode 100644 index 0000000..0fe89c4 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-4.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-5.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-5.png new file mode 100644 index 0000000..0aeb04e Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-5.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-6.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-6.png new file mode 100644 index 0000000..a120ce2 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-6.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-configuration.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-configuration.png new file mode 100644 index 0000000..fa97962 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/debug-configuration.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/generate-project-sources.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/generate-project-sources.png new file mode 100644 index 0000000..d593b51 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/generate-project-sources.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/pass-vmoption.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/pass-vmoption.png new file mode 100644 index 0000000..dcbed4b Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/java-on-truffle/images/pass-vmoption.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/build-artifacts-schema-v0.9.0.json b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/build-artifacts-schema-v0.9.0.json new file mode 100644 index 0000000..36562e2 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/build-artifacts-schema-v0.9.0.json @@ -0,0 +1,104 @@ +{ + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/build-artifacts-schema-v0.9.0.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "default": {}, + "examples": [ + { + "build_info": [ + "build-output.json" + ], + "debug_info": [ + "helloworld", + "sources" + ], + "executables": [ + "helloworld" + ] + } + ], + "properties": { + "build_info": { + "default": [], + "items": { + "format": "uri-reference", + "title": "Relative path to file or directory", + "type": "string" + }, + "title": "Build information generated by Native Image (not needed at run-time)", + "type": "array" + }, + "c_headers": { + "default": [], + "items": { + "format": "uri-reference", + "title": "Relative path to file or directory", + "type": "string" + }, + "title": "C header files generated by Native Image (not needed at run-time)", + "type": "array" + }, + "debug_info": { + "default": [], + "items": { + "format": "uri-reference", + "title": "Relative path to file or directory", + "type": "string" + }, + "title": "Debug information generated by Native Image (not needed at run-time)", + "type": "array" + }, + "executables": { + "default": [], + "items": { + "format": "uri-reference", + "title": "Relative path to file or directory", + "type": "string" + }, + "title": "Executables generated by Native Image (needed at run-time)", + "type": "array" + }, + "import_libraries": { + "default": [], + "items": { + "format": "uri-reference", + "title": "Relative path to file or directory", + "type": "string" + }, + "title": "Import libraries generated by Native Image (not needed at run-time)", + "type": "array" + }, + "jdk_libraries": { + "default": [], + "items": { + "format": "uri-reference", + "title": "Relative path to file or directory", + "type": "string" + }, + "title": "JDK libraries copied by Native Image (needed at run-time)", + "type": "array" + }, + "language_home": { + "default": [], + "items": { + "format": "uri-reference", + "title": "Relative path to file or directory", + "type": "string" + }, + "title": "Language home artifacts for Truffle languages (needed at run-time)", + "type": "array" + }, + "shared_libraries": { + "default": [], + "items": { + "format": "uri-reference", + "title": "Relative path to file or directory", + "type": "string" + }, + "title": "Shared libraries generated by Native Image (not needed at run-time)", + "type": "array" + } + }, + "required": [], + "title": "JSON schema for the build artifacts of GraalVM Native Image", + "type": "object" +} diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/build-output-schema-v0.9.0.json b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/build-output-schema-v0.9.0.json new file mode 100644 index 0000000..510eb42 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/build-output-schema-v0.9.0.json @@ -0,0 +1,540 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/build-output-schema-v0.9.0.json", + "type": "object", + "default": {}, + "title": "Schema for the JSON build output of GraalVM Native Image", + "required": [ + "general_info", + "analysis_results", + "image_details", + "resource_usage" + ], + "properties": { + "general_info": { + "type": "object", + "default": {}, + "title": "General information about the build process", + "required": [ + "name", + "graalvm_version", + "java_version", + "c_compiler", + "garbage_collector" + ], + "properties": { + "name": { + "type": "string", + "default": "", + "title": "The name of the native executable" + }, + "graalvm_version": { + "type": "string", + "default": "", + "title": "The GraalVM Native Image version. This value is also used for the 'java.vm.version' property within the generated image" + }, + "java_version": { + "type": ["string", "null"], + "default": null, + "title": "The Java version of the Native Image build process (or null if not available)" + }, + "c_compiler": { + "type": ["string", "null"], + "default": null, + "title": "The C compiler used by the Native Image build process (or null if not available)" + }, + "garbage_collector": { + "type": "string", + "default": "", + "title": "The garbage collector used within the generated image" + } + }, + "examples": [ + { + "name": "helloworld", + "graalvm_version": "GraalVM 22.2.0-dev Java 17 CE", + "java_version": "17.0.4+5-jvmci-22.2-b03", + "c_compiler": "gcc (linux, x86_64, 9.3.0)", + "garbage_collector": "Serial GC" + } + ] + }, + "analysis_results": { + "type": "object", + "default": {}, + "title": "Information from the analysis", + "required": [ + "classes", + "fields", + "methods" + ], + "properties": { + "classes": { + "type": "object", + "default": {}, + "title": "Class information from the analysis", + "required": [ + "total", + "reachable", + "reflection", + "jni" + ], + "properties": { + "total": { + "type": "integer", + "default": 0, + "title": "The total number of classes" + }, + "reachable": { + "type": "integer", + "default": 0, + "title": "The number of reachable classes" + }, + "reflection": { + "type": "integer", + "default": 0, + "title": "The number of classes registered for reflection" + }, + "jni": { + "type": "integer", + "default": -1, + "title": "The number of classes registered for JNI access (or -1 if unset)" + } + } + }, + "fields": { + "type": "object", + "default": {}, + "title": "Field information from the analysis", + "required": [ + "total", + "reachable", + "reflection", + "jni" + ], + "properties": { + "total": { + "type": "integer", + "default": 0, + "title": "The total number of fields" + }, + "reachable": { + "type": "integer", + "default": 0, + "title": "The number of reachable fields" + }, + "reflection": { + "type": "integer", + "default": 0, + "title": "The number of fields registered for reflection" + }, + "jni": { + "type": "integer", + "default": -1, + "title": "The number of fields registered for JNI access (or -1 if unset)" + } + } + }, + "methods": { + "type": "object", + "default": {}, + "title": "Method information from the analysis", + "required": [ + "total", + "reachable", + "reflection", + "jni" + ], + "properties": { + "total": { + "type": "integer", + "default": 0, + "title": "The total number of methods" + }, + "reachable": { + "type": "integer", + "default": 0, + "title": "The number of reachable methods" + }, + "reflection": { + "type": "integer", + "default": 0, + "title": "The number of methods registered for reflection" + }, + "jni": { + "type": "integer", + "default": -1, + "title": "The number of methods registered for JNI access (or -1 if unset)" + } + } + } + }, + "examples": [ + { + "classes": { + "total": 3850, + "reachable": 2839, + "reflection": 28, + "jni": 58 + }, + "fields": { + "total": 6665, + "reachable": 3400, + "reflection": 0, + "jni": 58 + }, + "methods": { + "total": 29038, + "reachable": 12916, + "reflection": 332, + "jni": 52 + } + } + ] + }, + "image_details": { + "type": "object", + "default": {}, + "title": "Statistics about the generated native image", + "required": [ + "total_bytes", + "code_area", + "image_heap" + ], + "properties": { + "total_bytes": { + "type": "integer", + "default": 0, + "title": "The total number of bytes of the image" + }, + "code_area": { + "type": "object", + "default": {}, + "title": "Code area statistics", + "required": [ + "bytes", + "compilation_units" + ], + "properties": { + "bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for the code area" + }, + "compilation_units": { + "type": "integer", + "default": 0, + "title": "The number of compilation units in the image" + } + } + }, + "image_heap": { + "type": "object", + "default": {}, + "title": "Image heap statistics", + "required": [ + "bytes", + "resources" + ], + "properties": { + "bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for image heap" + }, + "resources": { + "type": "object", + "default": {}, + "title": "Resource statistics", + "required": [ + "count", + "bytes" + ], + "properties": { + "count": { + "type": "integer", + "default": 0, + "title": "Number of resources embedded in the image" + }, + "bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for resource data" + } + } + } + } + }, + "debug_info": { + "type": "object", + "default": {}, + "title": "Debug info statistics", + "required": [ + "bytes" + ], + "properties": { + "bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for debug info" + } + } + }, + "runtime_compiled_methods": { + "type": "object", + "default": {}, + "title": "Statistics on runtime compiled methods (optional)", + "required": [ + "count", + "graph_encoding_bytes" + ], + "properties": { + "count": { + "type": "integer", + "default": 0, + "title": "Number of runtime compiled methods" + }, + "graph_encoding_bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for graph encodings bytes" + } + } + } + }, + "examples": [ + { + "total_bytes": 13057934, + "code_area": { + "bytes": 4610048, + "compilation_units": 67007 + }, + "image_heap": { + "bytes": 7307264, + "resources": { + "count": 134, + "bytes": 10200 + } + }, + "debug_info": { + "bytes": 1140622 + } + } + ] + }, + "resource_usage": { + "type": "object", + "default": {}, + "title": "Resource usage statistics", + "required": [ + "cpu", + "garbage_collection", + "memory" + ], + "properties": { + "cpu": { + "type": "object", + "default": {}, + "title": "CPU usage statistics", + "required": [ + "load", + "total_cores" + ], + "properties": { + "load": { + "type": "number", + "default": -1, + "title": "The CPU load of the build process before terminating (or -1 if unavailable)" + }, + "total_cores": { + "type": "integer", + "default": 0, + "title": "The total number of cores of the build machine" + } + } + }, + "garbage_collection": { + "type": "object", + "default": {}, + "title": "Garbage collection usage statistics", + "required": [ + "count", + "total_secs" + ], + "properties": { + "count": { + "type": "integer", + "default": 0, + "title": "The number of GC cycles performed during image generation" + }, + "total_secs": { + "type": "number", + "default": 0.0, + "title": "The total number of seconds spent in GC" + } + } + }, + "memory": { + "type": "object", + "default": {}, + "title": "Memory usage statistics", + "required": [ + "system_total", + "peak_rss_bytes" + ], + "properties": { + "system_total": { + "type": "integer", + "default": 0, + "title": "The total number of bytes of available system memory" + }, + "peak_rss_bytes": { + "type": "integer", + "default": -1, + "title": "Peak RSS value of the image builder process in bytes (or -1 if unavailable)" + } + } + } + }, + "examples": [ + { + "cpu": { + "load": 8.38, + "total_cores": 10 + }, + "garbage_collection": { + "count": 17, + "total_secs": 0.9245 + }, + "memory": { + "system_total": 33254146048, + "peak_rss_bytes": 3506065408 + } + } + ] + } + }, + "examples": [ + { + "general_info": { + "name": "helloworld", + "graalvm_version": "GraalVM 22.2.0-dev Java 17 CE", + "java_version": "17.0.4+5-jvmci-22.2-b03", + "c_compiler": "gcc (linux, x86_64, 9.3.0)", + "garbage_collector": "Serial GC" + }, + "analysis_results": { + "classes": { + "total": 3850, + "reachable": 2839, + "reflection": 28, + "jni": 58 + }, + "fields": { + "total": 6665, + "reachable": 3400, + "reflection": 0, + "jni": 58 + }, + "methods": { + "total": 29038, + "reachable": 12916, + "reflection": 332, + "jni": 52 + } + }, + "image_details": { + "total_bytes": 13057934, + "code_area": { + "bytes": 4610048, + "compilation_units": 67007 + }, + "image_heap": { + "bytes": 7307264, + "resources": { + "count": 134, + "bytes": 10200 + } + }, + "debug_info": { + "bytes": 1140622 + } + }, + "resource_usage": { + "cpu": { + "load": 8.38, + "total_cores": 10 + }, + "garbage_collection": { + "count": 17, + "total_secs": 0.9245 + }, + "memory": { + "system_total": 33254146048, + "peak_rss_bytes": 3506065408 + } + } + }, + { + "general_info": { + "name": "helloworld", + "graalvm_version": "GraalVM 22.2.0-dev Java 17 CE", + "java_version": null, + "c_compiler": null, + "garbage_collector": "Serial GC" + }, + "analysis_results": { + "classes": { + "total": 3850, + "reachable": 2839, + "reflection": 28, + "jni": -1 + }, + "fields": { + "total": 6665, + "reachable": 3400, + "reflection": 0, + "jni": -1 + }, + "methods": { + "total": 29038, + "reachable": 12916, + "reflection": 332, + "jni": -1 + } + }, + "image_details": { + "total_bytes": 13057934, + "code_area": { + "bytes": 4610048, + "compilation_units": 67007 + }, + "image_heap": { + "bytes": 7307264, + "resources": { + "count": 134, + "bytes": 10200 + } + }, + "runtime_compiled_methods": { + "count": 12744, + "graph_encoding_bytes": 1440000 + } + }, + "resource_usage": { + "cpu": { + "load": -1, + "total_cores": 10 + }, + "garbage_collection": { + "count": 17, + "total_secs": 0.9245 + }, + "memory": { + "system_total": 33254146048, + "peak_rss_bytes": -1 + } + } + } + ] +} diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/build-output-schema-v0.9.1.json b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/build-output-schema-v0.9.1.json new file mode 100644 index 0000000..fe34535 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/build-output-schema-v0.9.1.json @@ -0,0 +1,658 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/build-output-schema-v0.9.1.json", + "type": "object", + "default": {}, + "title": "Schema for the JSON build output of GraalVM Native Image", + "required": [ + "general_info", + "analysis_results", + "image_details", + "resource_usage" + ], + "properties": { + "general_info": { + "type": "object", + "default": {}, + "title": "General information about the build process", + "required": [ + "name", + "graalvm_version", + "java_version", + "vendor_version", + "graal_compiler", + "c_compiler", + "garbage_collector" + ], + "properties": { + "name": { + "type": "string", + "default": "", + "title": "The name of the native executable" + }, + "graalvm_version": { + "type": "string", + "default": "", + "title": "The GraalVM Native Image version. Deprecated: Please use vendor_version or java_version instead." + }, + "java_version": { + "type": "string", + "default": null, + "title": "The Java version of the Native Image build process. This value is also used for the 'java.vm.version' property within the generated image" + }, + "vendor_version": { + "type": "string", + "default": null, + "title": "The vendor version of the VM of the generated image. This value is also used for the 'java.vendor.version' property within the generated image" + }, + "graal_compiler": { + "type": "object", + "default": {}, + "required": ["optimization_level", "march"], + "properties": { + "optimization_level": { + "type": "string", + "default": "", + "title": "The optimization level used by Graal" + }, + "march": { + "type": "string", + "default": "", + "title": "The machine type targeted by Graal" + }, + "pgo": { + "type": "array", + "items": { + "type": "string", + "default": "", + "enum": ["instrument", "user-provided", "ML-inferred"], + "title": "The name of the enabled PGO mode" + }, + "default": null, + "title": "The names of the enabled PGO modes (or null if PGO is not used) (Oracle GraalVM only)" + } + }, + "title": "Information about the Graal compiler" + }, + "c_compiler": { + "type": ["string", "null"], + "default": null, + "title": "The C compiler used by the Native Image build process (or null if not available)" + }, + "garbage_collector": { + "type": "string", + "default": "", + "title": "The garbage collector used within the generated image" + } + }, + "examples": [ + { + "name": "helloworld", + "graalvm_version": "GraalVM CE 17.0.7+4.1", + "graal_compiler": { + "march": "x86-64-v3", + "optimization_level": "0" + }, + "java_version": "17.0.7+4", + "vendor_version": "GraalVM CE 17.0.7+4.1", + "c_compiler": "gcc (linux, x86_64, 9.3.0)", + "garbage_collector": "Serial GC" + } + ] + }, + "analysis_results": { + "type": "object", + "default": {}, + "title": "Information from the analysis", + "required": [ + "types", + "fields", + "methods" + ], + "properties": { + "types": { + "type": "object", + "default": {}, + "title": "Type information from the analysis", + "required": [ + "total", + "reachable", + "reflection", + "jni" + ], + "properties": { + "total": { + "type": "integer", + "default": 0, + "title": "The total number of types" + }, + "reachable": { + "type": "integer", + "default": 0, + "title": "The number of reachable types" + }, + "reflection": { + "type": "integer", + "default": 0, + "title": "The number of types registered for reflection" + }, + "jni": { + "type": "integer", + "default": -1, + "title": "The number of types registered for JNI access (or -1 if unset)" + } + } + }, + "classes": { + "type": "object", + "default": {}, + "title": "Class information from the analysis (deprecated, use 'types')", + "required": [ + "total", + "reachable", + "reflection", + "jni" + ], + "properties": { + "total": { + "type": "integer", + "default": 0, + "title": "The total number of classes" + }, + "reachable": { + "type": "integer", + "default": 0, + "title": "The number of reachable classes" + }, + "reflection": { + "type": "integer", + "default": 0, + "title": "The number of classes registered for reflection" + }, + "jni": { + "type": "integer", + "default": -1, + "title": "The number of classes registered for JNI access (or -1 if unset)" + } + } + }, + "fields": { + "type": "object", + "default": {}, + "title": "Field information from the analysis", + "required": [ + "total", + "reachable", + "reflection", + "jni" + ], + "properties": { + "total": { + "type": "integer", + "default": 0, + "title": "The total number of fields" + }, + "reachable": { + "type": "integer", + "default": 0, + "title": "The number of reachable fields" + }, + "reflection": { + "type": "integer", + "default": 0, + "title": "The number of fields registered for reflection" + }, + "jni": { + "type": "integer", + "default": -1, + "title": "The number of fields registered for JNI access (or -1 if unset)" + } + } + }, + "methods": { + "type": "object", + "default": {}, + "title": "Method information from the analysis", + "required": [ + "total", + "reachable", + "reflection", + "jni" + ], + "properties": { + "total": { + "type": "integer", + "default": 0, + "title": "The total number of methods" + }, + "reachable": { + "type": "integer", + "default": 0, + "title": "The number of reachable methods" + }, + "reflection": { + "type": "integer", + "default": 0, + "title": "The number of methods registered for reflection" + }, + "jni": { + "type": "integer", + "default": -1, + "title": "The number of methods registered for JNI access (or -1 if unset)" + } + } + } + }, + "examples": [ + { + "classes": { + "total": 3850, + "reachable": 2839, + "reflection": 28, + "jni": 58 + }, + "fields": { + "total": 6665, + "reachable": 3400, + "reflection": 0, + "jni": 58 + }, + "methods": { + "total": 29038, + "reachable": 12916, + "reflection": 332, + "jni": 52 + } + } + ] + }, + "image_details": { + "type": "object", + "default": {}, + "title": "Statistics about the generated native image", + "required": [ + "total_bytes", + "code_area", + "image_heap" + ], + "properties": { + "total_bytes": { + "type": "integer", + "default": 0, + "title": "The total number of bytes of the image" + }, + "code_area": { + "type": "object", + "default": {}, + "title": "Code area statistics", + "required": [ + "bytes", + "compilation_units" + ], + "properties": { + "bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for the code area" + }, + "compilation_units": { + "type": "integer", + "default": 0, + "title": "The number of compilation units in the image" + } + } + }, + "image_heap": { + "type": "object", + "default": {}, + "title": "Image heap statistics", + "required": [ + "bytes", + "objects", + "resources" + ], + "properties": { + "bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for image heap" + }, + "objects": { + "type": "object", + "default": {}, + "title": "Object statistics", + "required": [ + "count" + ], + "properties": { + "count": { + "type": "integer", + "default": 0, + "title": "Number of objects in the image heap" + } + } + }, + "resources": { + "type": "object", + "default": {}, + "title": "Resource statistics", + "required": [ + "count", + "bytes" + ], + "properties": { + "count": { + "type": "integer", + "default": 0, + "title": "Number of resources embedded in the image" + }, + "bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for resource data" + } + } + } + } + }, + "debug_info": { + "type": "object", + "default": {}, + "title": "Debug info statistics", + "required": [ + "bytes" + ], + "properties": { + "bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for debug info" + } + } + }, + "runtime_compiled_methods": { + "type": "object", + "default": {}, + "title": "Statistics on runtime compiled methods (optional)", + "required": [ + "count", + "graph_encoding_bytes" + ], + "properties": { + "count": { + "type": "integer", + "default": 0, + "title": "Number of runtime compiled methods" + }, + "graph_encoding_bytes": { + "type": "integer", + "default": 0, + "title": "The number of bytes used for graph encodings bytes" + } + } + } + }, + "examples": [ + { + "total_bytes": 13057934, + "code_area": { + "bytes": 4610048, + "compilation_units": 67007 + }, + "image_heap": { + "bytes": 7307264, + "objects": { + "count": 83319 + }, + "resources": { + "count": 134, + "bytes": 10200 + } + }, + "debug_info": { + "bytes": 1140622 + } + } + ] + }, + "resource_usage": { + "type": "object", + "default": {}, + "title": "Resource usage statistics", + "required": [ + "cpu", + "garbage_collection", + "memory", + "total_secs" + ], + "properties": { + "cpu": { + "type": "object", + "default": {}, + "title": "CPU usage statistics", + "required": [ + "load", + "total_cores" + ], + "properties": { + "load": { + "type": "number", + "default": -1, + "title": "The CPU load of the build process before terminating (or -1 if unavailable)" + }, + "total_cores": { + "type": "integer", + "default": 0, + "title": "The total number of cores of the build machine" + } + } + }, + "garbage_collection": { + "type": "object", + "default": {}, + "title": "Garbage collection usage statistics", + "required": [ + "count", + "total_secs" + ], + "properties": { + "count": { + "type": "integer", + "default": 0, + "title": "The number of GC cycles performed during image generation" + }, + "total_secs": { + "type": "number", + "default": 0.0, + "title": "The total number of seconds spent in GC" + } + } + }, + "memory": { + "type": "object", + "default": {}, + "title": "Memory usage statistics", + "required": [ + "system_total", + "peak_rss_bytes" + ], + "properties": { + "system_total": { + "type": "integer", + "default": 0, + "title": "The total number of bytes of available system memory" + }, + "peak_rss_bytes": { + "type": "integer", + "default": -1, + "title": "Peak RSS value of the image builder process in bytes (or -1 if unavailable)" + } + } + }, + "total_secs": { + "type": "number", + "default": 0.0, + "title": "The total number of seconds image generation took" + } + }, + "examples": [ + { + "cpu": { + "load": 8.38, + "total_cores": 10 + }, + "garbage_collection": { + "count": 17, + "total_secs": 0.9245 + }, + "memory": { + "system_total": 33254146048, + "peak_rss_bytes": 3506065408 + }, + "total_secs": 16.2 + } + ] + } + }, + "examples": [ + { + "general_info": { + "name": "helloworld", + "graalvm_version": "GraalVM CE 17.0.7+4.1", + "graal_compiler": { + "march": "x86-64-v3", + "optimization_level": "0" + }, + "java_version": "17.0.7+4", + "vendor_version": "GraalVM CE 17.0.7+4.1", + "c_compiler": "gcc (linux, x86_64, 9.3.0)", + "garbage_collector": "Serial GC" + }, + "analysis_results": { + "classes": { + "total": 3850, + "reachable": 2839, + "reflection": 28, + "jni": 58 + }, + "fields": { + "total": 6665, + "reachable": 3400, + "reflection": 0, + "jni": 58 + }, + "methods": { + "total": 29038, + "reachable": 12916, + "reflection": 332, + "jni": 52 + } + }, + "image_details": { + "total_bytes": 13057934, + "code_area": { + "bytes": 4610048, + "compilation_units": 67007 + }, + "image_heap": { + "bytes": 7307264, + "objects": { + "count": 83319 + }, + "resources": { + "count": 134, + "bytes": 10200 + } + }, + "debug_info": { + "bytes": 1140622 + } + }, + "resource_usage": { + "cpu": { + "load": 8.38, + "total_cores": 10 + }, + "garbage_collection": { + "count": 17, + "total_secs": 0.9245 + }, + "memory": { + "system_total": 33254146048, + "peak_rss_bytes": 3506065408 + }, + "total_secs": 16.2 + } + }, + { + "general_info": { + "name": "helloworld", + "graalvm_version": "GraalVM CE 17.0.7+4.1", + "graal_compiler": { + "march": "x86-64-v3", + "optimization_level": "0" + }, + "java_version": "17.0.7+4", + "vendor_version": "GraalVM CE 17.0.7+4.1", + "c_compiler": null, + "garbage_collector": "Serial GC" + }, + "analysis_results": { + "classes": { + "total": 3850, + "reachable": 2839, + "reflection": 28, + "jni": -1 + }, + "fields": { + "total": 6665, + "reachable": 3400, + "reflection": 0, + "jni": -1 + }, + "methods": { + "total": 29038, + "reachable": 12916, + "reflection": 332, + "jni": -1 + } + }, + "image_details": { + "total_bytes": 13057934, + "code_area": { + "bytes": 4610048, + "compilation_units": 67007 + }, + "image_heap": { + "bytes": 7307264, + "objects": { + "count": 83319 + }, + "resources": { + "count": 134, + "bytes": 10200 + } + }, + "runtime_compiled_methods": { + "count": 12744, + "graph_encoding_bytes": 1440000 + } + }, + "resource_usage": { + "cpu": { + "load": -1, + "total_cores": 10 + }, + "garbage_collection": { + "count": 17, + "total_secs": 0.9245 + }, + "memory": { + "system_total": 33254146048, + "peak_rss_bytes": -1 + }, + "total_secs": 16.2 + } + } + ] +} diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/fortunes.u8 b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/fortunes.u8 new file mode 100644 index 0000000..a96a729 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/fortunes.u8 @@ -0,0 +1,916 @@ +A day for firm decisions!!!!! Or is it? +% +A few hours grace before the madness begins again. +% +A gift of a flower will soon be made to you. +% +A long-forgotten loved one will appear soon. + +Buy the negatives at any price. +% +A tall, dark stranger will have more fun than you. +% +A visit to a fresh place will bring strange work. +% +A visit to a strange place will bring fresh work. +% +A vivid and creative mind characterizes you. +% +Abandon the search for Truth; settle for a good fantasy. +% +Accent on helpful side of your nature. Drain the moat. +% +Advancement in position. +% +After your lover has gone you will still have PEANUT BUTTER! +% +Afternoon very favorable for romance. Try a single person for a change. +% +Alimony and bribes will engage a large share of your wealth. +% +All the troubles you have will pass away very quickly. +% +Among the lucky, you are the chosen one. +% +An avocado-tone refrigerator would look good on your resume. +% +An exotic journey in downtown Newark is in your future. +% +Another good night not to sleep in a eucalyptus tree. +% +Are you a turtle? +% +Are you ever going to do the dishes? Or will you change your major to biology? +% +Are you making all this up as you go along? +% +Are you sure the back door is locked? +% +Artistic ventures highlighted. Rob a museum. +% +Avert misunderstanding by calm, poise, and balance. +% +Avoid gunfire in the bathroom tonight. +% +Avoid reality at all costs. +% +Bank error in your favor. Collect $200. +% +Be careful! Is it classified? +% +Be careful! UGLY strikes 9 out of 10! +% +Be cautious in your daily affairs. +% +Be cheerful while you are alive. + -- Phathotep, 24th Century B.C. +% +Be different: conform. +% +Be free and open and breezy! Enjoy! Things won't get any better so +get used to it. +% +Be security conscious -- National defense is at stake. +% +Beauty and harmony are as necessary to you as the very breath of life. +% +Best of all is never to have been born. Second best is to die soon. +% +Better hope the life-inspector doesn't come around while you have your +life in such a mess. +% +Beware of a dark-haired man with a loud tie. +% +Beware of a tall black man with one blond shoe. +% +Beware of a tall blond man with one black shoe. +% +Beware of Bigfoot! +% +Beware of low-flying butterflies. +% +Beware the one behind you. +% +Blow it out your ear. +% +Break into jail and claim police brutality. +% +Bridge ahead. Pay troll. +% +Caution: breathing may be hazardous to your health. +% +Caution: Keep out of reach of children. +% +Celebrate Hannibal Day this year. Take an elephant to lunch. +% +Change your thoughts and you change your world. +% +Cheer Up! Things are getting worse at a slower rate. +% +Chess tonight. +% +Chicken Little only has to be right once. +% +Chicken Little was right. +% +Cold hands, no gloves. +% +Communicate! It can't make things any worse. +% +Courage is your greatest present need. +% +Day of inquiry. You will be subpoenaed. +% +Do not overtax your powers. +% +Do not sleep in a eucalyptus tree tonight. +% +Do nothing unless you must, and when you must act -- hesitate. +% +Do something unusual today. Pay a bill. +% +Do what comes naturally. Seethe and fume and throw a tantrum. +% +Domestic happiness and faithful friends. +% +Don't feed the bats tonight. +% +Don't get stuck in a closet -- wear yourself out. +% +Don't get to bragging. +% +Don't go surfing in South Dakota for a while. +% +Don't hate yourself in the morning -- sleep till noon. +% +Don't kiss an elephant on the lips today. +% +Don't let your mind wander -- it's too little to be let out alone. +% +Don't look back, the lemmings are gaining on you. +% +Don't look now, but the man in the moon is laughing at you. +% +Don't look now, but there is a multi-legged creature on your shoulder. +% +Don't plan any hasty moves. You'll be evicted soon anyway. +% +Don't read any sky-writing for the next two weeks. +% +Don't read everything you believe. +% +Don't relax! It's only your tension that's holding you together. +% +Don't tell any big lies today. Small ones can be just as effective. +% +Don't worry so loud, your roommate can't think. +% +Don't Worry, Be Happy. + -- Meher Baba +% +Don't worry. Life's too long. + -- Vincent Sardi, Jr. +% +Don't you feel more like you do now than you did when you came in? +% +Don't you wish you had more energy... or less ambition? +% +Everything that you know is wrong, but you can be straightened out. +% +Everything will be just tickety-boo today. +% +Excellent day for putting Slinkies on an escalator. +% +Excellent day to have a rotten day. +% +Excellent time to become a missing person. +% +Executive ability is prominent in your make-up. +% +Exercise caution in your daily affairs. +% +Expect a letter from a friend who will ask a favor of you. +% +Expect the worst, it's the least you can do. +% +Fine day for friends. +So-so day for you. +% +Fine day to work off excess energy. Steal something heavy. +% +Fortune: You will be attacked next Wednesday at 3:15 p.m. by six samurai +sword wielding purple fish glued to Harley-Davidson motorcycles. + +Oh, and have a nice day! + -- Bryce Nesbitt '84 +% +Future looks spotty. You will spill soup in late evening. +% +Generosity and perfection are your everlasting goals. +% +Give him an evasive answer. +% +Give thought to your reputation. Consider changing name and moving to +a new town. +% +Give your very best today. Heaven knows it's little enough. +% +Go to a movie tonight. Darkness becomes you. +% +Good day for a change of scene. Repaper the bedroom wall. +% +Good day for overcoming obstacles. Try a steeplechase. +% +Good day to deal with people in high places; particularly lonely stewardesses. +% +Good day to let down old friends who need help. +% +Good news from afar can bring you a welcome visitor. +% +Good news. Ten weeks from Friday will be a pretty good day. +% +Good night to spend with family, but avoid arguments with your mate's +new lover. +% +Green light in A.M. for new projects. Red light in P.M. for traffic tickets. +% +Hope that the day after you die is a nice day. +% +If you can read this, you're too close. +% +If you learn one useless thing every day, in a single year you'll learn +365 useless things. +% +If you sow your wild oats, hope for a crop failure. +% +If you stand on your head, you will get footprints in your hair. +% +If you think last Tuesday was a drag, wait till you see what happens tomorrow! +% +If your life was a horse, you'd have to shoot it. +% +In the stairway of life, you'd best take the elevator. +% +Increased knowledge will help you now. Have mate's phone bugged. +% +Is that really YOU that is reading this? +% +Is this really happening? +% +It is so very hard to be an +on-your-own-take-care-of-yourself-because-there-is-no-one-else-to-do-it-for-you +grown-up. +% +It may or may not be worthwhile, but it still has to be done. +% +It was all so different before everything changed. +% +It's a very *__UN*lucky week in which to be took dead. + -- Churchy La Femme +% +It's all in the mind, ya know. +% +It's lucky you're going so slowly, because you're going in the wrong direction. +% +Just because the message may never be received does not mean it is +not worth sending. +% +Just to have it is enough. +% +Keep emotionally active. Cater to your favorite neurosis. +% +Keep it short for pithy sake. +% +Lady Luck brings added income today. Lady friend takes it away tonight. +% +Learn to pause -- or nothing worthwhile can catch up to you. +% +Let me put it this way: today is going to be a learning experience. +% +Life is to you a dashing and bold adventure. +% +"Life, loathe it or ignore it, you can't like it." + -- Marvin, "Hitchhiker's Guide to the Galaxy" +% +Live in a world of your own, but always welcome visitors. +% +Living your life is a task so difficult, it has never been attempted before. +% +Long life is in store for you. +% +Look afar and see the end from the beginning. +% +Love is in the offing. Be affectionate to one who adores you. +% +Make a wish, it might come true. +% +Many changes of mind and mood; do not hesitate too long. +% +Never be led astray onto the path of virtue. +% +Never commit yourself! Let someone else commit you. +% +Never give an inch! +% +Never look up when dragons fly overhead. +% +Never reveal your best argument. +% +Next Friday will not be your lucky day. As a matter of fact, you don't +have a lucky day this year. +% +Of course you have a purpose -- to find a purpose. +% +People are beginning to notice you. Try dressing before you leave the house. +% +Perfect day for scrubbing the floor and other exciting things. +% +Questionable day. + +Ask somebody something. +% +Reply hazy, ask again later. +% +Save energy: be apathetic. +% +Ships are safe in harbor, but they were never meant to stay there. +% +Slow day. Practice crawling. +% +Snow Day -- stay home. +% +So this is it. We're going to die. +% +So you're back... about time... +% +Someone is speaking well of you. +% +Someone is speaking well of you. + +How unusual! +% +Someone whom you reject today, will reject you tomorrow. +% +Stay away from flying saucers today. +% +Stay away from hurricanes for a while. +% +Stay the curse. +% +That secret you've been guarding, isn't. +% +The time is right to make new friends. +% +The whole world is a tuxedo and you are a pair of brown shoes. + -- George Gobel +% +There is a 20% chance of tomorrow. +% +There is a fly on your nose. +% +There was a phone call for you. +% +There will be big changes for you but you will be happy. +% +Things will be bright in P.M. A cop will shine a light in your face. +% +Think twice before speaking, but don't say "think think click click". +% +This life is yours. Some of it was given to you; the rest, you made yourself. +% +This will be a memorable month -- no matter how hard you try to forget it. +% +Time to be aggressive. Go after a tattooed Virgo. +% +Today is National Existential Ennui Awareness Day. +% +Today is the first day of the rest of the mess. +% +Today is the first day of the rest of your life. +% +Today is the last day of your life so far. +% +Today is the tomorrow you worried about yesterday. +% +Today is what happened to yesterday. +% +Today's weirdness is tomorrow's reason why. + -- Hunter S. Thompson +% +Tomorrow will be cancelled due to lack of interest. +% +Tomorrow, this will be part of the unchangeable past but fortunately, +it can still be changed today. +% +Tomorrow, you can be anywhere. +% +Tonight you will pay the wages of sin; Don't forget to leave a tip. +% +Tonight's the night: Sleep in a eucalyptus tree. +% +Troubled day for virgins over 16 who are beautiful and wealthy and live +in eucalyptus trees. +% +Truth will out this morning. (Which may really mess things up.) +% +Try the Moo Shu Pork. It is especially good today. +% +Try to get all of your posthumous medals in advance. +% +Try to have as good a life as you can under the circumstances. +% +Try to relax and enjoy the crisis. + -- Ashleigh Brilliant +% +Try to value useful qualities in one who loves you. +% +Tuesday After Lunch is the cosmic time of the week. +% +Tuesday is the Wednesday of the rest of your life. +% +What happened last night can happen again. +% +While you recently had your problems on the run, they've regrouped and +are making another attack. +% +Write yourself a threatening letter and pen a defiant reply. +% +You are a bundle of energy, always on the go. +% +You are a fluke of the universe; you have no right to be here. +% +You are a very redundant person, that's what kind of person you are. +% +You are always busy. +% +You are as I am with You. +% +You are capable of planning your future. +% +You are confused; but this is your normal state. +% +You are deeply attached to your friends and acquaintances. +% +You are destined to become the commandant of the fighting men of the +department of transportation. +% +You are dishonest, but never to the point of hurting a friend. +% +You are fairminded, just and loving. +% +You are farsighted, a good planner, an ardent lover, and a faithful friend. +% +You are fighting for survival in your own sweet and gentle way. +% +You are going to have a new love affair. +% +You are magnetic in your bearing. +% +You are not dead yet. But watch for further reports. +% +You are number 6! Who is number one? +% +You are only young once, but you can stay immature indefinitely. +% +You are scrupulously honest, frank, and straightforward. Therefore you +have few friends. +% +You are sick, twisted and perverted. I like that in a person. +% +You are so boring that when I see you my feet go to sleep. +% +You are standing on my toes. +% +You are taking yourself far too seriously. +% +You are the only person to ever get this message. +% +You are wise, witty, and wonderful, but you spend too much time reading +this sort of trash. +% +You attempt things that you do not even plan because of your extreme stupidity. +% +You can create your own opportunities this week. Blackmail a senior executive. +% +You can do very well in speculation where land or anything to do with dirt +is concerned. +% +You can rent this space for only $5 a week. +% +You could live a better life, if you had a better mind and a better body. +% +You definitely intend to start living sometime soon. +% +You dialed 5483. +% +You display the wonderful traits of charm and courtesy. +% +You don't become a failure until you're satisfied with being one. +% +You enjoy the company of other people. +% +You feel a whole lot more like you do now than you did when you used to. +% +You fill a much-needed gap. +% +You get along very well with everyone except animals and people. +% +You had some happiness once, but your parents moved away, and you had to +leave it behind. +% +You have a deep appreciation of the arts and music. +% +You have a deep interest in all that is artistic. +% +You have a reputation for being thoroughly reliable and trustworthy. +A pity that it's totally undeserved. +% +You have a strong appeal for members of the opposite sex. +% +You have a strong appeal for members of your own sex. +% +You have a strong desire for a home and your family interests come first. +% +You have a truly strong individuality. +% +You have a will that can be influenced by all with whom you come in contact. +% +You have an ability to sense and know higher truth. +% +You have an ambitious nature and may make a name for yourself. +% +You have an unusual equipment for success. Be sure to use it properly. +% +You have an unusual magnetic personality. Don't walk too close to +metal objects which are not fastened down. +% +You have an unusual understanding of the problems of human relationships. +% +You have been selected for a secret mission. +% +You have Egyptian flu: you're going to be a mummy. +% +You have had a long-term stimulation relative to business. +% +You have literary talent that you should take pains to develop. +% +You have many friends and very few living enemies. +% +You have no real enemies. +% +You have taken yourself too seriously. +% +You have the body of a 19 year old. Please return it before it gets wrinkled. +% +You have the capacity to learn from mistakes. You'll learn a lot today. +% +You have the power to influence all with whom you come in contact. +% +You learn to write as if to someone else because NEXT YEAR YOU WILL BE +"SOMEONE ELSE." +% +You like to form new friendships and make new acquaintances. +% +You look like a million dollars. All green and wrinkled. +% +You look tired. +% +You love peace. +% +You love your home and want it to be beautiful. +% +You may be gone tomorrow, but that doesn't mean that you weren't here today. +% +You may be infinitely smaller than some things, but you're infinitely +larger than others. +% +You may be recognized soon. Hide. +% +You may get an opportunity for advancement today. Watch it! +% +You may worry about your hair-do today, but tomorrow much peanut butter will +be sold. +% +You need more time; and you probably always will. +% +You need no longer worry about the future. This time tomorrow you'll be dead. +% +You never hesitate to tackle the most difficult problems. +% +You never know how many friends you have until you rent a house on the beach. +% +You now have Asian Flu. +% +You own a dog, but you can only feed a cat. +% +You plan things that you do not even attempt because of your extreme caution. +% +You possess a mind not merely twisted, but actually sprained. +% +You prefer the company of the opposite sex, but are well liked by your own. +% +You recoil from the crude; you tend naturally toward the exquisite. +% +You seek to shield those you love and you like the role of the provider. +% +You shall be rewarded for a dastardly deed. +% +You should emulate your heros, but don't carry it too far. Especially +if they are dead. +% +You should go home. +% +You single-handedly fought your way into this hopeless mess. +% +You teach best what you most need to learn. +% +You too can wear a nose mitten. +% +You two ought to be more careful--your love could drag on for years and years. +% +You will always get the greatest recognition for the job you least like. +% +You will always have good luck in your personal affairs. +% +You will attract cultured and artistic people to your home. +% +You will be a winner today. Pick a fight with a four-year-old. +% +You will be advanced socially, without any special effort on your part. +% +You will be aided greatly by a person whom you thought to be unimportant. +% +You will be attacked by a beast who has the body of a wolf, the tail of +a lion, and the face of Donald Duck. +% +You will be audited by the Internal Revenue Service. +% +You will be awarded a medal for disregarding safety in saving someone. +% +You will be awarded some great honor. +% +You will be awarded the Nobel Peace Prize... posthumously. +% +You will be called upon to help a friend in trouble. +% +You will be divorced within a year. +% +You will be given a post of trust and responsibility. +% +You will be held hostage by a radical group. +% +You will be honored for contributing your time and skill to a worthy cause. +% +You will be imprisoned for contributing your time and skill to a bank robbery. +% +You will be married within a year, and divorced within two. +% +You will be married within a year. +% +You will be misunderstood by everyone. +% +You will be recognized and honored as a community leader. +% +You will be reincarnated as a toad; and you will be much happier. +% +You will be run over by a beer truck. +% +You will be run over by a bus. +% +You will be singled out for promotion in your work. +% +You will be successful in love. +% +You will be surprised by a loud noise. +% +You will be surrounded by luxury. +% +You will be the last person to buy a Chrysler. +% +You will be the victim of a bizarre joke. +% +You will be Told about it Tomorrow. Go Home and Prepare Thyself. +% +You will be traveling and coming into a fortune. +% +You will be winged by an anti-aircraft battery. +% +You will become rich and famous unless you don't. +% +You will contract a rare disease. +% +You will engage in a profitable business activity. +% +You will experience a strong urge to do good; but it will pass. +% +You will feel hungry again in another hour. +% +You will forget that you ever knew me. +% +You will gain money by a fattening action. +% +You will gain money by a speculation or lottery. +% +You will gain money by an illegal action. +% +You will gain money by an immoral action. +% +You will get what you deserve. +% +You will give someone a piece of your mind, which you can ill afford. +% +You will have a long and boring life. +% +You will have a long and unpleasant discussion with your supervisor. +% +You will have domestic happiness and faithful friends. +% +You will have good luck and overcome many hardships. +% +You will have long and healthy life. +% +You will hear good news from one you thought unfriendly to you. +% +You will inherit millions of dollars. +% +You will inherit some money or a small piece of land. +% +You will live a long, healthy, happy life and make bags of money. +% +You will live to see your grandchildren. +% +You will lose your present job and have to become a door to door mayonnaise +salesman. +% +You will meet an important person who will help you advance professionally. +% +You will never know hunger. +% +You will not be elected to public office this year. +% +You will obey or molten silver will be poured into your ears. +% +You will outgrow your usefulness. +% +You will overcome the attacks of jealous associates. +% +You will pass away very quickly. +% +You will pay for your sins. If you have already paid, please disregard +this message. +% +You will pioneer the first Martian colony. +% +You will probably marry after a very brief courtship. +% +You will reach the highest possible point in your business or profession. +% +You will receive a legacy which will place you above want. +% +You will remember something that you should not have forgotten. +% +You will soon forget this. +% +You will soon meet a person who will play an important role in your life. +% +You will step on the night soil of many countries. +% +You will stop at nothing to reach your objective, but only because your +brakes are defective. +% +You will triumph over your enemy. +% +You will visit the Dung Pits of Glive soon. +% +You will win success in whatever calling you adopt. +% +You will wish you hadn't. +% +You work very hard. Don't try to think as well. +% +You worry too much about your job. Stop it. You are not paid enough to worry. +% +You would if you could but you can't so you won't. +% +You'd like to do it instantaneously, but that's too slow. +% +You'll be called to a post requiring ability in handling groups of people. +% +You'll be sorry... +% +You'll feel devilish tonight. Toss dynamite caps under a flamenco dancer's +heel. +% +You'll feel much better once you've given up hope. +% +You'll never be the man your mother was! +% +You'll never see all the places, or read all the books, but fortunately, +they're not all recommended. +% +You'll wish that you had done some of the hard things when they were easier +to do. +% +You're a card which will have to be dealt with. +% +You're almost as happy as you think you are. +% +You're at the end of the road again. +% +You're being followed. Cut out the hanky-panky for a few days. +% +You're currently going through a difficult transition period called "Life." +% +You're definitely on their list. The question to ask next is what list it is. +% +You're growing out of some of your problems, but there are others that +you're growing into. +% +You're not my type. For that matter, you're not even my species!!! +% +You're ugly and your mother dresses you funny. +% +You're working under a slight handicap. You happen to be human. +% +You've been leading a dog's life. Stay off the furniture. +% +Your aim is high and to the right. +% +Your aims are high, and you are capable of much. +% +Your analyst has you mixed up with another patient. Don't believe a +thing he tells you. +% +Your best consolation is the hope that the things you failed to get weren't +really worth having. +% +Your boss climbed the corporate ladder, wrong by wrong. +% +Your boss is a few sandwiches short of a picnic. +% +Your boyfriend takes chocolate from strangers. +% +Your business will assume vast proportions. +% +Your business will go through a period of considerable expansion. +% +Your depth of comprehension may tend to make you lax in worldly ways. +% +Your domestic life may be harmonious. +% +Your fly might be open (but don't check it just now). +% +Your goose is cooked. +(Your current chick is burned up too!) +% +Your heart is pure, and your mind clear, and your soul devout. +% +Your ignorance cramps my conversation. +% +Your life would be very empty if you had nothing to regret. +% +Your love life will be happy and harmonious. +% +Your love life will be... interesting. +% +Your lover will never wish to leave you. +% +Your lucky color has faded. +% +Your lucky number has been disconnected. +% +Your lucky number is 3552664958674928. Watch for it everywhere. +% +Your mode of life will be changed for the better because of good news soon. +% +Your mode of life will be changed for the better because of new developments. +% +Your motives for doing whatever good deed you may have in mind will be +misinterpreted by somebody. +% +Your nature demands love and your happiness depends on it. +% +Your object is to save the world, while still leading a pleasant life. +% +Your own qualities will help prevent your advancement in the world. +% +Your present plans will be successful. +% +Your reasoning is excellent -- it's only your basic assumptions that are wrong. +% +Your reasoning powers are good, and you are a fairly good planner. +% +Your sister swims out to meet troop ships. +% +Your society will be sought by people of taste and refinement. +% +Your step will soil many countries. +% +Your supervisor is thinking about you. +% +Your talents will be recognized and suitably rewarded. +% +Your temporary financial embarrassment will be relieved in a surprising manner. +% +Your true value depends entirely on what you are compared with. +% \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/logging.properties b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/logging.properties new file mode 100644 index 0000000..df03d06 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/logging.properties @@ -0,0 +1,4 @@ +handlers= java.util.logging.ConsoleHandler +.level= ALL +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/native-image-inspect-schema-v0.2.0.json b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/native-image-inspect-schema-v0.2.0.json new file mode 100644 index 0000000..9d9c1f4 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/assets/native-image-inspect-schema-v0.2.0.json @@ -0,0 +1,104 @@ +{ + "$id": "https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/assets/native-image-inspect-schema-v0.2.0.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "JSON schema for the output of the 'native-image-inspect' utility", + "type": "object", + "required": ["classes", "fields", "methods", "constructors"], + "default": {}, + "properties": { + "classes": { + "title": "The classes and interfaces included in the generated image", + "type": "array", + "default": [], + "items": { + "format": "string", + "title": "An informative string for the name of this class or interface", + "type": "string" + } + }, + "fields": { + "title": "Fields included in the generated image", + "type": "array", + "default": [], + "items": { + "title": "Field information", + "type": "object", + "required": ["declaringClass", "name"], + "default": {}, + "properties": { + "declaringClass": { + "default": "", + "title": "An informative string for the name of the field's declaring class or interface", + "type": "string" + }, + "name": { + "default": "", + "title": "The name of the field", + "type": "string" + } + } + } + }, + "methods": { + "title": "Methods included in the generated image", + "type": "array", + "default": [], + "items": { + "title": "Method information", + "type": "object", + "required": ["declaringClass", "name", "parameterTypes"], + "default": {}, + "properties": { + "declaringClass": { + "default": "", + "title": "An informative string for the name of the method's declaring class or interface", + "type": "string" + }, + "name": { + "default": "", + "title": "The name of the method", + "type": "string" + }, + "parameterTypes": { + "title": "Names of parameter types of the method", + "type": "array", + "default": [], + "items": { + "default": "", + "title": "An informative string for the name of the parameter's class or interface", + "type": "string" + } + } + } + } + }, + "constructors": { + "title": "Constructors included in the generated image", + "type": "array", + "default": [], + "items": { + "title": "Constructor information", + "type": "object", + "required": ["declaringClass", "parameterTypes"], + "default": {}, + "properties": { + "declaringClass": { + "default": "", + "title": "An informative string for the name of the constructor's declaring class or interface", + "type": "string" + }, + "parameterTypes": { + "title": "Names of parameter types of the constructor", + "type": "array", + "default": [], + "items": { + "default": "", + "title": "An informative string for the name of the parameter's class or interface", + "type": "string" + } + } + } + } + } + } +} diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/contribute/DevelopingNativeImage.md b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/contribute/DevelopingNativeImage.md new file mode 100644 index 0000000..588d74e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/contribute/DevelopingNativeImage.md @@ -0,0 +1,99 @@ +# The Substrate VM Project + +Substrate VM is an internal project name for the technology behind [GraalVM Native Image](../README.md). +This guide shows how to set up a development environment for the project. + +To get started, install [mx](https://github.com/graalvm/mx). +Then point the `JAVA_HOME` variable to a JDK that supports a compatible version +of the JVM Compiler Interface (JVMCI). JVMCI is a privileged, low-level interface +to the JVM that can read metadata from the VM, such as method bytecode, and +install machine code into the VM. Obtain JVMCI-enabled: +* JDK 8 from [GitHub](https://github.com/graalvm/openjdk8-jvmci-builder/releases) +* JDK 11 from [GitHub](https://github.com/graalvm/labs-openjdk-11/releases) + +#### Prerequisites +For compilation, `native-image` depends on the local toolchain. Install +`glibc-devel`, `zlib-devel` (header files for the C library and `zlib`) and +`gcc`, using a package manager available on your OS. Some Linux distributions +may additionally require `libstdc++-static`. + +On Windows, Native Image requires Visual Studio 2022 version 17.1.0 or later. + +```shell +cd substratevm +mx build + +echo "public class HelloWorld { public static void main(String[] args) { System.out.println(\"Hello World\"); } }" > HelloWorld.java +$JAVA_HOME/bin/javac HelloWorld.java +mx native-image HelloWorld +./helloworld +``` + +To build polyglot images, refer to the documentation in the [VM suite](https://github.com/oracle/vm/README.md). + +## Build Native Image + +Using Native Image in a development environment requires the `mx` tool to be installed first, so that it is on your `PATH`. +Visit the [MX Homepage](https://github.com/graalvm/mx) for more details. + +In the main directory, invoke `mx help` to see the list of commands. +Most of the commands are inherited from the [Graal](https://github.com/oracle/graal) and [Truffle](https://github.com/oracle/graal/tree/master/truffle) code bases. +More information on a specific command is available by running `mx help `. +The most important commands are: + +* `build`: compile all Java and native code +* `clean`: remove all compilation artifacts +* `ideinit`: create project files for Eclipse and other common IDEs +See the [documentation on IDE integration](https://github.com/oracle/compiler/docs/IDEs.md) for details. + +## Build Native Executables + +After running `mx build` you can use `mx native-image` to build native images. +You can specify the main entry point, i.e., the application you want to create the image for. For more information run `mx native-image --help`. + +A native image generation is performed by a Java program, a Native Image builder, that runs on a JVMCI-enabled JDK. You can debug it with a regular Java debugger. +Use `mx native-image --debug-attach` to start native image generation so that it waits for a Java debugger to attach first (by default, at port 8000). +In Eclipse, use the debugging configuration _substratevm-localhost-8000_ to attach to it. This debugging configuration is automatically generated by `mx ideinit`. + +If you have to debug the compiler graphs that are built as part of an image, proceed to the [debugging](https://github.com/oracle/compiler/docs/Debugging.md) page. +You can use the [Ideal Graph Visualizer (IGV)](https://www.graalvm.org/latest/tools/igv/) tool to view individual compilation steps: +```shell +mx igv &>/dev/null & +mx native-image HelloWorld -H:Dump= -H:MethodFilter=HelloWorld.* +``` + +## Options + +More information about options and the important distinction between hosted and runtime options is available [here](https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/Options.md). + +## Project Structure + +The list of projects is defined in a custom format in the file `mx.substratevm/suite.py`. It is never necessary to create new projects in the IDE. +Instead, a new project is created by adding it in `suite.py` and running `mx ideinit` to generate a corresponding IDE project. + +## Code Formatting + +Style rules and procedures for checking adherence are described in the [style guide](CodeStyle.md). + +## Troubleshooting Eclipse + +Sometimes, Eclipse gives strange error messages, especially after pulling a +bigger changeset. Also, projects are frequently added or removed, leading to +error messages about missing projects if you do not import new projects. The +following should reset everything: + +* Delete all projects in Eclipse +* `mx clean` +* `mx ideclean` +* `mx fsckprojects` +* `mx build` +* `mx ideinit` +* Import all projects into Eclipse again + +## Usage Documentation + +The documentation on how to use Native Image is available [here](../README.md) or [on the website](https://www.graalvm.org/reference-manual/native-image/). + +## License + +The Substrate VM project is licensed under the GPL 2 with Classpath Exception. diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/contribute/ErrorMessageGuide.md b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/contribute/ErrorMessageGuide.md new file mode 100644 index 0000000..edcb610 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/contribute/ErrorMessageGuide.md @@ -0,0 +1,39 @@ +# Error Reporting in Native Image + +The rules for errors reported during image generation are: + + 1) An error message that does not contain a stack trace is aimed for *Native Image users*. This message must be: a) clearly written, and b) actionable for someone without deep VM knowledge. + + 2) An error message that contains a stack trace is aimed for *Native Image developers*. This message represents a Native Image builder bug and should be reported to [GitHub](https://github.com/oracle/graal/issues) specifying `Native Image` in the `Component` field. The bug report needs to explain the used environment, the sequence of commands that led to the error, as well as the whole text of the error. + +## How to Report Errors to Native Image Users + +Report errors to the Native Image users with `UserError.abort`. A good example of reporting a clearly written and actionable error is: + + throw UserError.abort("No output file name specified. Use '%s'.", + SubstrateOptionsParser.commandArgument(SubstrateOptions.Name, "")); + +An example of a clearly written but non-actionable error message is: + + throw UserError.abort("Correct image building task must be provided."); + +This message relies on the internal slang (correct image building task) and does not directly and concisely explain to the user how to fix the error. + +The `UserError.abort` method will interrupt compilation with a `UserException` causing the `native image` generator to exit. To assure consistent error reporting throughout the project, this exception must be handled only once and must not be ignored anywhere in the project. + +## How to Report Errors to Native Image Developers + +To report VM errors use `throw VMError.shouldNotReachHere()`. The error message should be clearly written and actionable for a VM developer. A good example of reporting VM errors is: + + throw VMError.shouldNotReachHere("Cannot block in a single-threaded environment, because there is no other thread that could signal"); + +The following example shows improper error reporting: + + for (ResolvedJavaField field : declaringClass.getStaticFields()) { + if (field.getName().equals(name)) { + return field; + } + } + VMError.shouldNotReachHere(); + +Error reporting with no error message requires the user to unnecessarily read the context in order to understand the error. Furthermore, the `throw` in preceding `VMError.shouldNotReachHere()` is omitted making users and static analysis tools unaware of control flow interruption. diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/H2Example-json-configs.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/H2Example-json-configs.png new file mode 100644 index 0000000..5913a53 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/H2Example-json-configs.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/fortune-codesize.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/fortune-codesize.png new file mode 100644 index 0000000..bab68e6 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/fortune-codesize.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/fortune-heapsize.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/fortune-heapsize.png new file mode 100644 index 0000000..3214e78 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/fortune-heapsize.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/heap-dump-api.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/heap-dump-api.png new file mode 100644 index 0000000..1bfb6be Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/heap-dump-api.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/heap-dump.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/heap-dump.png new file mode 100644 index 0000000..1305d95 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/heap-dump.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/jfr.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/jfr.png new file mode 100644 index 0000000..0c02d39 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/jfr.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/reflect_config_file_merged.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/reflect_config_file_merged.png new file mode 100644 index 0000000..d0b0de7 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/reflect_config_file_merged.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/rjmx_attributes.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/rjmx_attributes.png new file mode 100644 index 0000000..db70ddf Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/rjmx_attributes.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/rjmx_monitor.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/rjmx_monitor.png new file mode 100644 index 0000000..5669408 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/rjmx_monitor.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/rjmx_operations.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/rjmx_operations.png new file mode 100644 index 0000000..9ce24ed Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/rjmx_operations.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/staticfortune-codesize.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/staticfortune-codesize.png new file mode 100644 index 0000000..eb4eaa7 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/staticfortune-codesize.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/staticfortune-heapsize.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/staticfortune-heapsize.png new file mode 100644 index 0000000..5a49b30 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/guides/img/staticfortune-heapsize.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/visual-bundle-compare.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/visual-bundle-compare.png new file mode 100644 index 0000000..76d265d Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/native-image/visual-bundle-compare.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/python/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/python/index.html new file mode 100644 index 0000000..4d5bc33 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/python/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/ruby/images/chrome.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/ruby/images/chrome.png new file mode 100644 index 0000000..8279aaa Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/ruby/images/chrome.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/ruby/images/netbeans.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/ruby/images/netbeans.png new file mode 100644 index 0000000..610d0dc Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/ruby/images/netbeans.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/ruby/logo/png/truffleruby_logo_horizontal_medium.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/ruby/logo/png/truffleruby_logo_horizontal_medium.png new file mode 100644 index 0000000..715a473 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/reference-manual/ruby/logo/png/truffleruby_logo_horizontal_medium.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/security-guide/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/security-guide/index.html new file mode 100644 index 0000000..9a4b619 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/security-guide/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/security/sandbox_security_boundary.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/security/sandbox_security_boundary.png new file mode 100644 index 0000000..3e36f08 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/security/sandbox_security_boundary.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/chrome-debugger/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/chrome-debugger/index.html new file mode 100644 index 0000000..6b073ad --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/chrome-debugger/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/AgentHistory.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/AgentHistory.png new file mode 100644 index 0000000..04b3a51 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/AgentHistory.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/ChromeInspector.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/ChromeInspector.png new file mode 100644 index 0000000..455ab86 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/ChromeInspector.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/HeapViewer_objects.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/HeapViewer_objects.png new file mode 100644 index 0000000..a59a465 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/HeapViewer_objects.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/HeapViewer_objects_dominators.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/HeapViewer_objects_dominators.png new file mode 100644 index 0000000..2094fe8 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/HeapViewer_objects_dominators.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/HeapViewer_thread.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/HeapViewer_thread.png new file mode 100644 index 0000000..2088ccd Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/HeapViewer_thread.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_add_source.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_add_source.png new file mode 100644 index 0000000..97cb50f Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_add_source.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_context_menu.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_context_menu.png new file mode 100644 index 0000000..99b24d4 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_context_menu.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_navigate_to_source.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_navigate_to_source.png new file mode 100644 index 0000000..06a91c5 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_navigate_to_source.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_satellite_view.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_satellite_view.png new file mode 100644 index 0000000..5a57bb4 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/IGV_satellite_view.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/add_java_launch_configuration.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/add_java_launch_configuration.png new file mode 100644 index 0000000..9a6b957 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/add_java_launch_configuration.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/available_action_icons.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/available_action_icons.png new file mode 100644 index 0000000..f452960 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/available_action_icons.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/available_instances.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/available_instances.png new file mode 100644 index 0000000..5088998 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/available_instances.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-js.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-js.png new file mode 100644 index 0000000..151c6b8 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-js.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-python.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-python.png new file mode 100644 index 0000000..00a51f1 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-python.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-r.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-r.png new file mode 100644 index 0000000..a7627a1 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-r.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-ruby.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-ruby.png new file mode 100644 index 0000000..c1cff16 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/code-completion-ruby.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/command-palette.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/command-palette.png new file mode 100644 index 0000000..423e84c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/command-palette.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/component-install.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/component-install.png new file mode 100644 index 0000000..dd8bb10 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/component-install.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/content_navigation.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/content_navigation.png new file mode 100644 index 0000000..04639c0 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/content_navigation.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/create_java_launch_configuration.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/create_java_launch_configuration.png new file mode 100644 index 0000000..fad5426 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/create_java_launch_configuration.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/create_launch_json.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/create_launch_json.png new file mode 100644 index 0000000..f57058c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/create_launch_json.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-polyglot.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-polyglot.png new file mode 100644 index 0000000..157867d Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-polyglot.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-python.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-python.png new file mode 100644 index 0000000..d7f16c2 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-python.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-r.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-r.png new file mode 100644 index 0000000..056e336 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-r.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-ruby.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-ruby.png new file mode 100644 index 0000000..e64f8de Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config-ruby.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config.png new file mode 100644 index 0000000..2da4f4d Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debugger.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debugger.png new file mode 100644 index 0000000..c8896c0 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/debugger.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/download_install_graalvm_view.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/download_install_graalvm_view.png new file mode 100644 index 0000000..4d5f708 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/download_install_graalvm_view.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/graalvm_install_actions.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/graalvm_install_actions.png new file mode 100644 index 0000000..b3ced03 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/graalvm_install_actions.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/graalvm_path.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/graalvm_path.png new file mode 100644 index 0000000..b46abc9 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/graalvm_path.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/igv-session-grouping.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/igv-session-grouping.png new file mode 100644 index 0000000..b71f0b5 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/igv-session-grouping.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/install_components_view.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/install_components_view.png new file mode 100644 index 0000000..0dc61a5 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/install_components_view.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/install_optional_components_popup.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/install_optional_components_popup.png new file mode 100644 index 0000000..1a87402 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/install_optional_components_popup.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/java_home_settings.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/java_home_settings.png new file mode 100644 index 0000000..29edffa Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/java_home_settings.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/java_overview_window.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/java_overview_window.png new file mode 100644 index 0000000..506678d Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/java_overview_window.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/language-embedding-js.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/language-embedding-js.png new file mode 100644 index 0000000..1c8dbf3 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/language-embedding-js.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/language-embedding-ruby.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/language-embedding-ruby.png new file mode 100644 index 0000000..77bd541 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/language-embedding-ruby.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/launch_json_script.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/launch_json_script.png new file mode 100644 index 0000000..34d08ae Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/launch_json_script.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/lsp-dynamic-completion.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/lsp-dynamic-completion.png new file mode 100644 index 0000000..aaf5283 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/lsp-dynamic-completion.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/manual_install.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/manual_install.png new file mode 100644 index 0000000..b5d1595 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/manual_install.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-path-to-graalvm.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-path-to-graalvm.png new file mode 100644 index 0000000..1d583ef Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-path-to-graalvm.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-python-component.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-python-component.png new file mode 100644 index 0000000..301dfcf Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-python-component.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-r-ls.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-r-ls.png new file mode 100644 index 0000000..88c9c63 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-r-ls.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-ruby-ls.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-ruby-ls.png new file mode 100644 index 0000000..3b6f284 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/no-ruby-ls.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/nodejs-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/nodejs-debug-config.png new file mode 100644 index 0000000..313c536 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/nodejs-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/otn_license_accept.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/otn_license_accept.png new file mode 100644 index 0000000..c5eb1dc Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/otn_license_accept.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/polyglot-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/polyglot-debug-config.png new file mode 100644 index 0000000..ac8d4f5 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/polyglot-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/profiler_flamegraph.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/profiler_flamegraph.png new file mode 100644 index 0000000..4d849d4 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/profiler_flamegraph.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/python-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/python-debug-config.png new file mode 100644 index 0000000..00adf18 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/python-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/r-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/r-debug-config.png new file mode 100644 index 0000000..09855ee Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/r-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/ruby-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/ruby-debug-config.png new file mode 100644 index 0000000..dd514c8 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/ruby-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-debug-config.png new file mode 100644 index 0000000..4909d27 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-graalvm.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-graalvm.png new file mode 100644 index 0000000..32cc874 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-graalvm.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-python-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-python-debug-config.png new file mode 100644 index 0000000..58c90fd Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-python-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-r-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-r-debug-config.png new file mode 100644 index 0000000..195aca3 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-r-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-ruby-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-ruby-debug-config.png new file mode 100644 index 0000000..fb62603 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/select-ruby-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/visualvm-shows-libgraal-memory.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/visualvm-shows-libgraal-memory.png new file mode 100644 index 0000000..6ab7077 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/visualvm-shows-libgraal-memory.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/visualvm_jfr.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/visualvm_jfr.png new file mode 100644 index 0000000..f85efd3 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/visualvm_jfr.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/vscode-coverage.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/vscode-coverage.png new file mode 100644 index 0000000..7687372 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/vscode-coverage.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/vscode_cc_1.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/vscode_cc_1.png new file mode 100644 index 0000000..1238850 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/img/vscode_cc_1.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/index.html b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/index.html new file mode 100644 index 0000000..83646a8 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/insight/img/Insight-HeapInspect.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/insight/img/Insight-HeapInspect.png new file mode 100644 index 0000000..ef19dc9 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/insight/img/Insight-HeapInspect.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/insight/img/Insight-HeapStack.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/insight/img/Insight-HeapStack.png new file mode 100644 index 0000000..2ca16e5 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/insight/img/Insight-HeapStack.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/insight/img/Insight-Jaeger.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/insight/img/Insight-Jaeger.png new file mode 100644 index 0000000..057f4a4 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/insight/img/Insight-Jaeger.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/NativeImageExecutableLocations.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/NativeImageExecutableLocations.png new file mode 100644 index 0000000..e92439c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/NativeImageExecutableLocations.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/add_java_launch_configuration.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/add_java_launch_configuration.png new file mode 100644 index 0000000..9a6b957 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/add_java_launch_configuration.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/agent_config_files_location.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/agent_config_files_location.png new file mode 100644 index 0000000..a398b9f Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/agent_config_files_location.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/available_action_icons.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/available_action_icons.png new file mode 100644 index 0000000..f452960 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/available_action_icons.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/available_instances.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/available_instances.png new file mode 100644 index 0000000..5088998 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/available_instances.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-js.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-js.png new file mode 100644 index 0000000..151c6b8 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-js.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-python.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-python.png new file mode 100644 index 0000000..00a51f1 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-python.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-r.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-r.png new file mode 100644 index 0000000..a7627a1 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-r.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-ruby.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-ruby.png new file mode 100644 index 0000000..c1cff16 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/code-completion-ruby.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/command-palette.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/command-palette.png new file mode 100644 index 0000000..423e84c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/command-palette.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/command_when_java_process_started.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/command_when_java_process_started.png new file mode 100644 index 0000000..bfdc702 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/command_when_java_process_started.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/continue_download_gds.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/continue_download_gds.png new file mode 100644 index 0000000..bab0b5d Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/continue_download_gds.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/continue_download_not_accepted.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/continue_download_not_accepted.png new file mode 100644 index 0000000..978bffe Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/continue_download_not_accepted.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/create_java_launch_configuration.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/create_java_launch_configuration.png new file mode 100644 index 0000000..fad5426 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/create_java_launch_configuration.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-polyglot.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-polyglot.png new file mode 100644 index 0000000..157867d Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-polyglot.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-python.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-python.png new file mode 100644 index 0000000..d7f16c2 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-python.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-r.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-r.png new file mode 100644 index 0000000..056e336 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-r.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-ruby.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-ruby.png new file mode 100644 index 0000000..e64f8de Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config-ruby.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config.png new file mode 100644 index 0000000..2da4f4d Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/define_custom_path.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/define_custom_path.png new file mode 100644 index 0000000..63f309e Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/define_custom_path.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/download_token_generated.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/download_token_generated.png new file mode 100644 index 0000000..e8fac02 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/download_token_generated.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/edit_ni_state.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/edit_ni_state.png new file mode 100644 index 0000000..0d6fc04 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/edit_ni_state.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/enable_ni_agent.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/enable_ni_agent.png new file mode 100644 index 0000000..60db85e Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/enable_ni_agent.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/graalvm_install_actions.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/graalvm_install_actions.png new file mode 100644 index 0000000..e5d532c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/graalvm_install_actions.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/gu_config_window.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/gu_config_window.png new file mode 100644 index 0000000..f7a5029 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/gu_config_window.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/install_components_action.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/install_components_action.png new file mode 100644 index 0000000..8dd93ff Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/install_components_action.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/install_optional_components_popup.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/install_optional_components_popup.png new file mode 100644 index 0000000..1a87402 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/install_optional_components_popup.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/java_debuggers.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/java_debuggers.png new file mode 100644 index 0000000..6f48668 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/java_debuggers.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/langs_vscode.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/langs_vscode.png new file mode 100644 index 0000000..ac4ede9 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/langs_vscode.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/language-embedding-js.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/language-embedding-js.png new file mode 100644 index 0000000..1c8dbf3 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/language-embedding-js.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/lsp-dynamic-completion.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/lsp-dynamic-completion.png new file mode 100644 index 0000000..aaf5283 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/lsp-dynamic-completion.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_agent_pane.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_agent_pane.png new file mode 100644 index 0000000..5d9941c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_agent_pane.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_debugging.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_debugging.png new file mode 100644 index 0000000..5008106 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_debugging.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_launch-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_launch-config.png new file mode 100644 index 0000000..e1ca2ad Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_launch-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_pane_windows.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_pane_windows.png new file mode 100644 index 0000000..a6bbba2 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/ni_pane_windows.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/no-r-ls.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/no-r-ls.png new file mode 100644 index 0000000..88c9c63 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/no-r-ls.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/no-ruby-ls.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/no-ruby-ls.png new file mode 100644 index 0000000..3b6f284 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/no-ruby-ls.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/open_visualvm.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/open_visualvm.png new file mode 100644 index 0000000..5398d1c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/open_visualvm.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/otn_license_accept.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/otn_license_accept.png new file mode 100644 index 0000000..8c6a8b9 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/otn_license_accept.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/path_build_tools_script.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/path_build_tools_script.png new file mode 100644 index 0000000..fc3839a Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/path_build_tools_script.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/polyglot-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/polyglot-debug-config.png new file mode 100644 index 0000000..ac8d4f5 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/polyglot-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-debug-config.png new file mode 100644 index 0000000..4909d27 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-python-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-python-debug-config.png new file mode 100644 index 0000000..58c90fd Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-python-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-r-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-r-debug-config.png new file mode 100644 index 0000000..195aca3 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-r-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-ruby-debug-config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-ruby-debug-config.png new file mode 100644 index 0000000..fb62603 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select-ruby-debug-config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select_java_process_to_monitor.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select_java_process_to_monitor.png new file mode 100644 index 0000000..f126ebc Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/select_java_process_to_monitor.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/set_as_java_for_terminal.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/set_as_java_for_terminal.png new file mode 100644 index 0000000..3b6adca Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/set_as_java_for_terminal.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/show_gu_config.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/show_gu_config.png new file mode 100644 index 0000000..943a75e Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/show_gu_config.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/supported_languages.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/supported_languages.png new file mode 100644 index 0000000..c19e256 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/supported_languages.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/uncalled_method.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/uncalled_method.png new file mode 100644 index 0000000..18b1002 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/uncalled_method.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_command_palette.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_command_palette.png new file mode 100644 index 0000000..b5166b1 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_command_palette.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_commands.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_commands.png new file mode 100644 index 0000000..983fb75 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_commands.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_pane.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_pane.png new file mode 100644 index 0000000..3edf3db Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_pane.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_troubleshooting.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_troubleshooting.png new file mode 100644 index 0000000..572fa13 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/visualvm_troubleshooting.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/vscode_visualvm.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/vscode_visualvm.png new file mode 100644 index 0000000..2d7ada1 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/graalvm/images/vscode_visualvm.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/attach-remote-debugger.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/attach-remote-debugger.png new file mode 100644 index 0000000..ac87b17 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/attach-remote-debugger.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/create_k8s_deployment.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/create_k8s_deployment.png new file mode 100644 index 0000000..7ab6806 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/create_k8s_deployment.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/database-explorer-actions.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/database-explorer-actions.png new file mode 100644 index 0000000..6a21997 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/database-explorer-actions.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/deploy_yaml.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/deploy_yaml.png new file mode 100644 index 0000000..b45e2ad Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/deploy_yaml.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/docker_image_location.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/docker_image_location.png new file mode 100644 index 0000000..a7845fa Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/docker_image_location.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/k8s_deply_output.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/k8s_deply_output.png new file mode 100644 index 0000000..054303a Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/k8s_deply_output.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/k8s_port_forwarding.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/k8s_port_forwarding.png new file mode 100644 index 0000000..2a50ac9 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/k8s_port_forwarding.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/k8s_quick_actions.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/k8s_quick_actions.png new file mode 100644 index 0000000..95f0259 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/k8s_quick_actions.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/kubernetes_icon.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/kubernetes_icon.png new file mode 100644 index 0000000..e8882ca Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/kubernetes_icon.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-build-commands.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-build-commands.png new file mode 100644 index 0000000..2abb8e6 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-build-commands.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-deploy-commands.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-deploy-commands.png new file mode 100644 index 0000000..b4a66b7 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-deploy-commands.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-project-features_view.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-project-features_view.png new file mode 100644 index 0000000..bfcb900 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-project-features_view.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-vs-code-commands.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-vs-code-commands.png new file mode 100644 index 0000000..34db5f5 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut-vs-code-commands.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut.png new file mode 100644 index 0000000..eee5204 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut_cmd_prompt.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut_cmd_prompt.png new file mode 100644 index 0000000..8b559d8 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut_cmd_prompt.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut_tools_page.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut_tools_page.png new file mode 100644 index 0000000..43e5f54 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/micronaut_tools_page.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/oracle_db_support.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/oracle_db_support.png new file mode 100644 index 0000000..c187588 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/oracle_db_support.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/pick_docker_repository.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/pick_docker_repository.png new file mode 100644 index 0000000..c436534 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/pick_docker_repository.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/provide_image_name_version.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/provide_image_name_version.png new file mode 100644 index 0000000..47fc1d2 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/provide_image_name_version.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/run_debug_activity.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/run_debug_activity.png new file mode 100644 index 0000000..fad5426 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/run_debug_activity.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/select_namespace.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/select_namespace.png new file mode 100644 index 0000000..3f3496b Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/select_namespace.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/select_secret.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/select_secret.png new file mode 100644 index 0000000..5e5279f Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/images/select_secret.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/img/database-explorer.png b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/img/database-explorer.png new file mode 100644 index 0000000..a1f8c1c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/docs/tools/vscode/micronaut/img/database-explorer.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/implement-instrument/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/implement-instrument/index.html new file mode 100644 index 0000000..0df316f --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/implement-instrument/index.html @@ -0,0 +1,624 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Getting Started with Instruments in GraalVM

+ +

Tools are sometimes referred to as Instruments within the GraalVM platform. +The Instrument API is used to implement such instruments. +Instruments can track very fine-grained, VM-level runtime events to profile, inspect, and analyze the runtime behavior of applications running on GraalVM.

+ +

Simple Tool

+ +

To provide an easier starting point for tool developers we have created a Simple Tool example project. +This is a javadoc-rich Maven project which implements a simple code coverage tool.

+ +

We recommend cloning the repository and exploring the source code as a starting point for tool development. +The following sections will provide a guided tour of the steps needed to build and run a GraalVM tool, using Simple Tool source code as the running example. +These sections do not cover all of the features of the Instrument API so we encourage you to check the javadoc for more details.

+ +

Requirements

+ +

As mentioned before, Simple Tool is a code coverage tool. +Ultimately, it should provide the developer with information on what percentage of source code lines was executed, as well as exactly which lines were executed. +With that in mind, we can define some high-level requirements from our tool:

+ +
    +
  1. The tool keeps track of loaded source code.
  2. +
  3. The tool keeps track of executed source code.
  4. +
  5. On application exit, the tool calculates and prints per-line coverage information.
  6. +
+ +

Instrument API

+ +

The main starting point for tools is subclassing the TruffleInstrument class. +Unsurprisingly, the simple tool code base does exactly this, creating the SimpleCoverageInstrument class.

+ +

The Registration annotation on the class ensures that the newly created instrument is registered with the Instrument API, i.e., that it will be automatically discovered by the framework. +It also provides some metadata about the instrument: ID, name, version, which services the instrument provides, and whether the instrument is internal or not. +In order for this annotation to be effective the DSL processor needs to process this class. +This is, in the case of Simple Tool, done automatically by having the DSL processor as a dependency in the Maven configuration.

+ +

Now we will look back at the implementation of the SimpleCoverageInstrument class, namely which methods from TruffleInstrument it overrides. +These are onCreate, onDispose, and getOptionDescriptors. +The onCreate and onDispose methods are self-explanatory: they are called by the framework when the instrument is created and disposed. +We will discuss their implementations later, but first let us discuss the remaining one: getOptionDescriptors.

+ +

The Truffle language implementation framework comes with its own system for specifying command-line options. +These options allow tool users to control the tool either from the command line or when creating polyglot contexts. +It is annotation-based, and examples for such options are the ENABLED and PRINT_COVERAGE fields of SimpleCoverageInstrument. +Both of these are static final fields of the type OptionKey annotated with Option which, similar to the Registration annotation, provides some metadata for the option. +Again, as with the Registration annotation, for the Option annotation to be effective the DSL processor is needed, which generates a subclass of OptionDescriptors (in our case named SimpleCoverageInstrumentOptionDescriptors). +An instance of this class should be returned from the getOptionDescriptors method to let the framework know which options the instrument provides.

+ +

Returning to the onCreate method, as an argument, we receive an instance of the Env class. +This object gives a lot of useful information, but for the onCreate method we are primarily interested in the getOptions method, which can be used to read which options are passed to the tool. +We use this to check whether the ENABLED option has been set and if so we enable our tool by calling the enable method. +Similarly, in the onDispose method we check the options for the state of the PRINT_COVERAGE option, and if it is enabled we call the printResults method which will print our results.

+ +

What does it mean “to enable a tool?” +In general, it means that we tell the framework about the events we are interested in and how we want to react to them. Looking at our enable method, it does the following:

+ +
    +
  • First, it defines SourceSectionFilter. +This filter is a declarative definition of the parts of the source code we are interested in. +In our example, we care about all nodes that are considered expressions, and we do not care about internal language parts.
  • +
  • Second, we obtain an instance of an +Instrumenter +class which is an object allowing us to specify which parts of the system we wish to instrument.
  • +
  • Finally, using the Instrumenter class, we specify a Source Section Listener and an Execution Event Factory which are both described in the next two sections.
  • +
+ +

Source Section Listener

+ +

The Language API provides the notion of a Source which is the source code unit, and a SourceSection which is one continuous part of a Source, e.g., one method, one statement, one expression, and so on. More details can be found in the respective javadoc.

+ +

The first requirement for Simple Tool is to keep track of loaded source code. +The Instrument API provides the LoadSourceSectionListener which, when subclassed and registered with the instrumenter, allows users to react to the runtime loading source sections. +This is exactly what we do with the GatherSourceSectionsListener, which is registered in the enable method of the instrument. +The implementation of GatherSourceSectionsListener is quite simple: we override the onLoad method to notify the instrument of each loaded source section. +The instrument keeps a mapping from each Source to a Coverage object which keeps a set of loaded source sections for each source.

+ +

Execution Event Node

+ +

Guest languages are implemented as Abstract Syntax Tree (AST) interpreters. +The language implementers annotate certain nodes with tags, which allows us to select which nodes we are interested in, by using the aforementioned SourceSectionFilter, in a language-agnostic manner.

+ +

The main power of the Instrument API lies in its ability to insert specialised nodes in the AST which “wrap” the nodes of interest. +These nodes are built using the same infrastructure that the language developers use, and are, from the perspective of the runtime, indistinguishable from the language nodes. +This means that all of the techniques used to optimize guest languages into such high performing language implementations are available to the tool developers as well.

+ +

More information about these techniques is available in the language implementation documentation. +Suffice it to say that for Simple Tool to meet its second requirement, we need to instrument all expressions with our own node that will notify us when that expression is executed.

+ +

For this task we use the CoverageNode. +It is a subclass of ExecutionEventNode which, as the name implies, is used to instrument events during execution. +The ExecutionEventNode offers many methods to override, but we are only interested in onReturnValue. +This method is invoked when the “wrapped” node returns a value, i.e., is successfully executed. +The implementation is rather simple. We just notify the instrument that the node with this particular SourceSection has been executed, and the instrument updates the Coverage object in its coverage map.

+ +

The instrument is notified only once per node, as the logic is guarded by the flag. +The fact that this flag is annotated with CompilationFinal and that the call to the instrument is preceded by a call to transferToInterpreterAndInvalidate() is a standard technique in Truffle, which ensures that once this instrumentation is no longer needed (i.e., a node has been executed), the instrumentation is removed from further compilations, along with any performance overhead.

+ +

In order for the framework to know how to instantiate the CoverageNode when it is needed, we need to provide a factory for it. +The factory is the CoverageEventFactory, a subclass of ExecutionEventNodeFactory. +This class just ensures that each CoverageNode knows the SourceSection it is instrumenting by looking it up in the provided EventContext.

+ +

Finally, when we are enabling the instrument, we tell the instrumenter to use our factory to “wrap” the nodes selected by our filter.

+ +

Interaction Between Users and Instruments

+ +

The third and final requirement Simple Tool has is to actually interact with its user by printing line coverage to standard output. +The instrument overriders the onDispose method which is unsurprisingly called when the instrument is being disposed of. +In this method we check that the proper option has been set and, if so, calculate and print the coverage as recorded by our map of Coverage objects.

+ +

This is a simple way of providing useful information to a user, but it is definitely not the only one. +A tool could dump its data directly to a file, or run a web endpoint which shows the information, etc. +One of the mechanisms that the Instrument API provides users with is registering instruments as services to be looked up by other instruments. +If we look at the Registration annotation of our instrument we can see that it provides a services field where we can specify which services the instrument provides to other instruments. +These services need to be explicitly registered. +This allows a nicer separation of concerns among instruments so that, for example, we could have a “real time coverage” instrument which would use our SimpleCoverageInstrument to provide on-demand coverage information to a user through a REST API, and an “aborts on low coverage” instrument which stops the execution if coverage drops below a threshold, both using the SimpleCoverageInstrument as a service.

+ +

Note: For reasons of isolation, instrument services are not available to application code, and instrument services can only be used from other instruments or guest languages.

+ +

Installing a Tool into GraalVM

+ +

So far, Simple Tool seems to meet all requirements but the question remains: how do we use it? +As mentioned before, Simple Tool is a Maven project. +Setting JAVA_HOME to a GraalVM installation and running ./mvnw package produces a target/simpletool-<version>.jar. +This is the Simple Tool distribution form.

+ +

The Truffle framework offers a clear separation between the language/tooling code and the application code. +For this reason, putting the JAR on the class path will not result in the framework realizing a new tool is needed. +To achieve this we use --vm.Dtruffle.class.path.append=/path/to/simpletool-<version>.jar as is illustrated in a launcher script for our simple tool. +This script also shows we can set the CLI options we specified for Simple Tool. +This means that if we execute ./simpletool js example.js, we will launch the js launcher of GraalVM, add the tool to the framework class path, and run the included example.js file with Simple Tool enabled, resulting in the following output:

+ +
==
+Coverage of /path/to/simpletool/example.js is 59.42%
++ var N = 2000;
++ var EXPECTED = 17393;
+
+  function Natural() {
++     x = 2;
++     return {
++         'next' : function() { return x++; }
++     };
+  }
+
+  function Filter(number, filter) {
++     var self = this;
++     this.number = number;
++     this.filter = filter;
++     this.accept = function(n) {
++       var filter = self;
++       for (;;) {
++           if (n % filter.number === 0) {
++               return false;
++           }
++           filter = filter.filter;
++           if (filter === null) {
++               break;
++           }
++       }
++       return true;
++     };
++     return this;
+  }
+
+  function Primes(natural) {
++     var self = this;
++     this.natural = natural;
++     this.filter = null;
++     this.next = function() {
++         for (;;) {
++             var n = self.natural.next();
++             if (self.filter === null || self.filter.accept(n)) {
++                 self.filter = new Filter(n, self.filter);
++                 return n;
++             }
++         }
++     };
+  }
+
++ var holdsAFunctionThatIsNeverCalled = function(natural) {
+-     var self = this;
+-     this.natural = natural;
+-     this.filter = null;
+-     this.next = function() {
+-         for (;;) {
+-             var n = self.natural.next();
+-             if (self.filter === null || self.filter.accept(n)) {
+-                 self.filter = new Filter(n, self.filter);
+-                 return n;
+-             }
+-         }
+-     };
++ }
+
+- var holdsAFunctionThatIsNeverCalledOneLine = function() {return null;}
+
+  function primesMain() {
++     var primes = new Primes(Natural());
++     var primArray = [];
++     for (var i=0;i<=N;i++) { primArray.push(primes.next()); }
+-     if (primArray[N] != EXPECTED) { throw new Error('wrong prime found: ' + primArray[N]); }
+  }
++ primesMain();
+
+ +

Other Examples

+ +

The following examples are intended to show common use-cases that can be solved with the Instrument API.

+ +
    +
  • Coverage Instrument: a coverage tool example which was used to build up Simple Tool. It is used as the running example in further text where appropriate.
  • +
  • Debugger Instrument: + a sketch on how a debugger can be implemented. Note that the Instrument API already provides a Debugger Instrument that can be used directly.
  • +
  • Statement Profiler: a profiler that is able to profile the execution of statements.
  • +
+ +

Instrumentation Event Listeners

+ +

The Instrument API is defined in the com.oracle.truffle.api.instrumentation package. +Instrumentation agents can be developed by extending the TruffleInstrument class, and can be attached to a running GraalVM instance using the Instrumenter class. +Once attached to a running language runtime, instrumentation agents remain usable as long as the language runtime is not disposed. +Instrumentation agents on GraalVM can monitor a variety of VM-level runtime events, including any of the following:

+ +
    +
  1. Source code-related events: The agent can be notified every time a new Source or SourceSection element is loaded by the monitored language runtime.
  2. +
  3. Allocation events: The agent can be notified every time a new object is allocated in the memory space of the monitored language runtime.
  4. +
  5. Language runtime and thread creation events: The agent can be notified as soon as a new execution context or a new thread for a monitored language runtime is created.
  6. +
  7. Application execution events: The agent gets notified every time a monitored application executes a specific set of language operations. Examples of such operations include language statements and expressions, thus allowing an instrumentation agent to inspect running applications with very high precision.
  8. +
+ +

For each execution event, instrumentation agents can define filtering criteria that will be used by the GraalVM instrumentation runtime to monitor only the relevant execution events. +Currently, GraalVM instruments accept one of the following two filter types:

+ +
    +
  1. AllocationEventFilter to filter allocation events by allocation type.
  2. +
  3. SourceSectionFilter to filter source code locations in an application.
  4. +
+ +

Filters can be created using the provided builder object. For example, the following builder creates a SourceSectionFilter:

+ +
SourceSectionFilter.newBuilder()
+                   .tagIs(StandardTag.StatementTag)
+                   .mimeTypeIs("x-application/js")
+                   .build()
+
+ +

The filter in the example can be used to monitor the execution of all JavaScript statements in a given application. +Other filtering options such as line numbers or file extensions can also be provided.

+ +

Source section filters like the one in the example can use Tags to specify a set of execution events to be monitored. Language-agnostic tags such as statements and expressions are defined in the com.oracle.truffle.api.instrumentation.Tag class, and are supported by all GraalVM languages. +In addition to standard tags, GraalVM languages may provide other, language-specific, tags to enable fine-grained profiling of language-specific events. +(As an example, the GraalVM JavaScript engine provides JavaScript-specific tags to track the usages of ECMA builtin objects such as Array, Map, or Math.)

+ +

Monitoring Execution Events

+ +

Application execution events enable very precise and detailed monitoring. GraalVM supports two different types of instrumentation agents to profile such events, namely:

+ +
    +
  1. Execution listener: an instrumentation agent that can be notified every time a given runtime event happens. Listeners implement the ExecutionEventListener interface, and cannot associate any state with source code locations.
  2. +
  3. Execution event node: an instrumentation agent that can be expressed using Truffle Framework AST nodes. Such agents extend the ExecutionEventNode class and have the same capabilities of an execution listener, but can associate state with source code locations.
  4. +
+ +

Simple Instrumentation Agent

+ +

A simple example of a custom instrumentation agent used to perform runtime code coverage can be found in the CoverageExample class. +What follows is an overview of the agent, its design, and its capabilities.

+ +

All instruments extend the TruffleInstrument abstract class and are registered in the GraalVM runtime through the @Registration annotation:

+ +
@Registration(id = CoverageExample.ID, services = Object.class)
+public final class CoverageExample extends TruffleInstrument {
+
+  @Override
+  protected void onCreate(final Env env) {
+  }
+
+  /* Other methods omitted... */
+}
+
+ +

Instruments override the onCreate(Env env) method to perform custom operations at instrument loading time. +Typically, an instrument would use this method to register itself in the existing GraalVM execution environment. +As an example, an instrument using AST nodes can be registered in the following way:

+ +
@Override
+protected void onCreate(final Env env) {
+  SourceSectionFilter.Builder builder = SourceSectionFilter.newBuilder();
+  SourceSectionFilter filter = builder.tagIs(EXPRESSION).build();
+  Instrumenter instrumenter = env.getInstrumenter();
+  instrumenter.attachExecutionEventFactory(filter, new CoverageEventFactory(env));
+}
+
+ +

The instrument connects itself to the running GraalVM using the attachExecutionEventFactory method, providing the following two arguments:

+
    +
  1. SourceSectionFilter: a source section filter used to inform the GraalVM about specific code sections to be tracked.
  2. +
  3. ExecutionEventNodeFactory: the Truffle AST factory that provides instrumentation AST nodes to be executed by the agent every time a runtime event (as specified by the source filter) is executed.
  4. +
+ +

A basic ExecutionEventNodeFactory that instruments the AST nodes of an application can be implemented in the following way:

+ +
public ExecutionEventNode create(final EventContext ec) {
+  return new ExecutionEventNode() {
+    @Override
+    public void onReturnValue(VirtualFrame vFrame, Object result) {
+      /*
+       * Code to be executed every time a filtered source code
+       * element is evaluated by the guest language.
+       */
+    }
+  };
+}
+
+ +

Execution event nodes can implement certain callback methods to intercept runtime execution events. Examples include:

+ +
    +
  1. onEnter: executed before an AST node corresponding to a filtered source code element (e.g., a language statement or an expression) is evaluated.
  2. +
  3. onReturnValue: executed after a source code element returns a value.
  4. +
  5. onReturnExceptional: executed in case the filtered source code element throws an exception.
  6. +
+ +

Execution event nodes are created on a per code location basis. +Therefore, they can be used to store data specific to a given source code location in the instrumented application. +As an example, an instrumentation node can simply keep track of all code locations that have already been visited using a node-local flag. +Such a node-local boolean flag can be used to track the execution of AST nodes in the following way:

+ +
// To keep track of all source code locations executed
+private final Set<SourceSection> coverage = new HashSet<>();
+
+public ExecutionEventNode create(final EventContext ec) {
+  return new ExecutionEventNode() {
+    // Per-node flag to keep track of execution for this node
+    @CompilationFinal private boolean visited = false;
+
+    @Override
+    public void onReturnValue(VirtualFrame vFrame, Object result) {
+      if (!visited) {
+        CompilerDirectives.transferToInterpreterAndInvalidate();
+        visited = true;
+        SourceSection src = ec.getInstrumentedSourceSection();
+        coverage.add(src);
+      }
+    }
+  };
+}
+
+ +

As the above code shows, an ExecutionEventNode is a valid AST node. +This implies that the instrumentation code will be optimized by the GraalVM runtime together with the instrumented application, resulting in minimal instrumentation overhead. Furthermore, this allows instrument developers to use the Truffle framework compiler directives directly from instrumentation nodes. +In the example, compiler directives are used to inform the Graal compiler that visited can be considered compilation-final.

+ +

Each instrumentation node is bound to a specific code location. +Such locations can be accessed by the agent using the provided EventContext object. The context object gives instrumentation nodes access to a variety of information about the current AST nodes being executed. +Examples of query APIs available to instrumentation agents through EventContext include:

+ +
    +
  1. hasTag: to query an instrumented node for a certain node Tag (e.g., to check if a statement node is also a conditional node).
  2. +
  3. getInstrumentedSourceSection: to access the SourceSection associated with the current node.
  4. +
  5. getInstrumentedNode: to access the Node corresponding to the current instrumentation event.
  6. +
+ +

Fine-grained Expression Profiling

+ +

Instrumentation agents can profile even fractional events such as language expressions. To this end, an agent needs to be initialized providing two source section filters:

+ +
// What source sections are we interested in?
+SourceSectionFilter sourceSectionFilter = SourceSectionFilter.newBuilder()
+  .tagIs(JSTags.BinaryOperation.class)
+  .build();
+
+// What generates input data to track?
+SourceSectionFilter inputGeneratingLocations = SourceSectionFilter.newBuilder()
+  .tagIs(StandardTags.ExpressionTag.class)
+  .build();
+
+instrumenter.attachExecutionEventFactory(sourceSectionFilter, inputGeneratingLocations, factory);
+
+ +

The first source section filter (sourceSectionFilter, in the example) is a normal filter equivalent to other filters described before, and is used to identify the source code locations to be monitored. +The second section filter, inputGeneratingLocations, is used by the agent to specify the intermediate values that should be monitored for a certain source section. +Intermediate values correspond to all observable values that are involved in the execution of a monitored code element, and are reported to the instrumentation agent by means of the onInputValue callback. +As an example, let us assume an agent needs to profile all operand values provided to sum operations (i.e., +) in JavaScript:

+ +
var a = 3;
+var b = 4;
+// the '+' expression is profiled
+var c = a + b;
+
+ +

By filtering on JavaScript binary expressions, an instrumentation agent would be able to detect the following runtime events for the above code snippet:

+
    +
  1. onEnter(): for the binary expression at line 3.
  2. +
  3. onInputValue(): for the first operand of the binary operation at line 3. The value reported by the callback will be 3, that is, the value of the a local variable.
  4. +
  5. onInputValue(): for the second operand of the binary operation. The value reported by the callback will be 4, that is, the value of the b local variable.
  6. +
  7. onReturnValue(): for the binary expression. The value provided to the callback will be the value returned by the expression after it has completed its evaluation, that is, the value 7.
  8. +
+ +

By extending the source section filters to all possible events, an instrumentation agent will observe something equivalent to the following execution trace (in pseudocode):

+
// First variable declaration
+onEnter - VariableWrite
+    onEnter - NumericLiteral
+    onReturnValue - NumericLiteral
+  onInputValue - (3)
+onReturnValue - VariableWrite
+
+// Second variable declaration
+onEnter - VariableWrite
+    onEnter - NumericLiteral
+    onReturnValue - NumericLiteral
+  onInputValue - (4)
+onReturnValue - VariableWrite
+
+// Third variable declaration
+onEnter - VariableWrite
+    onEnter - BinaryOperation
+        onEnter - VariableRead
+        onReturnValue - VariableRead
+      onInputValue - (3)
+        onEnter - VariableRead
+        onReturnValue - VariableRead
+      onInputValue - (4)
+    onReturnValue - BinaryOperation
+  onInputValue - (7)
+onReturnValue - VariableWrite
+
+ +

The onInputValue method can be used in combination with source section filters to intercept very fine-grained execution events such as intermediate values used by language expressions. +The intermediate values that are accessible to the Instrumentation framework greatly depend on the instrumentation support provided by each language. +Moreover, languages may provide additional metadata associated with language-specific Tag classes.

+ +

Altering the Execution Flow of an Application

+ +

The instrumentation capabilities that we have presented so far enable users to observe certain aspects of a running application. +In addition to passive monitoring of an application’s behavior, the Instrument API features support for actively altering the behavior of an application at runtime. +Such capabilities can be used to write complex instrumentation agents that affect the behavior of a running application to achieve specific runtime semantics. +For example, one could alter the semantics of a running application to ensure that certain methods or functions are never executed (e.g., by throwing an exception when they are called).

+ +

Instrumentation agents with such capabilities can be implemented by leveraging the onUnwind callback in execution event listeners and factories. +As an example, let’s consider the following JavaScript code:

+ +
function inc(x) {
+  return x + 1
+}
+
+var a = 10
+var b = a;
+// Let's call inc() with normal semantics
+while (a == b && a < 100000) {
+  a = inc(a);
+  b = b + 1;
+}
+c = a;
+// Run inc() and alter it's return type using the instrument
+return inc(c)
+
+ +

An instrumentation agent that modifies the return value of inc to always be 42 can be implemented using an ExecutionEventListener, in the following way:

+ +
ExecutionEventListener myListener = new ExecutionEventListener() {
+
+  @Override
+  public void onReturnValue(EventContext context, VirtualFrame frame, Object result) {
+    String callSrc = context.getInstrumentedSourceSection().getCharacters();
+    // is this the function call that we want to modify?
+    if ("inc(c)".equals(callSrc)) {
+      CompilerDirectives.transferToInterpreter();
+      // notify the runtime that we will change the current execution flow
+      throw context.createUnwind(null);
+    }
+  }
+
+  @Override
+  public Object onUnwind(EventContext context, VirtualFrame frame, Object info) {
+    // just return 42 as the return value for this node
+    return 42;
+  }
+}
+
+ +

The event listener can be executed intercepting all function calls, for example using the following instrument:

+ +
@TruffleInstrument.Registration(id = "UniversalAnswer", services = UniversalAnswerInstrument.class)
+public static class UniversalAnswerInstrument extends TruffleInstrument {
+
+  @Override
+  protected void onCreate(Env env) {
+    env.registerService(this);
+    env.getInstrumenter().attachListener(SourceSectionFilter.newBuilder().tagIs(CallTag.class).build(), myListener);
+  }
+}
+
+ +

When enabled, the instrument will execute its onReturnValue callback each time a function call returns. +The callback reads the associated source section (using getInstrumentedSourceSection) and looks for a specific source code pattern (the function call inc(c), in this case). +As soon as such code pattern is found, the instrument throws a special runtime exception, called UnwindException, that instructs the Instrumentation framework about a change in the current application’s execution flow. +The exception is intercepted by the onUnwind callback of the instrumentation agent, which can be used to return any arbitrary value to the original instrumented application.

+ +

In the example, all calls to inc(c) will return 42 regardless of any application-specific data. +A more realistic instrument might access and monitor several aspects of an application, and might not rely on source code locations, but rather on object instances or other application-specific data.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/implement-language/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/implement-language/index.html new file mode 100644 index 0000000..c7890a0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/implement-language/index.html @@ -0,0 +1,358 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Introduction to SimpleLanguage

+ +

To implement your own language, get started by extending an existing language such as SimpleLanguage. +SimpleLanguage is a demonstration language built using the Language API. +The SimpleLanguage project provides a showcase on how to use the Language APIs for writing your own language. +It aims to use most of the available Truffle language implementation framework (henceforth “Truffle”) features, and documents their use extensively with inline source documentation.

+ +

The SimpleLanguage demonstration language is licensed under the Universal Permissive License (UPL).

+ +

Prerequisites

+ +
    +
  • Maven available in your system.
  • +
  • GraalVM installed.
  • +
  • The mx tool for the development of GraalVM projects (see below).
  • +
+ +

Get Started

+ +
    +
  1. Clone the SimpleLanguage repository by running: +
     git clone https://github.com/graalvm/simplelanguage
    +
    +
  2. +
  3. Execute ./mvnw package from the SimpleLanguage folder to build the language. Before that, verify that native-image is available with your GraalVM installation to avoid a build failure: +
     cd simplelanguage
    +
    +
     native-image --version
    +
    +
     ./mvnw package
    +
    +

    The command builds the slnative executable in the simplelanguage/native directory and the sl-component.jar language component which can be later installed into GraalVM using the GraalVM Updater tool.

    + +

    You can disable the SimpleLanguage native executable build during the packaging phase by running:

    +
     export SL_BUILD_NATIVE=false
    + ./mvnw package
    +
    +
  4. +
  5. Run SimpleLanguage from the project root folder: +
     ./sl language/tests/HelloWorld.sl
    +
    +
  6. +
  7. To see assembly code for the compiled functions, run: +
     ./sl -disassemble language/tests/SumPrint.sl
    +
    +
  8. +
+ +

IDE Setup

+ +

The Truffle framework provides language-agnostic infrastructure to realize standard IDE features by providing additional APIs. +If you would like to experiment with your language and get the benefits of an IDE, consider importing SimpleLanguage as an example.

+ +

Eclipse

+ +

The SimpleLanguage teaching project has been tested with Eclipse Neon.2 Release 4.6.2, and Eclipse Oxygen.3A. To import the project folder to the desirable Eclipse environment:

+
    +
  1. Open Eclipse with a new workspace.
  2. +
  3. Install the m2e and m2e-apt plugins from the Eclipse marketplace (Help -> Eclipse Marketplace).
  4. +
  5. Finally, import the SimpleLanguage project from File -> Import -> Maven -> Existing Maven Projects -> browse to the SimpleLanguage folder -> Finish.
  6. +
+ +

NetBeans

+ +

NetBeans provides GUI support for debugging arbitrary languages. To upload SimpleLanguage to NetBeans interface, go to File -> Open Project -> select simplelanguage folder -> check Open Required Projects -> open Project.

+ +

IntelliJ IDEA

+ +

The SimpleLanguage project was tested with IntelliJ IDEA. Open IntelliJ IDEA and, from the main menu bar, select File -> Open -> Navigate to and select the simplelanguage folder -> Press OK. All dependencies will be included automatically.

+ +

Dump Graphs

+ +

To investigate performance issues, consider using the Ideal Graph Visualizer (IGV) on top of GraalVM. +IGV is developed to view and inspect intermediate representation graphs – a language-independent intermediate representation (IR) between the source language and the machine code, generated by the compiler. IGV is free to use.

+ +
    +
  1. Set up the mx tool on your computer. +
      +
    • Clone the mx repository by running: +
       git clone https://github.com/graalvm/mx.git
      +
      +
    • +
    • Clone the Graal repository to your work directory: +
       git clone https://github.com/oracle/graal.git
      +
      +
    • +
    • Add mx to the PATH environment variable: +
       export PATH="/path/to/mx:$PATH"
      +
      +
    • +
    • To check whether the installation was successful, run the command: +
       mx --version 
      +
      +
    • +
    +
  2. +
  3. Launch IGV with mx: +
     mx -p graal/compiler igv
    +
    +
  4. +
  5. Execute the following from the SimpleLanguage root folder to dump graphs to IGV: +
     ./sl -dump language/tests/SumPrint.sl
    +
    +
  6. +
+ +

This dumps compiler graphs in the IGV format over the network to an IGV process listening on 127.0.0.1:4445. +Once the connection is made, you are able to see the graphs in the Outline window. +Open a specific graph, search for nodes by name, ID, or by property=value data, and all matching results will be shown. +Learn more here.

+ +

Debug

+ +

To start debugging the SimpleLanguage implementation with a Java debugger, pass the -debug option to the command-line launcher of your program:

+
./sl -debug language/tests/HelloWorld.sl
+
+ +

Then attach a Java remote debugger (like Eclipse) on port 8000.

+ +

SimpleLanguage Component for GraalVM

+ +

Languages implemented with the Truffle framework can be packaged as components which later can be installed into GraalVM using the GraalVM Updater tool. +Running ./mvnw package in the SimpleLanguage folder also builds a sl-component.jar. +This file is the SimpleLanguage component for GraalVM and can be installed by running:

+
gu -L install /path/to/sl-component.jar
+
+ +

SimpleLanguage Native Image

+ +

A language built with Truffle can be AOT compiled using Native Image. +Running ./mvnw package in the SimpleLanguage folder also builds a slnative executable in the native directory. +This executable is the full SimpleLanguage implementation as a single native application, and has no need for GraalVM in order to execute SimpleLanguage code. +Besides this, a big advantage of using the native executable when compared to running on GraalVM is the greatly faster startup time as shown bellow:

+
time ./sl language/tests/HelloWorld.sl
+== running on org.graalvm.polyglot.Engine@2db0f6b2
+Hello World!
+
+real    0m0.405s
+user    0m0.660s
+sys     0m0.108s
+
+time ./native/slnative
+language/tests/HelloWorld.sl
+== running on org.graalvm.polyglot.Engine@7fd046f06898
+Hello World!
+
+real    0m0.004s
+user    0m0.000s
+sys     0m0.000s
+
+ +

This snipped shows a timed execution of a “Hello World” program using the sl launcher script, which runs SimpleLanguage on GraalVM, using Native Image. +We can see that when running on GraalVM the execution takes 405ms. +Since our SimpleLanguage program does just one print statement, we can conclude that almost all of this time is spent starting up GraalVM and initializing the language itself. +When using the native executable we see that the execution takes only 4ms, showing two orders of magnitude faster startup than running on GraalVM.

+ +

For more information on the native-image tool consider reading the reference manual.

+ +

Disable SimpleLanguage Native Image Build

+ +

Building the native executable through Maven is attached to the Maven package phase. +Since the native executable build can take a bit of time, we provide the option to skip this build by setting the SL_BUILD_NATIVE environment variable to false like so:

+ +
export SL_BUILD_NATIVE=false
+./mvnw package
+...
+[INFO]
+[INFO] ------------------------------------------------------------------------
+[INFO] Building simplelanguage-graalvm-native
+[INFO] ------------------------------------------------------------------------
+[INFO]
+[INFO] --- exec-maven-plugin:1.6.0:exec (make_native) @ simplelanguage-graalvm-native ---
+Skipping the native image build because SL_BUILD_NATIVE is set to false.
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESS
+[INFO] ------------------------------------------------------------------------
+...
+
+ +

Run SimpleLanguage with the Newest (Developement) version of the Compiler

+ +

To run SimpleLanguage with the development version of the Graal compiler we must build a GraalVM with that compiler. +Clone the graal repository (https://github.com/oracle/graal) and follow the instructions in the vm/README.md file to build a GraalVM.

+ +

Once that’s done, point JAVA_HOME to the newly built GraalVM and proceed with normal building and running of SimpleLanguage.

+ +

Run SimpleLanguage Using Command Line

+ +

Executing SimpleLanguage code is normally done with the sl script which sets up the necessary command line depending on whether JAVA_HOME points to GraalVM or another JVM installation. +The following subsections describe the command line for both cases.

+ +

Run SimpleLanguage with GraalVM as JAVA_HOME

+ +

Assuming JAVA_HOME points to the GraalVM installation and that the current working directory is the simplelanguage directory, to run SimpleLanguage one should execute the following command:

+ +
$JAVA_HOME/bin/java \
+    -cp launcher/target/launcher-22.1.0-SNAPSHOT.jar \
+    -Dtruffle.class.path.append=language/target/simplelanguage.jar \
+    com.oracle.truffle.sl.launcher.SLMain language/tests/Add.sl
+
+ +

In short, we place the launcher JAR on the class path and execute its main class, but we inform GraalVM of the presence of SimpleLanguage by using the -Dtruffle.class.path.append option and providing it the path to the fat language JAR. +Having the language on a separate class path ensures a strong separation between the language implementation and its embedding context (in this case, the launcher).

+ +

Disable Class Path Separation

+ +

NOTE! This should only be used during development.

+ +

For development purposes it is useful to disable the class path separation and enable having the language implementation on the application class path (for example, for testing +the internals of the language).

+ +

The Language API JAR on Maven Central exports all API packages in its module-info. +Apply the --upgrade-module-path option together with -Dgraalvm.locatorDisabled=true and this JAR to export Language API packages:

+
-Dgraalvm.locatorDisabled=true --module-path=<yourModulePath>:${truffle.dir} --upgrade-module-path=${truffle.dir}/truffle-api.jar
+
+ +

A sample POM using --upgrade-module-path to export Language API packages can be found in the Simple Language POM.xml file.

+ +
+

Note: Disabling the locator effectively removes all installed languages from the module path as the locator also creates the class loader for the languages. +To still use the builtin languages add them to the module-path by pointing the module-path to all needed language homes (for example, $GRAALVM/languages/js).

+
+ +

Other JVM Implementations

+ +

Unlike GraalVM, which includes all the dependencies needed to run a language implemented with Truffle, other JVM implementations need additional JARs to be present on the class path. +These are the Language API and GraalVM SDK JARs available from Maven Central.

+ +

Assuming JAVA_HOME points to a stock JDK installation, and that the current working directory is the simplelanguage directory and the Language API and GraalVM SDK JARs are present in that directory, one can execute SimpleLanguage with the following command:

+ +
$JAVA_HOME/bin/java \
+    -cp graal-sdk-22.1.0.jar:truffle-api-22.1.0.jar:launcher/target/launcher-22.1.0-SNAPSHOT.jar:language/target/simplelanguage.jar \
+    com.oracle.truffle.sl.launcher.SLMain language/tests/Add.sl
+
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/index.html new file mode 100644 index 0000000..95f5140 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/index.html @@ -0,0 +1,144 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

GraalVM as a Platform

+ +

GraalVM is an open ecosystem and allows users to implement a custom language or tool on top of it with the Truffle language implementation framework which offers APIs for writing interpreters for programming languages in the form of Java programs.

+ +

GraalVM loads and runs the Truffle framework, which itself is a Java program – a collection of JAR files – together with interpreters. +These get optimized at runtime into efficient machine code for executing loaded programs.

+ +

Learn more about this framework from its reference documentation.

+ +

Implement Your Language

+ +

With the Language API offered by the Truffle framework, you can implement a language interpreter on top of GraalVM.

+ +

To get started, proceed to Implement Your Language.

+ +

Implement Your Tool

+ +

With the Instrument API offered by the Truffle framework, you can create language-agnostic tools like debuggers, profilers, or other instruments on top of GraalVM.

+ +

To get started, proceed to Implement Your Tool.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/AOT/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/AOT/index.html new file mode 100644 index 0000000..7886129 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/AOT/index.html @@ -0,0 +1,159 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle AOT Tutorial

+ +

Many statically compiled languages, like C are designed to be compilable without prior execution. +By default, Truffle first interprets code before it is compiled. +In order to improve warmup speed of static languages AOT compilation can be supported. +The following tutorial describes how to support Truffle AOT in your language, how to trigger and test it.

+ +

Language Support

+ +

In order for languages to support AOT compilation the language needs to implement the RootNode.prepareForAOT() method. +The language implementation can indicate support for AOT by returning a non null value in this method. +The goal for implementing a root node for AOT is to prepare all the AST nodes such that they no longer deoptimize when they are compiled without prior execution.

+ +

Typical actions performed in an implementation of this method are:

+ +
    +
  • Initialize local variable types in the FrameDescriptor of the root node. If a language uses local variables and their types are known, then this information must be provided to the FrameDescriptor. This step can often be done already during parsing.
  • +
  • Compute the expected execution signature of a root node and return it. This step requires the parser to infer expected types +for arguments and return values.
  • +
  • Prepare specializing nodes with profiles that do not invalidate on first execution. Truffle DSL supports preparation of specializing nodes for AOT. See the example AOT language for details.
  • +
+ +

Trigger AOT compilation

+ +

AOT compilation can be triggered and tested by using the --engine.CompileAOTOnCreate=true option. +This will trigger AOT compilation for every created call target with a root node that supports AOT compilation. +A root node supports AOT compilation if it returns a non null value in RootNode.prepareForAOT(). +Note that enabling this flag will also disable background compilation which makes it not suitable for production usage.

+ +

Example Usage

+ +

Use the following documented and executable Truffle language as inspiration for AOT support: +AOT Tutorial

+ +

The example is executable as mx unittest using mx unittest AOTTutorial.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/AOTOverview/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/AOTOverview/index.html new file mode 100644 index 0000000..d5bf48d --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/AOTOverview/index.html @@ -0,0 +1,208 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle AOT Overview

+ +

There are several different flavors of AOT preinitialization, compilation, and caching supported in Truffle. +This document is intended to provide an overview of these capabilities.

+ +

Note that some of the features mentioned here are only supported in Oracle GraalVM.

+ +

Preinitialization of the First Context

+ +

Native image allows running Java code in static initializers at image build time. +After static initialization was run, values referenced from static fields are snapshotted and persisted in the image. +Context preinitialization leverages this feature by creating and initializing a language context at image build time to be used by the first context that gets created in an isolate or process at runtime. +This typically improves the initialization time of the first context significantly.

+ +

Context preinitialization can be enabled by setting the system property -Dpolyglot.image-build-time.PreinitializeContexts=ruby,llvm at image build time. +A language needs to implement TruffleLanguage.patchContext and return true to support context preinitialization. +In addition, languages need to be careful not to bind any host-specific data or create objects that would not be allowed to be stored in a native image, like java.lang.Thread instances.

+ +

For more information see TruffleLanguage.patchContext javadoc.

+ +

Code sharing within the same Isolate/Process

+ +

A polyglot engine can be used in order to determine the scope of code sharing between contexts. +An example of how that can be done can be found in the reference manual. +When a language is initialized for a polyglot context, a new language instance is requested from an engine. +If the language supports ContextPolicy.SHARED, then the language instance will be reused for an engine instance. +The source parsing cache is associated with a language instance, so parsing happens once per language instance. +Languages may choose to disallow reuse of a language instance for a new additional context by implementing TruffleLanguage.areOptionsCompatible. +This allows languages to assume specific context options to be compilation final for all root nodes created by the language. +An exception from this rule is InteropLibrary, where nodes may be shared unconditionally between languages instances.

+ +

Supporting Context Independent Code

+ +

Codesharing requires that all code data structures are independent of their context. +For example, code is context-independent if it can be executed with one context and then executed again with a new context without deoptimizing the code. +A good test to verify a language implementation’s context independence is to create a context with an explicit engine, run a test application, and then verify that the second context does not cause deoptimizations when running the same deterministic application.

+ +

The Truffle framework announces the potential use of a language instance in multiple contexts by calling TruffleLanguage.initializeMultipleContexts, typically even before the first context is created. +The framework is able to initialize multiple contexts before the first context is created when an explicit engine is used or --engine.CacheStore is set to true.

+ +

The following criteria should be satisfied when supporting context independent code:

+ +
    +
  • All speculation on runtime value identity must be disabled with multiple contexts initialized, as they will lead to a guaranteed deoptimization when used with the second context.
  • +
  • Function inline caches should be modified and implemented as a two-level inline cache. The first level speculates on the function instance’s identity and the second level on the underlying CallTarget instance. The first level cache must be disabled if multiple contexts are initialized, as this would unnecessarily cause deoptimization.
  • +
  • The DynamicObject root Shape instance should be stored in the language instance instead of the language context. Otherwise, any inline cache on shapes will not stabilize and ultimately end up in the generic state.
  • +
  • All Node implementations must not store context-dependent data structures or context-dependent runtime values.
  • +
  • Loading and parsing of sources, even with language-internal builtins, should be performed using TruffleLanguage.Env.parse to cache Source parsing per language instance.
  • +
  • All assumption instances should be stored in the language instance instead of the context. With multiple contexts initialized, the context instance read using context references may no longer be a constant. In this case any assumption read from the context would not be folded and they would cause significant runtime performance overhead. Assumptions from the language can be folded by the compiler in both single and multiple context mode.
  • +
+ +

It is expected that an AST created for multiple contexts is compiled to less efficient machine code as it does not allow for speculation on the identity of runtime values. +For example, instead of speculating on the function instance in an inline cache, it is necessary to speculate on the contained CallTarget. +This is slower because it requires an additional read to access the CallTarget stored in the function. +It may be costly to create context independent code, therefore, speculation on runtime values should still be performed if multiple contexts are not initialized.

+ +

SimpleLanguage and JavaScript are two languages that already support context independent code and might be useful as a guidance on concrete problems.

+ +

Persistent Context Independent Code with Auxiliary Engine Caching (Oracle GraalVM)

+ +

Oracle GraalVM supports persisting code data structures to disk. +This enables to almost eliminate warmup time for the first run of an application in an isolate/process. +The SVM auxiliary image feature is used to persist and load the necessary data structures to the disk. +Persisting the image can take a significant amount of time as compilation needs to be performed. +However, loading is designed to be as fast as possible, typically almost instantaneous.

+ +

Engine caching is enabled using options and functional even if the context was created without an explicit engine.

+ +

More information on engine caching can be found in the engine caching tutorial.

+ +

Compilation without Profiling

+ +

By default, if language functions are created but never executed, they are not compiled when they are stored in an auxiliary engine cache image. +Auxiliary engine caching supports triggering compilation for root nodes that were loaded but never executed. +In such a case the framework calls the RootNode.prepareForAOT method.

+ +

More information on making a language implementation ready for compilation without prior execution can be found in the AOT tutorial. +Note that not every language can be compiled without prior execution and produce efficient machine code. +Statically typed languages are typically more suitable for this.

+ +

Application Snapshotting

+ +

It is planned to also support persisting runtime values of polyglot context instances to disk. +More information will appear here as soon as this feature is implemented.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/AuxiliaryEngineCachingEnterprise/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/AuxiliaryEngineCachingEnterprise/index.html new file mode 100644 index 0000000..4cb5763 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/AuxiliaryEngineCachingEnterprise/index.html @@ -0,0 +1,325 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Auxiliary Engine Caching

+ +

The following document describes how the auxiliary engine cache of GraalVM works.

+ +

This feature is only available in Oracle GraalVM. In GraalVM Community Edition, these options are not available.

+ +

Introduction

+ +

Warmup of Truffle guest language programs can take a significant amount of time. +Warmup consists of work that is repeated every time a program is executed until peak performance is reached. +This includes:

+
    +
  1. Loading and parsing the guest application into Truffle AST data structures.
  2. +
  3. Execution and profiling of the guest application in the interpreter.
  4. +
  5. Compilation of the AST to machine code.
  6. +
+ +

Within a single OS process, the work performed during warmup can be shared by specifying an explicit engine. +This requires language implementations to disable context-related optimizations to avoid deoptimizations between contexts that share code. +Auxiliary engine caching builds upon the mechanism for disabling context-related optimizations and adds the capability to persist an engine with ASTs and optimized machine code to disk. +This way, the work performed during warmup can be significantly reduced in the first application context of a new process.

+ +

We use the SVM auxiliary image feature to persist and load the necessary data structures to the disk. +Persisting the image can take significant time as compilation needs to be performed. +However, loading is designed to be as fast as possible, typically almost instantaneous. +This reduces the warmup time of an application significantly.

+ +

Getting Started

+ +

Starting from Oracle GraalVM installation, you first need to (re)build an image with auxiliary engine caching capabilities. +For example, one can rebuild the JavaScript image by adding the auxiliary engine cache feature:

+ +
graalvm/bin/native-image --macro:js-launcher -H:+AuxiliaryEngineCache -H:ReservedAuxiliaryImageBytes=1073741824
+
+ +

The --macro argument value depends on the guest language +By default, auxiliary images of up to 1GB are possible. +The maximum size can be increased or decreased as needed. +The amount of reserved bytes does not actually impact the memory consumed by the application. +In future versions, the auxiliary engine cache will be enabled by default when the --macro:js-launcher macro is used.

+ +

After rebuilding the JavaScript launcher, the feature is used as follows:

+ +

Create a new file fib.js:

+ +
function fib(n) {
+   if (n == 1 || n == 2) {
+       return 1;
+   }
+   return fib(n - 1) + fib(n - 2);
+}
+console.log(fib(32))
+
+ +

In order to persist the engine of a profiling run to disk use the following command line:

+ +
graalvm/bin/js --experimental-options --engine.TraceCache=true --engine.CacheStore=fib.image fib.js
+
+ +

The ` –engine.TraceCache=true` option is optional and allows you to see what is going on.

+ +

The output is as follows:

+ +
[engine] [cache] No load engine cache configured.
+2178309
+[engine] [cache] Preparing engine for store (compile policy hot)...
+[engine] [cache] Force compile targets mode: hot
+[engine] [cache] Prepared engine in 1 ms.
+[engine] [cache] Persisting engine for store ...
+[engine] [cache] Persisted engine in 20 ms.
+[engine] [cache] Detecting changes (update policy always)...
+[engine] [cache]     New image contains         1 sources and  82 function roots.
+[engine] [cache]     Always persist policy.
+[engine] [cache] Writing image to fib.image...
+[engine] [cache] Finished writing 1,871,872 bytes in 4 ms.
+
+ +

The engine can now be loaded from disk using the following command:

+ +
graalvm/bin/js --experimental-options --engine.TraceCache --engine.CacheLoad=fib.image fib.js
+
+ +

which prints:

+ +
[engine] [cache] Try loading image './fib.image'...
+[engine] [cache] Loaded image in 0 ms. 1,871,872 bytes   1 sources  82 roots
+[engine] [cache] Engine from image successfully patched with new options.
+2178309
+[engine] [cache] No store engine cache configured.
+
+ +

Since there is no need to warm up the application, the application’s execution time should be significantly improved.

+ +

Usage

+ +

The cache store and load operations can be controlled using the following options:

+ +
    +
  • --engine.Cache=<path> Loads and stores the cached engine from/to path.
  • +
  • --engine.CacheStore=<path> Stores the cached engine to path.
  • +
  • --engine.CacheLoad=<path> Loads the cached engine from path.
  • +
  • --engine.CachePreinitializeContext=<boolean> Preinitialize a new context in the image (default true).
  • +
  • --engine.TraceCache=<boolean> Enables debug output.
  • +
  • --engine.TraceCompilation=<boolean> Prints forced compilations.
  • +
+ +

The compilation of roots may be forced when an image is stored using the --engine.CacheCompile=<policy> option. The supported policies are:

+ +
    +
  • none: No compilations will be persisted, and existing compilations will be invalidated.
  • +
  • compiled: No compilations will be forced, but finished compilations will be persisted.
  • +
  • hot: All started compilations will be completed and then persisted. (default)
  • +
  • aot: All started, and AOT compilable roots will be forced to compile and persisted.
  • +
  • executed: All executed and all AOT compilable roots will be forced to compile.
  • +
+ +

By default, all started compilations in the compile queue will be completed and then persisted. +Whether a function root is AOT compilable is determined by the language. +A language supports AOT by implementing RootNode.prepareForAOT().

+ +

An update policy can be specified if both load and store operations are set using the --engine.UpdatePolicy=<policy> option. +Available policies are:

+ +
    +
  • always Always persist.
  • +
  • newsource Store if new source was loaded that was not contained in the previously loaded image.
  • +
  • newroot Store if a new root was loaded and not contained in the previously loaded image.
  • +
  • never Never persist.
  • +
+ +

Known Restrictions

+ +
    +
  • +

    There are generally no restrictions on the kind of applications that can be persisted. +If the language supports a shared context policy, auxiliary engine caching should work. +If the language does not support it, then no data will be persisted.

    +
  • +
  • +

    The persisted auxiliary engine image can only be used with the same SVM native image that it was created with. +Using the engine image with any other native-image will fail.

    +
  • +
  • +

    There can only be one active auxiliary image per native-image isolate. +Trying to load multiple auxiliary images at the same time will fail. +Currently, auxiliary images can also not be unloaded, but it is planned to lift this restriction in the future.

    +
  • +
+ +

Security Considerations

+ +

All data that is persisted to disk represents code only and no application context-specific data like global variables. +However, profiled ASTs and code may contain artifacts of the optimizations performed in a Truffle AST. +For example, it is possible that runtime strings are used for optimizations and therefore persisted to an engine image.

+ +

Development and Debugging on NativeImage

+ +

There are several options useful for debugging auxiliary engines caching when running on NativeImage:

+ +
    +
  • -XX:+TraceAuxiliaryImageClassHistogram Prints a class histogram of all the objects contained in an image when persisting.
  • +
  • -XX:+TraceAuxiliaryImageReferenceTree Prints a class reference tree of all the objects contained in an image when persisting.
  • +
+ +

Development and Debugging on HotSpot

+ +

It can be useful to debug language implementation issues related to auxiliary image on HotSpot. +On Oracle GraalVM in JVM mode, we have additional options that can be used to help debug issues with this feature: +Since storing partial heaps on HotSpot is not supported, these debug features do not work on HotSpot.

+ +
    +
  • --engine.DebugCacheStore=<boolean> Prepares the engine for caching and stores it to a static field instead of writing it to disk.
  • +
  • --engine.DebugCacheLoad=<boolean> Prepares the engine to use the engine stored in the static field instead of reading it from disk.
  • +
  • --engine.DebugCacheCompile=<boolean> Policy to use to force compilation for executed call targets before persisting the engine. This supports the same values as --engine.CacheCompile.
  • +
  • --engine.DebugCacheTrace=<boolean> Enables tracing for the engine cache debug feature.
  • +
+ +

For example:

+ +
js --jvm  --experimental-options --engine.TraceCompilation --engine.DebugCacheTrace --engine.DebugCacheStore --engine.DebugCacheCompile=executed fib.js
+
+ +

Prints the following output:

+ +
[engine] opt done         fib                                                         |ASTSize            32 |Time   231( 147+84  )ms |Tier             Last |DirectCallNodes I    6/D    8 |GraalNodes   980/ 1857 |CodeSize         7611 |CodeAddress 0x10e20e650 |Source       fib.js:2
+2178309
+[engine] [cache] Preparing debug engine for storage...
+[engine] [cache] Force compile targets mode: executed
+[engine] [cache] Force compiling 4 roots for engine caching.
+[engine] opt done         @72fa3b00                                                   |ASTSize             3 |Time   211( 166+45  )ms |Tier             Last |DirectCallNodes I    2/D    1 |GraalNodes   500/ 1435 |CodeSize         4658 |CodeAddress 0x10e26c8d0 |Source            n/a
+[engine] opt done         :program                                                    |ASTSize            25 |Time   162( 123+39  )ms |Tier             Last |DirectCallNodes I    1/D    1 |GraalNodes   396/ 1344 |CodeSize         4407 |CodeAddress 0x10e27fd50 |Source       fib.js:1
+[engine] opt done         Console.log                                                 |ASTSize             3 |Time    26(  11+15  )ms |Tier             Last |DirectCallNodes I    0/D    0 |GraalNodes    98/  766 |CodeSize         2438 |CodeAddress 0x10e285710 |Source    <builtin>:1
+[engine] [cache] Stored debug engine in memory.
+
+ +

This allows rapidly iterating on problems related to the compilation as well as to attach a Java debugger. +A Java debugger can be attached using --vm.Xdebug --vm.Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000.

+ +

Debugging the loading of persisted engines is more difficult as writing an engine to disk is not supported on HotSpot. +However, it is possible to use the polyglot embedding API to simulate this use-case in a unit test. +See the com.oracle.truffle.enterprise.test.DebugEngineCacheTest class as an example.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/BranchInstrumentation/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/BranchInstrumentation/index.html new file mode 100644 index 0000000..880903e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/BranchInstrumentation/index.html @@ -0,0 +1,270 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Branches Instrumentation

+ +

In languages implemented on top of Truffle, it is common that the AST implementations contain fast and slow +execution paths, usually based on some condition, such as a profile. These execution +paths are organized into different conditional branches. In these cases, it is often +helpful to know if running the program actually exercised the code in each of those +executions paths.

+ +

The branch instrumentation functionality instruments if-statements in target methods +to track which of the branches have been taken during execution. Branch instrumentation +does this by instrumenting branches with code that writes to a global +table. Each branch has an entry in this table. When the program ends, the +contents of the table are decoded and dumped to the standard output in readable +form.

+ +

There are several flags that control how branch instrumentation works. These flags are +specified as system properties:

+ +
    +
  • --engine.InstrumentBranches - controls whether instrumentation is on (true +or false, default is false)
  • +
  • --engine.InstrumentFilter - filters methods in which instrumentation +should be done (method filter syntax, essentially <package>.<class>.<method>[.<signature>])
  • +
  • --engine.InstrumentationTableSize - controls the maximum number of +instrumented locations
  • +
  • --engine.InstrumentBranchesPerInlineSite - controls whether instrumentation +provides separate branch profiles for each guest language function/compilation unit +(default is false).
  • +
+ +

Example Usage

+ +

Here is an example of how to enable branch instrumentation on a program.

+ +

When using instrumentation to detect hot or infrequently used branches in a Truffle +language implementation, it usually starts by finding a language node with a +problematic method. The following command runs a unit test for the SimpleLanguage, +and instruments all the if-statements:

+ +
mx --jdk jvmci sl --engine.BackgroundCompilation=false \
+  --engine.InstrumentBranches \
+  '--engine.InstrumentFilter=*.*.*' \
+  ../truffle/truffle/com.oracle.truffle.sl.test/src/tests/LoopObjectDyn.sl
+
+ +

You get the following output:

+ +
Execution profile (sorted by hotness)
+=====================================
+  0: *****************************************************
+  1: **************************
+
+com.oracle.truffle.sl.nodes.access.SLPropertyCacheNode.namesEqual(SLPropertyCacheNode.java:109) [bci: 120]
+[0] state = IF(if=36054#, else=0#)
+
+com.oracle.truffle.sl.nodes.controlflow.SLWhileRepeatingNode.executeRepeating(SLWhileRepeatingNode.java:102) [bci: 5]
+[1] state = BOTH(if=18000#, else=18#)
+
+ +

This output tells that both branches were visited in the if-statement in the file +SLWhileRepeatingNode.java at line 102, and only the true branch was visited for +the if-statement in the file SLPropertyCacheNode.java at line 109. +However, it does not tell, e.g., where this specific SLPropertyCacheNode node was +used from – the same execute method can be called from many different SimpleLanguage +nodes, and you may wish to distinguish these occurrences. Therefore, set the +per-inline-site flag to true, and change the filter to focus only on +SLPropertyCacheNode:

+ +
mx --jdk jvmci sl -Dgraal.TruffleBackgroundCompilation=false \
+  --engine.InstrumentBranchesPerInlineSite \
+  --engine.InstrumentBranches \
+  '--engine.InstrumentFilter=*.SLPropertyCacheNode.*' \
+  ../truffle/truffle/com.oracle.truffle.sl.test/src/tests/LoopObjectDyn.sl
+
+ +

This time you get more output, because the method namesEqual was inlined at +multiple sites (each site is represented by its inlining chain). The following output +fragment first shows the histogram with the if-statement ID and its occurrence +count. It then shows the exact call stacks and execution counts for the branches. +For example, for [1], when namesEqual is called from executeRead, the true +branch is taken 18018 times. When the namesEqual is called from executeWrite +([0]), the true branch is taken only 18 times:

+ +
Execution profile (sorted by hotness)
+=====================================
+  1: ***************************************
+  2: ***************************************
+  0:
+  3:
+
+com.oracle.truffle.sl.nodes.access.SLPropertyCacheNode.namesEqual(SLPropertyCacheNode.java:109) [bci: 120]
+com.oracle.truffle.sl.nodes.access.SLReadPropertyCacheNodeGen.executeRead(SLReadPropertyCacheNodeGen.java:76) [bci: 88]
+com.oracle.truffle.sl.nodes.access.SLReadPropertyNode.read(SLReadPropertyNode.java:71) [bci: 7]
+com.oracle.truffle.sl.nodes.access.SLReadPropertyNodeGen.executeGeneric(SLReadPropertyNodeGen.java:30) [bci: 35]
+com.oracle.truffle.sl.nodes.SLExpressionNode.executeLong(SLExpressionNode.java:81) [bci: 2]
+com.oracle.truffle.sl.nodes.expression.SLLessThanNodeGen.executeBoolean_long_long0(SLLessThanNodeGen.java:42) [bci: 5]
+com.oracle.truffle.sl.nodes.expression.SLLessThanNodeGen.executeBoolean(SLLessThanNodeGen.java:33) [bci: 14]
+com.oracle.truffle.sl.nodes.controlflow.SLWhileRepeatingNode.evaluateCondition(SLWhileRepeatingNode.java:133) [bci: 5]
+com.oracle.truffle.sl.nodes.controlflow.SLWhileRepeatingNode.executeRepeating(SLWhileRepeatingNode.java:102) [bci: 2]
+org.graalvm.compiler.truffle.OptimizedOSRLoopNode.executeLoop(OptimizedOSRLoopNode.java:113) [bci: 61]
+com.oracle.truffle.sl.nodes.controlflow.SLWhileNode.executeVoid(SLWhileNode.java:69) [bci: 5]
+com.oracle.truffle.sl.nodes.controlflow.SLBlockNode.executeVoid(SLBlockNode.java:84) [bci: 37]
+com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode.executeGeneric(SLFunctionBodyNode.java:81) [bci: 5]
+com.oracle.truffle.sl.nodes.SLRootNode.execute(SLRootNode.java:78) [bci: 28]
+[1] state = IF(if=18018#, else=0#)
+
+...
+
+com.oracle.truffle.sl.nodes.access.SLPropertyCacheNode.namesEqual(SLPropertyCacheNode.java:109) [bci: 120]
+com.oracle.truffle.sl.nodes.access.SLWritePropertyCacheNodeGen.executeWrite(SLWritePropertyCacheNodeGen.java:111) [bci: 244]
+com.oracle.truffle.sl.nodes.access.SLWritePropertyNode.write(SLWritePropertyNode.java:73) [bci: 9]
+com.oracle.truffle.sl.nodes.access.SLWritePropertyNodeGen.executeGeneric(SLWritePropertyNodeGen.java:33) [bci: 47]
+com.oracle.truffle.sl.nodes.access.SLWritePropertyNodeGen.executeVoid(SLWritePropertyNodeGen.java:41) [bci: 2]
+com.oracle.truffle.sl.nodes.controlflow.SLBlockNode.executeVoid(SLBlockNode.java:84) [bci: 37]
+com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode.executeGeneric(SLFunctionBodyNode.java:81) [bci: 5]
+com.oracle.truffle.sl.nodes.SLRootNode.execute(SLRootNode.java:78) [bci: 28]
+[0] state = IF(if=18#, else=0#)
+
+...
+
+ +

Truffle Call Boundary Instrumentation

+ +

The Truffle Call Boundary Instrumentation tool instruments callsites to methods that +have a TruffleCallBoundary annotation, and counts the calls to those methods. It is +controlled by the following set of flags:

+ +
    +
  • --engine.InstrumentBoundaries - controls whether instrumentation is on (true +or false, default is false)
  • +
  • --engine.InstrumentFilter - filters methods in which instrumentation +should be done (method filter syntax, essentially <package>.<class>.<method>[.<signature>])
  • +
  • --engine.InstrumentationTableSize - controls the maximum number of +instrumented locations
  • +
  • --engine.InstrumentBoundariesPerInlineSite - controls whether instrumentation +is done per a declaration of an Truffle boundary call (false), or per every call +stack where that callsite was inlined (true)
  • +
+ +

This tool can be used together with the Branch Instrumentation tool.

+ +

Assume that you need to find frequently occurring methods that were not, for example, +inlined. The usual steps in identifying the Truffle call boundaries is to first run the +program with the InstrumentBoundariesPerInlineSite flag set to false, and +then, after identifying the problematic methods, set that flag to true and set the +InstrumentFilter to identify the particular call stacks for those methods.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/DSLNodeObjectInlining/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/DSLNodeObjectInlining/index.html new file mode 100644 index 0000000..cdd54d8 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/DSLNodeObjectInlining/index.html @@ -0,0 +1,713 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle DSL Node Object Inlining

+ +

In 23.0, we have introduced a new annotation called @GenerateInline. This annotation instructs the Truffle DSL annotation processor to generate an inlinable version of a node. This works analogously to @GenerateCached and @GenerateUncached, which generate a cached or uncached node version. +By default, the DSL does not generate an inlined version of a node. +Node inlining provides a simple way to reduce the memory footprint of nodes but often also improves interpreter execution speed.

+ +

Basic Usage

+ +

Let us assume we have a node with specializations that computes the sum of the absolute value of two values. +For simplicity, we will only look at the long typed specializations in this example.

+ +

A runnable but slightly more advanced version of this example can be found in the Truffle unit tests.

+ + +

Consider the following examples that specify two regular nodes it specializations. One node computes the sum of two values, and one computes the absolute number of a number. The AbsNode is then reused in the AddAbsNode to share the implementation.

+ +
public abstract class AddAbsNode extends Node {
+
+    abstract long execute(Object left, Object right);
+
+    @Specialization
+    long add(long left, long right,
+                    @Cached AbsNode leftAbs,
+                    @Cached AbsNode rightAbs) {
+        return leftAbs.execute(left) + rightAbs.execute(right);
+    }
+    // ...
+}
+
+public abstract class AbsNode extends Node {
+
+    abstract long execute(long value);
+
+    @Specialization(guards = "v >= 0")
+    long doInt(long v) {
+        return v;
+    }
+
+    @Specialization(guards = "v < 0")
+    long doLong(long v) {
+        return -v;
+    }
+}
+
+ +

The compressed memory footprint for AbsNode and AddAbsNode after one execution are computed as follows:

+ +
AbsNodeGen = object header
+   + Node field for Node.parent
+   + int field for state
+
+AddAbsNodeGen = object header
+   + Node field for Node.parent
+   + int  field for state
+   + Node field for @Cached AbsNode leftAbs
+   + Node field for @Cached AbsNode rightAbs
+
+Footprint = headerCount * 12 + pointerCount * 4 + primitiveByteSize
+Footprint = 3 * 12 + 5 * 4 + 12 = 68 bytes
+
+ +

Therefore, we use 68 bytes to represent a single operation with nodes.

+ +

With 23.0, the Truffle DSL annotation processor will produce the following warning for the AbsNode class:

+ +
This node is a candidate for node object inlining. The memory footprint is estimated to be reduced from 20 to 1 byte(s). Add @GenerateInline(true) to enable object inlining for this node or @GenerateInline(false) to disable this warning. Also, consider disabling cached node generation with @GenerateCached(false) if all usages will be inlined. This warning may be suppressed using @SuppressWarnings("truffle-inlining").
+
+ +

Following the recommendation of this warning, we modify our example as follows by adding the @GenerateInline annotation:

+ +
@GenerateInline
+public abstract class AbsNode extends Node {
+
+    abstract long execute(long value);
+
+    @Specialization(guards = "v >= 0")
+    long doInt(long v) {
+        return v;
+    }
+
+    @Specialization(guards = "v < 0")
+    long doLong(long v) {
+        return -v;
+    }
+
+}
+
+ +

Now the DSL reports a compile error for AbsNode:

+ +
Error generating code for @GenerateInline: Found non-final execute method without a node parameter execute(long). Inlinable nodes
+ must use the Node type as the first parameter after the optional frame for all non-final execute methods. A valid signature for an
+ inlinable node is execute([VirtualFrame frame, ] Node node, ...).
+
+ +

For inlinable nodes, we must pass a node parameter to the execute method as the first parameter. +This is necessary as inlined nodes become singletons and no longer have their own state, but instead, it is passed as a parameter to the execute method.

+ +

Again, we follow the error and modify our example as follows:

+ +
@GenerateInline
+public abstract class AbsNode extends Node {
+
+    abstract long execute(Node node, long value);
+
+    @Specialization(guards = "v >= 0")
+    static long doInt(long v) {
+        return v;
+    }
+
+    @Specialization(guards = "v < 0")
+    static long doLong(long v) {
+        return -v;
+    }
+
+}
+
+ +

Note that the node parameter is optional for specialization methods, but they are typically needed if transitively inlined nodes are used.

+ +

Next, we also need to modify AddAbsNode to pass this as a node parameter to the new execute signature:

+ +
public abstract static class AddAbsNode extends Node {
+
+    abstract long execute(long left, long right);
+
+    @Specialization
+    long add(long left, long right,
+                    @Cached AbsNode leftAbs,
+                    @Cached AbsNode rightAbs) {
+        return leftAbs.execute(this, left) + rightAbs.execute(this, right);
+    }
+    // ...
+}
+
+ +

The DSL now produces a warning for each of the @Cached AbsNode parameters:

+ +
The cached type 'AbsNode' supports object-inlining. The footprint is estimated to be reduced from 36 to 1 byte(s). Set @Cached(..., inline=true|false) to determine whether object-inlining should be performed. Alternatively, @GenerateCached(alwaysInlineCached=true) can be used to enable inlining for an entire class or in combination with the inherit option for a hierarchy of node classes. This warning may be suppressed using @SuppressWarnings("truffle-inlining").
+
+ +

We follow the recommendation in this message and enable object inlining:

+ +
public abstract static class AddAbsNode extends Node {
+
+    abstract long execute(long left, long right);
+
+    @Specialization
+    long add(long left, long right,
+                    @Cached(inline = true) AbsNode leftAbs,
+                    @Cached(inline = true) AbsNode rightAbs) {
+        return leftAbs.execute(this, left) + rightAbs.execute(this, right);
+    }
+    // ...
+}
+
+ +

Now we have achieved object-inlining of AbsNode into AddAbsNode. +The new memory footprint computes as follows:

+ +
AddAbsNodeGen = object header
+   + Node field for Node.parent
+   + int  field for state
+
+Footprint = headerCount * 12 + pointerCount * 4 + primitiveByteSize
+Footprint = 1 * 12 + 1 * 4 + 4 = 20 bytes
+
+ +

The footprint has gone down from 68 bytes to only 20 bytes for each instance of AddAbsNodeGen.

+ +

But we are still going. Since all cached nodes are inlined we can also make the AddAbsNode inlinable for its usages. +The DSL helps us again by detecting such cases and prints a warning for AddAbsNode now:

+ +
This node is a candidate for node object inlining. The memory footprint is estimated to be reduced from 20 to 1 byte(s). Add @GenerateInline(true) to enable object inlining for this node or @GenerateInline(false) to disable this warning. Also consider disabling cached node generation with @GenerateCached(false) if all usages will be inlined. This warning may be suppressed using @SuppressWarnings("truffle-inlining").
+
+ +

Again, we follow the guide and add a @GenerateInline annotation to AddAbsNode. Just like before, we also add a Node parameter to the execute method:

+ +
@GenerateInline
+public abstract static class AddAbsNode extends Node {
+
+    abstract long execute(Node node, long left, long right);
+
+    @Specialization
+    static long add(Node node, long left, long right,
+                    @Cached AbsNode leftAbs,
+                    @Cached AbsNode rightAbs) {
+        return leftAbs.execute(node, left) + rightAbs.execute(node, right);
+    }
+    // ...
+}
+
+ +

We also need to use the Node parameter in the specialization method and pass it on to the child nodes. +Again, we want all specializations to be static to avoid accidentally passing this. +In addition, the DSL complained about the inline=true attribute, which is now always implied as the parent node uses the @GenerateInline annotation.

+ +

To measure the overhead of our new inlinable AddAbsNode node, we declare a new operation called Add4AbsNode that adds four numbers using our AddAbsNode operation:

+ +
@GenerateCached(alwaysInlineCached = true)
+public abstract static class Add4AbsNode extends Node {
+
+    abstract long execute(long v0, long v1, long v2, long v3);
+
+    @Specialization
+    long doInt(long v0, long v1, long v2, long v3,
+                    @Cached AddAbsNode add0,
+                    @Cached AddAbsNode add1,
+                    @Cached AddAbsNode add2) {
+        long v;
+        v = add0.execute(this, v0, v1);
+        v = add1.execute(this, v, v2);
+        v = add2.execute(this, v, v3);
+        return v;
+    }
+
+}
+
+ +

This time, instead of specifying @Cached(inline=true), we auto-enable inlining wherever possible using @GenerateCached(alwaysInlineCached = true). +Depending on the use case, it can hinder readability to repeat individual inlining commands for every cached node.

+ +

Computing the overhead now becomes more tricky. We need to understand how many state bits each node requires to keep track of active specializations. +That computation is generally implementation specific and subject to change. However, a good rule of thumb is that the DSL requires one bit per declared specialization. +Implicit casts, replace rules, @Fallback and specializations with multiple instances may further increase the number of required state bits.

+ +

For this example, each AddAbsNode requires 5 bits. 2 bits for each of the AbsNode usages and one bit for the AddAbsNode specializations. +The Add4AbsNode uses three instances of AddAbsNode, has one specialization, and therefore needs 3 * 5 + 1 state bits in total. +Since the number of bits is below 32, we can assume that we need a single int field in the generated code. +The memory footprint of an executed Add4AbsNode is therefore computed as follows:

+ +
Footprint = 1 * 12 + 1 * 4 + 4 = 20 bytes
+
+ +

As you can see, this is the same memory footprint a single AddAbsNode had. +If we use the same formula to compute the memory footprint of an Add4AbsNode without any object inlining

+ +
Footprint = 1 * 12 + 4 * 4 + 4 + 3 * 68 = 236 bytes
+
+ +

We have reduced the overhead from 236 bytes to 20 bytes.

+ +

In addition to the memory footprint advantages, interpreter-only execution may be faster, as we save the reads for the node fields and benefit from better CPU cache locality due to smaller memory consumption. +After compilation using partial evaluation, both cached and uncached versions are expected to perform the same.

+ +

There is a last thing we should do. Since our AddAbsNode and AbsNode are no longer used in their cached version, we can turn off cached generation using @GenerateCached(false) to save Java code footprint. +After doing this we can omit the alwaysInlineCached property in the @GenerateCached annotation as nodes are automatically inlined if only an inlined version is available.

+ +

This is the final example:

+ +
@GenerateInline
+@GenerateCached(false)
+public abstract static class AbsNode extends Node {
+
+    abstract long execute(Node node, long value);
+
+    @Specialization(guards = "v >= 0")
+    static long doInt(long v) {
+        return v;
+    }
+
+    @Specialization(guards = "v < 0")
+    static long doLong(long v) {
+        return -v;
+    }
+
+}
+
+@GenerateInline
+@GenerateCached(false)
+public abstract static class AddAbsNode extends Node {
+
+    abstract long execute(Node node, long left, long right);
+
+    @Specialization
+    static long add(Node node, long left, long right,
+                    @Cached AbsNode leftAbs,
+                    @Cached AbsNode rightAbs) {
+        return leftAbs.execute(node, left) + rightAbs.execute(node, right);
+    }
+    // ...
+}
+
+@GenerateCached(alwaysInlineCached = true)
+@GenerateInline(false)
+public abstract static class Add4AbsNode extends Node {
+
+    abstract long execute(long v0, long v1, long v2, long v3);
+
+    @Specialization
+    long doInt(long v0, long v1, long v2, long v3,
+                    @Cached AddAbsNode add0,
+                    @Cached AddAbsNode add1,
+                    @Cached AddAbsNode add2) {
+        long v;
+        v = add0.execute(this, v0, v1);
+        v = add1.execute(this, v, v2);
+        v = add2.execute(this, v, v3);
+        return v;
+    }
+}
+
+ +

Note that the DSL again informed us that Add4AbsNode could use @GenerateInline by emitting the following warning:

+ +
This node is a candidate for node object inlining. The memory footprint is estimated to be reduced from 20 to 2 byte(s). Add @GenerateInline(true) to enable object inlining for this node or @GenerateInline(false) to disable this warning. Also consider disabling cached node generation with @GenerateCached(false) if all usages will be inlined. This warning may be suppressed using @SuppressWarnings("truffle-inlining").
+
+ +

This time we suppressed the warning by explicitly specifying @GenerateInline(false).

+ +

Advanced Inline Cache Usage

+ +

The following example explains how specialization unrolling and new inlinable cache classes can be helpful in reducing the memory footprint of nodes with specializations that have multiple instances.

+ +

Examples:

+ + +

Passing along Nodes correctly

+ +

The usage of inlined nodes requires to access and pass the correct node to execute methods of the respective inlined nodes. +It is a common mistake to pass the wrong node to execute methods. +Typically such mistakes fail with an error at runtime, but the DSL also emits warnings and errors depending on the situation at compile time.

+ +

Inlined Nodes

+ +

For inlined nodes that use themselves inlined nodes it is sufficient to pass a long the Node dynamic parameter. +For example. in the previous section we used AddAbsNode with a similar pattern:

+ +
@GenerateInline
+@GenerateCached(false)
+public abstract static class AddAbsNode extends Node {
+
+    abstract long execute(Node node, long left, long right);
+
+    @Specialization
+    static long add(Node node, long left, long right,
+                    @Cached AbsNode leftAbs,
+                    @Cached AbsNode rightAbs) {
+        return leftAbs.execute(node, left) + rightAbs.execute(node, right);
+    }
+    // ...
+}
+
+ +

Cached Nodes with Multiple Instances

+ +

For nodes with specializations that may have multiple instances a @Bind("this") Node node parameter must be used to access the inline target node. +This is simliar to the SumArrayNode node in the advanced usage example.

+ +
@ImportStatic(AbstractArray.class)
+public abstract static class SumArrayNode extends Node {
+
+    abstract int execute(Object v0);
+
+    @Specialization(guards = {"kind != null", "kind.type == array.getClass()"}, limit = "2", unroll = 2)
+    static int doDefault(Object array,
+                    @Bind("this") Node node,
+                    @Cached("resolve(array)") ArrayKind kind,
+                    @Cached GetStoreNode getStore) {
+        Object castStore = kind.type.cast(array);
+        int[] store = getStore.execute(node, castStore);
+        int sum = 0;
+        for (int element : store) {
+            sum += element;
+            TruffleSafepoint.poll(node);
+        }
+        return sum;
+    }
+
+    static Class<?> getCachedClass(Object array) {
+        if (array instanceof AbstractArray) {
+            return array.getClass();
+        }
+        return null;
+    }
+}
+
+ +

Exported Library Messages

+ +

For exported library messages the this keyword is already reserved for the receiver value, so $node can be used instead.

+ +

For example:

+ +
    @ExportLibrary(ExampleArithmeticLibrary.class)
+    static class ExampleNumber {
+
+        final long value;
+
+        /* ... */
+
+        @ExportMessage
+        final long abs(@Bind("$node") Node node,
+                       @Cached InlinedConditionProfile profile) {
+            if (profile.profile(node, this.value >= 0)) {
+                return  this.value;
+            } else {
+                return  -this.value;
+            }
+        }
+
+    }
+
+ +

Limitations

+ +

Node object inlining supports arbitrary deep nestings. However, there are some limitations to using @GenerateInline.

+ +
    +
  • There must not be any instance fields on the node class or a parent class.
  • +
  • The node must not use @NodeField or @NodeChild.
  • +
  • The usage of inlined nodes must not be recursive.
  • +
+ +

Manually implementing Inlinable Nodes and Profiles

+ +

Nodes or profiles that can be inlined in the DSL can also be implemented manually. +The class must implement a static method called inline. +For example, most inlinable Truffle profiles use custom inlining. +Extra care must be taken when implementing such inlinable classes and if possible, a DSL generated node should be used instead. +See InlinedBranchProfile or InlinedIntValueProfile class as an example on how to implement the inline method.

+ +

API Compatibility for Inlinable Nodes

+ +

The TruffleString API extensively uses DSL nodes like in the above example. +However, allowing nodes to be inlined makes every change to the specializations of that node an incompatible API change. +This is because the signature of the static inline method changes depending on the required state bits of the specializations.

+ +

In order to support inlining across stable API boundaries, it is recommended to manually specify an inline method that forwards to the generated inline method.

+ +

As an example, consider the following node:

+ +
@GenerateInline
+@GenerateUncached
+@GeneratePackagePrivate
+public abstract static class APINode extends Node {
+
+    abstract long execute(Node node, long value);
+
+    @Specialization(guards = "v >= 0")
+    static long doInt(long v) {
+        return v;
+    }
+
+    @Specialization(guards = "v < 0")
+    static long doLong(long v) {
+        return -v;
+    }
+
+    public static APINode inline(@RequiredField(value = StateField.class, bits = 32) InlineContext context) {
+        return APINodeGen.inline(context);
+    }
+
+    public static APINode create() {
+        return APINodeGen.create();
+    }
+
+    public static APINode getUncached() {
+        return APINodeGen.getUncached();
+    }
+}
+
+ +

We use @GeneratePackagePrivate in order not to expose any generated code as public. +We specify a manual inline method that specifies the required bits for this node. +If the specializations of a node require more bits or more additional fields other than specified, then the annotation processor fails with an error. +If the node requires fewer bits, then this does not cause any compiler error. +This allows API to use node inlining across stable API boundaries as long as the reserved field capacity is not exceeded.

+ +

A change is compatible if:

+
    +
  • There was previously no inline method for this node before.
  • +
  • If the required bit space is reduced and all other fields are changed.
  • +
+ +

A change is incompatible if:

+
    +
  • A new @RequiredField annotation to an existing inline method was added or removed.
  • +
  • The required bits were increased.
  • +
+ +

The DSL validates whether the required fields are matching to the state specification of the parent node and emits a warning if it is not compatible to the node specification.

+ +

Lazy Initialized Nodes with DSL Inlining

+ +

Full source code of the example: NodeInliningAndLazyInitExample.java.

+ +

DSL inlining can be used to provide lazy initialization for otherwise cached node that is only used in code blocks that +are protected by conditions that trigger rarely. Consider this example:

+
@GenerateInline(false)
+@GenerateUncached
+public abstract static class RaiseErrorNode extends Node {
+    abstract void execute(Object type, String message);
+
+    // ...
+}
+
+@GenerateInline(false)
+@GenerateUncached(false)
+public abstract static class LazyInitExampleBefore extends Node {
+    abstract void execute(Object value);
+
+    @Specialization
+    void doIt(Object value,
+              @Cached RaiseErrorNode raiseError) {
+        Object result = doSomeWork(value);
+        if (result == null) {
+            raiseError.execute(value, "Error: doSomeWork returned null");
+        }
+    }
+}
+
+

RaiseErrorNode is always instantiated even-though we do not need it if doSomeWork always returns +non null result at runtime. Before DSL inlining, this issue was usually solved by lazy-initialized +@Child node:

+
@GenerateInline(false)
+@GenerateUncached(false)
+public abstract static class LazyInitExampleBefore2 extends Node {
+    @Child RaiseErrorNode raiseError;
+
+    abstract void execute(Object value);
+
+    @Specialization
+    void doIt(Object value) {
+        Object result = doSomeWork(value);
+        if (result == null) {
+            if (raiseError == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                raiseError = insert(RaiseErrorNodeGen.create());
+            }
+            raiseError.execute(value, "Error: doSomeWork returned null");
+        }
+    }
+}
+
+

However @Child nodes have some drawbacks. Most notably, the @Specialization cannot be static and we +cannot generate uncached variant of the node.

+ +

With DSL inlining, one should either make the RaiseErrorNode inlineable if beneficial, or if it is a node that:

+ +
    +
  • has a lot of specializations with multiple instances, or
  • +
  • cannot currently be inlined, or
  • +
  • has a lot of cached fields that cannot be inlined
  • +
+ +

then one can create an inlinable wrapper node that initializes the RaiseErrorNode on demand:

+
@GenerateInline
+@GenerateUncached
+@GenerateCached(false)
+public abstract static class LazyRaiseNode extends Node {
+    public final RaiseErrorNode get(Node node) {
+        return execute(node);
+    }
+
+    abstract RaiseErrorNode execute(Node node);
+
+    @Specialization
+    static RaiseErrorNode doIt(@Cached(inline = false) RaiseErrorNode node) {
+        return node;
+    }
+}
+
+@GenerateInline(false)
+@GenerateUncached
+public abstract static class LazyInitExample extends Node {
+    abstract void execute(Object value);
+
+    @Specialization
+    void doIt(Object value,
+              @Cached LazyRaiseNode raiseError) {
+        Object result = doSomeWork(value);
+        if (result == null) {
+            raiseError.get(this).execute(value, "Error: doSomeWork returned null");
+        }
+    }
+}
+
+

Unless LazyRaiseNode.execute gets called, the cost of the wrapper is single reference field +and one bit from the bitset of LazyInitExample node. Except for the extra bit, it is the same as +with the lazy initialized @Child node field.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/DSLWarnings/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/DSLWarnings/index.html new file mode 100644 index 0000000..13ecedb --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/DSLWarnings/index.html @@ -0,0 +1,155 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle DSL Warnings

+ +

Since version 23.0, Truffle DSL now produces significantly more warnings. +These warnings are intended to guide the user to better DSL usage. +The following sections describe how to handle and eventually suppress warnings.

+ +

All warnings of Truffle DSL can be suppressed using the -Atruffle.dsl.SuppressAllWarnings=true option. +If a language uses strict checks where warnings are treated as errors in their CI, it is recommended to add this option to the Java compilation command line. This can be useful to avoid CI failures when Truffle DSL adds new warning messages. Adding new warning messages in Truffle DSL is considered a compatible change.

+ +

Truffle DSL warnings can be suppressed just like Java warnings using the @SuppressWarnings annotation or with@SuppressPackageWarnings for entire packages. +The following warning keys are supported:

+ +
    +
  • all all warnings emitted by the Java compiler or Truffle DSL
  • +
  • truffle all warnings emitted by Truffle DSL
  • +
  • truffle-sharing warnings when the DSL recommends sharing between cached values
  • +
  • truffle-inlining warnings when the DSL recommends using node object inlining.
  • +
  • truffle-neverdefault warnings for when cached initializers should be marked as never having a default value.
  • +
  • truffle-limit warnings when a specialization limit is recommended, but not specified.
  • +
  • truffle-static-method warnings when the DSL recommends to use the static modifier.
  • +
  • truffle-unused warnings if a DSL attribute or annotation has no effect and is recommended to be removed.
  • +
  • truffle-abstract-export warnings if an abstract message of a Truffle library is not exported.
  • +
  • truffle-assumption if the assumptions feature is used with a specialization that reaches a @Fallback specialization.
  • +
  • truffle-guard if a guard uses methods where a @Idempotent or @NonIdempotent method may be beneficial for the generated code.
  • +
+ +

Specific warnings can also be suppressed globally using the -Atruffle.dsl.SuppressWarnings=truffle-inlining,truffle-neverdefault Java compiler processor option. +Note that also Java system properties can be used to configure the annotation processor. (e.g. by passing -J-Dtruffle.dsl.SuppressWarnings=truffle-inlining,truffle-neverdefault to javac)

+ +

Suppressing a specific warning should be preferred over suppressing all warnings. +Find the latest list of warnings in the source code

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/DynamicObjectModel/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/DynamicObjectModel/index.html new file mode 100644 index 0000000..72474a0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/DynamicObjectModel/index.html @@ -0,0 +1,299 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Dynamic Object Model

+ +

This guide demonstrates how to get started with using the DynamicObject and DynamicObjectLibrary APIs introduced with GraalVM 20.2.0. +The full documentation can be found in the Javadoc.

+ +

Motivation

+ +

When implementing a dynamic language, the object layout of user-defined objects/classes often cannot be statically inferred and needs to accommodate dynamically added members and changing types. +This is where the Dynamic Object API comes in: it takes care of the object layout and classifies objects by their shape, i.e., their properties, and the types of their values. +Access nodes can then cache the encountered shapes, forego costly checks and access object properties more efficiently.

+ +

Getting Started

+ +

A guest language should have a common base class for all language objects that extends DynamicObject and implements TruffleObject. For example:

+ +
@ExportLibrary(InteropLibrary.class)
+public class BasicObject extends DynamicObject implements TruffleObject {
+
+    public BasicObject(Shape shape) {
+        super(shape);
+    }
+
+    @ExportMessage
+    boolean hasLanguage() {
+        return true;
+    }
+    // ...
+}
+
+

It makes sense to also export common InteropLibrary messages in this class.

+ +

Builtin object classes can then extend this base class and export additional messages, and, as usual, extra Java fields and methods:

+
@ExportLibrary(InteropLibrary.class)
+public class Array extends BasicObject {
+
+    private final Object[] elements;
+
+    public Array(Shape shape, Object[] elements) {
+        super(shape);
+        this.elements = elements;
+    }
+
+    @ExportMessage
+    boolean hasArrayElements() {
+        return true;
+    }
+
+    @ExportMessage
+    long getArraySize() {
+        return elements.length;
+    }
+    // ...
+}
+
+ +

Dynamic object members can be accessed using the DynamicObjectLibrary, which can be obtained using the @CachedLibrary annotation of the Truffle DSL and DynamicObjectLibrary.getFactory() + getUncached(), create(DynamicObject), and createDispatched(int). +Here is an example of how it could be used to implement InteropLibrary messages:

+
@ExportLibrary(InteropLibrary.class)
+public class SimpleObject extends BasicObject {
+
+    public UserObject(Shape shape) {
+        super(shape);
+    }
+
+    @ExportMessage
+    boolean hasMembers() {
+        return true;
+    }
+
+    @ExportMessage
+    Object readMember(String name,
+                    @CachedLibrary("this") DynamicObjectLibrary objectLibrary)
+                    throws UnknownIdentifierException {
+        Object result = objectLibrary.getOrDefault(this, name, null);
+        if (result == null) {
+            /* Property does not exist. */
+            throw UnknownIdentifierException.create(name);
+        }
+        return result;
+    }
+
+    @ExportMessage
+    void writeMember(String name, Object value,
+                    @CachedLibrary("this") DynamicObjectLibrary objectLibrary) {
+        objectLibrary.put(this, name, value);
+    }
+
+    @ExportMessage
+    boolean isMemberReadable(String member,
+                    @CachedLibrary("this") DynamicObjectLibrary objectLibrary) {
+        return objectLibrary.containsKey(this, member);
+    }
+    // ...
+}
+
+ +

In order to construct instances of these objects, you first need a Shape that you can pass to the DynamicObject constructor. +This shape is created using Shape.newBuilder().build(). +The returned shape describes the initial shape of the object and forms the root of a new shape tree. +As you are adding new properties with DynamicObjectLibrary#put, the object will mutate into other shapes in this shape tree.

+ +

Note: You should reuse the same initial shapes because shapes are internally cached per root shape. +It is recommended that you store the initial shapes in the TruffleLanguage instance, so they can be shared across contexts of the same engine. +Static shapes should be avoided except for singletons (like a null value).

+ +

For example:

+ +
@TruffleLanguage.Registration(...)
+public final class MyLanguage extends TruffleLanguage<MyContext> {
+
+    private final Shape initialObjectShape;
+    private final Shape initialArrayShape;
+
+    public MyLanguage() {
+        this.initialObjectShape = Shape.newBuilder(ExtendedObject.class).build();
+        this.initialArrayShape = Shape.newBuilder().build();
+    }
+
+    public createObject() {
+        return new MyObject(initialObjectShape);
+    }
+    //...
+}
+
+ +

Extended Object Layout

+ +

You can extend the default object layout with extra dynamic fields that you hand over to the dynamic object model by adding @DynamicField-annotated field declarations of type Object or long in your subclasses, and specifying the layout class with Shape.newBuilder().layout(ExtendedObject.class).build();. +Dynamic fields declared in this class and its superclasses will then automatically be used to store dynamic object properties and allow faster access to properties that fit into this reserved space. +Note: You must not access dynamic fields directly. Always use DynamicObjectLibrary for this purpose.

+ +
@ExportLibrary(InteropLibrary.class)
+public class ExtendedObject extends SimpleObject {
+
+    @DynamicField private Object _obj0;
+    @DynamicField private Object _obj1;
+    @DynamicField private Object _obj2;
+    @DynamicField private long _long0;
+    @DynamicField private long _long1;
+    @DynamicField private long _long2;
+
+    public ExtendedObject(Shape shape) {
+        super(shape);
+    }
+}
+
+ +

Caching Considerations

+ +

In order to ensure optimal caching, avoid reusing the same cached DynamicObjectLibrary for multiple, independent operations (get, put, etc.). +Try to minimize the number of different shapes and property keys seen by each cached library instance. +When the property keys are known statically (compilation-final), always use a separate DynamicObjectLibrary for each property key. +Use dispatched libraries (@CachedLibrary(limit=...)) when putting multiple properties in succession. +For example:

+
public abstract class MakePairNode extends BinaryExpressionNode {
+    @Specialization
+    Object makePair(Object left, Object right,
+                    @CachedLanguage MyLanguage language,
+                    @CachedLibrary(limit = "3") putLeft,
+                    @CachedLibrary(limit = "3") putRight) {
+        MyObject obj = language.createObject();
+        putLeft.put(obj, "left", left);
+        putRight.put(obj, "right", right);
+        return obj;
+    }
+}
+
+ +
+ +

Further Reading

+ +

A high-level description of the object model has been published in An Object Storage Model for the Truffle Language Implementation Framework.

+ +

See Truffle publications for more presentations and publications about Truffle and GraalVM.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Exit/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Exit/index.html new file mode 100644 index 0000000..e3451eb --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Exit/index.html @@ -0,0 +1,273 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Context Exit

+ +

Different Truffle (guest) languages may use different mechanisms for exiting. This is not optimal as a Truffle language has no way to detect and handle an exit triggered by a different language.

+ +

As of 22.0 Truffle has support for polyglot context hard explicit exit triggered by guest languages using TruffleContext.closeExited(Node,int). +It provides a unified way for languages to trigger the exit of the underlying polyglot context. When triggered, all initialized +guest languages are first notified using TruffleLanguage.exitContext(C,ExitMode,int), then all context threads are stopped, and finally, the context is closed. +The hard explicit exit is simply referred to as “hard exit”.

+ +

The hard exit is only one of the three types of exit in Truffle. There are the following types of exit.

+ +
    +
  • The new hard exit introduced above +
      +
    • Hard exit notifications are executed with ExitMode.HARD as the second parameter of TruffleLanguage.exitContext(C,ExitMode,int).
    • +
    +
  • +
  • Implicit “natural exit” +
      +
    • Occurs during normal context close.
    • +
    • The newly introduced exit notifications are executed for the natural exit as well, but threads are not forced to stop after the natural exit notifications, and so context threads that are still running can block the natural exit.
    • +
    • Natural exit notifications are executed with ExitMode.NATURAL as the second parameter of TruffleLanguage.exitContext(C,ExitMode,int).
    • +
    +
  • +
  • “Soft exit” +
      +
    • Explicit exit triggered by throwing a Truffle exception of the exception type ExceptionType.EXIT.
    • +
    • The exception is not automatically thrown in other threads by Truffle and it does not trigger exit notifications or context close on its own.
    • +
    • The context is still fully usable after a soft exit and the soft exit can be followed by either the natural or the hard exit.
    • +
    +
  • +
+ +

For completeness, a polyglot context can be also closed by cancelling it using Context.close(true), TruffleContext.closeCancelled(Node,String), or TruffleContext.closeResourceExhausted(Node,String). +The cancel operation leads to immediate stopping of all context threads and closing the context without any exit notifications.

+ +

Soft exit

+ +

The soft exit is triggered by throwing a Truffle exception of the exception type ExceptionType.EXIT - soft exit exception. The exception is not automatically thrown in other threads by Truffle and it does not trigger exit notifications or context close on its own. +If not caught by languages, the soft exit exception eventually causes the embedder thread to throw a PolyglotException to the host with PolyglotException.isExit() == true and PolyglotException.getExitStatus() equal to the value specified by the soft exit exception.

+ +

The context is still fully usable after a soft exit, but the embedder should close the context when it sees the PolyglotException with PolyglotException.isExit() == true, in which case the soft exit is followed by the natural exit.

+ +

Natural Exit

+ +

Natural exit schema

+ +

The natural exit occurs during a normal context close triggered by Context.close() or TruffleContext.close(). The natural exit illustrated in the figure above consists of the following steps:

+ +
    +
  1. +

    The natural exit is triggered by Context.close() or TruffleContext.close().

    +
  2. +
  3. Exit notifications for all initialized languages are executed - TruffleLanguage.exitContext(C,ExitMode,int), where ExitMode.NATURAL is used as the ExitMode parameter. +
      +
    • Guest code runs normally during exit notifications.
    • +
    +
  4. +
  5. All initialized languages are finalized. +
      +
    • TruffleLanguage.finalizeContext(C) is called for all initalized languages.
    • +
    • Guest code runs normally during finalization.
    • +
    +
  6. +
  7. All languages are disposed. +
      +
    • TruffleLanguage.disposeContext(C) is called for all languages.
    • +
    +
  8. +
+ +

Hard Exit

+ +

Hard exit schema

+ +

This section describes the hard exit process in detail. The hard exit for a polyglot context can be customized by Context.Builder.useSystemExit(boolean). Therefore, +the following description is divided into two subsections. One for the case when +system exit is not enabled (Context.Builder.useSystemExit(false) - the default) and one for the case when system exit +is enabled (Context.Builder#useSystemExit(true)). The illustration of the exit process is depicted in the figure above. The figure also relates the exit process to the context cancelling process. +The red color of some of the boxes indicates that the context is invalid at that point and no guest code can be run. More precisely, the first Truffle safepoint will throw either a special ThreadDeath exit exception or +a special ThreadDeath cancel exception depending on whether the hard exit or the cancel operation is in progress.

+ +

Behavior if useSystemExit is disabled (default)

+ +
    +
  1. +

    The exit is triggered by TruffleContext.closeExited(Node,int).

    +
  2. +
  3. Exit notifications for all initialized languages are executed - TruffleLanguage.exitContext(C,ExitMode,int), where ExitMode.HARD is used as the ExitMode parameter. +
      +
    • Guest code runs normally during exit notifications.
    • +
    • If the context is not cancelled during exit notifications (during step 2a) and reaches step 2b, the exit process proceeds with the next step. Otherwise, the exit notifications are interrupted and the context is immediately cancelled.
    • +
    +
  4. +
  5. All context threads are forced to stop by throwing a special ThreadDeath exit exception from Truffle safepoints. +
      +
    • To exit threads reliably, languages need to ensure that ThreadDeath is always immediately rethrown and guest language exception handlers and finally blocks are not run.
    • +
    • The embedder threads eventually throw a PolyglotException to the host with PolyglotException.isExit() == true and PolyglotException.getExitStatus() +being equal to the exit code originally specified as the second parameter to the first call toTruffleContext.closeExited(Node,int).
    • +
    • Note that from step 3 the exit process is similar to the cancelling process as indicated by the figure, but the cancelling process uses a special ThreadDeath cancel exception +and the PolyglotException thrown to the host has PolyglotException.isCancelled() == true instead of PolyglotException.isExit() == true.
    • +
    +
  6. +
  7. All initialized languages are finalized. +
      +
    • TruffleLanguage.finalizeContext(C) is called for all initalized languages.
    • +
    • Running any guest code in TruffleLanguage.finalizeContext(C) will throw the special ThreadDeath exit exception from the first Truffle safepoint.
    • +
    • Languages should skip any finalization that would require running guest code. A language can find out if it can run guest code in TruffleLanguage.finalizeContext(C) by checking if TruffleLanguage.exitContext(C,ExitMode,int) was previously called with ExitMode.NATURAL, +or by checking that TruffleContext.isClosed() returns false.
    • +
    +
  8. +
  9. All languages are disposed. +
      +
    • TruffleLanguage.disposeContext(C) is called for all languages.
    • +
    +
  10. +
  11. The context is closed.
  12. +
+ +

Behavior if useSystemExit is enabled

+ +
    +
  1. The exit is triggered by TruffleContext.closeExited(Node,int). +
      +
    • Same as with system exit disabled.
    • +
    +
  2. +
  3. Exit notifications for all initialized languages are executed - TruffleLanguage.exitContext(C,ExitMode,int), where ExitMode.HARD is used as the ExitMode parameter. +
      +
    • Same as with system exit disabled.
    • +
    +
  4. +
  5. System.exit(int) is called to terminate the whole host application providing the fastest exit possible. +
      +
    • The exit code passed to System.exit(int) +is the one originally specified as the second parameter to the first call toTruffleContext.closeExited(Node,int).
    • +
    +
  6. +
+ +

Example Usage

+ +

The SimpleLanguage demonstrates the usage of the hard context exit. The following aspects are demonstrated.

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/HostOptimization/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/HostOptimization/index.html new file mode 100644 index 0000000..77de565 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/HostOptimization/index.html @@ -0,0 +1,486 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Host Compilation for Interpreter Java code

+ +

For the following document, we disambiguate between host and guest compilation.

+ +
    +
  • Host compilation is applied to the Java implementation of the interpreter. If the interpreter runs on HotSpot, this kind of compilation is applied when Truffle interpreters are JIT compiled (or dynamically compiled) as Java applications. This compilation is applied ahead of time during native image generation.
  • +
  • Guest compilation is applied to guest language code. This kind of compilation uses Partial Evaluation and Futamura projections to derive optimized code from Truffle ASTs and bytecodes.
  • +
+ +

This section discusses domain-specific host compilations applied to Truffle AST and bytecode interpreters.

+ +

Host Inlining

+ +

Truffle interpreters are written to support runtime compilation by applying the first Futamura projection. +Runtime compilable code, also often referred to as partial evaluatable code, has the following characteristics:

+ +
    +
  • It is naturally designed for high-performance, as it also defines the performance after runtime compilation of a language.
  • +
  • It is written to avoid recursion, as recursive code cannot quickly be partially evaluated.
  • +
  • It avoids complex abstractions and third-party code as they are typically not designed for PE.
  • +
  • The boundaries of partially evaluatable code are reliably defined by methods annotated with @TruffleBoundary, blocks dominated by a call to CompilerDirectives.transferToInterpreter() or a block protected by a call to CompilerDirectives.inInterpreter().
  • +
+ +

Truffle host inlining leverages these properties and forces inlining during host compilation for runtime compilable code paths as much as possible. +The general assumption is that code important for runtime compilation is also important for interpreter execution. +Whenever a PE boundary is detected, the host inlining phase no longer makes any inlining decisions and defers them to later inlining phases better suited for regular Java code.

+ +

The source code for this phase can be found in HostInliningPhase.

+ +

Truffle host inlining is applied when compiling a method annotated with @HostCompilerDirectives.BytecodeInterpreterSwitch. +The maximum node cost for such methods can be configured using -H:TruffleHostInliningByteCodeInterpreterBudget=100000 for native images and -Dgraal.TruffleHostInliningByteCodeInterpreterBudget=100000 on HotSpot. +If a method that is annotated with @BytecodeInterpreterSwitch calls a method with the same annotation then the method is directly inlined as long as the cost of both methods do not exceed the budget. +In other words, any such method will be treated by the inlining phase just as if they would be part of the root bytecode switch method. +This allows bytecode interpreter switches to be composed of multiple methods if needed.

+ +

Native image, during closed world analysis, computes all methods that are reachable for runtime compilation. +Any potentially reachable method from RootNode.execute(...) is determined as runtime compilable. +For native images, in addition to bytecode interpreter switches, all runtime compilable methods are optimized using Truffle host inlining. +The maximum node cost of such an inlining pass can be configured with -H:TruffleHostInliningBaseBudget=5000. +On HotSpot the set of runtime compilable methods is unknown. +Therefore, we can only rely on regular Java method inlining for methods not annotated as bytecode interpreter switch on HotSpot.

+ +

Whenever the maximum budget for a compilation unit is reached, inlining will be stopped. +The same budget will be used for the exploration of subtrees during inlining. +If a call cannot be fully explored and inlined within the budget, then no decision is taken on the individual subtree. +For the vast majority of runtime compilable methods, this limit will not be reached, as it is prevented by natural PE boundaries as well as polymorphic calls to execute methods of @Child nodes. +If there are methods that exceed the budget limit, then the recommendation is to optimize such nodes by adding more PE boundaries. +If a method exceeds the limit, it is likely that the same code also has a high cost for runtime compilation.

+ +

Debugging Host Inlining

+ +

The inlining decisions performed by this phase is best debugged with -H:Log=TruffleHostInliningPhase,~CanonicalizerPhase,~GraphBuilderPhase for native images or -Dgraal.Log=TruffleHostInliningPhase,~CanonicalizerPhase,~GraphBuilderPhase on HotSpot.

+ +

Consider the following example, which shows previously described common patterns of partial evaluatable code in Truffle interpreters:

+ +
class BytecodeNode extends Node {
+
+    @CompilationFinal(dimensions = 1) final byte[] ops;
+    @Children final BaseNode[] polymorphic = new BaseNode[]{new SubNode1(), new SubNode2()};
+    @Child SubNode1 monomorphic = new SubNode1();
+
+    BytecodeNode(byte[] ops) {
+        this.ops = ops;
+    }
+
+    @BytecodeInterpreterSwitch
+    @ExplodeLoop(kind = LoopExplosionKind.MERGE_EXPLODE)
+    public void execute() {
+        int bci = 0;
+        while (bci < ops.length) {
+            switch (ops[bci++]) {
+                case 0:
+                    // regular operation
+                    add(21, 21);
+                    break;
+                case 1:
+                    // complex operation in @TruffleBoundary annotated method
+                    truffleBoundary();
+                    break;
+                case 2:
+                    // complex operation protected behind inIntepreter
+                    if (CompilerDirectives.inInterpreter()) {
+                        protectedByInIntepreter();
+                    }
+                    break;
+                case 3:
+                    // complex operation dominated by transferToInterpreter
+                    CompilerDirectives.transferToInterpreterAndInvalidate();
+                    dominatedByTransferToInterpreter();
+                    break;
+                case 4:
+                    // first level of recursion is inlined
+                    recursive(5);
+                    break;
+                case 5:
+                    // can be inlined is still monomorphic (with profile)
+                    monomorphic.execute();
+                    break;
+                case 6:
+                    for (int y = 0; y < polymorphic.length; y++) {
+                        // can no longer be inlined (no longer monomorphic)
+                        polymorphic[y].execute();
+                    }
+                    break;
+                default:
+                    // propagates transferToInterpeter from within the call
+                    throw CompilerDirectives.shouldNotReachHere();
+            }
+        }
+    }
+
+    private static int add(int a, int b) {
+        return a + b;
+    }
+
+    private void protectedByInIntepreter() {
+    }
+
+    private void dominatedByTransferToInterpreter() {
+    }
+
+    private void recursive(int i) {
+        if (i == 0) {
+            return;
+        }
+        recursive(i - 1);
+    }
+
+    @TruffleBoundary
+    private void truffleBoundary() {
+    }
+
+    abstract static class BaseNode extends Node {
+        abstract int execute();
+    }
+
+    static class SubNode1 extends BaseNode {
+        @Override
+        int execute() {
+            return 42;
+        }
+    }
+
+    static class SubNode2 extends BaseNode {
+        @Override
+        int execute() {
+            return 42;
+        }
+    }
+}
+
+ +

We can run this as a unittest in the Graal repository (see class HostInliningBytecodeInterpreterExampleTest) by running the following command line in graal/compiler:

+ +
mx unittest  -Dgraal.Log=TruffleHostInliningPhase,~CanonicalizerPhase,~GraphBuilderPhase -Dgraal.Dump=:3  HostInliningBytecodeInterpreterExampleTest
+
+ +

This prints:

+ +
[thread:1] scope: main
+  [thread:1] scope: main.Testing
+  Context: HotSpotMethod<HostInliningBytecodeInterpreterExampleTest$BytecodeNode.execute()>
+  Context: StructuredGraph:1{HotSpotMethod<HostInliningBytecodeInterpreterExampleTest$BytecodeNode.execute()>}
+      [thread:1] scope: main.Testing.EnterpriseHighTier.TruffleHostInliningPhase
+      Truffle host inlining completed after 2 rounds. Graph cost changed from 136 to 137 after inlining:
+      Root[org.graalvm.compiler.truffle.test.HostInliningBytecodeInterpreterExampleTest$BytecodeNode.execute]
+          INLINE org.graalvm.compiler.truffle.test.HostInliningBytecodeInterpreterExampleTest$BytecodeNode.add(int, int)                      [inlined    2, monomorphic false, deopt false, inInterpreter false, propDeopt false, subTreeInvokes    0, subTreeCost    8, incomplete false,  reason null]
+          CUTOFF org.graalvm.compiler.truffle.test.HostInliningBytecodeInterpreterExampleTest$BytecodeNode.truffleBoundary()                  [inlined   -1, monomorphic false, deopt false, inInterpreter false, propDeopt false, subTreeInvokes    1, subTreeCost    0, incomplete false,  reason truffle boundary]
+          INLINE com.oracle.truffle.api.CompilerDirectives.inInterpreter()                                                                    [inlined    0, monomorphic false, deopt false, inInterpreter false, propDeopt false, subTreeInvokes    0, subTreeCost    6, incomplete false,  reason null]
+          CUTOFF org.graalvm.compiler.truffle.test.HostInliningBytecodeInterpreterExampleTest$BytecodeNode.protectedByInIntepreter()          [inlined   -1, monomorphic false, deopt false, inInterpreter  true, propDeopt false, subTreeInvokes    1, subTreeCost    0, incomplete false,  reason protected by inInterpreter()]
+          INLINE com.oracle.truffle.api.CompilerDirectives.transferToInterpreterAndInvalidate()                                               [inlined    3, monomorphic false, deopt  true, inInterpreter false, propDeopt false, subTreeInvokes    0, subTreeCost   32, incomplete false,  reason null]
+            INLINE com.oracle.truffle.api.CompilerDirectives.inInterpreter()                                                                  [inlined    3, monomorphic false, deopt  true, inInterpreter false, propDeopt false, subTreeInvokes    0, subTreeCost    6, incomplete false,  reason null]
+            CUTOFF org.graalvm.compiler.truffle.runtime.hotspot.AbstractHotSpotTruffleRuntime.traceTransferToInterpreter()                    [inlined   -1, monomorphic false, deopt  true, inInterpreter  true, propDeopt false, subTreeInvokes    0, subTreeCost    0, incomplete false,  reason dominated by transferToInterpreter()]
+          CUTOFF org.graalvm.compiler.truffle.test.HostInliningBytecodeInterpreterExampleTest$BytecodeNode.dominatedByTransferToInterpreter() [inlined   -1, monomorphic false, deopt  true, inInterpreter false, propDeopt false, subTreeInvokes    0, subTreeCost    0, incomplete false,  reason dominated by transferToInterpreter()]
+          INLINE org.graalvm.compiler.truffle.test.HostInliningBytecodeInterpreterExampleTest$BytecodeNode.recursive(int)                     [inlined    4, monomorphic false, deopt false, inInterpreter false, propDeopt false, subTreeInvokes    1, subTreeCost   20, incomplete false,  reason null]
+            CUTOFF org.graalvm.compiler.truffle.test.HostInliningBytecodeInterpreterExampleTest$BytecodeNode.recursive(int)                   [inlined   -1, monomorphic false, deopt false, inInterpreter false, propDeopt false, subTreeInvokes    1, subTreeCost    0, incomplete false,  reason recursive]
+          INLINE org.graalvm.compiler.truffle.test.HostInliningBytecodeInterpreterExampleTest$BytecodeNode$SubNode1.execute()                 [inlined    1, monomorphic false, deopt false, inInterpreter false, propDeopt false, subTreeInvokes    0, subTreeCost    6, incomplete false,  reason null]
+          CUTOFF org.graalvm.compiler.truffle.test.HostInliningBytecodeInterpreterExampleTest$BytecodeNode$BaseNode.execute()                 [inlined   -1, monomorphic false, deopt false, inInterpreter false, propDeopt false, subTreeInvokes    1, subTreeCost    0, incomplete false,  reason not direct call: no type profile]
+          CUTOFF com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere()                                                               [inlined   -1, monomorphic false, deopt false, inInterpreter false, propDeopt  true, subTreeInvokes    0, subTreeCost   98, incomplete false,  reason propagates transferToInterpreter]
+
+ +

Note that we have also used the -Dgraal.Dump=:3 option, which sends the graphs to any running IdealGraphVisualizer instance for further inspection. +To debug CUTOFF decisions for incomplete exploration (entries with incomplete true) use the -Dgraal.TruffleHostInliningPrintExplored=true option to see all incomplete subtrees in the log.

+ +

Tuning Host Inlining

+ +

After learning how to debug and trace host inlining decisions, it is time to look at some ways to tune it. +As a first step, it is necessary to identify compilation units essential for good interpreter performance. +To do this, a Truffle interpreter can be executed in interpreter-only mode by setting the engine.Compilation flag to false. +After that, a Java profiler may be used to identify hot spots in the execution. +For further details on profiling, see Profiling.md +If you are looking for advice on how and when to optimize Truffle interpreters, see Optimizing.md

+ +

After identifying a hot method, for example, the bytecode dispatch loop in a Truffle bytecode interpreter, we can further investigate using host inlining logging as described in the previous section. +Interesting entries are prefixed with CUTOFF and have a reason that explains the reason for the individual cutoff.

+ +

Common reasons for CUTOFF entries are:

+
    +
  • dominated by transferToInterpreter() or protected by inInterpreter(): This means that the is call performed in a slow-path. Host inlining will not decide on such calls and just mark them as CUTOFF.
  • +
  • target method not inlinable this happens for host VM methods that cannot be inlined. There is typically not much we can do about that.
  • +
  • Out of budget we ran out of budget for inlining this method. This happens if the cost of the method becomes too high.
  • +
+ +

Additionally, to avoid the explosion of code size, host inlining has a built-in heuristic to detect call subtrees that are considered too complex to inline. +For example, the tracing may print the following:

+ +
CUTOFF com.oracle.truffle.espresso.nodes.BytecodeNode.putPoolConstant(VirtualFrame, int, char, int)   [inlined   -1, explored    0, monomorphic false, deopt false, inInterpreter false, propDeopt false, graphSize 1132, subTreeCost 5136, invokes    1, subTreeInvokes   12, forced false, incomplete false,  reason call has too many fast-path invokes - too complex, please optimize, see truffle/docs/HostOptimization.md
+
+ +

This indicates that there are too many fast-path invokes (by default 10) in the subtree, it also stops exploring after that number. +The -Dgraal.TruffleHostInliningPrintExplored=true flag may be provided to see the entire subtree for the decision. +The following calls are considered fast-path invokes:

+ +
    +
  • Invokes where the target method is annotated by @TruffleBoundary.
  • +
  • Invokes that are polymorphic or where no monomorphic profiling feedback is available. For example, a call to a subexpression’s execute method.
  • +
  • Invokes that are recursive.
  • +
  • Invokes that are too complex themselves. For example, invokes that have too many fast-path invokes.
  • +
+ +

The following calls are not considered fast-path invokes:

+ +
    +
  • Invokes that can be inlined using the host inlining heuristic.
  • +
  • Invokes in a slow-path, like any invoke that is dominated by transferToInterpreter() or protected by isInterpreter().
  • +
  • Invokes that cannot be inlined due to limitations of the host VM, like calls to Throwable.fillInStackTrace().
  • +
  • Invokes that are no longer reachable.
  • +
+ +

It is impossible to avoid fast-path invokes entirely, as, for example, child nodes need to be executed in an AST. +It is theoretically possible to avoid all fast-path invokes in a bytecode interpreter. +In practice, languages will rely on @TruffleBoundary to the runtime to implement more complex bytecodes.

+ +

In the following sections, we discuss techniques on how to improve host interpreter code:

+ +

Optimization: Manually cut code paths with @HostCompilerDirectives.InliningCutoff

+ +

As mentioned in the previous section, a heuristic automatically cuts inlining subtrees with too many calls in them. +One way to optimize this is by using the @InliningCutoff annotation.

+ +

Consider the following example:

+ +
abstract class AddNode extends Node {
+
+   abstract Object execute(Object a, Object b);
+
+   @Specialization int doInt(int a, int b) { return a + b; }
+   
+   @Specialization double doDouble(double a, double b) { return a + b; }
+   
+   @Specialization double doGeneric(Object a, Object b, @Cached LookupAndCallNode callNode) { 
+       return callNode.execute("__add", a, b); 
+   }
+}
+
+
+ +

In this example, the specializations doInt and doDouble are very simple, but there is also the doGeneric specialization, which calls into a complex lookup chain. +Assuming that the LookupAndCallNode.execute is a very complex method with more than ten fast-path subtree calls, we could not expect the execute method to get inlined. +Host inlining currently does not support automatic component analysis; though it can be specified manually using the @InliningCutoff annotation:

+ +
abstract class AddNode extends Node {
+
+   abstract Object execute(Object a, Object b);
+
+   @Specialization int doInt(int a, int b) { return a + b; }
+   
+   @Specialization double doDouble(double a, double b) { return a + b; }
+   
+   @HostCompilerDirectives.InliningCutoff
+   @Specialization double doGeneric(Object a, Object b, @Cached LookupAndCallNode callNode) { 
+       return callNode.execute("__add__", a, b); 
+   }
+}
+
+
+ +

After changing the code, host inlining may now decide to inline the execute method of the AddNode if it fits into the host inlining budget, but force a CUTOFF at the doGeneric(...) method call. +Please see the javadoc on other use-cases for using this annotation.

+ +

Optimization: Deduplicating calls from branches that fold during partial evaluation

+ +

The following is an example where the code is efficient for compilation using partial evaluation but is not ideal for host compilation.

+ +
@Child HelperNode helperNode;
+
+final boolean negate;
+// ....
+
+int execute(int argument) {
+	if (negate) {
+		return helperNode.execute(-argument);
+	} else {
+         return helperNode.execute(argument);
+	}
+}
+
+ +

When this code is compiled using partial evaluation, this code is efficient as the condition is guaranteed to fold to a single case, as the negate field is compilation final. +During host optimization, the negate field is not compilation final, and the compiler would either inline the code twice or decide not to inline the execute method. +In order to avoid this the code can be rewritten as follows:

+ +
@Child HelperNode helperNode;
+
+final boolean negate;
+// ....
+
+int execute(int argument) {
+    int negatedArgument;
+    if (negate) {
+        negatedArgument = -argument;
+    } else {
+        negatedArgument = argument;
+    }
+    return helperNode.execute(negatedArgument);
+}
+
+ +

Similar code patterns can arise indirectly through code generation if many specializations with the same method body are used. +Host compilers typically have a hard time optimizing such patterns automatically.

+ +

Optimization: Extract complex slow-path code in separate methods

+ +

Consider the following example:

+ +
int execute(int argument) {
+	if (argument == 0) {
+	   CompilerDirectives.transferToInterpeterAndInvalidate();
+	   throw new RuntimeException("Invalid zero argument " + argument);
+	}
+	return argument;
+}
+
+ +

The Java compiler generates bytecode equivalent to the following code:

+ +
int execute(int argument) {
+	if (argument == 0) {
+	   CompilerDirectives.transferToInterpeterAndInvalidate();
+	   throw new RuntimeException(new StringBuilder("Invalid zero argument ").append(argument).build());
+	}
+	return argument;
+}
+
+ +

While this code is efficient for partial evaluation, this code takes up unnecessary space during host inlining. +It is therefore recommended to extract a single method for the slow-path part of the code:

+ +
int execute(int argument) {
+	if (argument == 0) {
+	   CompilerDirectives.transferToInterpeterAndInvalidate();
+	   throw invalidZeroArgument(argument);
+	}
+	return argument;
+}
+
+RuntimeException invalidZeroArgument(int argument) {
+   throw new RuntimeException("Invalid zero argument " + argument);
+}
+
+
+ + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Inlining/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Inlining/index.html new file mode 100644 index 0000000..0281295 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Inlining/index.html @@ -0,0 +1,268 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Approach to Function Inlining

+ +

Truffle provides automated inlining for all languages built with the framework. +Since the 20.2.0 release a new approach to inlining was introduced. +This document describes how the new approach works, compares it to the legacy +inlining approach, and motivates the design choices made for the new approach.

+ +

Inlining

+ +

Inlining is the process of replacing a call to a function with the body of that function. +This removes the overhead of the call but more importantly it opens up more optimization opportunities for later phases of the compiler. +The down side of the process is that the size of the compilation grows with each inlined function. +Overly large compilation units are hard to optimize and there is finite memory for installing code.

+ +

Because of all this, choosing which functions to inline is a delicate trade-off +between the expected gains of inlining a function versus the cost of the +increase of the size of the compilation unit.

+ +

Truffle Legacy Inlining

+ +

Truffle has had an approach to inlining for quite a while. +Unfortunately, this early approach suffered from multiple issues, the main one being that it relied on the number of Truffle AST nodes in a call target to approximate the size of the call target.

+ +

AST nodes are a very poor proxy for actual code size of the call target since there is no guarantee how much code a single AST node will produce. +For example, an addition node specialized for adding two integers will produce significantly less code than that same node if specialized for adding integers, doubles, and strings (not to mention a different node and nodes from different languages). +This made it impossible to have a single inlining approach that would work reliably across all the Truffle languages.

+ +

One notable thing about the legacy inlining is that, since it only uses information from the AST, inlining decisions are made before partial evaluation begins. +This means that we only ever partially evaluate call targets that we decide to inline. +The advantage of this approach is that no time is spent on the partial evaluation of call targets that do not end up being inlined. +On the other hand this results in frequent compilation problems stemming from the poor decisions made by the inliner. +For example, the resulting compilation unit would be too big to compile.

+ +

Language-agnostic Inlining

+ +

The main design goal of the new inlining approach is to use the number of Graal nodes (compiler nodes) after partial evaluation as a proxy for call target size. +This is a much better size proxy since partial evaluation removes all the abstractions of the AST and results in a graph that is much closer to the low-level instructions that the call target actually performs. +This results in a more precise cost model when deciding whether or not to inline a call target, and it removes much of +the language-specific information that the AST carries (hence the name: Language-agnostic inlining).

+ +

This is achieved by performing partial evaluation on every candidate call target and then making the inlining decision after that (as opposed to the legacy inlining which made decisions before doing any partial evaluation). +Both the amount of partial evaluation that will be done as well as the amount that will be inlined are controlled by the notion of budgets. +These are the “exploration budget” and “inlining budget” respectively, both expressed in terms of Graal node counts.

+ +

The downside of this approach is that we need to do partial evaluation even on call targets which we ultimately decide not to inline. +This results in a measurable increase in average compilation time compared to legacy inlining (approximate 10%).

+ +

Observing and Impacting the Inlining

+ +

The inliner keeps an internal call tree to keep track of the states of individual calls to targets, as well as the inlining decisions that were made. +The following sections explain the states in which calls in the call tree can be, as well as how to find out which decisions were made during compilations.

+ +

Call Tree States

+ +

Nodes in the inline call tree +represent calls to particular targets. +This means that if one target calls another twice, we will see this as two nodes despite it being the same call target.

+ +

Each node can be in one of six states explained here:

+
    +
  • Inlined - This state means that the call was inlined. Initially, only the +root of the compilation is in this state since it is implicitly “inlined” +(i.e., part of the compilation unit).
  • +
  • Cutoff - This state means that the call target was not partially evaluated, +thus was not even considered for inlining. This is normally due to the +inliner hitting its exploration budget limitations.
  • +
  • Expanded - This state means that the call target was partially evaluated +(thus, considered for inlining) but a decision was made not to inline. This +could be due to inlining budget limitations or the target being deemed too +expensive to inline (e.g., inlining a small target with multiple outgoing +“Cutoff” calls would just introduce more calls to the compilation unit).
  • +
  • Removed - This state means that this call is present in the AST but partial +evaluation removed the call. This is an advantage over the legacy inlining +which made the decisions ahead of time and had no way of noticing such +situations.
  • +
  • Indirect - This state denotes an indirect call. We cannot inline an indirect +call.
  • +
  • BailedOut - This state should be very rare and is considered a performance +problem. It means that partial evaluation of the target resulted in a +BailoutException, i.e., it could not be completed successfully. This means there is +some problem with that particular target, but rather than quit the entire +compilation, we treat that call as not possible to inline.
  • +
+ +

Tracing Inlining Decisions

+ +

Truffle provides an engine option to trace the final state of the call tree, including a lot of accompanying data, during compilation. +This option is TraceInlining and can be set in all the usual ways: by adding --engine.TraceInlining=true to the language launchers, adding -Dpolyglot.engine.TraceInlining=true to the command line if +running a regular Java program that executes guest languages (languages implemented with Truffle), or setting the option explicitly for an engine.

+ +

Here is an example output of TraceInlining for a JavaScript function:

+ +
[engine] inline start     M.CollidePolygons                                           |call diff        0.00 |Recursion Depth      0 |Explore/inline ratio     1.07 |IR Nodes        27149 |Frequency        1.00 |Truffle Callees     14 |Forced          false |Depth               0
+[engine] Inlined            M.FindMaxSeparation <opt>                                 |call diff       -8.99 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes         4617 |Frequency        1.00 |Truffle Callees      7 |Forced          false |Depth               1
+[engine] Inlined              parseInt <opt>                                          |call diff       -1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          111 |Frequency        1.00 |Truffle Callees      0 |Forced           true |Depth               2
+[engine] Inlined              M.EdgeSeparation                                        |call diff       -3.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes         4097 |Frequency        1.00 |Truffle Callees      2 |Forced          false |Depth               2
+[engine] Inlined                parseInt <opt>                                        |call diff       -1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          111 |Frequency        1.00 |Truffle Callees      0 |Forced           true |Depth               3
+[engine] Inlined                parseInt <opt>                                        |call diff       -1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          111 |Frequency        1.00 |Truffle Callees      0 |Forced           true |Depth               3
+[engine] Inlined              parseInt <opt>                                          |call diff       -1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          111 |Frequency        1.00 |Truffle Callees      0 |Forced           true |Depth               2
+[engine] Expanded             M.EdgeSeparation                                        |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes         4097 |Frequency        1.00 |Truffle Callees      2 |Forced          false |Depth               2
+[engine] Inlined              parseInt <opt>                                          |call diff       -1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          111 |Frequency        1.00 |Truffle Callees      0 |Forced           true |Depth               2
+[engine] Inlined              M.EdgeSeparation                                        |call diff       -3.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes         4097 |Frequency        1.00 |Truffle Callees      2 |Forced          false |Depth               2
+[engine] Inlined                parseInt <opt>                                        |call diff       -1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          111 |Frequency        1.00 |Truffle Callees      0 |Forced           true |Depth               3
+[engine] Inlined                parseInt <opt>                                        |call diff       -1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          111 |Frequency        1.00 |Truffle Callees      0 |Forced           true |Depth               3
+[engine] Cutoff               M.EdgeSeparation                                        |call diff        0.01 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        0.01 |Truffle Callees      2 |Forced          false |Depth               2
+[engine] Cutoff             M.FindMaxSeparation <opt>                                 |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees      7 |Forced          false |Depth               1
+[engine] Cutoff             M.FindIncidentEdge <opt>                                  |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees     19 |Forced          false |Depth               1
+[engine] Cutoff             parseInt <opt>                                            |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees      0 |Forced           true |Depth               1
+[engine] Cutoff             parseInt <opt>                                            |call diff        0.98 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        0.98 |Truffle Callees      0 |Forced           true |Depth               1
+[engine] Cutoff             A.Set <split-16abdeb5>                                    |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees      0 |Forced          false |Depth               1
+[engine] Cutoff             A.Normalize <split-866f516>                               |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees      1 |Forced          false |Depth               1
+[engine] Cutoff             A.Set <split-1f7fe4ae>                                    |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees      0 |Forced          false |Depth               1
+[engine] Cutoff             M.ClipSegmentToLine                                       |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees      2 |Forced          false |Depth               1
+[engine] Cutoff             M.ClipSegmentToLine                                       |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees      2 |Forced          false |Depth               1
+[engine] Cutoff             A.SetV <split-7c14e725>                                   |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees      0 |Forced          false |Depth               1
+[engine] Cutoff             A.SetV <split-6029dec7>                                   |call diff        1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes            0 |Frequency        1.00 |Truffle Callees      0 |Forced          false |Depth               1
+[engine] Inlined            L.Set <split-2ef5921d>                                    |call diff       -3.97 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          205 |Frequency        1.98 |Truffle Callees      1 |Forced          false |Depth               1
+[engine] Inlined              set <split-969378b>                                     |call diff       -1.98 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          716 |Frequency        1.98 |Truffle Callees      0 |Forced          false |Depth               2
+[engine] Inlined            set                                                       |call diff       -1.98 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          381 |Frequency        1.98 |Truffle Callees      0 |Forced          false |Depth               1
+[engine] inline done      M.CollidePolygons                                           |call diff        0.00 |Recursion Depth      0 |Explore/inline ratio     1.07 |IR Nodes        27149 |Frequency        1.00 |Truffle Callees     14 |Forced          false |Depth               0
+
+ +

Dumping Inlining Decisions

+ +

The same information that is provided in textual form through tracing is also available in the IGV dumps. +The graphs are part of the Graal Graphs group in a Call Tree subgroup. +The graphs show the state of the call tree before inlining and after.

+ +

Controlling Inlining Budgets

+ +

Note: The default values for inlining-related budgets were carefully chosen with consideration for compilation time, performance, and compiler stability in mind. +Changing these parameters can impact all of these.

+ +

Language-agnostic inlining provides two options to control the amount of exploration and the amount of inlining the compiler can do. +These are InliningExpansionBudget and InliningInliningBudget, respectively. +Both are expressed in terms of Graal node count. +They can be controlled as any other engine options (i.e., the same way as described in the “Tracing inlining decisions” section).

+ +

InliningExpansionBudget controls at which point the inliner will stop partially evaluating candidates. +Increasing this budget can thus have a very negative impact on average compilation time (notably on the time spent doing +partial evaluation), but may provide more candidates for inlining.

+ +

InliningInliningBudget controls how many Graal nodes the compilation unit is allowed to have as a result of inlining. Increasing this budget will likely result in more candidates being inlined, which will result in a larger compilation unit. This, in turn might slow down compilation, notably in the post partial evaluation phases since larger graphs take more time to optimize. +It may also improve performance (removed calls, optimization phases have a bigger picture) or hurt performance, e.g., when a graph is too big to optimize correctly or to compile at all.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/InteropMigration/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/InteropMigration/index.html new file mode 100644 index 0000000..a189591 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/InteropMigration/index.html @@ -0,0 +1,684 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Interop 2.0

+ +

This document is targeted at guest language and tool implementers. +It is recommended to read the Truffle Library Tutorial first, before proceeding.

+ +

Motivation

+ +

In Truffle Version 1.0 RC15 a new API called “Truffle Libraries” was introduced. +Truffle Libraries allow users to use polymorphism with support for profiling/caching. +With Interop 2.0 it is planned to use Truffle Libraries for the interoperability protocol. +The current interoperability APIs are mature and well-tested and already +adopted by languages and tools.

+ +

Here is a list of arguments why current interoperability APIs were changed and Interop 2.0 was introduced:

+ +
    +
  • Footprint: In the current interop API every message send goes through a CallTarget and the arguments are boxed into an Object[]. This makes current interop inefficient for interpreter calls and it requires additional memory. Truffle Libraries use simple nodes and type-specialized call signatures that do not require argument array boxing or call targets.
  • +
  • Uncached dispatch: There is no way to execute current interop messages from the slow-path without allocating a temporary node. Truffle Libraries automatically generate an uncached version of every exported message. This allows the use of interop messages from slow-path/runtime without allocating any temporary data structures.
  • +
  • Reuse dispatch for multiple messages: In current interop, the dispatch to exported messages is repeated for each message that is sent. If multiple messages need to be sent and the receiver type becomes polymorphic, this produces bad code. Interop libraries instances can be specialized for input values. This allows users to do the dispatch once and invoke multiple messages without repeating the dispatch. This leads to more efficient code in polymorphic cases.
  • +
  • Support for default implementations: Current interop can only be used for implementations of TruffleObject. Truffle Libraries can be used with any receiver type. For example, it is possible to invoke the isExecutable message on primitive numbers and it just returns false.
  • +
  • Error proneness: There were some common issues with message resolutions that Truffle Libraries try to avoid by not making them possible, such as mixing up receiver types or implementing a wrong type check. The new assertion feature for Truffle Libraries allows specifying message specific assertions that allow verifying invariants, pre, and post-conditions.
  • +
  • Redundancy in documentation: Current interop documents the messages in the Message constant and in the ForeignAccess static accessor method. This leads to mostly redundant documentation. With Truffle interop, there is only one place for the documentation, which is the instance method in the library class.
  • +
  • Generality: Truffle Libraries can be used for language representation abstractions, since it is now efficient enough in terms of memory consumption and interpreter performance. The current interop API could not realistically be used that way because of this issue.
  • +
  • Address protocol issues: There are some design issues with the current interop API that interop 2.0 tries to address (see later).
  • +
+ +

Compatibility

+ +

The change from interop 1.0 to 2.0 was done in a compatible way. +Therefore, the old interop should continue to work and adoption can be incremental. +This means that if one language still calls using the old interop API and the other language has already adopted the new interop API, a compatibility bridge will map the APIs. +If you are curious about how this works, look for the class DefaultTruffleObjectExports for new interop calls to old interop. And LegacyToLibraryNode for old interop calls to new interop. Note that using the compatibility bridge may cause performance regressions. +That is why languages should migrate as early as possible.

+ +

Interop Protocol Changes

+ +

Interop 2.0 comes with many protocol changes. Th +is section is intended to provide rationales for these changes. For fully detailed reference documentation see the InteropLibrary Javadoc. +Note: Every deprecated API describes its migration path in the Javadoc tagged by @deprecated.

+ +

Replace IS_BOXED and UNBOX with Explicit Types

+ +

There are some problems with the IS_BOXED/UNBOX design:

+ +
    +
  • In order to find out if a value is of a particular type, e.g., a String, the value needs to be unboxed first. Unboxing may be an expensive operation leading to inefficient code just to check the type of a value.
  • +
  • The old API cannot be used for values that did not implement TruffleObject. Therefore, the handling of primitive numbers needed to be separated from the TruffleObject case, making the UNBOX design necessary to reuse existing code. Truffle Libraries support primitive receiver types.
  • +
  • The design of UNBOX relies on the specified set of primitive types that it returns. It is hard to introduce additional, new interop types this way, as language refers to the primitive types directly.
  • +
+ +

The following new messages were introduced in InteropLibrary as a replacement:

+ +
boolean isBoolean(Object)
+boolean asBoolean(Object)
+boolean isString(Object)
+String  asString(Object)
+boolean isNumber(Object)
+boolean fitsInByte(Object)
+boolean fitsInShort(Object)
+boolean fitsInInt(Object)
+boolean fitsInLong(Object)
+boolean fitsInFloat(Object)
+boolean fitsInDouble(Object)
+byte asByte(Object)
+short asShort(Object)
+int asInt(Object)
+long asLong(Object)
+float asFloat(Object)
+double asDouble(Object)
+
+ +

The InteropLibrary specifies default implementations for the receiver types Boolean, Byte, Short, Integer, Long, Float, Double, Character, and String. +This design is extendable to support new values like big numbers or a custom String abstraction as Java primitive types are no longer directly used. +It is no longer recommended to use primitive types in specializations directly, as the set of interop primitive types may change in the future. +Instead, always use the interop library to check for a particular type, e.g., use fitsInInt instead of instanceof Integer.

+ +

By using the new messages it is possible to emulate the original UNBOX message like this:

+ +
@Specialization(limit="5")
+Object doUnbox(Object value, @CachedLibrary("value") InteropLibrary interop) {
+    if (interop.isBoolean(value)) {
+      return interop.asBoolean(value);
+    } else if (interop.isString(value)) {
+      return interop.asString(value);
+    } else if (interop.isNumber(value)) {
+      if (interop.fitsInByte(value)) {
+        return interop.asByte(value);
+      } else if (interop.fitsInShort(value)) {
+        return interop.asShort(value);
+      } else if (interop.fitsInInt(value)) {
+        return interop.asInt(value);
+      } else if (interop.fitsInLong(value)) {
+        return interop.asLong(value);
+      } else if (interop.fitsInFloat(value)) {
+        return interop.asFloat(value);
+      } else if (interop.fitsInDouble(value)) {
+        return interop.asDouble(value);
+      }
+    }
+    throw UnsupportedMessageException.create();
+}
+
+

Note: It is not recommended to unbox all primitive types like this. +Instead a language should only unbox to the primitive types it actually uses. +Ideally an unbox operation is not needed and the interop library is directly used to implement the operation, like this:

+ +
@Specialization(guards = {
+                "leftValues.fitsInLong(l)",
+                "rightValues.fitsInLong(r)"}, limit="5")
+long doAdd(Object l, Object r,
+             @CachedLibrary("l") InteropLibrary leftValues,
+             @CachedLibrary("r") InteropLibrary rightValues) {
+       return leftValues.asLong(l) + rightValues.asLong(r);
+}
+
+ +

Explicit Namespaces for Array and Member Elements

+ +

The generic READ and WRITE messages were originally designed with primarily JavaScript use-cases in mind. +With the adoption of interop by more languages, it became apparent that there is a need for explicit namespaces for arrays and object members. +Over time, the interpretation of READ and WRITE was changed to represent array accesses when used with numbers and object member accesses when used with strings. +The HAS_SIZE message was reinterpreted as whether the value contains array elements with additional guarantees, e.g., that array elements were iterable between index 0 and size.

+ +

For better interop between languages, there is a need for an explicit Hash/Map/Dictionary entry namespace. +Originally it was intended to reuse the generic READ/WRITE namespace for this. +For JavaScript, this was possible, as the dictionary and member namespaces were equivalent. +Most languages, however, separate Map entries from Object members, which leads to ambiguous keys. +It is not possible for the source language (the protocol implementer) to know how this conflict needs to be resolved. +Instead, by having explicit namespaces we can let the target language (the protocol caller) decide how to resolve the ambiguity. +For example, whether dictionary or member elements should take precedence can now be decided in the target language operation.

+ +

The following interop messages were changed:

+
READ, WRITE, REMOVE, HAS_SIZE, GET_SIZE, HAS_KEYS, KEYS
+
+ +

The updated protocol with separate member and array namespace in InteropLibrary looks like this:

+ +

Object Namespace:

+ +
hasMembers(Object)
+getMembers(Object, boolean)
+readMember(Object, String)
+writeMember(Object, String, Object)
+removeMember(Object, String)
+invokeMember(Object, String, Object...)
+
+ +

Array Namespace:

+ +
hasArrayElements(Object)
+readArrayElement(Object, long)
+getArraySize(Object)
+writeArrayElement(Object, long, Object)
+removeArrayElement(Object, long)
+
+ +

Array access messages no longer throw UnknownIdentifierException; they instead throw InvalidArrayIndexException. +This was a bug in the original design, where the accessed number needed to be converted to the identifier string in the UnknownIdentifierException.

+ +

Replaced KeyInfo with Individual Messages

+ +

In the previous section, we did not mention the KEY_INFO message. +The KEY_INFO message was useful to query all properties of a member or array element. +While this was a conveniently small API, it was often inefficient as it required the implementer to return all the key info properties. +At the same time, it is rare that the caller really needed all key info properties. With Interop 2.0 we removed the KEY_INFO message. +Instead, we introduced explicit messages for each namespace, to address this issue.

+ +

Object Namespace:

+ +
isMemberReadable(Object, String)
+isMemberModifiable(Object, String)
+isMemberInsertable(Object, String)
+isMemberRemovable(Object, String)
+isMemberInvocable(Object, String)
+isMemberInternal(Object, String)
+isMemberWritable(Object, String)
+isMemberExisting(Object, String)
+hasMemberReadSideEffects(Object, String)
+hasMemberWriteSideEffects(Object, String)
+
+ +

Array Namespace:

+ +
isArrayElementReadable(Object, long)
+isArrayElementModifiable(Object, long)
+isArrayElementInsertable(Object, long)
+isArrayElementRemovable(Object, long)
+isArrayElementWritable(Object, long)
+isArrayElementExisting(Object, long)
+
+ +

Note: The array namespace no longer supports querying for read or write side-effects. +These messages might be reintroduced but, at the moment, there was no use-case. +Also, the array namespace does not allow invocations.

+ +

Remove Return Type for TO_NATIVE

+ +

The TO_NATIVE message was renamed to toNative in the InteropLibrary with the difference that it no longer returns a value, but performs the native transition as a side-effect if supported by the receiver. +This allows the caller of the message to simplify their code. No cases the toNative transition required to return a different value were found. +The default behaviour of toNative was changed to not return any value.

+ +

Minor Changes

+ +

The following messages were mostly unchanged. The NEW message was renamed to instantiate to be consistent with isInstantiable.

+ +
Message.IS_NULL         -> InteropLibrary.isNull
+Message.EXECUTE         -> InteropLibrary.execute
+Message.IS_INSTANTIABLE -> InteropLibrary.isInstantiable
+Message.NEW             -> InteropLibrary.instantiate
+Message.IS_EXECUTABLE   -> InteropLibrary.isExecutable
+Message.EXECUTE         -> InteropLibrary.execute
+Message.IS_POINTER      -> InteropLibrary.isPointer
+Message.AS_POINTER      -> InteropLibrary.asPointer
+
+ +

Stronger Assertions

+ +

Many new assertions were introduced as part of the migration. + The concrete pre-post and invariant conditions are described in the Javadoc. + Unlike the old interop nodes, cached libraries can only be used when adopted as part of the AST.

+ +

No More Unchecked/Checked Exceptions

+ +

With Interop 2.0 InteropException.raise was deprecated. +While possible, it is considered an anti-pattern to rethrow checked exceptions as unchecked exceptions. +With Truffle Libraries the target language nodes are directly inserted into the AST of the caller so there is no longer a limiting CallTarget that does not support checked exceptions. +Together with additional support for checked Exceptions from Truffle DSL, it should no longer be necessary to use the raise methods. +Instead, a new create factory method was introduced for all interop exception types.

+ +

It is planned to remove stack traces from interop exceptions in order to improve their efficiency, as interop exceptions are intended to be always immediately caught and never be rethrown. +This was deferred until the compatibility layer can be removed.

+ +

Migration

+ +

With the use of Truffle Libraries for interop, most existing interop APIs had to be deprecated. +The following comparison of Interop 1.0 with Interop 2.0 is designed to help migrate existing uses of interop.

+ +

Fast-Path Sending Interop Messages

+ +

This is the fast-path way of sending interop messages embedded in an operation node. +This is the most common way of sending interop messages.

+ +

Interop 1.0:

+ +
@ImportStatic({Message.class, ForeignAccess.class})
+abstract static class ForeignExecuteNode extends Node {
+
+    abstract Object execute(Object function, Object[] arguments);
+
+    @Specialization(guards = "sendIsExecutable(isExecutableNode, function)")
+    Object doDefault(TruffleObject function, Object[] arguments,
+                    @Cached("IS_EXECUTABLE.createNode()") Node isExecutableNode,
+                    @Cached("EXECUTE.createNode()") Node executeNode) {
+        try {
+            return ForeignAccess.sendExecute(executeNode, function, arguments);
+        } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
+            // ... convert errors to guest language errors ...
+        }
+    }
+}
+
+ +

Interop 2.0:

+ +
abstract static class ForeignExecuteNode extends Node {
+
+    abstract Object execute(Object function, Object[] arguments);
+
+    @Specialization(guards = "functions.isExecutable(function)", limit = "2")
+    Object doDefault(Object function, Object[] arguments,
+                    @CachedLibrary("function") InteropLibrary functions) {
+        try {
+            return functions.execute(function, arguments);
+        } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
+            // ... convert errors to guest language errors ...
+        }
+    }
+}
+
+ +

Note the following differences:

+ +
    +
  • To invoke messages we call instance methods on TruffleLibrary instead of calling a static method on ForeignAccess.
  • +
  • The old interop required the creation of one node for each operation. With the new version, only one specialized interop library is created.
  • +
  • In the old API we needed to specialize the receiver type for TruffleObject. The new interop library can be invoked with any interop value. By default isExecutable will return false for values that don’t export the interop library. E.g., it is now valid to call the library with boxed primitive receiver values.
  • +
  • Instead of using @Cached in the old interop, in the new interop we use @CachedLibrary.
  • +
  • The new @CachedLibrary annotation specifies the value the library specializes on. This allows the DSL to specialize the library instance to that value. This again allows the dispatch on the receiver value to be performed once for all message invocations. In the old interop version, the nodes could not be specialized to values. Therefore the dispatch needed to be repeated for every interop message send.
  • +
  • The specialized library instance requires specifying a limit for the specialization method. If this limit overflows, the uncached version of the library will be used that does not perform any profiling/caching. The old interop API assumed a constant specialization limit of 8 per interop node.
  • +
  • The new interop API allows for using a dispatched version of the library by specifying @CachedLibrary(limit="2") instead. This allows the interop library to be used with any value, but it has the disadvantage of duplicating the inline cache for every message invocation, like with the old interop API. It is therefore recommended to use specialized libraries whenever possible.
  • +
+ +

Slow-Path Sending Interop Messages

+ +

It is sometimes necessary to call interop messages from the runtime without the context of a node:

+ +

Interop 1.0:

+ +
ForeignAccess.sendRead(Message.READ.createNode(), object, "property")
+
+ +

Interop 2.0:

+ +
InteropLibrary.getFactory().getUncached().read(object, "property");
+
+ +

Note the following differences:

+ +
    +
  • The old interface allocated a node for each invocation.
  • +
  • The new library uses the uncached version of the library that does not require any allocation or boxing for each invocation.
  • +
  • With InteropLibrary.getFactory().getUncached(object) an uncached and specialized version of a library can be looked up. This can be used to avoid repeated export lookups if multiple uncached interop messages need to be sent to the same receiver.
  • +
+ +

Custom Fast-Path Sending Interop Messages

+ +

Sometimes Truffle DSL cannot be used and the nodes need to be written manually. Both APIs allow you to do so:

+ +

Interop 1.0:

+ +

+final class ForeignExecuteNode extends Node {
+
+    @Child private Node isExecutableNode = Message.IS_EXECUTABLE.createNode();
+    @Child private Node executeNode = Message.EXECUTE.createNode();
+
+    Object execute(Object function, Object[] arguments) {
+        if (function instanceof TruffleObject) {
+            TruffleObject tFunction = (TruffleObject) function;
+            if (ForeignAccess.sendIsExecutable(isExecutableNode, tFunction)) {
+                try {
+                    return ForeignAccess.sendExecute(executeNode, tFunction, arguments);
+                } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
+                    // TODO handle errors
+                }
+            }
+        }
+        // throw user error
+    }
+}
+
+
+ +

Interop 2.0:

+ +
static final class ForeignExecuteNode extends Node {
+
+    @Child private InteropLibrary functions = InteropLibrary.getFactory().createDispatched(5);
+
+    Object execute(Object function, Object[] arguments) {
+        if (functions.isExecutable(function)) {
+            try {
+                return functions.execute(function, arguments);
+            } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
+                // handle errors
+                return null;
+            }
+        }
+        // throw user error
+    }
+}
+
+ +

Note the following differences:

+ +
    +
  • The new interop creates nodes through the LibraryFactory<InteropLibrary> accessible through InteropLibrary.getFactory(). The old interop creates dispatching nodes through the Message instance.
  • +
  • The dispatch limit can be specified for the new interop libraries. The old interop API always assumed a constant limit of 8.
  • +
  • For the new interop we do not need to check for the type TruffleObject as Truffle Libraries can be used with any receiver type. For non-function values, isExecutable will just return false.
  • +
+ +

Implementing/Exporting Interop Messages

+ +

To implement/export interop library messages, see the following example:

+ +

Interop 1.0:

+ +
@MessageResolution(receiverType = KeysArray.class)
+final class KeysArray implements TruffleObject {
+
+    private final String[] keys;
+
+    KeysArray(String[] keys) {
+        this.keys = keys;
+    }
+
+    @Resolve(message = "HAS_SIZE")
+    abstract static class HasSize extends Node {
+
+        public Object access(KeysArray receiver) {
+            return true;
+        }
+    }
+
+    @Resolve(message = "GET_SIZE")
+    abstract static class GetSize extends Node {
+
+        public Object access(KeysArray receiver) {
+            return receiver.keys.length;
+        }
+    }
+
+    @Resolve(message = "READ")
+    abstract static class Read extends Node {
+
+        public Object access(KeysArray receiver, int index) {
+            try {
+                return receiver.keys[index];
+            } catch (IndexOutOfBoundsException e) {
+                CompilerDirectives.transferToInterpreter();
+                throw UnknownIdentifierException.raise(String.valueOf(index));
+            }
+        }
+    }
+
+    @Override
+    public ForeignAccess getForeignAccess() {
+        return KeysArrayForeign.ACCESS;
+    }
+
+    static boolean isInstance(TruffleObject array) {
+        return array instanceof KeysArray;
+    }
+}
+
+ +

Interop 2.0:

+ +
@ExportLibrary(InteropLibrary.class)
+final class KeysArray implements TruffleObject {
+
+    private final String[] keys;
+
+    KeysArray(String[] keys) {
+        this.keys = keys;
+    }
+
+    @ExportMessage
+    boolean hasArrayElements() {
+        return true;
+    }
+
+    @ExportMessage
+    boolean isArrayElementReadable(long index) {
+        return index >= 0 && index < keys.length;
+    }
+
+    @ExportMessage
+    long getArraySize() {
+        return keys.length;
+    }
+
+    @ExportMessage
+    Object readArrayElement(long index) throws InvalidArrayIndexException {
+        if (!isArrayElementReadable(index) {
+            throw InvalidArrayIndexException.create(index);
+        }
+        return keys[(int) index];
+    }
+}
+
+ +

Note the following differences:

+ +
    +
  • Instead of @MessageResolution we use @ExportLibrary.
  • +
  • Both versions need to implement TruffleObject. The new interop API only requires a TruffleObject type for compatibility reasons.
  • +
  • Instead of @Resolve, the @ExportMessage annotation is used. The latter annotation can infer the name of the message from the method name. If the method name is ambiguous, e.g., when multiple libraries are exported, then the name and library can be specified explicitly.
  • +
  • There is no need to specify classes for exports/resolves. However, it is still possible to do so if an export needs multiple specializations. See the Truffle Library tutorial for details.
  • +
  • Exceptions are now thrown as checked exceptions.
  • +
  • It is no longer needed to implement getForeignAccess(). The implementation discovers implementations for receiver types automatically.
  • +
  • It is no longer needed to implement isInstance. The implementation is now derived from the class signature. Note that the check can be more efficient if the receiver type is declared final. For non-final receiver types, it is recommended to specify exported methods as final.
  • +
+ +

Integration with DynamicObject

+ +

The old interop allowed for specifying a foreign access factory through ObjectType.getForeignAccessFactory(). This method is now deprecated and a new method, ObjectType.dispatch(), was introduced. Instead of a foreign access factory, the dispatch method needs to return a class that exports the InteropLibrary with an explicit receiver:

+ +

Interop 1.0:

+ +
public final class SLObjectType extends ObjectType {
+
+    public static final ObjectType SINGLETON = new SLObjectType();
+
+    private SLObjectType() {
+    }
+
+    public static boolean isInstance(TruffleObject obj) {
+        return SLContext.isSLObject(obj);
+    }
+
+    @Override
+    public ForeignAccess getForeignAccessFactory(DynamicObject obj) {
+        return SLObjectMessageResolutionForeign.ACCESS;
+    }
+}
+
+@MessageResolution(receiverType = SLObjectType.class)
+public class SLObjectMessageResolution {
+
+    @Resolve(message = "WRITE")
+    public abstract static class SLForeignWriteNode extends Node {...}
+
+    @Resolve(message = "READ")
+    public abstract static class SLForeignReadNode extends Node {...}
+    ...
+
+ +

Interop 2.0:

+ +
@ExportLibrary(value = InteropLibrary.class, receiverType = DynamicObject.class)
+public final class SLObjectType extends ObjectType {
+
+    public static final ObjectType SINGLETON = new SLObjectType();
+
+    private SLObjectType() {
+    }
+
+    @Override
+    public Class<?> dispatch() {
+        return SLObjectType.class;
+    }
+
+    @ExportMessage
+    static boolean hasMembers(DynamicObject receiver) {
+        return true;
+    }
+
+    @ExportMessage
+    static boolean removeMember(DynamicObject receiver, String member) throws UnknownIdentifierException {...}
+    // other exports omitted
+ }
+
+ +

Note the following differences:

+ +
    +
  • The object type can be reused as the export class.
  • +
  • The isInstance method no longer needs to be specified.
  • +
  • The new interop requires specifying the receiver type to DynamicObject.
  • +
+ +

Extending Interop

+ +

The languages implemented with Truffle rarely need to extend interop, but they might need to extend their own language specific protocol:

+ +

Interop 1.0:

+ +
    +
  • Add new KnownMessage subclass called FooBar.
  • +
  • Add a new method sendFooBar to ForeignAccess.
  • +
  • Add a new method to ForeignAccess.Factory: createFooBar.
  • +
  • Modify the interop annotation processor to generate the code for createFooBar.
  • +
+ +

Interop 2.0:

+ +
    +
  • Add a new method fooBar in InteropLibrary. Everything else is done automatically.
  • +
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/LanguageTutorial/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/LanguageTutorial/index.html new file mode 100644 index 0000000..c5f66a2 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/LanguageTutorial/index.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Implementing a New Language with Truffle

+ +

For an in-depth presentation on how to implement your language with Truffle, +watch this three-hour walkthrough presented at the +Conference on Programming Language Design and Implementation PLDI 2016.

+ +

+Youtube Video Player +

+ +

Download Slides

+ +

Next Steps:

+ + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Languages/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Languages/index.html new file mode 100644 index 0000000..98902b6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Languages/index.html @@ -0,0 +1,173 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Language Implementations

+ +

This page is intended to keep track of the growing number of language implementations and experiments on top of Truffle. +The following language implementations exist already:

+ +
    +
  • Espresso, a meta-circular Java bytecode interpreter. *
  • +
  • FastR, an implementation of GNU R. *
  • +
  • Graal.js, an ECMAScript 2020 compliant JavaScript implementation. *
  • +
  • GraalPy, an early-stage implementation of Python. *
  • +
  • grCUDA, a polyglot CUDA integration.
  • +
  • SimpleLanguage, a toy language implementation to demonstrate Truffle features.
  • +
  • SOMns, a Newspeak implementation for Concurrency Research.
  • +
  • Sulong, an LLVM bitcode interpreter. *
  • +
  • TRegex, a generic regular expression engine (internal, for use by other languages only). *
  • +
  • TruffleRuby, an implementation of Ruby. *
  • +
  • TruffleSOM, a SOM Smalltalk implementation.
  • +
  • TruffleSqueak, a Squeak/Smalltalk VM implementation and polyglot programming environment.
  • +
  • Yona, the reference implementation of a minimalistic, strongly and dynamically-typed, parallel and non-blocking, polyglot, strict, functional programming language.
  • +
  • Enso, an open source, visual language for data science that lets you design, prototype and develop any application by connecting visual elements together.
  • +
+ +

* Shipped as part of GraalVM.

+ +

Experiments

+ +
    +
  • BACIL, .NET CIL interpreter.
  • +
  • bf, an experimental Brainfuck programming language implementation.
  • +
  • brainfuck-jvm, another Brainfuck language implementation.
  • +
  • Cover, a Safe Subset of C++.
  • +
  • DynSem, a DSL for declarative specification of dynamic semantics of languages.
  • +
  • Heap Language, a tutorial showing the embedding of Truffle languages via interoperability.
  • +
  • hextruffe, an implementation of Hex.
  • +
  • LuaTruffle, an implementation of the Lua language.
  • +
  • Mozart-Graal, an implementation of the Oz programming language.
  • +
  • Mumbler, an experimental Lisp programming language.
  • +
  • PorcE, an Orc language implementation.
  • +
  • ProloGraal a Prolog language implementation supporting interoperability.
  • +
  • PureScript, a small, strongly-typed programming language.
  • +
  • Reactive Ruby, TruffleRuby meets Reactive Programming.
  • +
  • shen-truffle, a port of the Shen programming language.
  • +
  • TruffleBF, a completed Brainfuck programming language implementation, compiled to native-image.
  • +
  • TruffleMATE, a Smalltalk with a completely reified runtime system.
  • +
  • TrufflePascal, a Pascal interpreter.
  • +
  • ZipPy, a Python implementation.
  • +
+ +

Submit a pull request to add/remove from this list.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/NFI/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/NFI/index.html new file mode 100644 index 0000000..7ea692d --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/NFI/index.html @@ -0,0 +1,529 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Native Function Interface

+ +

Truffle includes a way to call native functions, called the Native Function Interface (NFI). +It is implemented as an internal language on top of Truffle that language implementors can access via the standard polyglot eval interface and Truffle interoperability. +NFI is intended to be used, for example, to implement a language’s FFI, or to call native runtime routines that are not available in Java.

+ +

NFI uses libffi. +On a standard JVM it calls it using JNI, and on GraalVM Native Image it uses system Java. +In the future it may be optimised by the Graal Compiler in native executables so that native calls are made directly from the compiled code.

+ +

Stability

+ +

The NFI is an internal language designed for language implementors. +It is not considered stable and the interface and behavior may change without warning. +It is not intended to be used directly by end-users.

+ +

Basic Concepts

+ +

The NFI is accessed via the polyglot interface of whatever language you are using. +This could be Java, or it could be a Truffle language. +This lets you use the NFI from both your Java language implementation code, or from your guest language to reduce the amount of Java that you need to write.

+ +

The entry point is the polyglot eval interface. +This runs a special DSL, and returns Truffle interoperability objects which can then expose more methods.

+ +

Below are some examples using Ruby’s polyglot interface, but any other JVM or a language implementation could be used instead.

+ +

Basic Example

+ +

Here is a basic working example, before going into the details:

+ +
library = Polyglot.eval('nfi', 'load "libSDL2.dylib"')  # load a library
+symbol = library['SDL_GetRevisionNumber']               # load a symbol from the library
+signature = Polyglot.eval('nfi', '():UINT32')           # prepare a signature
+function = signature.bind(symbol)                       # bind the symbol to the signature to create a function
+puts function.call # => 12373                           # call the function
+
+ +

Loading libaries

+ +

To load a library, a script written in the ‘nfi’ language DSL is evaluated. +It returns an object that represents the loaded library.

+ +
library = Polyglot.eval('nfi', '...load command...')
+
+ +

The load command can be any of these forms:

+ +
    +
  • default
  • +
  • load "filename"
  • +
  • load (flag | flag | ...) "filename"
  • +
+ +

The default command returns a pseudo-library that contains all symbols already loaded in +the process, equivalent to RTLD_DEFAULT in the Posix interface.

+ +

The load "filename" command loads a library from a file. +You are responsible for any cross-platform concerns about library naming conventions and load paths.

+ +

The load (flag | flag | ...) "filename" command allows you to specify flags to load the library. +For the default backend (backends will be described later), and when running on a Posix platform, the flags available are RTLD_GLOBAL, RTLD_LOCAL, RTLD_LAZY, and RTLD_NOW, which have the conventional Posix semantics. +The default is RTLD_NOW if neither RTLD_LAZY nor RTLD_NOW were specified.

+ +

Loading Symbols from Libraries

+ +

To load a symbol from a library, read the symbol as a property from the library object that was previously loaded.

+ +
symbol = library['symbol_name']
+
+ +

Producing Native Function Objects from Symbols

+ +

To get an executable object that you can call in order to invoke the native function, bind the symbol object that was previously loaded, by creating a signature object and calling the bind method on it. +The signature object needs to match the native function’s actual type signature.

+ +
signature = Polyglot.eval('nfi', '...signature...')
+function = signature.bind(symbol)
+
+ +

The format of the signature is (arg, arg, ...) : return, where arg and return are types.

+ +

Types can be one of the simple types:

+ +
    +
  • VOID
  • +
  • UINT8
  • +
  • SINT8
  • +
  • UINT16
  • +
  • SINT16
  • +
  • UINT32
  • +
  • SINT32
  • +
  • UINT64
  • +
  • SINT64
  • +
  • FLOAT
  • +
  • DOUBLE
  • +
  • POINTER
  • +
  • STRING
  • +
  • OBJECT
  • +
  • ENV
  • +
+ +

Array types are formed by placing another type in square brackets. +For example [UINT8]. These are C-style arrays.

+ +

Function pointer types are formed by writing a nested signature. +For example the signature of qsort would be (POINTER, UINT64, UINT64, (POINTER, POINTER) : SINT32) : VOID.

+ +

For a function with a signature with variadic arguments, you specify ... where the variadic arguments start, but then you must specify the actual types that you will be calling the function with. +You may therefore need to bind the same symbol multiple times in order to call it with different types or a different number of arguments. +For example, to call printf with %d %f you would use the type signature (STRING, ...SINT32, DOUBLE) : SINT32.

+ +

Type expressions can be nested arbitrarily deep.

+ +

Two additional special types, ENV and OBJECT, are described in the section on the native API, later in this document.

+ +

Types can be written in any case.

+ +

You are responsible for any mapping of types from a foreign language such as C into NFI types.

+ +

Calling Native Function Objects

+ +

To call a native function, execute it.

+ +
return_value = function.call(...arguments...)
+
+ +

Calling back from Native Code to Managed Functions

+ +

Using nested signatures, a function call can get function pointers as arguments. +The managed caller needs to pass a Polyglot executable object, that will be converted to a native function pointer. +When calling this function pointer from the native side, the execute message is sent to the Polyglot object.

+ +
void native_function(int32_t (*fn)(int32_t)) {
+  printf("%d\n", fn(15));
+}
+
+ +
signature = Polyglot.eval('nfi', '((SINT32):SINT32):VOID')
+native_function = signature.bind(library['native_function'])
+native_function.call(->(x) { x + 1 })
+
+ +

The arguments and return values of callback functions are converted the same as for regular function calls, with the conversion in the other direction, i.e., arguments are converted from native to managed, and return values are converted from managed to native.

+ +

Callback function pointers can themselves have function pointer arguments. +That works as you would expect: the function accepts a native function pointer as argument, and it is converted to a Truffle executable object. +Sending the execute message to that object calls the native function pointer, same as calling a regular NFI function.

+ +

Function pointer types are also supported as return types.

+ +

Combined Loading and Binding

+ +

You can optionally combine loading a library with loading symbols and binding them. +This is achieved with an extended load command, which then returns an object with the already bound functions as methods.

+ +

These two examples are equivalent:

+ +
library = Polyglot.eval('nfi', 'load libSDL2.dylib')
+symbol = library['SDL_GetRevisionNumber']
+signature = Polyglot.eval('nfi', '():UINT32')
+function = signature.bind(symbol)
+puts function.call # => 12373
+
+ +
library = Polyglot.eval('nfi', 'load libSDL2.dylib { SDL_GetRevisionNumber():UINT32; }')
+puts library.SDL_GetRevisionNumber # => 12373
+
+ +

The definitions in the curly braces {} can contain multiple function bindings, so that many functions can be loaded from a library at once.

+ +

Backends

+ +

The load command can be prefixed by with in order to select a specific NFI backend. +Multiple NFI backends are available. +The default is called native, and will be used if there is no with prefix, or the selected backend is not available.

+ +

Depending on the configuration of components you are running, available backends may include:

+
    +
  • native
  • +
  • llvm, which uses the GraalVM LLVM runtime to run the native code
  • +
+ +

Truffle NFI on Native Image

+ +

To build a native image that contains the Truffle NFI, it is sufficient to use the --language:nfi argument, or specify Requires = language:nfi in native-image.properties. +It is possible to select what implementation to use for the native backend using --language:nfi=<backend>.

+ +

Note that the --language:nfi=<backend> argument must come before any other arguments that might pull in the NFI as dependency via Requires = language:nfi. +The first instance of language:nfi wins and determines what backend will be built into the native image.

+ +

Available arguments for --language:nfi=<backend> are:

+
    +
  • libffi (the default)
  • +
  • none
  • +
+ +

Selecting the none native backend will effectively disable access to native functions using the Truffle NFI. +This will break users of the NFI that rely on native access (e.g. the GraalVM LLVM Runtime, unless used with --llvm.managed on EE).

+ +

Native API

+ +

The NFI can be used with unmodified, already compiled native code, but it can also be used with a Truffle-specific API being used by the native code.

+ +

The special type ENV adds an additional parameter TruffleEnv *env to the signature. +An additional simple type OBJECT translates to an opaque TruffleObject type.

+ +

The trufflenfi.h header file provides declarations for working with these types, that can then be used by the native code called through the NFI. +See trufflenfi.h for more documentation on this API.

+ +

Type Marshalling

+ +

This section describes in detail how argument values and return values are converted for all types in the function signature.

+ +

The following table shows the possible types in NFI signatures with their corresponding C language types on the native side, and what polyglot values these arguments map to on the managed side:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NFI typeC language typePolyglot value
VOIDvoidPolyglot object with isNull == true (only valid as return type).
SINT8/16/32/64int8/16/32/64_tPolyglot isNumber that fitsIn... the corresponding integer type.
UINT8/16/32/64uint8/16/32/64_tPolyglot isNumber that fitsIn... the corresponding integer type.
FLOATfloatPolyglot isNumber that fitsInFloat.
DOUBLEdoublePolyglot isNumber that fitsInDouble.
POINTERvoid *Polyglot object with isPointer == true or isNull == true.
STRINGchar * (zero-terminated UTF-8 string)Polyglot isString.
OBJECTTruffleObjectArbitrary object.
[type]type * (array of primitive)Java host primitive array.
(args):retret (*)(args) (function pointer type)Polyglot function with isExecutable == true.
ENVTruffleEnv *nothing (injected argument)
+ +

The following sections describe the type conversions in detail.

+ +

The type conversion behavior with function pointers can be slightly confusing, because the direction of the arguments is reversed. +When in doubt, always try to figure out in which direction arguments or return values flow, from managed to native or from native to managed.

+ +

VOID

+ +

This type is only allowed as return type, and is used to denote functions that do not return a value.

+ +

Since in the Polyglot API, all executable objects have to return a value, a Polyglot object with isNull == true will be returned from native functions that have a VOID return type.

+ +

The return value of managed callback functions with return type VOID will be ignored.

+ +

Primitive Numbers

+ +

The primitive number types are converted as you might expect. +The argument needs to be a Polyglot number, and its value needs to fit in the value range of the specified numeric type.

+ +

One thing to note is the handling of the unsigned integer types. +Even though the Polyglot API does not specify separate messages for values fitting in unsigned types, the conversion is still using the unsigned value ranges. +For example, the value 0xFF passed from native to managed through a return value of type SINT8 will result in a Polyglot number -1, which fitsInByte. +But the same value returned as UINT8 results in a Polyglot number 255, which does not fitsInByte.

+ +

When passing numbers from managed code to native code, the signedness of the number is ignored, only the bits of the number are relevant. +So for example, passing -1 to an argument of type UINT8 is allowed, and the result on the native side is 255, since it has the same bits as -1. +The other way round, passing 255 to an argument of type SINT8 is also allowed, and the result on the native side is -1.

+ +

Since in the current Polyglot API it is not possible to represent numbers outside of the signed 64-bit range, the UINT64 type is currently handled with signed semantics. +This is a known bug in the API, and will change in a future release.

+ +

POINTER

+ +

This type is a generic pointer argument. +On the native side, it does not matter what exact pointer type the argument is.

+ +

A polyglot object passed to POINTER arguments will be converted to a native pointer if possible (using the isPointer, asPointer and toNative messages as necessary). +An object with isNull == true will be passed as a native NULL.

+ +

POINTER return values will produce a polyglot object with isPointer == true. +The native NULL pointer will additionally have isNull == true.

+ +

STRING

+ +

This is a pointer type with special conversion semantics for strings.

+ +

Polyglot strings passed from managed to native using the STRING type will be converted to a zero-terminated UTF-8 encoded string. +For STRING arguments, the pointer is owned by the caller, and is guaranteed to stay alive for the duration of the call only. +The STRING values returned from managed function pointers to a native caller are also owned by the caller. +They have to be freed with free after use.

+ +

Polyglot pointer values or null values can also be passed to STRING arguments. +The semantics is the same as for POINTER arguments. +The user is responsible for ensuring that the pointer is a valid UTF-8 string.

+ +

The STRING values passed from native functions to managed code behave like POINTER return values, but in addition they have isString == true. +The user is responsible for the ownership of the pointer and it might be necessary to free the return value, depending on the semantics of the called native function. +After freeing the returned pointer, the returned polyglot string is invalid and reading it results in undefined behavior. +In that sense, the returned polyglot string is not a safe object, similar to a raw pointer. +It is recommented that the user of the NFI copies the returned string before passing it along to untrusted managed code.

+ +

OBJECT

+ +

This argument corresponds to the C type TruffleObject. +This type is defined in trufflenfi.h, and is an opaque pointer type. +A value of type TruffleObject represents a reference to an arbitrary managed object.

+ +

Native code can do nothing with values of type TruffleObject except pass them back to managed code, either through return values or passing them to callback function pointers.

+ +

The lifetime of TruffleObject references needs to be managed manually. +See the documentation in trufflenfi.h for API functions to manage the lifetime of TruffleObject references.

+ +

A TruffleObject passed as an argument is owned by the caller, and guaranteed to stay alive for the duration of the call. +A TruffleObject reference returned from a callback function pointer is owned by the caller, and needs to be freed after use. +Returning a TruffleObject from a native function does not transfer ownership (but there is an API function in trufflenfi.h to do that).

+ +

[...] (Native Primitive Arrays)

+ +

This type is only allowed as an argument from managed code to a native function, and only arrays of primitive numeric types are supported.

+ +

On the managed side, only Java host objects containing a Java primitive array are supported. +On the native side, the type is a pointer to the contents of the array. +It is the user’s responsibility to pass along the array length as a separate argument.

+ +

The pointer is valid for the duration of the native call only.

+ +

Modifications to the contents are propagated back to the Java array after returning from the call. +The effects of concurrent access to the Java array during the native call are unspecified.

+ +

(...):... (Function Pointer)

+ +

On the native side, a nested signature type corresponds to a function pointer with the given signature, calling back to managed code.

+ +

Polyglot executable objects passed from managed to native using a function pointer type are converted to a function pointer that can be called by the native code. +For function pointer arguments, the function pointer is owned by the caller, and is guaranteed to stay alive for the duration of the call only. +Function pointer return values are owned by the caller, and have to be freed manually. +See polyglot.h for API functions to manage the lifetime of function pointer values.

+ +

Polyglot pointer values or null values can also be passed to function pointer arguments. +The semantics is the same as for POINTER arguments. +The user is responsible for ensuring that the pointer is a valid function pointer.

+ +

Function pointer return types are the same as regular POINTER return types, but in addition they are already bound to the given signature type. +They support the execute message, and behave the same as regular NFI functions.

+ +

ENV

+ +

This type is a special argument of type TruffleEnv *. +It is only valid as argument type, not as a return type. +It is an injected argument on the native side, there is no corresponding argument on the managed side.

+ +

When used as argument type of a native function, the native function will get an environment pointer on this position. +That environment pointer can be used to call API functions (see trufflenfi.h). +The argument is injected, for example, if the signature is (SINT32, ENV, SINT32):VOID. +This function object is expected to be called with two integer arguments, and the corresponding native function will be called with three arguments: first the first real argument, then the injected ENV argument, and then the second real argument.

+ +

When the ENV type is used as an argument type for a function pointer parameter, that function pointer must be called with a valid NFI environment as an argument. +If the caller already has an environment, threading it through to callback function pointers is more efficient than calling them without an ENV argument.

+ +

Calling Convention

+ +

Native functions must use the system’s standard ABI. +There is currently no support for alternative ABIs.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/OnStackReplacement/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/OnStackReplacement/index.html new file mode 100644 index 0000000..da0167d --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/OnStackReplacement/index.html @@ -0,0 +1,301 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

On-Stack Replacement (OSR)

+ +

During execution, Truffle will schedule “hot” call targets for compilation. +Once a target is compiled, later invocations of the target can execute the compiled version. +However, an ongoing execution of a call target will not benefit from this compilation, since it cannot transfer execution to the compiled code. +This means that a long-running target can get “stuck” in the interpreter, harming warmup performance.

+ +

On-stack replacement (OSR) is a technique used in Truffle to “break out” of the interpreter, transferring execution from interpreted to compiled code. +Truffle supports OSR for both AST interpreters (i.e., ASTs with LoopNodes) and bytecode interpreters (i.e., nodes with dispatch loops). +In either case, Truffle uses heuristics to detect when a long-running loop is being interpreted and can perform OSR to speed up execution.

+ +

OSR for AST interpreters

+ +

Languages using standard Truffle APIs get OSR for free on Graal. +The runtime tracks the number of times a LoopNode (created using TruffleRuntime.createLoopNode(RepeatingNode)) executes in the interpreter. +Once the loop iterations exceed a threshold, the runtime considers the loop “hot”, and it will transparently compile the loop, poll for completion, and then call the compiled OSR target. +The OSR target uses the same Frame used by the interpreter. +When the loop exits in the OSR execution, it returns to the interpreted execution, which forwards the result.

+ +

See the LoopNode javadoc for more details.

+ +

OSR for bytecode interpreters

+ +

OSR for bytecode interpreters requires slightly more cooperation from the language. +A bytecode dispatch node typically looks something like the following:

+ +
class BytecodeDispatchNode extends Node {
+  @CompilationFinal byte[] bytecode;
+
+  ...
+
+  @ExplodeLoop(kind = ExplodeLoop.LoopExplosionKind.MERGE_EXPLODE)
+  Object execute(VirtualFrame frame) {
+    int bci = 0;
+    while (true) {
+      int nextBCI;
+      switch (bytecode[bci]) {
+        case OP1:
+          ...
+          nextBCI = ...
+          ...
+        case OP2:
+          ...
+          nextBCI = ...
+          ...
+        ...
+      }
+      bci = nextBCI;
+    }
+  }
+}
+
+ +

Unlike with AST interpreters, loops in a bytecode interpreter are often unstructured (and implicit). +Though bytecode languages do not have structured loops, backward jumps in the code (“back-edges”) tend to be a good proxy for loop iterations. +Thus, Truffle’s bytecode OSR is designed around back-edges and the destination of those edges (which often correspond to loop headers).

+ +

To make use of Truffle’s bytecode OSR, a language’s dispatch node should implement the BytecodeOSRNode interface. +This interface requires (at minimum) three method implementations:

+ +
    +
  • executeOSR(osrFrame, target, interpreterState): This method dispatches execution to the given target (i.e., bytecode index) using osrFrame as the current program state. The interpreterState object can pass any additional interpreter state needed to resume execution.
  • +
  • getOSRMetadata() and setOSRMetadata(osrMetadata): These methods proxy accesses to a field declared on the class. The runtime will use these accessors to maintain state related to OSR compilation (e.g., back-edge counts). The field should be annotated @CompilationFinal.
  • +
+ +

In the main dispatch loop, when the language hits a back-edge, it should invoke the provided BytecodeOSRNode.pollOSRBackEdge(osrNode) method to notify the runtime of the back-edge. +If the runtime deems the node eligible for OSR compilation, this method returns true.

+ +

If (and only if) pollOSRBackEdge returns true, the language can call BytecodeOSRNode.tryOSR(osrNode, target, interpreterState, beforeTransfer, parentFrame) to attempt OSR. +This method will request compilation starting from target, and once compiled code is available, a subsequent call can transparently invoke the compiled code and return the computed result. +We will discuss the interpreterState and beforeTransfer parameters shortly.

+ +

The example above can be refactored to support OSR as follows:

+ +
class BytecodeDispatchNode extends Node implements BytecodeOSRNode {
+  @CompilationFinal byte[] bytecode;
+  @CompilationFinal private Object osrMetadata;
+
+  ...
+
+  Object execute(VirtualFrame frame) {
+    return executeFromBCI(frame, 0);
+  }
+
+  Object executeOSR(VirtualFrame osrFrame, int target, Object interpreterState) {
+    return executeFromBCI(osrFrame, target);
+  }
+
+  Object getOSRMetadata() {
+    return osrMetadata;
+  }
+
+  void setOSRMetadata(Object osrMetadata) {
+    this.osrMetadata = osrMetadata;
+  }
+
+  @ExplodeLoop(kind = ExplodeLoop.LoopExplosionKind.MERGE_EXPLODE)
+  Object executeFromBCI(VirtualFrame frame, int bci) {
+    while (true) {
+      int nextBCI;
+      switch (bytecode[bci]) {
+        case OP1:
+          ...
+          nextBCI = ...
+          ...
+        case OP2:
+          ...
+          nextBCI = ...
+          ...
+        ...
+      }
+
+      if (nextBCI < bci) { // back-edge
+        if (BytecodeOSRNode.pollOSRBackEdge(this)) { // OSR can be tried
+          Object result = BytecodeOSRNode.tryOSR(this, nextBCI, null, null, frame);
+          if (result != null) { // OSR was performed
+            return result;
+          }
+        }
+      }
+      bci = nextBCI;
+    }
+  }
+}
+
+ +

A subtle difference with bytecode OSR is that the OSR execution continues past the end of the loop until the end of the call target. +Thus, execution does not need to continue in the interpreter once execution returns from OSR; the result can simply be forwarded to the caller.

+ +

The interpreterState parameter to tryOSR can contain any additional interpreter state required for execution. +This state is passed to executeOSR and can be used to resume execution. +For example, if an interpreter uses a data pointer to manage reads/writes, and it is unique for each target, this pointer can be passed in interpreterState. +It will be visible to the compiler and used in partial evaluation.

+ +

The beforeTransfer parameter to tryOSR is an optional callback which will be invoked before performing OSR. +Since tryOSR may or may not perform OSR, this parameter is a way to perform any actions before transferring to OSR code. +For example, a language may pass a callback to send an instrumentation event before jumping to OSR code.

+ +

The BytecodeOSRNode interface also contains a few hook methods whose default implementations can be overridden:

+ +
    +
  • copyIntoOSRFrame(osrFrame, parentFrame, target) and restoreParentFrame(osrFrame, parentFrame): Reusing the interpreted Frame inside OSR code is not optimal, because it escapes the OSR call target and prevents scalar replacement (for background on scalar replacement, see this paper). +When possible, Truffle will use copyIntoOSRFrame to copy the interpreted state (parentFrame) into the OSR Frame (osrFrame), and restoreParentFrame to copy state back into the parent Frame afterwards. +By default, both hooks copy each slot between the source and destination frames, but this can be overridden for finer control (e.g., to only copy over live variables). +If overridden, these methods should be written carefully to support scalar replacement.
  • +
  • prepareOSR(target): This hook gets called before compiling an OSR target. +It can be used to force any initialization to happen before compilation. +For example, if a field can only be initialized in the interpreter, prepareOSR can ensure it is initialized, so that OSR code does not deoptimize when trying to access it.
  • +
+ +

Bytecode-based OSR can be tricky to implement. Some debugging tips:

+ +
    +
  • Ensure that the metadata field is marked @CompilationFinal.
  • +
  • If a Frame with a given FrameDescriptor has been materialized before, Truffle will reuse the interpreter Frame instead of copying (if copying is used, any existing materialized Frame could get out of sync with the OSR Frame).
  • +
  • It is helpful to trace compilation and deoptimization logs to identify any initialization work which could be done in prepareOSR.
  • +
  • Inspecting the compiled OSR targets in IGV can be useful to ensure the copying hooks interact well with partial evaluation.
  • +
+ +

See the BytecodeOSRNode javadoc for more details.

+ +

Command-line options

+

There are two (experimental) options which can be used to configure OSR:

+
    +
  • engine.OSR: whether to perform OSR (default: true)
  • +
  • engine.OSRCompilationThreshold: the number of loop iterations/back-edges required to trigger OSR compilation (default: 100,352).
  • +
+ +

Debugging

+

OSR compilation targets are marked with <OSR> (or <OSR@n> where n is the dispatch target, in the case of bytecode OSR). +These targets can be seen and debugged using standard debugging tools like the compilation log and IGV. +For example, in the compilation log, a bytecode OSR entry may look something like:

+ +
[engine] opt done     BytecodeNode@2d3ca632<OSR@42>                               |AST    2|Tier 1|Time   21(  14+8   )ms|Inlined   0Y   0N|IR   161/  344|CodeSize   1234|Addr 0x7f3851f45c10|Src n/a
+
+ +

See Debugging for more details on debugging Graal compilations.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Optimizing/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Optimizing/index.html new file mode 100644 index 0000000..d11aa09 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Optimizing/index.html @@ -0,0 +1,813 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Optimizing Truffle Interpreters

+ +

This document discusses tools for optimizing or debugging Truffle interpreters for peak temporal performance.

+ +

Strategy

+ +
    +
  1. +

    Run with a profiler to sample the application and identify responsible compilation units. Use a sampling delay (--cpusampler.Delay=MILLISECONDS) to only profile after warmup. See the Profiling guide.

    +
  2. +
  3. +

    Understand what is being compiled and look for deoptimizations. Methods that are listed to run mostly in the interpreter likely have a problem with deoptimization.

    +
  4. +
  5. +

    Simplify the code as much as possible where it still shows the performance problem.

    +
  6. +
  7. +

    Enable performance warnings and list boundary calls.

    +
  8. +
  9. Dump the Graal graph of the responsible compilation unit and look at the phase After TruffleTier. +
      +
    1. Look at the Graal graphs at the phases After TruffleTier and After PartialEscape and check if it is what you would expect. +If there are nodes there that you don’t want to be there, think about how to guard against including them. +If there are more complex nodes there than you want, think about how to add specialisations that generate simpler code. +If there are nodes you think should be there in a benchmark that are not, think about how to make values dynamic so they are not optimized away.
    2. +
    +
  10. +
  11. +

    Search for Invoke nodes in the Graal IR. Invoke nodes that are not representing guest language calls should be specialized away. This may not be always possible, e.g., if the method does I/O.

    +
  12. +
  13. +

    Search for control flow splits (red lines) and investigate whether they result from control flow caused by the guest application or are just artifacts from the language implementation. The latter should be avoided if possible.

    +
  14. +
  15. Search for indirections in linear code (Load and LoadIndexed) and try to minimize the code. The less code that is on the hot-path the better.
  16. +
+ +

Truffle Compiler Options

+ +

A full list of the latest expert and internal options can be found in the Options guide.

+ +

Observing Compilations

+ +

This section provides an overview of most of the available command line options to observe compilations.

+ +

Note: Most options also require the additional --experimental-options flag set.

+ +

The --engine.TraceCompilation command prints a line each time a method is compiled:

+ +
[engine] opt done   id=244   EqualityConstraint.execute                         |Tier 1|Time   268( 220+47  )ms|AST   17|Inlined   0Y   2N|IR    238/   437|CodeSize    1874|Timestamp 758868036671903|Src octane-deltablue.js:528
+
+ +

Here is a quick overview of the information provided in these logs:

+
    +
  • id - Unique identifier of the call target.
  • +
  • Tier - For which compilation tier was the targed scheduled.
  • +
  • Time - How long did the compilation last, with separation between the Truffle tier (mainly partial evaluation) and the Graal Tiers.
  • +
  • AST - The targets non-trivial node count.
  • +
  • Inlined - How many calls were inlined and how many remained calls after inlining.
  • +
  • IR - Graal node count after partial evaluation and after compilation.
  • +
  • CodeSize - The size of the code generated for the call target.
  • +
  • Timestamp - The time when the event happened as reported by System.nanoTime().
  • +
  • Src - Abbreviated source section of the call target.
  • +
+ +

The --engine.TraceCompilationDetails command prints a line when compilation is queued, unqueued, started, or completed:

+ +
[engine] opt queued id=237   BinaryConstraint.output                            |Tier 1|Count/Thres         25/       25|Queue: Size    1 Change +1  Load  0.06 Time     0us|Timestamp 758865671350686|Src octane-deltablue.js:416
+[engine] opt start  id=237   BinaryConstraint.output                            |Tier 1|Priority        25|Rate 0.000000|Queue: Size    0 Change +0  Load  0.06 Time     0us|Timestamp 758865708273384|Src octane-deltablue.js:416
+[engine] opt queued id=239   OrderedCollection.size                             |Tier 1|Count/Thres         25/       25|Queue: Size    1 Change +1  Load  0.06 Time     0us|Timestamp 758865727664193|Src octane-deltablue.js:71
+[engine] opt queued id=52    Array.prototype.push                               |Tier 1|Count/Thres         25/       50|Queue: Size    2 Change +1  Load  0.13 Time     0us|Timestamp 758865744191674|Src <builtin>:1
+... more log ...
+[engine] opt start  id=239   OrderedCollection.size                             |Tier 1|Priority    181875|Rate 0.000001|Queue: Size   11 Change -1  Load  0.63 Time   575us|Timestamp 758866381654116|Src octane-deltablue.js:71
+[engine] opt done   id=237   BinaryConstraint.output                            |Tier 1|Time   717( 654+64  )ms|AST   19|Inlined   0Y   0N|IR    143/   220|CodeSize     882|Timestamp 758866435391354|Src octane-deltablue.js:416
+[engine] opt start  id=236   BinaryConstraint.input                             |Tier 1|Priority    144000|Rate 0.000001|Queue: Size   10 Change -1  Load  0.56 Time    48us|Timestamp 758866452554530|Src octane-deltablue.js:409
+... more log ...
+[engine] opt queued id=239   OrderedCollection.size                             |Tier 2|Count/Thres       8750/     8125|Queue: Size   18 Change +1  Load  0.81 Time     0us|Timestamp 758867756295139|Src octane-deltablue.js:71
+[engine] opt queued id=237   BinaryConstraint.output                            |Tier 2|Count/Thres       8499/     8750|Queue: Size   19 Change +1  Load  0.88 Time     0us|Timestamp 758867758263099|Src octane-deltablue.js:416
+[engine] opt start  id=244   EqualityConstraint.execute                         |Tier 1|Priority   2618289|Rate 0.000015|Queue: Size   19 Change -1  Load  0.88 Time   180us|Timestamp 758867767116908|Src octane-deltablue.js:528
+... more log ...
+[engine] opt done   id=246   OrderedCollection.at                               |Tier 2|Time    89(  80+9   )ms|AST   15|Inlined   0Y   0N|IR     50/   110|CodeSize     582|Timestamp 758873628915755|Src octane-deltablue.js:67
+[engine] opt start  id=237   BinaryConstraint.output                            |Tier 2|Priority    173536|Rate 0.000054|Queue: Size   18 Change -1  Load  0.94 Time    18us|Timestamp 758873629411012|Src octane-deltablue.js:416
+[engine] opt queued id=238   Planner.addPropagate                               |Tier 2|Count/Thres       9375/     8750|Queue: Size   19 Change +1  Load  0.88 Time     0us|Timestamp 758873663196884|Src octane-deltablue.js:696
+[engine] opt queued id=226   Variable.addConstraint                             |Tier 2|Count/Thres       8771/     9375|Queue: Size   20 Change +1  Load  0.94 Time     0us|Timestamp 758873665823697|Src octane-deltablue.js:556
+[engine] opt done   id=293   change                                             |Tier 1|Time   167( 130+37  )ms|AST   60|Inlined   0Y   6N|IR    576/  1220|CodeSize    5554|Timestamp 758873669483749|Src octane-deltablue.js:867
+[engine] opt start  id=270   Plan.execute                                       |Tier 2|Priority    157871|Rate 0.000072|Queue: Size   19 Change -1  Load  1.00 Time    17us|Timestamp 758873669912101|Src octane-deltablue.js:778
+[engine] opt done   id=237   BinaryConstraint.output                            |Tier 2|Time    58(  52+6   )ms|AST   19|Inlined   0Y   0N|IR    103/   181|CodeSize     734|Timestamp 758873687678394|Src octane-deltablue.js:416
+... more log ...
+[engine] opt unque. id=304   Date.prototype.valueOf                             |Tier 2|Count/Thres      80234/     3125|Queue: Size    4 Change  0  Load  0.31 Time     0us|Timestamp 758899904132076|Src <builtin>:1|Reason Target inlined into only caller
+
+ +

Here is a quick overview of the information added in these logs:

+
    +
  • Count/Thres - What is the call and loop count of the target and what is the threshold needed to add the compilation to the queue.
  • +
  • Queue: Size - How many compilations are in the compilation queue.
  • +
  • Queue: Change - How did this event impact the compilation queue (e.g. certain events can prune the queue of unneeded compilation tasks).
  • +
  • Queue: Load - A metric of whether the queue is over/under loaded. Normal load is represented with 1, less then 1 is underloaded and greater than 1 is overloaded.
  • +
  • Queue: Time - How long did the event take.
  • +
  • Reason - The runtime reported reason for the event.
  • +
+ +

The --engine.TraceCompilationAST command prints the Truffle AST for each compilation:

+ +
[engine] opt AST          OrderedCollection.size <split-57429b3a>                     |ASTSize      10/   10 |Calls/Thres   10559/    3 |CallsAndLoop/Thres   10559/ 1000
+  FunctionRootNode
+    body = FunctionBodyNode
+      body = DualNode
+        left = JSWriteCurrentFrameSlotNodeGen
+          rhsNode = JSPrepareThisNodeGen
+            operandNode = AccessThisNode
+        right = TerminalPositionReturnNode
+          expression = PropertyNode
+            target = PropertyNode
+              target = JSReadCurrentFrameSlotNodeGen
+              cache = PropertyGetNode
+                cacheNode = ObjectPropertyGetNode
+                  receiverCheck = ShapeCheckNode
+            cache = PropertyGetNode
+              cacheNode = ArrayLengthPropertyGetNode
+                receiverCheck = ShapeCheckNode
+                arrayLengthRead = ArrayLengthReadNodeGen
+
+ +

The --engine.TraceInlining command prints guest-language inlining decisions for each compilation:

+ +
[engine] inline start     Plan.execute                                                |call diff        0.00 |Recursion Depth      0 |Explore/inline ratio     1.00 |IR Nodes         2704 |Frequency        1.00 |Truffle Callees      5 |Forced          false |Depth               0
+[engine] Inlined            Plan.size                                                 |call diff     -203.75 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          175 |Frequency      101.88 |Truffle Callees      1 |Forced          false |Depth               1
+[engine] Inlined              OrderedCollection.size <split-e13c02e>                  |call diff     -101.88 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          157 |Frequency      101.88 |Truffle Callees      0 |Forced          false |Depth               2
+[engine] Inlined            Plan.constraintAt                                         |call diff     -201.75 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          206 |Frequency      100.88 |Truffle Callees      1 |Forced          false |Depth               1
+[engine] Inlined              OrderedCollection.at                                    |call diff     -100.88 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          232 |Frequency      100.88 |Truffle Callees      0 |Forced          false |Depth               2
+[engine] Inlined            ScaleConstraint.execute                                   |call diff       -0.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          855 |Frequency        0.00 |Truffle Callees      0 |Forced          false |Depth               1
+[engine] Inlined            EqualityConstraint.execute                                |call diff     -299.63 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          295 |Frequency       99.88 |Truffle Callees      2 |Forced          false |Depth               1
+[engine] Inlined              BinaryConstraint.output <split-1e163df7>                |call diff      -99.88 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          259 |Frequency       99.88 |Truffle Callees      0 |Forced          false |Depth               2
+[engine] Inlined              BinaryConstraint.input <split-2dfade22>                 |call diff      -99.88 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes          259 |Frequency       99.88 |Truffle Callees      0 |Forced          false |Depth               2
+[engine] Inlined            EditConstraint.execute                                    |call diff       -1.00 |Recursion Depth      0 |Explore/inline ratio      NaN |IR Nodes           22 |Frequency        1.00 |Truffle Callees      0 |Forced          false |Depth               1
+[engine] inline done      Plan.execute                                                |call diff        0.00 |Recursion Depth      0 |Explore/inline ratio     1.00 |IR Nodes         2704 |Frequency        1.00 |Truffle Callees      5 |Forced          false |Depth               0
+
+ +

The --engine.TraceSplitting command prints guest-language splitting decisions:

+ +
[engine] split   0-4310d43-1     Strength                                                    |ASTSize       6/    6 |Calls/Thres       2/    3 |CallsAndLoop/Thres       2/ 1000 |SourceSection /Users/christianhumer/graal/4dev/js-benchmarks/octane-deltablue.js~139:4062-4089
+[engine] split   1-4b0d79fc-1     Strength                                                    |ASTSize       6/    6 |Calls/Thres       2/    3 |CallsAndLoop/Thres       2/ 1000 |SourceSection /Users/christianhumer/graal/4dev/js-benchmarks/octane-deltablue.js~140:4119-4150
+
+ +

The --engine.TracePerformanceWarnings=(call|instanceof|store|all) command prints code which may not be ideal for performance. The call enables warnings when partial evaluation cannot inline the virtual runtime call. The instanceof enables warnings when partial evaluation cannot resolve virtual instanceof to an exact type. +The store enables warnings when the store location argument is not a partial evaluation constant:

+ +
[engine] perf warn        ScaleConstraint.execute                                     |Partial evaluation could not inline the virtual runtime call Virtual to HotSpotMethod<ConditionProfile.profile(boolean)> (167|MethodCallTarget).
+  Approximated stack trace for [167 | MethodCallTarget:    at com.oracle.truffle.js.nodes.control.IfNode.execute(IfNode.java:158)
+    at com.oracle.truffle.js.nodes.binary.DualNode.execute(DualNode.java:125)
+    at com.oracle.truffle.js.nodes.function.FunctionBodyNode.execute(FunctionBodyNode.java:73)
+    at com.oracle.truffle.js.nodes.function.FunctionRootNode.executeInRealm(FunctionRootNode.java:147)
+    at com.oracle.truffle.js.runtime.JavaScriptRealmBoundaryRootNode.execute(JavaScriptRealmBoundaryRootNode.java:93)
+    at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.executeRootNode(OptimizedCallTarget.java:503)
+    at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:480)
+
+ +

The --engine.CompilationStatistics command prints statistics on compilations at the end of the process:

+ +
[engine] Truffle runtime statistics for engine 1
+    Compilations                : 2763
+      Success                   : 2743
+      Temporary Bailouts        : 17
+        org.graalvm.compiler.core.common.CancellationBailoutException: Compilation cancelled.: 16
+        org.graalvm.compiler.core.common.RetryableBailoutException: Assumption invalidated while compiling code: IsolatedObjectConstant[Object]: 1
+      Permanent Bailouts        : 0
+      Failed                    : 0
+      Interrupted               : 3
+    Invalidated                 : 84
+        Unknown Reason          : 45
+        Profiled Argument Types : 22
+        validRootAssumption Split call node: 12
+        expression invalidatePropertyAssumption: 1
+        getTextPos invalidatePropertyAssumption: 1
+        X_OK invalidateAllPropertyAssumptions: 1
+        statements invalidatePropertyAssumption: 1
+        typed object location generalizing object type !class com.oracle.truffle.js.runtime.objects.Nullish => !class java.lang.Object Object@0[final=false][type=class com.oracle.truffle.js.runtime.objects.Nullish]: 1
+    Queues                      : 3410
+    Dequeues                    : 262
+        Split call node         : 124
+        Target inlined into only caller: 87
+        validRootAssumption Split call node: 28
+        null                    : 17
+        Profiled Argument Types : 6
+    Splits                      : 5581
+    Compilation Accuracy        : 0,969598
+    Queue Accuracy              : 0,923167
+    Compilation Utilization     : 2,685016
+    Remaining Compilation Queue : 409
+    Time to queue               : count=3410, sum=12895957640, min=       0, average=  3781805,76, max=11311217, maxTarget=Math.floor
+    Time waiting in queue       : count=2763, sum=1247424699, min=      40, average=   451474,74, max= 4762017, maxTarget=tryGetTypeFromEffectiveTypeNode
+  ---------------------------   :
+  AST node statistics           :
+    Truffle node count          : count=2752, sum=    348085, min=       1, average=      126,48, max=    5404, maxTarget=checkSignatureDeclaration
+      Trivial                   : count=2752, sum=    124817, min=       0, average=       45,36, max=    2707, maxTarget=visitEachChild
+      Non Trivial               : count=2752, sum=    223268, min=       1, average=       81,13, max=    3162, maxTarget=checkSignatureDeclaration
+        Monomorphic             : count=2752, sum=    217660, min=       1, average=       79,09, max=    3042, maxTarget=checkSignatureDeclaration
+        Polymorphic             : count=2752, sum=      4239, min=       0, average=        1,54, max=      71, maxTarget=checkSignatureDeclaration
+        Megamorphic             : count=2752, sum=      1369, min=       0, average=        0,50, max=      49, maxTarget=checkSignatureDeclaration
+    Truffle call count          : count=2752, sum=      7789, min=       0, average=        2,83, max=     110, maxTarget=forEachChild
+      Indirect                  : count=2752, sum=        29, min=       0, average=        0,01, max=       3, maxTarget=emitLeadingComments
+      Direct                    : count=2752, sum=      7760, min=       0, average=        2,82, max=     110, maxTarget=forEachChild
+        Dispatched              : count=2752, sum=      6285, min=       0, average=        2,28, max=     110, maxTarget=forEachChild
+        Inlined                 : count=2752, sum=      1475, min=       0, average=        0,54, max=      60, maxTarget=parseList <split-8113>
+        ----------              :
+        Cloned                  : count=2752, sum=         0, min=       0, average=        0,00, max=       0, maxTarget=Array.prototype.push
+        Not Cloned              : count=2752, sum=      7747, min=       0, average=        2,82, max=     110, maxTarget=forEachChild
+    Truffle loops               : count=2752, sum=       723, min=       0, average=        0,26, max=      12, maxTarget=pipelineEmitWithComments
+  ---------------------------   :
+  Compilation Tier 1            :
+    Compilation Rate            :    471075,74 bytes/second
+    Truffle Tier Rate           :   1273853,94 bytes/second
+    Graal Tier Rate             :    922948,70 bytes/second
+    Installation Rate           :   5640071,94 bytes/second
+    Time for compilation (us)   : count=2637, sum=  20538922, min=     727, average=     7788,75, max= 2154173, maxTarget=createPrinter
+      Truffle Tier (us)         : count=2623, sum=   7595366, min=     255, average=     2895,68, max=  306107, maxTarget=createPrinter
+      Graal Tier (us)           : count=2623, sum=  10483126, min=     378, average=     3996,62, max= 1665018, maxTarget=createPrinter
+      Code Installation (us)    : count=2623, sum=   1715472, min=      39, average=      654,01, max=  183048, maxTarget=createPrinter
+    Graal node count            :
+      After Truffle Tier        : count=2627, sum=   1089218, min=      88, average=      414,62, max=   12999, maxTarget=forEachChild
+      After Graal Tier          : count=2624, sum=   2190826, min=     127, average=      834,92, max=   63837, maxTarget=createPrinter
+    Graal compilation result    :
+      Code size                 : count=2623, sum=   9675388, min=     492, average=     3688,67, max=  238448, maxTarget=createPrinter
+      Total frame size          : count=2623, sum=    350080, min=      64, average=      133,47, max=    5328, maxTarget=createPrinter
+      Exception handlers        : count=2623, sum=      9316, min=       1, average=        3,55, max=     125, maxTarget=forEachChild
+      Infopoints                : count=2623, sum=    105619, min=       5, average=       40,27, max=    1837, maxTarget=forEachChild
+        CALL                    : count=2623, sum=    105619, min=       5, average=       40,27, max=    1837, maxTarget=forEachChild
+    Marks                       : count=2623, sum=     13115, min=       5, average=        5,00, max=       5, maxTarget=Array.prototype.push
+    Data references             : count=2623, sum=    185670, min=       9, average=       70,79, max=    4153, maxTarget=createPrinter
+  ---------------------------   :
+  Compilation Tier 2            :
+    Compilation Rate            :    141825,62 bytes/second
+    Truffle Tier Rate           :    397107,38 bytes/second
+    Graal Tier Rate             :    243094,58 bytes/second
+    Installation Rate           :   4122848,22 bytes/second
+    Time for compilation (us)   : count= 123, sum=  11418247, min=     745, average=    92831,28, max=  456707, maxTarget=checkSignatureDeclaration
+      Truffle Tier (us)         : count= 120, sum=   4077990, min=     288, average=    33983,25, max=  152158, maxTarget=WhileNode$WhileDoRepeatingNode@72d2eaac typescript-polybench.js:16129~ 'utf8ToBytes'<OSR>
+      Graal Tier (us)           : count= 120, sum=   6661604, min=     310, average=    55513,37, max=  343028, maxTarget=checkSignatureDeclaration
+      Code Installation (us)    : count= 120, sum=    392786, min=      42, average=     3273,22, max=   68576, maxTarget=JSArrayBufferView.@52bcd02
+    Graal node count            :
+      After Truffle Tier        : count= 125, sum=    331695, min=      13, average=     2653,56, max=   11989, maxTarget=checkSignatureDeclaration
+      After Graal Tier          : count= 120, sum=    432639, min=      46, average=     3605,33, max=   19199, maxTarget=checkSignatureDeclaration
+    Graal compilation result    :
+      Code size                 : count= 120, sum=   1619400, min=     192, average=    13495,00, max=   72744, maxTarget=checkSignatureDeclaration
+      Total frame size          : count= 120, sum=     25728, min=      48, average=      214,40, max=     768, maxTarget=bindEach <split-2459>
+      Exception handlers        : count= 120, sum=      1080, min=       0, average=        9,00, max=      84, maxTarget=checkSignatureDeclaration
+      Infopoints                : count= 120, sum=      7236, min=       3, average=       60,30, max=     401, maxTarget=checkSignatureDeclaration
+        CALL                    : count= 120, sum=      7236, min=       3, average=       60,30, max=     401, maxTarget=checkSignatureDeclaration
+    Marks                       : count= 120, sum=      1116, min=       5, average=        9,30, max=      35, maxTarget=checkIndexConstraintForProperty
+    Data references             : count= 120, sum=     25252, min=       4, average=      210,43, max=    1369, maxTarget=checkSignatureDeclaration
+
+ +

The --engine.CompilationStatisticDetails command prints histogram information on individual Graal nodes in addition to the previous compilation statistics:

+ +
  Graal nodes after Truffle tier                    :
+      FrameState                                    : count= 168, sum=   35502, min=       1, average=      211.32, max=    2048, maxTarget=deltaBlue
+      FixedGuardNode                                : count= 168, sum=   18939, min=       0, average=      112.73, max=    1048, maxTarget=change
+      LoadFieldNode                                 : count= 168, sum=   14432, min=       0, average=       85.90, max=     814, maxTarget=EditConstraint
+      ...
+  Graal nodes after Graal tier                      :
+      BeginNode                                     : count= 166, sum=   33333, min=       0, average=      200.80, max=    2110, maxTarget=change
+      FrameState                                    : count= 166, sum=   30591, min=       0, average=      184.28, max=    2393, maxTarget=MeasureDefault
+      AMD64AddressNode                              : count= 166, sum=   20072, min=       0, average=      120.92, max=    1960, maxTarget=MeasureDefault
+      ...
+
+ +

The --engine.TraceMethodExpansion=truffleTier command prints a tree of all expanded Java methods with statistics after each compilation:

+ +
[engine] Expansion tree for test after truffleTier:
+Name                                                                                Frequency | Count    Size  Cycles   Ifs Loops Invokes Allocs | Self Count  Size Cycles   Ifs Loops Invokes Allocs | IRNode ASTNode Lang:File:Line:Chars
+<root>                                                                                   1.00 |    64      72      42     1     1       0      1 |         34    20      0     0     0       0      0 |  -
+ OptimizedCallTarget.profiledPERoot(Object)                                              1.00 |    30      52      42     1     1       0      1 |          1     2      2     0     0       0      0 |    121
+  OptimizedCallTarget.injectArgumentsProfile(Object)                                     1.00 |     9      19      16     0     0       0      0 |          4     3      0     0     0       0      0 |      5
+   OptimizedCallTarget.unsafeCast(Object, Class, Z, Z, Z)                                1.00 |     1       0       0     0     0       0      0 |          1     0      0     0     0       0      0 |     10
+   OptimizedCallTarget.castArgumentsImpl(Object, Class)                                  1.00 |     4      16      16     0     0       0      0 |          4    16     16     0     0       0      0 |     12
+  OptimizedCallTarget.executeRootNode(VirtualFrame)                                      1.00 |    20      31      24     1     1       0      1 |          0     0      0     0     0       0      0 |
+   JavaScriptRealmBoundaryRootNode.execute(VirtualFrame)                                 1.00 |    20      31      24     1     1       0      1 |          1     1      0     0     0       0      0 |     34       0 js:test.js:1:0-100
+    JavaScriptRealmBoundaryRootNode.getRealm()                                           1.00 |     1       1       0     0     0       0      0 |          0     0      0     0     0       0      0 |              0 js:test.js:1:0-100
+     JSContext.getRealm()                                                                1.00 |     1       1       0     0     0       0      0 |          0     0      0     0     0       0      0 |
+      PolyglotReferences$AssumeSingleContext.get()                                       1.00 |     1       1       0     0     0       0      0 |          0     0      0     0     0       0      0 |
+       PolyglotReferences$WeakSingleContext.get()                                        1.00 |     1       1       0     0     0       0      0 |          1     1      0     0     0       0      0 |     37
+    FunctionRootNode.executeInRealm(VirtualFrame)                                        1.00 |    18      29      24     1     1       0      1 |          1     1      0     0     0       0      0 |     41       0 js:test.js:1:0-100
+     FunctionBodyNode.execute(VirtualFrame)                                              1.00 |    17      28      24     1     1       0      1 |          0     0      0     0     0       0      0 |              1 js:test.js:1:0-100
+      AbstractBlockNode.execute(VirtualFrame)                                            1.00 |    17      28      24     1     1       0      1 |          0     0      0     0     0       0      0 |
+       AbstractBlockNode.executeVoid(VirtualFrame, JavaScriptNode, I, I)                 1.00 |    16      20      16     1     1       0      0 |          1     1      0     0     0       0      0 |     50       2 js:test.js:1:16-100
+        WhileNode.executeVoid(VirtualFrame)                                              1.00 |    15      19      16     1     1       0      0 |          0     0      0     0     0       0      0 |              7 js:test.js:3:35-84
+         OptimizedOSRLoopNode.execute(VirtualFrame)                                    101.00 |    15      19      16     1     1       0      0 |          4     3      2     0     1       0      0 |     46       8 js:test.js:3:35-84
+          RepeatingNode.executeRepeatingWithValue(VirtualFrame)                          1.00 |    11      16      14     1     0       0      0 |          1     1      0     0     0       0      0 |    100       9 js:test.js:3:35-84
+           WhileNode$WhileDoRepeatingNode.executeRepeating(VirtualFrame)               101.00 |    10      15      14     1     0       0      0 |          3     2      1     1     0       0      0 |     63       9 js:test.js:3:35-84
+            AbstractRepeatingNode.executeCondition(VirtualFrame)                       101.00 |     1       1       1     0     0       0      0 |          0     0      0     0     0       0      0 |              9 js:test.js:3:35-84
+             StatementNode.executeConditionAsBoolean(VirtualFrame, JavaScriptNode)     101.00 |     1       1       1     0     0       0      0 |          0     0      0     0     0       0      0 |
+              JSLessThanNodeGen.executeBoolean(VirtualFrame)                           101.00 |     1       1       1     0     0       0      0 |          0     0      0     0     0       0      0 |             10 js:test.js:3:51-58
+               JSLessThanNodeGen.executeBoolean_int_int0(VirtualFrame, J)              101.00 |     1       1       1     0     0       0      0 |          0     0      0     0     0       0      0 |
+                JSLessThanNode.doInt(I, I)                                             101.00 |     1       1       1     0     0       0      0 |          1     1      1     0     0       0      0 |     59      10 js:test.js:3:51-58
+            AbstractRepeatingNode.executeBody(VirtualFrame)                            101.00 |     6      12      12     0     0       0      0 |          0     0      0     0     0       0      0 |              9 js:test.js:3:35-84
+             AbstractBlockNode.executeVoid(VirtualFrame)                               101.00 |     6      12      12     0     0       0      0 |          0     0      0     0     0       0      0 |
+              AbstractBlockNode.executeVoid(VirtualFrame, JavaScriptNode, I, I)        101.00 |     6      12      12     0     0       0      0 |          0     0      0     0     0       0      0 |             13 js:test.js:3:35-84
+               JSWriteCurrentFrameSlotNodeGen.executeVoid(VirtualFrame)                101.00 |     6      12      12     0     0       0      0 |          0     0      0     0     0       0      0 |             14 js:test.js:4:71-79
+                JSWriteCurrentFrameSlotNodeGen.executeInt(VirtualFrame)                101.00 |     3       6       6     0     0       0      0 |          0     0      0     0     0       0      0 |             14 js:test.js:4:71-79
+                 JSAddNodeGen.executeInt(VirtualFrame)                                 101.00 |     3       6       6     0     0       0      0 |          0     0      0     0     0       0      0 |
+                  JSAddNode.doInt(I, I)                                                101.00 |     3       6       6     0     0       0      0 |          0     0      0     0     0       0      0 |
+                   Math.addExact(I, I)                                                 100.00 |     3       6       6     0     0       0      0 |          3     6      6     0     0       0      0 |     75      15 js:test.js:4:71-74
+                LocalVarPostfixIncNodeGen.executeInt(VirtualFrame)                     101.00 |     3       6       6     0     0       0      0 |          0     0      0     0     0       0      0 |             18 js:test.js:3:60-63
+                 LocalVarPostfixIncNode.doInt(Frame)                                   101.00 |     3       6       6     0     0       0      0 |          0     0      0     0     0       0      0 |             18 js:test.js:3:60-63
+                  LocalVarIncNode$IncOp.doInt(I)                                       101.00 |     3       6       6     0     0       0      0 |          0     0      0     0     0       0      0 |
+                   Math.addExact(I, I)                                                 100.00 |     3       6       6     0     0       0      0 |          3     6      6     0     0       0      0 |     85
+       AbstractBlockNode.executeGeneric(VirtualFrame, JavaScriptNode, I, I)              1.00 |     1       8       8     0     0       0      1 |          0     0      0     0     0       0      0 |              2 js:test.js:1:16-100
+        ReturnNode$TerminalPositionReturnNode.execute(VirtualFrame)                      1.00 |     1       8       8     0     0       0      1 |          0     0      0     0     0       0      0 |             20 js:test.js:6:87-98
+         JSReadCurrentFrameSlotNodeGen.execute(VirtualFrame)                             1.00 |     1       8       8     0     0       0      1 |          0     0      0     0     0       0      0 |
+          Integer.valueOf(I)                                                             1.00 |     1       8       8     0     0       0      1 |          1     8      8     0     0       0      1 |    139      21 js:test.js:6:94-97
+
+ +

The --engine.TraceNodeExpansion=truffleTier command prints a tree of all expanded Truffle nodes with statistics after each compilation. +This view groups the method expansion tree by node id:

+ +
[engine] Expansion tree for test after truffleTier:
+Name                                                       Frequency | Count    Size  Cycles   Ifs Loops Invokes Allocs | Self Count  Size Cycles   Ifs Loops Invokes Allocs | IRNode ASTNode Lang:File:Line:Chars
+<call-root>                                                     1.00 |    64      72      42     1     1       0      1 |         44    41     18     0     0       0      0 |      0
+ FunctionRootNode                                               1.00 |    20      31      24     1     1       0      1 |          3     3      0     0     0       0      0 |     34       0 js:test.js:1:0-100
+  FunctionBodyNode                                              1.00 |    17      28      24     1     1       0      1 |          0     0      0     0     0       0      0 |              1 js:test.js:1:0-100
+   ExprBlockNode                                                1.00 |    17      28      24     1     1       0      1 |          1     1      0     0     0       0      0 |     50       2 js:test.js:1:16-100
+    WhileNode                                                   1.00 |    15      19      16     1     1       0      0 |          0     0      0     0     0       0      0 |              7 js:test.js:3:35-84
+     OptimizedOSRLoopNode$OptimizedDefaultOSRLoopNode         101.00 |    15      19      16     1     1       0      0 |          4     3      2     0     1       0      0 |     46       8 js:test.js:3:35-84
+      WhileNode$WhileDoRepeatingNode                          101.00 |    11      16      14     1     0       0      0 |          4     3      1     1     0       0      0 |    100       9 js:test.js:3:35-84
+       JSLessThanNodeGen                                      101.00 |     1       1       1     0     0       0      0 |          1     1      1     0     0       0      0 |     59      10 js:test.js:3:51-58
+       VoidBlockNode                                          101.00 |     6      12      12     0     0       0      0 |          0     0      0     0     0       0      0 |             13 js:test.js:3:35-84
+        JSWriteCurrentFrameSlotNodeGen                        101.00 |     6      12      12     0     0       0      0 |          0     0      0     0     0       0      0 |             14 js:test.js:4:71-79
+         JSAddNodeGen                                         100.00 |     3       6       6     0     0       0      0 |          3     6      6     0     0       0      0 |     75      15 js:test.js:4:71-74
+         LocalVarPostfixIncNodeGen                            100.00 |     3       6       6     0     0       0      0 |          3     6      6     0     0       0      0 |     85      18 js:test.js:3:60-63
+    ReturnNode$TerminalPositionReturnNode                       1.00 |     1       8       8     0     0       0      1 |          0     0      0     0     0       0      0 |             20 js:test.js:6:87-98
+     JSReadCurrentFrameSlotNodeGen                              1.00 |     1       8       8     0     0       0      1 |          1     8      8     0     0       0      1 |    139      21 js:test.js:6:94-97
+
+ +

The --engine.MethodExpansionStatistics=truffleTier command prints statistics on expanded Java methods during partial evaluation at the end of a run. +This can be useful to detect code that produces too many or certain Graal nodes unexpectedly:

+ +
[engine] Method expansion statistics after truffleTier:
+Name                                                                       Count IR Nodes (min avg max)        Size (min avg max)      Cycles (min avg max)       Ifs  Loops Invokes Allocs | Max IRNode ASTNode Unit:Lang:File:Line:Chars
+  <no-source-position>                                                         1      212 (212 212.0 212)       117 (117 117.0 117)         0 (0 0.0 0)             0      0       0      0 |          0         mandelbrot
+  OptimizedOSRLoopNode.execute(VirtualFrame)                                   4       13 (0 3.3 5)               9 (0 2.3 3)               6 (0 1.5 2)             0      3       0      0 |        172      60 mandelbrot:js:mandelbrot.js:68:2589-2888
+  Math.addExact(I, I)                                                          4       12 (3 3.0 3)              24 (6 6.0 6)              24 (6 6.0 6)             0      0       0      0 |        485     103 mandelbrot:js:mandelbrot.js:80:2874-2875
+  WhileNode$WhileDoRepeatingNode.executeRepeating(VirtualFrame)                4        9 (0 2.3 3)               6 (0 1.5 2)               3 (0 0.8 1)             3      0       0      0 |         88      17 mandelbrot:js:mandelbrot.js:57:2374-3431
+  JSTypes.intToDouble(I)                                                       7        7 (1 1.0 1)               7 (1 1.0 1)              24 (0 3.4 8)             0      0       0      0 |        144      41 mandelbrot:js:mandelbrot.js:62:2478-2486
+  OptimizedCallTarget.castArgumentsImpl(Object, Class)                         1        7 (7 7.0 7)              25 (25 25.0 25)           24 (24 24.0 24)          0      0       0      0 |         12         mandelbrot
+  JSWriteCurrentFrameSlotNodeGen.executeVoid(VirtualFrame)                     6        6 (0 1.0 3)               4 (0 0.7 2)               2 (0 0.3 1)             2      0       0      0 |        563      46 mandelbrot:js:mandelbrot.js:64:2519-2544
+  AbstractBlockNode.executeVoid(VirtualFrame, JavaScriptNode, I, I)            8        6 (0 0.8 4)               6 (0 0.8 4)               0 (0 0.0 0)             0      0       0      0 |        177      39 mandelbrot:js:mandelbrot.js:61:2459-3416
+  Math.multiplyExact(I, I)                                                     4        6 (1 1.5 2)              12 (2 3.0 4)              20 (4 5.0 6)             0      0       0      0 |        155      49 mandelbrot:js:mandelbrot.js:64:2529-2534
+  OptimizedCallTarget.injectArgumentsProfile(Object)                           1        4 (4 4.0 4)               3 (3 3.0 3)               0 (0 0.0 0)             0      0       0      0 |          5         mandelbrot
+  JSMultiplyNode.doDouble(D, D)                                                4        4 (1 1.0 1)               4 (1 1.0 1)               8 (2 2.0 2)             0      0       0      0 |        280      75 mandelbrot:js:mandelbrot.js:70:2657-2663
+  IfNode.executeVoid(VirtualFrame)                                             3        3 (0 1.0 3)               2 (0 0.7 2)               1 (0 0.3 1)             1      0       0      0 |        606     126 mandelbrot:js:mandelbrot.js:93:3240-3397
+  Math.subtractExact(I, I)                                                     1        3 (3 3.0 3)               6 (6 6.0 6)               6 (6 6.0 6)             0      0       0      0 |        589     129 mandelbrot:js:mandelbrot.js:93:3249-3257
+  JSSubtractNode.doDouble(D, D)                                                3        3 (1 1.0 1)               3 (1 1.0 1)               3 (1 1.0 1)             0      0       0      0 |        167      47 mandelbrot:js:mandelbrot.js:64:2528-2544
+  JSLessThanNode.doInt(I, I)                                                   3        3 (1 1.0 1)               3 (1 1.0 1)               3 (1 1.0 1)             0      0       0      0 |        187      62 mandelbrot:js:mandelbrot.js:68:2596-2602
+  JSAddNode.doDouble(D, D)                                                     3        3 (1 1.0 1)               3 (1 1.0 1)               3 (1 1.0 1)             0      0       0      0 |        263      67 mandelbrot:js:mandelbrot.js:69:2623-2639
+  JSDivideNode.doDouble(D, D)                                                  2        2 (1 1.0 1)               2 (1 1.0 1)              64 (32 32.0 32)          0      0       0      0 |        165      48 mandelbrot:js:mandelbrot.js:64:2528-2540
+  JSBitwiseXorNode.doInteger(I, I)                                             2        2 (1 1.0 1)               2 (1 1.0 1)               2 (1 1.0 1)             0      0       0      0 |        575     119 mandelbrot:js:mandelbrot.js:90:3170-3173
+  JSEqualNode.doInt(I, I)                                                      2        2 (1 1.0 1)               2 (1 1.0 1)               2 (1 1.0 1)             0      0       0      0 |        592     127 mandelbrot:js:mandelbrot.js:93:3244-3257
+  RepeatingNode.executeRepeatingWithValue(VirtualFrame)                        4        1 (0 0.3 1)               1 (0 0.3 1)               0 (0 0.0 0)             0      0       0      0 |        499      61 mandelbrot:js:mandelbrot.js:68:2589-2888
+  FunctionRootNode.executeInRealm(VirtualFrame)                                1        1 (1 1.0 1)               1 (1 1.0 1)               0 (0 0.0 0)             0      0       0      0 |         53       0 mandelbrot:js:mandelbrot.js:50:2279-3447
+  OptimizedCallTarget.profiledPERoot(Object)                                   1        1 (1 1.0 1)               2 (2 2.0 2)               2 (2 2.0 2)             0      0       0      0 |        737         mandelbrot
+  PolyglotReferences$WeakSingleContext.get()                                   1        1 (1 1.0 1)               1 (1 1.0 1)               0 (0 0.0 0)             0      0       0      0 |         41         mandelbrot
+  JSLeftShiftNode.doInteger(I, I)                                              1        1 (1 1.0 1)               1 (1 1.0 1)               1 (1 1.0 1)             0      0       0      0 |        619     134 mandelbrot:js:mandelbrot.js:94:3269-3277
+  Integer.intValue()                                                           1        1 (1 1.0 1)               2 (2 2.0 2)               2 (2 2.0 2)             0      0       0      0 |         50       4 mandelbrot:js:1:0-0
+  JSSubtractNode.doInt(I, I)                                                   2        1 (0 0.5 1)               1 (0 0.5 1)               1 (0 0.5 1)             0      0       0      0 |        940     136 mandelbrot:js:mandelbrot.js:94:3282-3295
+  JSLeftShiftConstantNode.doInteger(I)                                         1        1 (1 1.0 1)               1 (1 1.0 1)               1 (1 1.0 1)             0      0       0      0 |        527     107 mandelbrot:js:mandelbrot.js:83:2907-2922
+  JSSubtractNodeGen.executeDouble(VirtualFrame)                                3        1 (0 0.3 1)               1 (0 0.3 1)               0 (0 0.0 0)             0      0       0      0 |         33      47 mandelbrot:js:mandelbrot.js:64:2528-2544
+  JSReadCurrentFrameSlotNodeGen.executeInt(VirtualFrame)                       1        1 (1 1.0 1)               1 (1 1.0 1)               0 (0 0.0 0)             0      0       0      0 |         74      19 mandelbrot:js:mandelbrot.js:57:2381-2382
+  Integer.valueOf(I)                                                           1        1 (1 1.0 1)               8 (8 8.0 8)               8 (8 8.0 8)             0      0       0      1 |        939     154 mandelbrot:js:mandelbrot.js:105:3442-3445
+  JSBitwiseOrNode.doInteger(I, I)                                              1        1 (1 1.0 1)               1 (1 1.0 1)               1 (1 1.0 1)             0      0       0      0 |        532     106 mandelbrot:js:mandelbrot.js:83:2907-2931
+  JSGreaterThanNode.doDouble(D, D)                                             1        1 (1 1.0 1)               1 (1 1.0 1)               2 (2 2.0 2)             0      0       0      0 |        461      93 mandelbrot:js:mandelbrot.js:76:2800-2815
+  OptimizedCallTarget.unsafeCast(Object, Class, Z, Z, Z)                       1        1 (1 1.0 1)               0 (0 0.0 0)               0 (0 0.0 0)             0      0       0      0 |         10         mandelbrot
+  JavaScriptRealmBoundaryRootNode.execute(VirtualFrame)                        1        1 (1 1.0 1)               1 (1 1.0 1)               0 (0 0.0 0)             0      0       0      0 |         38         mandelbrot
+  JSLeftShiftConstantNodeGen.executeInt(VirtualFrame)                          1        1 (1 1.0 1)               1 (1 1.0 1)               0 (0 0.0 0)             0      0       0      0 |         36     107 mandelbrot:js:mandelbrot.js:83:2907-2922
+  DualNode.execute(VirtualFrame)                                               1        0 (0 0.0 0)               0 (0 0.0 0)               0 (0 0.0 0)             0      0       0      0 |                  2 mandelbrot:js:mandelbrot.js:50:2279-3447
+  ...
+
+ +

The --engine.NodeExpansionStatistics=truffleTier command prints statistics on expanded Truffle nodes during partial evaluation at the end of a run. +This can be useful to detect code that produces too many or certain Graal nodes unexpectedly. +It also shows individual specialization combinations as they were observed during compilation:

+ +
[engine] Node expansion statistics after truffleTier:
+Name                                                    Count IR Nodes (min avg max)        Size (min avg max)      Cycles (min avg max)       Ifs  Loops Invokes Allocs | Max IRNode ASTNode Unit:Lang:File:Line:Chars
+  <call-root>                                               1      226 (226 226.0 226)       148 (148 148.0 148)        26 (26 26.0 26)          0      0       0      0 |          0         mandelbrot
+  OptimizedOSRLoopNode$OptimizedDefaultOSRLoopNode          4       13 (0 3.3 5)               9 (0 2.3 3)               6 (0 1.5 2)             0      3       0      0 |        172      60 mandelbrot:js:mandelbrot.js:68:2589-2888
+  JSAddConstantRightNumberNodeGen                           4       12 (3 3.0 3)              24 (6 6.0 6)              24 (6 6.0 6)             0      0       0      0 |        485     103 mandelbrot:js:mandelbrot.js:80:2874-2875
+    [doInt(I)]                                              4       12 (3 3.0 3)              24 (6 6.0 6)              24 (6 6.0 6)             0      0       0      0 |        485     103 mandelbrot:js:mandelbrot.js:80:2874-2875
+  JSMultiplyNodeGen                                         6       11 (1 1.8 3)              17 (1 2.8 6)              28 (2 4.7 10)            0      0       0      0 |        155      49 mandelbrot:js:mandelbrot.js:64:2529-2534
+    [doIntALargerZero(I, I), doIntBLargerZero(I, I)]        2        6 (3 3.0 3)              12 (6 6.0 6)              20 (10 10.0 10)          0      0       0      0 |        155      49 mandelbrot:js:mandelbrot.js:64:2529-2534
+    [doDouble(D, D)]                                        4        5 (1 1.3 2)               5 (1 1.3 2)               8 (2 2.0 2)             0      0       0      0 |        275      75 mandelbrot:js:mandelbrot.js:70:2657-2663
+  WhileNode$WhileDoRepeatingNode                            4       10 (0 2.5 4)               7 (0 1.8 3)               3 (0 0.8 1)             3      0       0      0 |        499      61 mandelbrot:js:mandelbrot.js:68:2589-2888
+  JSSubtractNodeGen                                         5        9 (1 1.8 3)              12 (1 2.4 6)              10 (1 2.0 6)             0      0       0      0 |        589     129 mandelbrot:js:mandelbrot.js:93:3249-3257
+    [doDouble(D, D)]                                        3        5 (1 1.7 2)               5 (1 1.7 2)               3 (1 1.0 1)             0      0       0      0 |         33      47 mandelbrot:js:mandelbrot.js:64:2528-2544
+    [doInt(I, I)]                                           2        4 (1 2.0 3)               7 (1 3.5 6)               7 (1 3.5 6)             0      0       0      0 |        589     129 mandelbrot:js:mandelbrot.js:93:3249-3257
+  JSWriteCurrentFrameSlotNodeGen                           18        7 (0 0.4 3)               5 (0 0.3 2)               2 (0 0.1 1)             2      0       0      0 |        563      46 mandelbrot:js:mandelbrot.js:64:2519-2544
+  JSDivideNodeGen                                           2        5 (2 2.5 3)               5 (2 2.5 3)              88 (40 44.0 48)          0      0       0      0 |        158      48 mandelbrot:js:mandelbrot.js:64:2528-2540
+    [doDouble(D, D)]                                        2        5 (2 2.5 3)               5 (2 2.5 3)              88 (40 44.0 48)          0      0       0      0 |        158      48 mandelbrot:js:mandelbrot.js:64:2528-2540
+  VoidBlockNode                                             7        5 (0 0.7 4)               5 (0 0.7 4)               0 (0 0.0 0)             0      0       0      0 |        177      39 mandelbrot:js:mandelbrot.js:61:2459-3416
+  JSAddNodeGen                                              3        3 (1 1.0 1)               3 (1 1.0 1)               3 (1 1.0 1)             0      0       0      0 |        263      67 mandelbrot:js:mandelbrot.js:69:2623-2639
+    [doDouble(D, D)]                                        3        3 (1 1.0 1)               3 (1 1.0 1)               3 (1 1.0 1)             0      0       0      0 |        263      67 mandelbrot:js:mandelbrot.js:69:2623-2639
+  JSLessThanNodeGen                                         3        3 (1 1.0 1)               3 (1 1.0 1)               3 (1 1.0 1)             0      0       0      0 |        187      62 mandelbrot:js:mandelbrot.js:68:2596-2602
+    [doInt(I, I)]                                           3        3 (1 1.0 1)               3 (1 1.0 1)               3 (1 1.0 1)             0      0       0      0 |        187      62 mandelbrot:js:mandelbrot.js:68:2596-2602
+  IfNode                                                    4        3 (0 0.8 3)               2 (0 0.5 2)               1 (0 0.3 1)             1      0       0      0 |        606     126 mandelbrot:js:mandelbrot.js:93:3240-3397
+  JSGreaterThanNodeGen                                      1        2 (2 2.0 2)               2 (2 2.0 2)               2 (2 2.0 2)             0      0       0      0 |        460      93 mandelbrot:js:mandelbrot.js:76:2800-2815
+    [doDouble(D, D)]                                        1        2 (2 2.0 2)               2 (2 2.0 2)               2 (2 2.0 2)             0      0       0      0 |        460      93 mandelbrot:js:mandelbrot.js:76:2800-2815
+  JSBitwiseXorNodeGen                                       2        2 (1 1.0 1)               2 (1 1.0 1)               2 (1 1.0 1)             0      0       0      0 |        575     119 mandelbrot:js:mandelbrot.js:90:3170-3173
+    [doInteger(I, I)]                                       2        2 (1 1.0 1)               2 (1 1.0 1)               2 (1 1.0 1)             0      0       0      0 |        575     119 mandelbrot:js:mandelbrot.js:90:3170-3173
+  JSLeftShiftConstantNodeGen                                1        2 (2 2.0 2)               2 (2 2.0 2)               1 (1 1.0 1)             0      0       0      0 |         36     107 mandelbrot:js:mandelbrot.js:83:2907-2922
+    [doInteger(I)]                                          1        2 (2 2.0 2)               2 (2 2.0 2)               1 (1 1.0 1)             0      0       0      0 |         36     107 mandelbrot:js:mandelbrot.js:83:2907-2922
+  JSReadCurrentFrameSlotNodeGen                             2        2 (1 1.0 1)               9 (1 4.5 8)               8 (0 4.0 8)             0      0       0      1 |         74      19 mandelbrot:js:mandelbrot.js:57:2381-2382
+  JSEqualNodeGen                                            2        2 (1 1.0 1)               2 (1 1.0 1)               2 (1 1.0 1)             0      0       0      0 |        592     127 mandelbrot:js:mandelbrot.js:93:3244-3257
+    [doInt(I, I)]                                           2        2 (1 1.0 1)               2 (1 1.0 1)               2 (1 1.0 1)             0      0       0      0 |        592     127 mandelbrot:js:mandelbrot.js:93:3244-3257
+  FunctionRootNode                                          1        2 (2 2.0 2)               2 (2 2.0 2)               0 (0 0.0 0)             0      0       0      0 |         53       0 mandelbrot:js:mandelbrot.js:50:2279-3447
+  ExprBlockNode                                             1        1 (1 1.0 1)               1 (1 1.0 1)               0 (0 0.0 0)             0      0       0      0 |         69       5 mandelbrot:js:mandelbrot.js:50:2305-3447
+  JSBitwiseOrNodeGen                                        1        1 (1 1.0 1)               1 (1 1.0 1)               1 (1 1.0 1)             0      0       0      0 |        532     106 mandelbrot:js:mandelbrot.js:83:2907-2931
+    [doInteger(I, I)]                                       1        1 (1 1.0 1)               1 (1 1.0 1)               1 (1 1.0 1)             0      0       0      0 |        532     106 mandelbrot:js:mandelbrot.js:83:2907-2931
+  AccessIndexedArgumentNode                                 1        1 (1 1.0 1)               2 (2 2.0 2)               2 (2 2.0 2)             0      0       0      0 |         50       4 mandelbrot:js:1:0-0
+  JSLeftShiftNodeGen                                        2        1 (0 0.5 1)               1 (0 0.5 1)               1 (0 0.5 1)             0      0       0      0 |        619     134 mandelbrot:js:mandelbrot.js:94:3269-3277
+    [doInteger(I, I)]                                       1        1 (1 1.0 1)               1 (1 1.0 1)               1 (1 1.0 1)             0      0       0      0 |        619     134 mandelbrot:js:mandelbrot.js:94:3269-3277
+    <unknown>                                               1        0 (0 0.0 0)               0 (0 0.0 0)               0 (0 0.0 0)             0      0       0      0 |                134 mandelbrot:js:mandelbrot.js:94:3269-3277
+  FunctionBodyNode                                          1        0 (0 0.0 0)               0 (0 0.0 0)               0 (0 0.0 0)             0      0       0      0 |                  1 mandelbrot:js:mandelbrot.js:50:2279-3447
+  ReturnNode$TerminalPositionReturnNode                     1        0 (0 0.0 0)               0 (0 0.0 0)               0 (0 0.0 0)             0      0       0      0 |                153 mandelbrot:js:mandelbrot.js:105:3435-3445
+  DualNode                                                  1        0 (0 0.0 0)               0 (0 0.0 0)               0 (0 0.0 0)             0      0       0      0 |                  2 mandelbrot:js:mandelbrot.js:50:2279-3447
+  WhileNode                                                 4        0 (0 0.0 0)               0 (0 0.0 0)               0 (0 0.0 0)             0      0       0      0 |                 15 mandelbrot:js:mandelbrot.js:57:2374-3431
+  DirectBreakTargetNode                                     2        0 (0 0.0 0)               0 (0 0.0 0)               0 (0 0.0 0)             0      0       0      0 |                 14 mandelbrot:js:mandelbrot.js:50:2305-3447
+
+ +

The --engine.InstrumentBoundaries command prints, at the end of the process, information about runtime calls (@TruffleBoundary) made from compiled code. +These cause objects to escape (are black-boxes to further optimization) and should generally be minimized. +Also see the Branch Instrumentation guide for more details about instrumenting branches and boundaries.

+ +
Execution profile (sorted by hotness)
+=====================================
+  0: *******************************************************************************
+  1:
+
+com.oracle.truffle.js.nodes.binary.JSAddNode.doStringInt(JSAddNode.java:177) [bci: 2]
+[0] count = 22525269
+
+com.oracle.truffle.js.builtins.ConstructorBuiltins$ConstructDateNode.constructDateZero(ConstructorBuiltins.java:837) [bci: 6]
+[1] count = 69510
+
+ +

The --engine.InstrumentBranches command prints, at the end of the process, information of branch usage in compiled code:

+ +
Execution profile (sorted by hotness)
+=====================================
+  2: ***************
+  1: **************
+  5: *************
+  4: ************
+  3: *********
+ 10: **
+  8: *
+  9: *
+ 14: *
+ ...
+
+com.oracle.truffle.js.nodes.access.PropertyGetNode.getValueOrDefault(PropertyGetNode.java:301) [bci: 55]
+[2] state = BOTH(if=36486564#, else=44603498#)
+
+com.oracle.truffle.js.nodes.control.IfNode.execute(IfNode.java:158) [bci: 12]
+[1] state = BOTH(if=72572593#, else=1305851#)
+
+com.oracle.truffle.js.nodes.function.JSFunctionCallNode.executeCall(JSFunctionCallNode.java:233) [bci: 18]
+[5] state = BOTH(if=38703322#, else=32550439#)
+
+com.oracle.truffle.js.nodes.access.PropertyCacheNode$PrototypeShapeCheckNode.accept(PropertyCacheNode.java:364) [bci: 4]
+[4] state = ELSE(if=0#, else=64094316#)
+
+com.oracle.truffle.js.nodes.control.WhileNode$WhileDoRepeatingNode.executeRepeating(WhileNode.java:230) [bci: 5]
+[3] state = BOTH(if=44392142#, else=7096299#)
+...
+
+ +

The --engine.SpecializationStatistics command prints detailed histograms about Node classes and their usage of Truffle DSL specializations. +See Specialization Statistics for a tutorial on how to use it.

+ +

Note: Specialization statistics require a recompilation of the interpeter.

+ +
 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+| Name                                                                         Instances          Executions     Executions per instance
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+| JSWriteCurrentFrameSlotNodeGen                                               8 (17%)            18 (12%)        Min=         1 Avg=        2.25 Max=          5  MaxNode= test.js~5-7:76-128
+|   doBoolean <boolean>                                                          1 (13%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~4:52-71
+|   doInt <int>                                                                  1 (13%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5-7:76-128
+|   doSafeIntegerInt                                                             0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doSafeInteger                                                                0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doLong                                                                       0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doDouble                                                                     0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doObject                                                                     7 (88%)            16 (89%)        Min=         1 Avg=        2.29 Max=          5  MaxNode= test.js~5-7:76-128
+|     <DynamicObjectBasic>                                                         6 (86%)            12 (75%)        Min=         1 Avg=        2.00 Max=          5  MaxNode= test.js~5-7:76-128
+|     <IteratorRecord>                                                             1 (14%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~1-8:16-130
+|     <String>                                                                     2 (29%)             2 (13%)        Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5-7:76-128
+|     <Integer>                                                                    1 (14%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~6:105-123
+|   --------------------------------------------------------------------------------------------------------------------------------------------------------------------
+|   [doBoolean]                                                                  1 (13%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~4:52-71
+|   [doInt, doObject]                                                            1 (13%)             4 (22%)        Min=         4 Avg=        4.00 Max=          4  MaxNode= test.js~5-7:76-128
+|     doInt                                                                        1 (100%)            1 (25%)        Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5-7:76-128
+|     doObject                                                                     1 (100%)            3 (75%)        Min=         3 Avg=        3.00 Max=          3  MaxNode= test.js~5-7:76-128
+|   [doObject]                                                                   6 (75%)            13 (72%)        Min=         1 Avg=        2.17 Max=          5  MaxNode= test.js~5-7:76-128
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+| Name                                                                         Instances          Executions     Executions per instance
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+| JSReadCurrentFrameSlotNodeGen                                                8 (17%)            25 (17%)        Min=         1 Avg=        3.13 Max=          5  MaxNode= test.js~5-7:76-128
+|   doBoolean                                                                    0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doInt <no-args>                                                              1 (13%)             1 (4%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5:81-87
+|   doDouble                                                                     0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doObject <no-args>                                                           8 (100%)           24 (96%)        Min=         1 Avg=        3.00 Max=          5  MaxNode= test.js~5-7:76-128
+|   doSafeInteger                                                                0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   --------------------------------------------------------------------------------------------------------------------------------------------------------------------
+|   [doInt, doObject]                                                            1 (13%)             4 (16%)        Min=         4 Avg=        4.00 Max=          4  MaxNode= test.js~5:81-87
+|     doInt                                                                        1 (100%)            1 (25%)        Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5:81-87
+|     doObject                                                                     1 (100%)            3 (75%)        Min=         3 Avg=        3.00 Max=          3  MaxNode= test.js~5:81-87
+|   [doObject]                                                                   7 (88%)            21 (84%)        Min=         1 Avg=        3.00 Max=          5  MaxNode= test.js~5-7:76-128
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+ +

Controlling What Is Compiled

+ +

To make the best use of the former options, limit what is compiled to the methods that you are interested in.

+ +
    +
  • +

    --engine.CompileOnly=foo restricts compilation to methods with foo in their name. Use this in combination with returning a value or taking parameters to avoid code being compiled away.

    +
  • +
  • +

    --engine.CompileImmediately compiles methods as soon as they are run.

    +
  • +
  • +

    --engine.BackgroundCompilation=false compiles synchronously, which can simplify things.

    +
  • +
  • +

    --engine.Inlining=false disables inlining which can make code easier to understand.

    +
  • +
  • +

    --engine.OSR=false disables on-stack-replacement (compilation of the bodies of while loops for example) which can make code easier to understand.

    +
  • +
  • +

    --engine.Compilation=false turns off Truffle compilation all together.

    +
  • +
+ +

Debugging Deoptimizations

+ +

Sometimes compiled code deoptimizes (goes from Truffle compiled code back to the interpreter) unexpectedly. +These are some ways to investigate why the code is deoptimized. +It is very important for performance to avoid repeated deoptimizations.

+ +

The --engine.TraceCompilation option shows deoptimizations with an [engine] opt deopt prefix, which is useful to evaluate if many deoptimizations happen. +However, it shows no other details.

+ +

Materializing a frame with FrameInstance#getFrame(READ_WRITE|MATERIALIZE) from the stack causes deoptimizations (but no invalidation). +These deoptimizations can be traced with --engine.TraceDeoptimizeFrame.

+ +

When using native images, you need to build the native image with -H:+IncludeNodeSourcePositions to enable stack traces for deoptimizations. +These are disabled by default to save on image size.

+ +

On natives images, --engine.TraceTransferToInterpreter prints an accurate stack trace for any deoptimization, it is effectively the same as --vm.XX:+TraceDeoptimization --engine.NodeSourcePositions. +This is often the most efficient way to find where a deoptimization comes from thanks to the stracktrace.

+ +
[Deoptimization initiated
+    name: String#[]
+    sp: 0x7ffd7b992710  ip: 0x7f26a8d8079f
+    reason: TransferToInterpreter  action: InvalidateReprofile
+    debugId: 25  speculation: jdk.vm.ci.meta.SpeculationLog$NoSpeculationReason@13dbed9e
+    stack trace that triggered deoptimization:
+        at org.truffleruby.core.string.StringNodesFactory$StringSubstringPrimitiveNodeFactory$StringSubstringPrimitiveNodeGen.execute(StringNodesFactory.java:12760)
+        at org.truffleruby.core.string.StringNodes$GetIndexNode.substring(StringNodes.java:836)
+        at org.truffleruby.core.string.StringNodes$GetIndexNode.getIndex(StringNodes.java:650)
+        at org.truffleruby.core.string.StringNodesFactory$GetIndexNodeFactory$GetIndexNodeGen.execute(StringNodesFactory.java:1435)
+        at org.truffleruby.language.RubyCoreMethodRootNode.execute(RubyCoreMethodRootNode.java:53)
+        at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.executeRootNode(OptimizedCallTarget.java:632)
+        at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:603)
+[Deoptimization of frame
+    name: String#[]
+    sp: 0x7ffd7b992710  ip: 0x7f26a8d8079f
+    stack trace where execution continues:
+        at org.truffleruby.core.string.StringNodesFactory$StringSubstringPrimitiveNodeFactory$StringSubstringPrimitiveNodeGen.execute(StringNodesFactory.java:12760) bci 99  return address 0x4199a1d
+        at org.truffleruby.core.string.StringNodes$GetIndexNode.substring(StringNodes.java:836) bci 32 duringCall  return address 0x41608e0
+        at org.truffleruby.core.string.StringNodes$GetIndexNode.getIndex(StringNodes.java:650) bci 25 duringCall  return address 0x415f197
+        at org.truffleruby.core.string.StringNodesFactory$GetIndexNodeFactory$GetIndexNodeGen.execute(StringNodesFactory.java:1435) bci 109 duringCall  return address 0x4182391
+        at org.truffleruby.language.RubyCoreMethodRootNode.execute(RubyCoreMethodRootNode.java:53) bci 14 duringCall  return address 0x4239a29
+        at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.executeRootNode(OptimizedCallTarget.java:632) bci 9 duringCall  return address 0x3f1c4c9
+        at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:603) bci 37 duringCall  return address 0x3f1d965
+]
+]
+
+ +

On HotSpot, --engine.TraceTransferToInterpreter prints a stack trace only for explicit deoptimizations via CompilerDirectives.transferToInterpreterAndInvalidate() or CompilerDirectives.transferToInterpreter(). +The reported location can be incorrect if the deoptimization was caused by something else. +In that case it will report the stracktrace of the next CompilerDirectives.transferToInterpreter* call even though it is not the cause.

+ +
[engine] transferToInterpreter at
+  BinaryConstraint.output(../../../../4dev/js-benchmarks/octane-deltablue.js:416)
+    Constraint.satisfy(../../../../4dev/js-benchmarks/octane-deltablue.js:183)
+    Planner.incrementalAdd(../../../../4dev/js-benchmarks/octane-deltablue.js:597) <split-609bcfb6>
+    Constraint.addConstraint(../../../../4dev/js-benchmarks/octane-deltablue.js:165) <split-7d94beb9>
+    UnaryConstraint(../../../../4dev/js-benchmarks/octane-deltablue.js:219) <split-560348e6>
+    Function.prototype.call(<builtin>:1) <split-1df8b5b8>
+    EditConstraint(../../../../4dev/js-benchmarks/octane-deltablue.js:315) <split-23202fce>
+    ...
+  com.oracle.truffle.api.CompilerDirectives.transferToInterpreterAndInvalidate(CompilerDirectives.java:90)
+    com.oracle.truffle.js.nodes.access.PropertyCacheNode.deoptimize(PropertyCacheNode.java:1269)
+    com.oracle.truffle.js.nodes.access.PropertyGetNode.getValueOrDefault(PropertyGetNode.java:305)
+    com.oracle.truffle.js.nodes.access.PropertyGetNode.getValueOrUndefined(PropertyGetNode.java:191)
+    com.oracle.truffle.js.nodes.access.PropertyNode.executeWithTarget(PropertyNode.java:153)
+    com.oracle.truffle.js.nodes.access.PropertyNode.execute(PropertyNode.java:140)
+    ...
+
+ +

On HotSpot, --vm.XX:+UnlockDiagnosticVMOptions --vm.XX:+DebugNonSafepoints --vm.XX:+TraceDeoptimization prints all deoptimization events (but no stacktraces), whether the code is compiled by Truffle or conventional compilers. +The TraceDeoptimization option might require using a fastdebug JDK.

+ +
Uncommon trap   bci=9 pc=0x00000001097f2235, relative_pc=501, method=com.oracle.truffle.js.nodes.access.PropertyNode.executeInt(Ljava/lang/Object;Ljava/lang/Object;)I, debug_id=0
+Uncommon trap occurred in org.graalvm.compiler.truffle.runtime.OptimizedCallTarget::profiledPERoot compiler=JVMCI compile_id=2686 (JVMCI: installed code name=BinaryConstraint.output#2)  (@0x00000001097f2235) thread=5891 reason=transfer_to_interpreter action=reinterpret unloaded_class_index=-1 debug_id=0
+
+ +

Finally, on native images, --vm.XX:+TraceDeoptimizationDetails prints additional information:

+ +
[Deoptimization initiated
+    name: BinaryConstraint.output
+    sp: 0x7ffee7324d90  ip: 0x1126c51a8
+    reason: TransferToInterpreter  action: InvalidateReprofile
+    debugId: 3  speculation: jdk.vm.ci.meta.SpeculationLog$NoSpeculationReason@10f942aa0
+[Deoptimization of frame
+    name: BinaryConstraint.output
+    sp: 0x7ffee7324d90  ip: 0x1126c51a8
+    stack trace where execution continues:
+        at org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:475) bci 0  return address 0x10aab9e5e
+            org.graalvm.compiler.truffle.runtime.OptimizedCallTarget.profiledPERoot(OptimizedCallTarget.java:475)
+            bci: 0  deoptMethodOffset: 35524160  deoptMethod: 0x10aab9e40  return address: 0x10aab9e5e  offset: 0
+            slot 0  kind: Object  value: com.oracle.svm.truffle.api.SubstrateOptimizedCallTarget@0x112cbbaa0  offset: 64
+            slot 1  kind: Object  value: [Ljava.lang.Object;@0x1144a7db0  offset: 56
+]
+]
+
+ +

You might notice the presence of a debugId or debug_id in the output of these options. +This id might only be set if you also enable dumping, e.g., via --vm.Dgraal.Dump=Truffle:1 (see below). +In that case, the debug id will correspond to the id of a node in the IGV graph. +First, open the first phase of the relevant compilation. +That id can be searched via id=NUMBER in IGV’s Search in Nodes search box, +then selecting Open Search for node NUMBER in Node Searches window, +and then clicking the Search in following phases button.

+ +

Debugging Invalidations

+ +

Invalidations happen when a compiled CallTarget is thrown away.

+ +

The most common causes are:

+
    +
  • An explicit CompilerDirectives.transferToInterpreterAndInvalidate() (an internal invalidation)
  • +
  • One of the Assumption used by that CallTarget has been invalidated (an external invalidation). +Use --engine.TraceAssumptions to trace those with more details.
  • +
+ +

The --engine.TraceCompilation option also shows CallTarget invalidations with an [engine] opt inv. prefix.

+ +

Ideal Graph Visualizer

+ +

The Ideal Graph Visualizer (IGV) is a tool to understand Truffle ASTs and the Graal Compiler graphs.

+ +

A typical usage is to run with --vm.Dgraal.Dump=Truffle:1 --vm.Dgraal.PrintGraph=Network, which will show you Truffle ASTs, guest-language call graphs, and the Graal graphs as they leave the Truffle phase. +If the -Dgraal.PrintGraph=Network flag is omitted then the dump files are placed in the graal_dumps directory, which you should then open in IGV.

+ +

Use --vm.Dgraal.Dump=Truffle:2 to dump Graal graphs between each compiler phase.

+ +

C1 Visualizer

+ +

The C1 Visualizer is a tool to understand the Low Level IR (LIR), register allocation, and +code generation stages of GraalVM. It is available here.

+ +

A typical usage is --vm.Dgraal.Dump=:3. +Files are put into a graal_dumps directory which you should then open in the C1 Visualizer.

+ +

Disassembler

+ +

THe --vm.XX:+UnlockDiagnosticVMOptions --vm.XX:+PrintAssembly commands combination prints assembly code. +You will need to install hsdis using mx hsdis in graal/compiler, or manually install it into the current directory from here.

+ +

Typical usage is --vm.Dgraal.Dump --vm.Dgraal.PrintBackendCFG=true. Files are +put into a graal_dumps directory which you should then open in the +C1 Visualizer.

+ +

Combine with --vm.XX:TieredStopAtLevel=0 to disable compilation of runtime routines so that it’s easier to find your guest-language method.

+ +

Note: You can also look at assembly code in the C1 Visualizer.

+ +

These have been the most common and powerful ways to optimize or debug Truffle interpreters.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Options/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Options/index.html new file mode 100644 index 0000000..ac0fa20 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Options/index.html @@ -0,0 +1,339 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Options

+ +

You can list options from the command line with any language launcher:

+ +
language-launcher --help:expert
+
+ +

Or, for options only relevant for Truffle language implementers:

+ +
language-launcher --help:internal
+
+ +

In addition, the Graal Compiler options can be listed with:

+ +
language-launcher --jvm --vm.XX:+JVMCIPrintProperties
+
+

See graalvm_ce_jdk8_options for a list of Graal Compiler options.

+ +

Default Language Launcher Options

+ +
    +
  • --polyglot : Run with all other guest languages accessible.
  • +
  • --native : Run using the native launcher with limited Java access (default).
  • +
  • --jvm : Run on the Java Virtual Machine with Java access.
  • +
  • --vm.[option] : Pass options to the host VM. To see available options, use ‘–help:vm’.
  • +
  • --log.file=<String> : Redirect guest languages logging into a given file.
  • +
  • --log.[logger].level=<String> : Set language log level to OFF, SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST or ALL.
  • +
  • --help : Print this help message.
  • +
  • --help:vm : Print options for the host VM.
  • +
  • --version:graalvm : Print GraalVM version information and exit.
  • +
  • --show-version:graalvm : Print GraalVM version information and continue execution.
  • +
  • --help:languages : Print options for all installed languages.
  • +
  • --help:tools : Print options for all installed tools.
  • +
  • --help:expert : Print additional options for experts.
  • +
  • --help:internal : Print internal options for debugging language implementations and tools.
  • +
+ +

Expert Engine Options

+ +

These are advanced options for controlling the engine. +They are useful to users and language and tool implementers.

+ + +
    +
  • --engine.PreinitializeContexts= : Preinitialize language contexts for given languages.
  • +
  • --engine.RelaxStaticObjectSafetyChecks : On property accesses, the Static Object Model does not perform shape checks and uses unsafe casts
  • +
  • --engine.TraceStackTraceInterval=[1, inf) : Prints the stack trace for all threads for a time interval. By default 0, which disables the output.
  • +
  • --engine.DebugCacheCompileUseLastTier=true|false : If true uses the last tier instead of the first tier compiler. By default the last tier compiler is used (default: true).
  • +
  • --engine.BackgroundCompilation=true|false : Enable asynchronous truffle compilation in background threads (default: true)
  • +
  • --engine.Compilation=true|false : Enable or disable Truffle compilation.
  • +
  • --engine.CompilerIdleDelay=<ms> : Set the time in milliseconds an idle Truffle compiler thread will wait for new tasks before terminating. New compiler threads will be started once new compilation tasks are submitted. Select ‘0’ to never terminate the Truffle compiler thread. The option is not supported by all Truffle runtimes. On the runtime which doesn’t support it the option has no effect. default: 10000
  • +
  • --engine.CompilerThreads=[1, inf) : Manually set the number of compiler threads. By default, the number of compiler threads is scaled with the number of available cores on the CPU.
  • +
  • --engine.EncodedGraphCache=true|false : Cache encoded graphs across Truffle compilations to speed up partial evaluation. (default: true).
  • +
  • --engine.EncodedGraphCachePurgeDelay=<ms> : Delay, in milliseconds, after which the encoded graph cache is dropped when a Truffle compiler thread becomes idle (default: 10000).
  • +
  • --engine.FirstTierBackedgeCounts=true|false : Whether to emit look-back-edge counters in the first-tier compilations. (default: true)
  • +
  • --engine.FirstTierCompilationThreshold=[1, inf) : Number of invocations or loop iterations needed to compile a guest language root in first tier under normal compilation load.Might be reduced/increased when compilation load is low/high if DynamicCompilationThresholds is enabled. (default: 400).
  • +
  • --engine.FirstTierMinInvokeThreshold=[1, inf) : Minimum number of calls before a call target is compiled in the first tier (default: 1)
  • +
  • --engine.FirstTierUseEconomy=true|false : Whether to use the economy configuration in the first-tier compilations. (default: true)
  • +
  • --engine.ForceFrameLivenessAnalysis : Forces the frame clearing mechanism to be executed, even if Frame.clear() is not used.
  • +
  • --engine.Inlining=true|false : Enable automatic inlining of guest language call targets (default: true).
  • +
  • --engine.InliningExpansionBudget=[1, inf) : The base expansion budget for language-agnostic inlining (default: 12000).
  • +
  • --engine.InliningInliningBudget=[1, inf) : The base inlining budget for language-agnostic inlining (default: 12000)
  • +
  • --engine.InliningRecursionDepth=[0, inf) : Maximum depth for recursive inlining (default: 2).
  • +
  • --engine.InvalidationReprofileCount= : Delay compilation after an invalidation to allow for reprofiling. Deprecated: no longer has any effect.
  • +
  • --engine.LastTierCompilationThreshold=[1, inf) : Number of invocations or loop iterations needed to compile a guest language root in first tier under normal compilation load.Might be reduced/increased when compilation load is low/high if DynamicCompilationThresholds is enabled. (default: 10000).
  • +
  • --engine.MinInvokeThreshold=[1, inf) : Minimum number of calls before a call target is compiled (default: 3).
  • +
  • --engine.Mode=latency|throughput : Configures the execution mode of the engine. Available modes are ‘latency’ and ‘throughput’. The default value balances between the two.
  • +
  • --engine.MultiTier=true|false : Whether to use multiple Truffle compilation tiers by default. (default: true)
  • +
  • --engine.OSR=true|false : Enable automatic on-stack-replacement of loops (default: true).
  • +
  • --engine.PartialBlockCompilation=true|false : Enable partial compilation for BlockNode (default: true).
  • +
  • --engine.PartialBlockCompilationSize=[1, inf) : Sets the target non-trivial Truffle node size for partial compilation of BlockNode nodes (default: 3000).
  • +
  • --engine.PartialBlockMaximumSize=[1, inf) : Sets the maximum non-trivial Truffle node size for partial compilation of BlockNode nodes (default: 10000).
  • +
  • --engine.ReplaceReprofileCount= : Delay compilation after a node replacement. Deprecated: no longer has any effect.
  • +
  • --engine.SingleTierCompilationThreshold=[1, inf) : Minimum number of invocations or loop iterations needed to compile a guest language root when not using multi tier (default: 1000).
  • +
  • --engine.Splitting=true|false : Enable automatic duplication of compilation profiles (splitting) (default: true).
  • +
  • --engine.TraceCompilation : Print information for compilation results.
  • +
  • --engine.HostCallStackHeadRoom=[1, inf)<B>|<KB>|<MB>|<GB> : Stack space headroom for calls to the host.
  • +
  • --engine.IsolateMemoryProtection=true|false : Enable memory protection for the isolate.
  • +
  • --engine.IsolateOption.<key>=<value> : Isolate VM options. +
  • +
+ +

Internal Engine Options

+ +

These are internal options for debugging language implementations and tools.

+ + +
    +
  • --engine.DisableCodeSharing : Option to force disable code sharing for this engine, even if the context was created with an explicit engine. This option is intended for testing purposes only.
  • +
  • --engine.ForceCodeSharing : Option to force enable code sharing for this engine, even if the context was created with a bound engine. This option is intended for testing purposes only.
  • +
  • --engine.InstrumentExceptionsAreThrown=true|false : Propagates exceptions thrown by instruments. (default: true)
  • +
  • --engine.SafepointALot : Repeadly submits thread local actions and collects statistics about safepoint intervals in the process. Prints event and interval statistics when the context is closed for each thread. This option significantly slows down execution and is therefore intended for testing purposes only.
  • +
  • --engine.ShowInternalStackFrames : Show internal frames specific to the language implementation in stack traces.
  • +
  • --engine.SpecializationStatistics : Enables specialization statistics for nodes generated with Truffle DSL and prints the result on exit. In order for this flag to be functional -Atruffle.dsl.GenerateSpecializationStatistics=true needs to be set at build time. Enabling this flag and the compiler option has major implications on the performance and footprint of the interpreter. Do not use in production environments.
  • +
  • --engine.StaticObjectStorageStrategy=default|array-based|field-based : Set the storage strategy used by the Static Object Model. Accepted values are: [‘default’, ‘array-based’, ‘field-based’]
  • +
  • --engine.TraceCodeSharing : Enables printing of code sharing related information to the logger. This option is intended to support debugging language implementations.
  • +
  • --engine.TraceThreadLocalActions : Traces thread local events and when they are processed on the individual threads.Prints messages with the [engine] [tl] prefix.
  • +
  • --engine.TriggerUncaughtExceptionHandlerForCancel : Propagates cancel execution exception into UncaughtExceptionHandler. For testing purposes only.
  • +
  • --engine.UseConservativeContextReferences : Enables conservative context references. This allows invalid sharing between contexts. For testing purposes only.
  • +
  • --engine.UsePreInitializedContext=true|false : Use pre-initialized context when it’s available (default: true).
  • +
  • --engine.DebugCacheCompile=none|compiled|hot|aot|executed : Policy to use to to force compilation for executed call targets before persisting the engine. Possible values are: +
      +
    • ‘none’: No compilations will be persisted and existing compilations will be invalidated.
    • +
    • ‘compiled’: No compilations will be forced but finished compilations will be persisted.
    • +
    • ‘hot’: (default) All started compilations will be completed and then persisted.
    • +
    • ‘aot’: All started and AOT compilable roots will be forced to compile and persisted.
    • +
    • ‘executed’: All executed and all AOT compilable roots will be forced to compile.
    • +
    +
  • +
  • --engine.DebugCacheLoad : Prepares the engine to take the stored engine from the static field instead of reading it from disk.
  • +
  • --engine.DebugCachePreinitializeContext=true|false : Preinitialize a new context with all languages that support it and that were used during the run (default: true).
  • +
  • --engine.DebugCacheStore : Prepares the engine for caching and stores it a static field instead of writing it to disk.
  • +
  • --engine.DebugTraceCache : Enables tracing for the engine cache debug feature.
  • +
  • --engine.ArgumentTypeSpeculation=true|false : Speculate on arguments types at call sites (default: true)
  • +
  • --engine.CompilationExceptionsAreFatal : Treat compilation exceptions as fatal exceptions that will exit the application
  • +
  • --engine.CompilationExceptionsArePrinted : Prints the exception stack trace for compilation exceptions
  • +
  • --engine.CompilationExceptionsAreThrown : Treat compilation exceptions as thrown runtime exceptions
  • +
  • --engine.CompilationFailureAction=Silent|Print|Throw|Diagnose|ExitVM : Specifies the action to take when Truffle compilation fails.
    +The accepted values are:
    + Silent - Print nothing to the console.
    + Print - Print the exception to the console.
    + Throw - Throw the exception to caller.
    +Diagnose - Retry compilation with extra diagnostics enabled.
    + ExitVM - Exit the VM process.
  • +
  • --engine.CompilationStatisticDetails : Print additional more verbose Truffle compilation statistics at the end of a run.
  • +
  • --engine.CompilationStatistics : Print Truffle compilation statistics at the end of a run.
  • +
  • --engine.CompileAOTOnCreate : Compiles created call targets immediately with last tier. Disables background compilation if enabled.
  • +
  • --engine.CompileImmediately : Compile immediately to test Truffle compilation
  • +
  • --engine.CompileOnly=<name>,<name>,... : Restrict compilation to ‘,’-separated list of includes (or excludes prefixed with ‘~’). No restriction by default.
  • +
  • --engine.DynamicCompilationThresholds=true|false : Reduce or increase the compilation threshold depending on the size of the compilation queue (default: true).
  • +
  • --engine.DynamicCompilationThresholdsMaxNormalLoad=[1, inf) : The desired maximum compilation queue load. When the load rises above this value, the compilation thresholds are increased. The load is scaled by the number of compiler threads. (default: 10)
  • +
  • --engine.DynamicCompilationThresholdsMinNormalLoad=[1, inf) : The desired minimum compilation queue load. When the load falls bellow this value, the compilation thresholds are decreased. The load is scaled by the number of compiler threads (default: 10).
  • +
  • --engine.DynamicCompilationThresholdsMinScale=[0.0, inf) : The minimal scale the compilation thresholds can be reduced to (default: 0.1).
  • +
  • --engine.ExcludeAssertions=true|false : Exclude assertion code from Truffle compilations (default: true)
  • +
  • --engine.FirstTierInliningPolicy=<policy> : Explicitly pick a first tier inlining policy by name (None, TrivialOnly). If empty (default) the lowest priority policy (TrivialOnly) is chosen.
  • +
  • --engine.InlineAcrossTruffleBoundary : Enable inlining across Truffle boundary
  • +
  • --engine.InlineOnly=<name>,<name>,... : Restrict inlined methods to ‘,’-separated list of includes (or excludes prefixed with ‘~’). No restriction by default.
  • +
  • --engine.InliningPolicy=<policy> : Explicitly pick a inlining policy by name. If empty (default) the highest priority chosen by default.
  • +
  • --engine.InliningUseSize : Use the graph size as a cost model during inlining (default: false).
  • +
  • --engine.InstrumentBoundaries : Instrument Truffle boundaries and output profiling information to the standard output.
  • +
  • --engine.InstrumentBoundariesPerInlineSite : Instrument Truffle boundaries by considering different inlining sites as different branches.
  • +
  • --engine.InstrumentBranches : Instrument branches and output profiling information to the standard output.
  • +
  • --engine.InstrumentBranchesPerInlineSite : Instrument branches by considering different inlining sites as different branches.
  • +
  • --engine.InstrumentFilter=<method>,<method>,... : Method filter for host methods in which to add instrumentation.
  • +
  • --engine.InstrumentationTableSize=[1, inf) : Maximum number of instrumentation counters available (default: 10000).
  • +
  • --engine.IterativePartialEscape : Run the partial escape analysis iteratively in Truffle compilation.
  • +
  • --engine.MaximumGraalGraphSize=[1, inf) : Stop partial evaluation when the graph exceeded this size (default: 150000).
  • +
  • --engine.MaximumGraalNodeCount=[1, inf) : Stop partial evaluation when the graph exceeded this many nodes (default: 40000).
  • +
  • --engine.MaximumInlineNodeCount=[1, inf) : Ignore further truffle inlining decisions when the graph exceeded this many nodes (default: 150000).
  • +
  • --engine.MethodExpansionStatistics=true|false|peTier|truffleTier|lowTier|<tier>,<tier>,... : Print statistics on expanded Java methods during partial evaluation at the end of a run.Accepted values are:
    + true - Collect data for the default tier ‘truffleTier’.
    + false - No data will be collected.
    +Or one or multiple tiers separated by comma (e.g. truffleTier,lowTier):
    + peTier - After partial evaluation without additional phases applied.
    + truffleTier - After partial evaluation with additional phases applied.
    + lowTier - After low tier phases were applied.
  • +
  • --engine.NodeExpansionStatistics=true|false|peTier|truffleTier|lowTier|<tier>,<tier>,... : Print statistics on expanded Truffle nodes during partial evaluation at the end of a run.Accepted values are:
    + true - Collect data for the default tier ‘truffleTier’.
    + false - No data will be collected.
    +Or one or multiple tiers separated by comma (e.g. truffleTier,lowTier):
    + peTier - After partial evaluation without additional phases applied.
    + truffleTier - After partial evaluation with additional phases applied.
    + lowTier - After low tier phases were applied.
  • +
  • --engine.NodeSourcePositions : Enable node source positions in truffle partial evaluations.
  • +
  • --engine.OSRCompilationThreshold=[1, inf) : Number of loop iterations until on-stack-replacement compilation is triggered (default 100352).
  • +
  • --engine.OSRMaxCompilationReAttempts=[0, inf) : Number of compilation re-attempts before bailing out of OSR compilation for a given method (default 30). This number is an approximation of the acceptable number of deopts.
  • +
  • --engine.ParsePEGraphsWithAssumptions=true|false : Allow assumptions during parsing of seed graphs for partial evaluation. Disables the persistent encoded graph cache ‘engine.EncodedGraphCache’. (default: false).
  • +
  • --engine.PerformanceWarningsAreFatal= : Treat performance warnings as fatal occurrences that will exit the applications
  • +
  • --engine.PrintExpansionHistogram : Prints a histogram of all expanded Java methods.
  • +
  • --engine.PriorityQueue=true|false : Use the priority of compilation jobs in the compilation queue (default: true).
  • +
  • --engine.Profiling=true|false : Enable/disable builtin profiles in com.oracle.truffle.api.profiles. (default: true)
  • +
  • --engine.PropagateLoopCountToLexicalSingleCaller=true|false : Enables hotness propagation to lexical parent to lexically parent single callers.
  • +
  • --engine.PropagateLoopCountToLexicalSingleCallerMaxDepth=[0, inf) : How high to propagate call and loop count (hotness proxy) up a single caller chain to lexical scope parent.
  • +
  • --engine.ReturnTypeSpeculation=true|false : Speculate on return types at call sites (default: true)
  • +
  • --engine.SplittingAllowForcedSplits=true|false : Should forced splits be allowed (default: true)
  • +
  • --engine.SplittingDumpDecisions : Dumps to IGV information on polymorphic events
  • +
  • --engine.SplittingGrowthLimit=[0.0, inf) : Disable call target splitting if the number of nodes created by splitting exceeds this factor times node count (default: 1.5).
  • +
  • --engine.SplittingMaxCalleeSize=[1, inf) : Disable call target splitting if tree size exceeds this limit (default: 100)
  • +
  • --engine.SplittingMaxPropagationDepth=[1, inf) : Propagate info about a polymorphic specialize through maximum this many call targets (default: 5)
  • +
  • --engine.SplittingTraceEvents : Trace details of splitting events and decisions.
  • +
  • --engine.ThrowOnMaxOSRCompilationReAttemptsReached=true|false : Whether an AssertionError is thrown when the maximum number of OSR compilation attempts is reached for a given method (default ‘false’). This should only be set to ‘true’ in testing environments.
  • +
  • --engine.TraceAssumptions : Print stack trace on assumption invalidation
  • +
  • --engine.TraceCompilationAST : Print the entire AST after each compilation
  • +
  • --engine.TraceCompilationDetails : Print information for compilation queuing.
  • +
  • --engine.TraceCompilationPolymorphism : Print all polymorphic and generic nodes after each compilation
  • +
  • --engine.TraceDeoptimizeFrame : Print stack trace when deoptimizing a frame from the stack with FrameInstance#getFrame(READ_WRITE|MATERIALIZE).
  • +
  • --engine.TraceInlining : Print information for inlining decisions.
  • +
  • --engine.TraceInliningDetails : Print detailed information for inlining (i.e. the entire explored call tree).
  • +
  • --engine.TraceMethodExpansion=true|false|peTier|truffleTier|lowTier|<tier>,<tier>,... : Print a tree of all expanded Java methods with statistics after each compilation. Accepted values are:
    + true - Collect data for the default tier ‘truffleTier’.
    + false - No data will be collected.
    +Or one or multiple tiers separated by comma (e.g. truffleTier,lowTier):
    + peTier - After partial evaluation without additional phases applied.
    + truffleTier - After partial evaluation with additional phases applied.
    + lowTier - After low tier phases were applied.
  • +
  • --engine.TraceNodeExpansion=true|false|peTier|truffleTier|lowTier|<tier>,<tier>,... : Print a tree of all expanded Truffle nodes with statistics after each compilation. Accepted values are:
    + true - Collect data for the default tier ‘truffleTier’.
    + false - No data will be collected.
    +Or one or multiple tiers separated by comma (e.g. truffleTier,lowTier):
    + peTier - After partial evaluation without additional phases applied.
    + truffleTier - After partial evaluation with additional phases applied.
    + lowTier - After low tier phases were applied.
  • +
  • --engine.TracePerformanceWarnings=none|all|<perfWarning>,<perfWarning>,... : Print potential performance problems, Performance warnings are: call, instanceof, store, frame_merge, trivial.
  • +
  • --engine.TraceSplitting : Print information for splitting decisions.
  • +
  • --engine.TraceSplittingSummary : Used for debugging the splitting implementation. Prints splitting summary directly to stdout on shutdown
  • +
  • --engine.TraceStackTraceLimit=[1, inf) : Number of stack trace elements printed by TraceTruffleTransferToInterpreter, TraceTruffleAssumptions and TraceDeoptimizeFrame (default: 20).
  • +
  • --engine.TraceTransferToInterpreter : Print stack trace on transfer to interpreter.
  • +
  • --engine.TraversingCompilationQueue=true|false : Use a traversing compilation queue. (default: true)
  • +
  • --engine.TraversingQueueFirstTierBonus=[0.0, inf) : Controls how much of a priority should be given to first tier compilations (default 15.0).
  • +
  • --engine.TraversingQueueFirstTierPriority : Traversing queue gives first tier compilations priority.
  • +
  • --engine.TraversingQueueWeightingBothTiers=true|false : Traversing queue uses rate as priority for both tier. (default: true)
  • +
  • --engine.TreatPerformanceWarningsAsErrors=none|all|<perfWarning>,<perfWarning>,... : Treat performance warnings as error. Handling of the error depends on the CompilationFailureAction option value. Performance warnings are: call, instanceof, store, frame_merge, trivial.
  • +
  • --engine.IsolateLibrary=<path> : Path to the isolate library. +
  • +
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Profiling/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Profiling/index.html new file mode 100644 index 0000000..8880f6f --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Profiling/index.html @@ -0,0 +1,243 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Profiling Truffle Interpreters

+ +

There is no shortage of tools for profiling interpreters written using Truffle. +When running in JVM mode you can use standard JVM tooling such as VisualVM, Java Flight Recorder, and Oracle Developer Studio. When running in Native Image you can use callgrind from the Valgrind tool suite, and other system tools such as strace. +As a language running on GraalVM, other GraalVM tools can be used. +For a broad enough definition of profiling, you can also use the Ideal Graph Visualizer (IGV) and C1 Visualizer to inspect the compiler output.

+ +

This guide is less about how to use each tool and more about suggestions for extracting the most useful information from the tools, assuming a basic knowledge of their usage.

+ +

Profiling with CPU Sampler

+ +

The simplest way to profile the application level, for example, to find in which guest-language function(s) most of the time is spent, is to use CPU Sampler, which is part of the /tools suite and part of GraalVM. +Simply pass --cpusampler to your language launcher:

+ +
language-launcher --cpusampler --cpusampler.Delay=MILLISECONDS -e 'p :hello'
+
+ +

You probably want to use a sampling delay with --cpusampler.Delay=MILLISECONDS to only start profiling after warmup. That way, you can easily identify which functions get compiled and which do not and yet take a significant amount of time to execute.

+ +

See language-launcher --help:tools for more --cpusampler options.

+ +

Getting Compilation Data from the CPU Sampler

+ +

The CPU sampler does not show information about time spent in compiled code. +This was, at least in part, motivated by the introduction of multi-tier compilation where “compiled code” was not descriptive enough. +Using the --cpusampler.ShowTiers option allows users to control whether they wish to see compilation data at all, as well as to specify exactly which compilation tiers should be considered in the report. +For example, adding --cpusampler.ShowTiers=true will show all the compilation tiers encountered during execution as shown bellow.

+ +
-----------------------------------------------------------------------------------------------------------------------------------------------------------
+Sampling Histogram. Recorded 553 samples with period 10ms.
+  Self Time: Time spent on the top of the stack.
+  Total Time: Time spent somewhere on the stack.
+  T0: Percent of time spent in interpreter.
+  T1: Percent of time spent in code compiled by tier 1 compiler.
+  T2: Percent of time spent in code compiled by tier 2 compiler.
+-----------------------------------------------------------------------------------------------------------------------------------------------------------
+Thread[main,5,main]
+ Name              ||             Total Time    |   T0   |   T1   |   T2   ||              Self Time    |   T0   |   T1   |   T2   || Location
+-----------------------------------------------------------------------------------------------------------------------------------------------------------
+ accept            ||             4860ms  87.9% |  31.1% |  18.3% |  50.6% ||             4860ms  87.9% |  31.1% |  18.3% |  50.6% || ../primes.js~13-22:191-419
+ :program          ||             5530ms 100.0% | 100.0% |   0.0% |   0.0% ||              360ms   6.5% | 100.0% |   0.0% |   0.0% || ../primes.js~1-46:0-982
+ next              ||             5150ms  93.1% |  41.7% |  39.4% |  18.8% ||              190ms   3.4% | 100.0% |   0.0% |   0.0% || ../primes.js~31-37:537-737
+ DivisibleByFilter ||              190ms   3.4% |  89.5% |  10.5% |   0.0% ||              100ms   1.8% |  80.0% |  20.0% |   0.0% || ../primes.js~7-23:66-421
+ AcceptFilter      ||               30ms   0.5% | 100.0% |   0.0% |   0.0% ||               20ms   0.4% | 100.0% |   0.0% |   0.0% || ../primes.js~1-5:0-63
+ Primes            ||               40ms   0.7% | 100.0% |   0.0% |   0.0% ||                0ms   0.0% |   0.0% |   0.0% |   0.0% || ../primes.js~25-38:424-739
+-----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+ +

Alternatively --cpusampler.ShowTiers=0,2 will only show interpreted time and time spent in tier two compiled code, as shown bellow.

+ +
-----------------------------------------------------------------------------------------------------------------------------------------
+Sampling Histogram. Recorded 620 samples with period 10ms.
+  Self Time: Time spent on the top of the stack.
+  Total Time: Time spent somewhere on the stack.
+  T0: Percent of time spent in interpreter.
+  T2: Percent of time spent in code compiled by tier 2 compiler.
+-----------------------------------------------------------------------------------------------------------------------------------------
+Thread[main,5,main]
+ Name              ||             Total Time    |   T0   |   T2   ||              Self Time    |   T0   |   T2   || Location
+-----------------------------------------------------------------------------------------------------------------------------------------
+ accept            ||             5510ms  88.9% |  30.9% |  52.3% ||             5510ms  88.9% |  30.9% |  52.3% || ../primes.js~13-22:191-419
+ :program          ||             6200ms 100.0% | 100.0% |   0.0% ||              320ms   5.2% | 100.0% |   0.0% || ../primes.js~1-46:0-982
+ next              ||             5870ms  94.7% |  37.3% |  20.6% ||              190ms   3.1% |  89.5% |  10.5% || ../primes.js~31-37:537-737
+ DivisibleByFilter ||              330ms   5.3% | 100.0% |   0.0% ||              170ms   2.7% | 100.0% |   0.0% || ../primes.js~7-23:66-421
+ AcceptFilter      ||               20ms   0.3% | 100.0% |   0.0% ||               10ms   0.2% | 100.0% |   0.0% || ../primes.js~1-5:0-63
+ Primes            ||               20ms   0.3% | 100.0% |   0.0% ||                0ms   0.0% |   0.0% |   0.0% || ../primes.js~25-38:424-739
+-----------------------------------------------------------------------------------------------------------------------------------------
+
+ +

Creating a Flame Graph from CPU Sampler

+ +

The histogram output from CPUSampler can be quite large, making it difficult to analyze. +Additionally, as a flat format it is nto possible to analyze a call graph as that information simply is not encoded in the output. +A flame graph shows the entire call graph. +Its structure makes it considerably simpler to see where the application time is being spent.

+ +

Creating the flame graph is a multi-stage process. First, we need to profile the application with the JSON formatter:

+ +
language-launcher --cpusampler --cpusampler.SampleInternal --cpusampler.Output=json -e 'p :hello' > simple-app.json
+
+ +

Use the --cpusampler.SampleInternal=true option if you want to profile internal sources, such as standard library functions.

+ +

The JSON formatter encodes call graph information that isn’t available in the histogram format. +To make a flame graph out of this output, however, we need to transform it into a format that folds the call stack samples into single lines. +This can be done using stackcollapse-graalvm.rb from Benoit Daloze’s fork of FlameGraph.

+ +

If you have not yet, you should clone this fork of FlameGraph into the parent directory. +Now you can run the script to transform the output and pipe it into the script that will generate the SVG data:

+ +
../FlameGraph/stackcollapse-graalvm.rb simple-app.json | ../FlameGraph/flamegraph.pl > simple-app.svg
+
+ +

At this point, you should open the SVG file in a Chromium-based web browser. +Your system might have a different image manipulation application configured as the default application for SVG files. +While loading the file in such an application may render a graph, it likely will not handle the interactive components of the flame graph. Firefox may work as well, but Chromium-based browsers currently seem to have better support and performance for the flame graph files.

+ +

Profiling with Oracle Developer Studio

+ +

Oracle Developer Studio includes a +Performance Analyzer that can be used with GraalVM. +Developer Studio can be downloaded from OTN and the current version at time of writing (12.6) provides a perpetual no-cost license for production use and the development of commercial applications.

+ +

Using the Developer Studio Performance Analyser is straightforward. Include the path to the Developer Studio binaries in your PATH and then prefix your normal command-line with collect. +For example:

+ +
collect js mybenchmark.js
+
+ +

On completion an “experiment” (.er) directory will have been created containing the profiling data for the command execution, test.1.er by default. +To view the profiling results, use the analyzer tool:

+ +
analyzer test.1.er
+
+ +

The analyzer GUI allows you to view the captured profiling information in several different ways, e.g., the timeline of your application, a flat function list, the call tree, a flame graph, etc. +There is also a command-line tool, er_print, which can be used for outputting the profiling information in textual form, for further analysis.

+ +

For full details, see the Performance Analyzer documentation.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Safepoint/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Safepoint/index.html new file mode 100644 index 0000000..51ed4d9 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/Safepoint/index.html @@ -0,0 +1,300 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Language Safepoint Tutorial

+ +

As of 21.1 Truffle has support for guest language safepoints. +Truffle safepoints allow to interrupt the guest language execution to perform thread local actions submitted by a language or tool. +A safepoint is a location during the guest language execution where the state is consistent and other operations can read its state.

+ +

This replaces previous instrumentation or assumption-based approaches to safepoints, which required the code to be invalidated for a thread local action to be performed. +The new implementation uses fast thread local checks and callee register saved stub calls to optimize for performance and keep the overhead minimal. +This means that for every loop back-edge and method exit we perform an additional non-volatile read which can potentially lead to slight slow-downs.

+ +

Use Cases

+ +

Common use-cases of Truffle language safepoints are:

+ +
    +
  • Cancellation, requested exit or interruptions during guest language execution. The stack is unwound by submitting a thread local action.
  • +
  • Reading the current stack trace information for other threads than the currently executing thread.
  • +
  • Enumerating all object references active on the stack.
  • +
  • Running a guest signal handler or guest finalizer on a given thread.
  • +
  • Implement guest languages that expose a safepoint mechanism as part of their development toolkit.
  • +
  • Debuggers evaluating expressions in languages that do not support execution on multiple threads.
  • +
+ +

Language Support

+ +

Safepoints are explicitly polled by invoking the TruffleSafepoint.poll(Node) method. +A Truffle guest language implementation must ensure that a safepoint is polled repeatedly within a constant time interval. +For example, a single arithmetic expression completes within a constant number of CPU cycles. +However, a loop that summarizes values over an array uses a non-constant time dependent on the actual array size. +This typically means that safepoints are best polled at the end of loops and at the end of function or method calls to cover recursion. +In addition, any guest language code that blocks the execution, like guest language locks, need to use the TruffleSafepoint.setBlocked(Interrupter) API to allow cooperative polling of safepoints while the thread is waiting.

+ +

Please read more details on what steps language implementations need to take to support thread local actions in the javadoc.

+ +

Thread Local Actions

+ +

Languages and instruments can submit actions using their environment.

+ +

Usage example:

+ +

+Env env; // language or instrument environment
+
+env.submitThreadLocal(null, new ThreadLocalAction(true /*side-effecting*/, true /*synchronous*/) {
+     @Override
+     protected void perform(Access access) {
+         assert access.getThread() == Thread.currentThread();
+     }
+});
+
+
+ +

Read more in the javadoc.

+ +

Current Limitations

+ +

There is currently no way to run thread local actions while the thread is executing in boundary annotated methods unless the method cooperatively polls safepoints or uses the blocking API. +Unfortunately it is not always possible to cooperatively poll safepoints, for example, if the code currently executes third party native code. +A future improvement will allow to run code for other threads while they are blocked. +This is one of the reasons why it is recommended to use ThreadLocalAction.Access.getThread() instead of directly using Thread.currentThread(). +When the native call returns it needs to wait for any thread local action that is currently executing for this thread. +This will enable to collect guest language stack traces from other threads while they are blocked by uncooperative native code. +Currently the action will be performed on the next safepoint location when the native code returns.

+ +

Tooling for Debugging

+ +

There are several debug options available:

+ +

Excercise safepoints with SafepointALot

+ +

SafepointALot is a tool to exercise every safepoint of an application and collect statistics.

+ +

If enabled with the --engine.SafepointALot option it prints the statistics on the cpu time interval between safepoints at the end of an execution.

+ +

For example, running:

+ +
graalvm/bin/js --engine.SafepointALot js-benchmarks/harness.js -- octane-deltablue.js
+
+ +

Prints the following output to the log on context close:

+ +
DeltaBlue: 540
+[engine] Safepoint Statistics
+  --------------------------------------------------------------------------------------
+   Thread Name         Safepoints | Interval     Avg              Min              Max
+  --------------------------------------------------------------------------------------
+   main                  48384054 |            0.425 us           0.1 us       44281.1 us
+  -------------------------------------------------------------------------------------
+   All threads           48384054 |            0.425 us           0.1 us       42281.1 us
+
+ +

It is recommended for guest language implementations to try to stay below 1ms on average. +Note that precise timing can depend on CPU and interruptions by the GC. +Since GC times are included in the safepoint interval times, it is expected that the maximum is close to the maximum GC interruption time. +Future versions of this tool will be able to exclude GC interruption times from this statistic.

+ +

Trace thread local actions

+ +

The option --engine.TraceThreadLocalActions allows to trace all thread local actions of any origin.

+ +

Example output:

+ +
[engine] [tl] submit                 0  thread[main]                action[SampleAction$8@5672f0d1]     all-threads[alive=4]        side-effecting     asynchronous
+[engine] [tl]   perform-start        0  thread[pool-1-thread-410]   action[SampleAction$8@5672f0d1]
+[engine] [tl]   perform-start        0  thread[pool-1-thread-413]   action[SampleAction$8@5672f0d1]
+[engine] [tl]   perform-start        0  thread[pool-1-thread-412]   action[SampleAction$8@5672f0d1]
+[engine] [tl]   perform-done         0  thread[pool-1-thread-413]   action[SampleAction$8@5672f0d1]
+[engine] [tl]   perform-done         0  thread[pool-1-thread-410]   action[SampleAction$8@5672f0d1]
+[engine] [tl]   perform-start        0  thread[pool-1-thread-411]   action[SampleAction$8@5672f0d1]
+[engine] [tl]   perform-done         0  thread[pool-1-thread-412]   action[SampleAction$8@5672f0d1]
+[engine] [tl]   perform-done         0  thread[pool-1-thread-411]   action[SampleAction$8@5672f0d1]
+[engine] [tl] done                   0  thread[pool-1-thread-411]   action[SampleAction$8@5672f0d1]
+
+ +

Printing guest and host stack frames every time interval.

+ +

The option --engine.TraceStackTraceInterval=1000 allows to set the time interval in milliseconds to repeatedly print the current stack trace. +Note that the stack trace is printed on the next safepoint poll and therefore might not be accurate.

+ +
graalvm/bin/js --engine.TraceStackTraceInterval=1000 js-benchmarks/harness.js -- octane-deltablue.js
+
+ +

Prints the following output:

+ +
[engine] Stack Trace Thread main: org.graalvm.polyglot.PolyglotException
+	at <js> BinaryConstraint.chooseMethod(octane-deltablue.js:359-381:9802-10557)
+	at <js> Constraint.satisfy(octane-deltablue.js:176:5253-5275)
+	at <js> Planner.incrementalAdd(octane-deltablue.js:597:16779-16802)
+	at <js> Constraint.addConstraint(octane-deltablue.js:165:4883-4910)
+	at <js> UnaryConstraint(octane-deltablue.js:219:6430-6449)
+	at <js> StayConstraint(octane-deltablue.js:297:8382-8431)
+	at <js> chainTest(octane-deltablue.js:817:23780-23828)
+	at <js> deltaBlue(octane-deltablue.js:883:25703-25716)
+	at <js> MeasureDefault(harness.js:552:20369-20383)
+	at <js> BenchmarkSuite.RunSingleBenchmark(harness.js:614:22538-22550)
+	at <js> RunNextBenchmark(harness.js:340:11560-11614)
+	at <js> RunStep(harness.js:141:5673-5686)
+	at <js> BenchmarkSuite.RunSuites(harness.js:160:6247-6255)
+	at <js> runBenchmarks(harness.js:686-688:24861-25023)
+	at <js> main(harness.js:734:26039-26085)
+	at <js> :program(harness.js:783:27470-27484)
+	at org.graalvm.polyglot.Context.eval(Context.java:348)
+	at com.oracle.truffle.js.shell.JSLauncher.executeScripts(JSLauncher.java:347)
+	at com.oracle.truffle.js.shell.JSLauncher.launch(JSLauncher.java:88)
+	at org.graalvm.launcher.AbstractLanguageLauncher.launch(AbstractLanguageLauncher.java:124)
+	at org.graalvm.launcher.AbstractLanguageLauncher.launch(AbstractLanguageLauncher.java:71)
+	at com.oracle.truffle.js.shell.JSLauncher.main(JSLauncher.java:73)
+
+[engine] Stack Trace Thread main: org.graalvm.polyglot.PolyglotException
+	at <js> EqualityConstraint.execute(octane-deltablue.js:528-530:14772-14830)
+	at <js> Plan.execute(octane-deltablue.js:781:22638-22648)
+	at <js> chainTest(octane-deltablue.js:824:24064-24077)
+	at <js> deltaBlue(octane-deltablue.js:883:25703-25716)
+	at <js> MeasureDefault(harness.js:552:20369-20383)
+	at <js> BenchmarkSuite.RunSingleBenchmark(harness.js:614:22538-22550)
+	at <js> RunNextBenchmark(harness.js:340:11560-11614)
+	at <js> RunStep(harness.js:141:5673-5686)
+	at <js> BenchmarkSuite.RunSuites(harness.js:160:6247-6255)
+	at <js> runBenchmarks(harness.js:686-688:24861-25023)
+	at <js> main(harness.js:734:26039-26085)
+	at <js> :program(harness.js:783:27470-27484)
+	at org.graalvm.polyglot.Context.eval(Context.java:348)
+	at com.oracle.truffle.js.shell.JSLauncher.executeScripts(JSLauncher.java:347)
+	at com.oracle.truffle.js.shell.JSLauncher.launch(JSLauncher.java:88)
+	at org.graalvm.launcher.AbstractLanguageLauncher.launch(AbstractLanguageLauncher.java:124)
+	at org.graalvm.launcher.AbstractLanguageLauncher.launch(AbstractLanguageLauncher.java:71)
+	at com.oracle.truffle.js.shell.JSLauncher.main(JSLauncher.java:73)
+
+ +

Further Reading

+ +

Daloze, Benoit, Chris Seaton, Daniele Bonetta, and Hanspeter Mössenböck. +“Techniques and applications for guest-language safepoints.” +In Proceedings of the 10th Workshop on Implementation, Compilation, Optimization of Object-Oriented Languages, Programs and Systems, pp. 1-10. 2015.

+ +

https://dl.acm.org/doi/abs/10.1145/2843915.2843921

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/SpecializationHistogram/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/SpecializationHistogram/index.html new file mode 100644 index 0000000..4019515 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/SpecializationHistogram/index.html @@ -0,0 +1,232 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Specialization Histogram

+ +

This guide explains how to use the --engine.SpecializationStatistics option.

+ +

The specialization histogram requires Truffle DSL nodes to be generated in a special way. +So if you use the plain specialization histogram option it will just print the following:

+ +
js --engine.SpecializationStatistics test.js
+
+[engine] Specialization histogram:
+No specialization statistics data was collected. Either no node with @Specialization annotations was executed or the interpreter was not compiled with -Atruffle.dsl.GenerateSpecializationStatistics=true e.g as parameter to the javac tool.
+
+

Follow the advice of the error and recompile our interpreter. +For mx users this is as simple as:

+ +
mx build -c -A-Atruffle.dsl.GenerateSpecializationStatistics=true
+
+ +

After the rebuild, the specialization statistics are ready to be used. +Make sure that your IDE does not recompile the sources automatically in the meantime. +In this tutorial, a simple test.js script will be used:

+ +
function test() {
+  var array = [42, "", {}, []]
+
+  var globalVar = true;
+  for (element of array) {
+    globalVar = element;
+  }
+}
+test();
+
+ +

Now the specialization statistics need to be enabled, in this example using the JavaScript launcher of GraalVM:

+ +
js --experimental-options --engine.SpecializationStatistics test.js
+
+ +

After the script is executed a histogram for each class will be printed. +The histograms will be ordered by the sum of executions of each node, whereas the most frequently used node class will be printed last.

+ +

These are some of the histograms printed when executing test.js: +(Note: The output is likely already outdated.)

+ +
 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+| Name                                                                         Instances          Executions     Executions per instance
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+| JSWriteCurrentFrameSlotNodeGen                                               8 (17%)            18 (12%)        Min=         1 Avg=        2.25 Max=          5  MaxNode= test.js~5-7:76-128
+|   doBoolean <boolean>                                                          1 (13%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~4:52-71
+|   doInt <int>                                                                  1 (13%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5-7:76-128
+|   doSafeIntegerInt                                                             0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doSafeInteger                                                                0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doLong                                                                       0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doDouble                                                                     0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doObject                                                                     7 (88%)            16 (89%)        Min=         1 Avg=        2.29 Max=          5  MaxNode= test.js~5-7:76-128
+|     <DynamicObjectBasic>                                                         6 (86%)            12 (75%)        Min=         1 Avg=        2.00 Max=          5  MaxNode= test.js~5-7:76-128
+|     <IteratorRecord>                                                             1 (14%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~1-8:16-130
+|     <String>                                                                     2 (29%)             2 (13%)        Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5-7:76-128
+|     <Integer>                                                                    1 (14%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~6:105-123
+|   --------------------------------------------------------------------------------------------------------------------------------------------------------------------
+|   [doBoolean]                                                                  1 (13%)             1 (6%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~4:52-71
+|   [doInt, doObject]                                                            1 (13%)             4 (22%)        Min=         4 Avg=        4.00 Max=          4  MaxNode= test.js~5-7:76-128
+|     doInt                                                                        1 (100%)            1 (25%)        Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5-7:76-128
+|     doObject                                                                     1 (100%)            3 (75%)        Min=         3 Avg=        3.00 Max=          3  MaxNode= test.js~5-7:76-128
+|   [doObject]                                                                   6 (75%)            13 (72%)        Min=         1 Avg=        2.17 Max=          5  MaxNode= test.js~5-7:76-128
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+| Name                                                                         Instances          Executions     Executions per instance
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+| JSReadCurrentFrameSlotNodeGen                                                8 (17%)            25 (17%)        Min=         1 Avg=        3.13 Max=          5  MaxNode= test.js~5-7:76-128
+|   doBoolean                                                                    0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doInt <no-args>                                                              1 (13%)             1 (4%)         Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5:81-87
+|   doDouble                                                                     0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   doObject <no-args>                                                           8 (100%)           24 (96%)        Min=         1 Avg=        3.00 Max=          5  MaxNode= test.js~5-7:76-128
+|   doSafeInteger                                                                0 (0%)              0 (0%)         Min=         0 Avg=        0.00 Max=          0  MaxNode=  -
+|   --------------------------------------------------------------------------------------------------------------------------------------------------------------------
+|   [doInt, doObject]                                                            1 (13%)             4 (16%)        Min=         4 Avg=        4.00 Max=          4  MaxNode= test.js~5:81-87
+|     doInt                                                                        1 (100%)            1 (25%)        Min=         1 Avg=        1.00 Max=          1  MaxNode= test.js~5:81-87
+|     doObject                                                                     1 (100%)            3 (75%)        Min=         3 Avg=        3.00 Max=          3  MaxNode= test.js~5:81-87
+|   [doObject]                                                                   7 (88%)            21 (84%)        Min=         1 Avg=        3.00 Max=          5  MaxNode= test.js~5-7:76-128
+ -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+ +

The histogram prints two inner tables for every node class.

+ +

The first table groups specialization and dynamic type combination. +For example, in this histogram the node class JSWriteCurrentFrameSlotNodeGen was instantiated 8 and executed 18 times. +This is 20% of the total instances and 11% of all node executions of the run.

+ +

Three specializations were instantiated in this script, namely doBoolean, doObject, and doInt. +The doBoolean specialization was instantiated and executed only once which accounts for 13% of all instances and 6% of all executions of this node class. +The doObject specializations was invoked using three different input value combinations: DynamicObjectBasic, IteratorRecord, and String. +Similar to specializations, we can see the numbers of times per node they were used and how many times they were executed. +For each line you can see minimum, average, and maximum execution numbers per instance. +The last column prints the source section of the instance with the maximum executions.

+ +

The second table groups for each combination of specializations that were used by node class.

+ +

Here are some questions you would want to ask these specialization statistics:

+ +
    +
  1. Is a certain specialization combination used only rarely and can it be removed/consolidated into a single specialization?
  2. +
  3. Is there a specialization with a very common type combination that could benefit from further specialization?
  4. +
  5. Which specialization combination is common and could deserve its own specialization? This could indicate common polymorphism in the code that could be investigated.
  6. +
  7. What are common specializations, and does the order match the number of executions? Specializations that are most commonly used should be ordered first in the node class. This may lead to improvements in interpreter performance.
  8. +
  9. Are there unexpected specializations instantiated? If yes, investigate further using the printed source section.
  10. +
  11. Which specializations are instantiated often, and should therefore be optimized for memory footprint?
  12. +
  13. Were there nodes with the name Uncached in the profile? The use of uncached nodes should be rare. If they were used often, it can be worthwhile to dig deeper to see why.
  14. +
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/SpecializationTesting/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/SpecializationTesting/index.html new file mode 100644 index 0000000..7e4201a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/SpecializationTesting/index.html @@ -0,0 +1,170 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Testing Truffle DSL Specializations

+ +

This document discusses the tools for testing Truffle DSL specializations.

+ +

Slow Path Specializations in Only Compilation Mode

+ +

The following example will be used in this guide:

+
abstract class PowNode extends Node {
+  public abstract double execute(double a, int exp);
+
+  @Specialization(guards = "exp==1")
+  double doOne(double a, int exp) {
+    return a;
+  }
+
+  @Specialization(replaces = "doOne")
+  int doGeneric(double a, int exp) {
+    double res = 1;
+    for (int i = 0; i < exp; i++)
+      res *= a;
+    return res;
+  }
+}
+
+ +

In order to test that doGeneric produces the correct result for argument exp == 1, you first need to execute this node with a different value. +For example, you can use exp == 2 to activate the doGeneric specialization and only then with 1, which will now +be handled by the doGeneric specialization instead of the doOne specialization. +With a real-world code, writing a test that covers specializations that replace other specializations can be much more complicated and it leads to fragile tests. +Changes in the production code may cause the test to suddenly cover different specializations. +This can easily happen unnoticed.

+ +

Truffle DSL provides a mode where the “fast-path” specializations (those that are “replaced” by some other specialization, doOne in our example) are ignored. +This allows you to simply increase test coverage by running the same tests, which now may cover different code paths.

+ +

When building a language with mx, pass the additional option:

+
mx build -c -A-Atruffle.dsl.GenerateSlowPathOnly=true
+
+ +

After the rebuild, the generated code will call only “slow-path” specializations. +Make sure that your IDE does not recompile the sources automatically in the meantime. +Note that if you compile your dependencies (e.g., Truffle) from source as part of your build, this option will apply to the code of those dependencies as well. +You may choose to apply this option only to some classes by using a filter:

+ +
mx build -c -A-Atruffle.dsl.GenerateSlowPathOnly=true -A-Atruffle.dsl.GenerateSlowPathOnlyFilter=org.my.truffle.language.package
+
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/StaticObjectModel/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/StaticObjectModel/index.html new file mode 100644 index 0000000..98383f1 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/StaticObjectModel/index.html @@ -0,0 +1,363 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Static Object Model

+ +

This guide demonstrates how to get started with using the StaticShape and StaticProperty APIs introduced with GraalVM 21.3.0. +The full documentation can be found in the Javadoc.

+ +

Motivation

+ +

The Static Object Model provides abstractions to represent the layout of objects that, once defined, do not change the number and the type of their properties. +It is particularly well suited for, but not limited to, the implementation of the object model of static programming languages. +Its APIs define the object layout (StaticShape), execute property accesses (StaticProperty), and allocate static objects (DefaultStaticObjectFactory). +The implementation is efficient and executes safety checks on property accesses that can be disabled if they are already executed by the language implementation, for example by a verifier.

+ +

The Static Object Model does not provide constructs to model the visibility of properties and does not distinguish between static and instance properties. +Its APIs are not compatible with those of the Dynamic Object Model, which is more suited for dynamic languages.

+ +

Getting Started

+ +

In this first example, let’s assume that:

+
    +
  1. language is an instance of the TruffleLanguage that we are implementing.
  2. +
  3. We want to represent an object with the following static layout: +
      +
    • An int property named property1.
    • +
    • An Object property named property2 which can be stored as a final field. Later we will see in detail what this implies.
    • +
    +
  4. +
+ +

Here is how to use the Static Object Model to represent this layout:

+ +
public class GettingStarted {
+    public void simpleShape(TruffleLanguage<?> language) {
+        StaticShape.Builder builder = StaticShape.newBuilder(language);
+        StaticProperty p1 = new DefaultStaticProperty("property1");
+        StaticProperty p2 = new DefaultStaticProperty("property2");
+        builder.property(p1, int.class, false);
+        builder.property(p2, Object.class, true);
+        StaticShape<DefaultStaticObjectFactory> shape = builder.build();
+        Object staticObject = shape.getFactory().create();
+        ...
+    }
+}
+
+ +

We start by creating a StaticShape.Builder instance, passing a reference to the language that we are implementing. +Then, we create DefaultStaticProperty instances that represent the properties that we want to add to the static object layout. +The String id passed as argument must be unique within a builder. +After creating the properties we register them to the builder instance:

+
    +
  • The first argument is the StaticProperty that we register.
  • +
  • The second argument is the type of the property. It can be a primitive class or Object.class.
  • +
  • The third argument is a boolean value that defines if the property can be stored as a final field. +This gives the compiler the opportunity to perform additional optimizations. +For example, reads to this property might be constant-folded. +It’s important to note that the Static Object Model does not check if a property stored as final is not assigned more than once and that it is assigned before it is read. +Doing so might lead to wrong behavior of the program, and it is up to the user to enforce that this cannot happen. +We then create a new static shape calling builder.build(). +To allocate the static object, we retrieve the DefaultStaticObjectFactory from the shape, and we invoke its create() method.
  • +
+ +

Now that we have our static object instance, let’s see how to use the static properties to perform property accesses. +Expanding the example above:

+
public class GettingStarted {
+    public void simpleShape(TruffleLanguage<?> language) {
+        ...
+        p1.setInt(staticObject, 42);
+        p2.setObject(staticObject, "42");
+        assert p1.getInt(staticObject) == 42;
+        assert p2.getObject(staticObject).equals("42");
+    }
+}
+
+ +

Shape Hierarchies

+ +

It is possible to create a shape hierarchy by declaring that a new shape should extend an existing one. +This is done by passing the parent shape as argument to StaticShape.Builder.build(StaticShape) when creating the child shape. +Properties of the parent shape can then be used to access values stored in static objects of the child shape.

+ +

In the following example we create a parent shape identical to the one discussed in the previous section, then we extend it with a child shape that hides one of the properties of the parent shape. +Finally, we demonstrate how the various properties can be accessed.

+ +
public class Subshapes {
+    public void simpleSubShape(TruffleLanguage<?> language) {
+        // Create a shape
+        StaticShape.Builder b1 = StaticShape.newBuilder(language);
+        StaticProperty s1p1 = new DefaultStaticProperty("property1");
+        StaticProperty s1p2 = new DefaultStaticProperty("property2");
+        b1.property(s1p1, int.class, false).property(s1p2, Object.class, true);
+        StaticShape<DefaultStaticObjectFactory> s1 = b1.build();
+
+        // Create a sub-shape
+        StaticShape.Builder b2 = StaticShape.newBuilder(language);
+        StaticProperty s2p1 = new DefaultStaticProperty("property1");
+        b2.property(s2p1, int.class, false);
+        StaticShape<DefaultStaticObjectFactory> s2 = b2.build(s1); // passing a shape as argument builds a sub-shape
+
+        // Create a static object for the sub-shape
+        Object o2 = s2.getFactory().create();
+
+        // Perform property accesses
+        s1p1.setInt(o2, 42);
+        s1p2.setObject(o2, "42");
+        s2p1.setInt(o2, 24);
+        assert s1p1.getInt(o2) == 42;
+        assert s1p2.getObject(o2).equals("42");
+        assert s2p1.getInt(o2) == 24;    }
+}
+
+ +

Extending custom base classes

+ +

To reduce memory footprint, the language implementor might want static objects to extend the class that represents guest-level objects. +This is complicated by the fact that StaticShape.getFactory() must return an instance of the factory class that allocates static objects. +To achieve this, we first need to declare an interface that:

+
    +
  • Defines a method for each visible constructor of the static object super class that we want to invoke.
  • +
  • The arguments of each method must match those of the corresponding constructor.
  • +
  • The return type of each method must be assignable from the static object super class.
  • +
+ +

For example, if the static objects should extend this class:

+
public abstract class MyStaticObject {
+    final String arg1;
+    final Object arg2;
+
+    public MyStaticObject(String arg1) {
+        this(arg1, null);
+    }
+
+    public MyStaticObject(String arg1, Object arg2) {
+        this.arg1 = arg1;
+        this.arg2 = arg2;
+    }
+}
+
+ +

We need to declare the following factory interface:

+
public interface MyStaticObjectFactory {
+    MyStaticObject create(String arg1);
+    MyStaticObject create(String arg1, Object arg2);
+}
+
+ +

Finally, this is how to allocate the custom static objects:

+
public void customStaticObject(TruffleLanguage<?> language) {
+    StaticProperty property = new DefaultStaticProperty("arg1");
+    StaticShape<MyStaticObjectFactory> shape = StaticShape.newBuilder(language).property(property, Object.class, false).build(MyStaticObject.class, MyStaticObjectFactory.class);
+    MyStaticObject staticObject = shape.getFactory().create("arg1");
+    property.setObject(staticObject, "42");
+    assert staticObject.arg1.equals("arg1"); // fields of the custom super class are directly accessible
+    assert property.getObject(staticObject).equals("42"); // static properties are accessible as usual
+}
+
+ +

As you can see from the example above, fields and methods of the custom parent class are directly accessible and are not hidden by the static properties of the static object.

+ +

Reducing memory footprint

+ +

Reading the Javadoc, you might have noticed that StaticShape does not provide an API to access the associated static properties. +This reduces memory footprint in case the language implementation already has a way to store this information. +For example, an implementation of the Java language might want to store the static shape in the class that represents a Java class, and a static property in the class that represents a Java field. +In this case, the class representing a Java class should already have a way to retrieve the Java fields associated to it, hence the static properties associated to the shape. +To further reduce memory footprint, the language implementor might want the class representing a Java field to extend StaticProperty.

+ +

Instead of storing the static property in the class that represents fields:

+
class MyField {
+    final StaticProperty p;
+
+    MyField(StaticProperty p) {
+        this.p = p;
+    }
+}
+
+new MyField(new DefaultStaticProperty("property1"));
+
+ +

The class that represents fields can extend StaticProperty:

+
class MyField extends StaticProperty {
+    final Object name;
+
+    MyField(Object name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getId() {
+        return name.toString(); // this string must be a unique identifier within a Builder
+    }
+}
+
+new MyField("property1");
+
+ +

Safety Checks

+ +

On property access, the Static Object Model performs two types of safety checks:

+
    +
  1. That the StaticProperty method matches the type of the static property.
  2. +
+ +

Example of wrong access:

+
public void wrongMethod(TruffleLanguage<?> language) {
+    StaticShape.Builder builder = StaticShape.newBuilder(language);
+    StaticProperty property = new DefaultStaticProperty("property");
+    Object staticObject = builder.property(property, int.class, false).build().getFactory().create();
+
+    property.setObject(staticObject, "wrong access type"); // throws IllegalArgumentException
+
+ +
    +
  1. That the object passed to the accessor method matches the shape generated by the builder to which the property is associated, or one of its child shapes.
  2. +
+ +

Example of wrong access:

+
public void wrongShape(TruffleLanguage<?> language) {
+    StaticShape.Builder builder = StaticShape.newBuilder(language);
+    StaticProperty property = new DefaultStaticProperty("property");;
+    Object staticObject1 = builder.property(property, Object.class, false).build().getFactory().create();
+    Object staticObject2 = StaticShape.newBuilder(language).build().getFactory().create();
+
+    property.setObject(staticObject2, "wrong shape"); // throws IllegalArgumentException
+}
+
+ +

While these checks are often useful, they might be redundant if the language implementation already performs them, for example using a verifier. +While the first type of checks (on property type) is very efficient and cannot be disabled, the second type of checks (on the shape) is computationally expensive and can be disabled via a command line argument:

+
--experimental-options --engine.RelaxStaticObjectSafetyChecks=true
+
+ +

or when creating the Context:

+
Context context = Context.newBuilder() //
+                         .allowExperimentalOptions(true) //
+                         .option("engine.RelaxStaticObjectSafetyChecks", "true") //
+                         .build();
+
+ +

It is highly discouraged to relax safety checks in absence of other equivalent checks. +If the assumption on the correctness of the shape of the static objects is wrong, the VM is likely to crash.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TCK/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TCK/index.html new file mode 100644 index 0000000..e8df385 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TCK/index.html @@ -0,0 +1,210 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Polyglot API-based Test Compatibility Kit

+ +

The Test Compatibility Kit (TCK) is a collection of tests verifying the TruffleLanguage inter-operability and instrumentation. +The TCK is based on the org.graalvm.polyglot API.

+ +

Adding a Language

+ +

To test your language, implement the LanguageProvider. +The LanguageProviders are loaded using the java.util.ServiceLoader, so you need to register your implementation in the META-INF/services/org.graalvm.polyglot.tck.LanguageProvider file. +The LanguageProvider should provide the language data types, language expressions (operators), and language control flow statements represented as functions returning the data type or executing the operator (statement). +To allow composition of the returned functions, the parameter and return types have to be assigned to them using +the Snippet.Builder. +The LanguageProvider should also provide simple but representative scripts which the TCK can use to test instrumentation.

+ +

Running TCK Tests with mx

+ +

The tests are executed using mx unitest. When running the tests, all LanguageProviders in the primary suite and dependent suites are used. The truffle suite provides the java-host LanguageProvider, creating Java data types and Proxies to test Java inter-operability.

+ +

To run just the TCK tests use:

+ +

mx unittest com.oracle.truffle.tck.tests

+ +

Or, simply use:

+ +

mx tck

+ +

To restrict the TCK tests to test a certain language, use the tck.language property. +The following example tests JavaScript with data types from all available languages:

+ +

mx tck -Dtck.language=js

+ +

To restrict the data types to a certain language, use the tck.values property. +The following example tests JavaScript with Java types:

+ +

mx tck -Dtck.values=java-host -Dtck.language=js

+ +

To run a single test, specify the full test name. +For example, to run a test for SimpleLanguage + operator with SimpleLanguage number and big number use:

+ +

mx tck 'ExpressionTest#testExpression[sl::+(sl::number, sl::number)]'

+ +

To run the TCK tests on GraalVM it is enough to set the mx --java-home to point to GraalVM:

+ +

mx --java-home=<path_to_graalvm> tck

+ +

To disable output and error output use the tck.verbose property:

+ +

mx tck -Dtck.verbose=false

+ +

To disable output and error output only for a certain test, use the tck.{TestSimpleName}.verbose property:

+ +

mx tck -Dtck.ErrorTypeTest.verbose=false

+ +

You can also disable output and error output for all tests but one:

+ +

mx tck -Dtck.verbose=false -Dtck.ErrorTypeTest.verbose=true

+ +

Running TCK Tests without mx

+ +

The Python TCK runner can be used to execute the Truffle TCK on top of GraalVM. The script requires Maven for downloading the TCK artifacts.

+ +

To execute TCK tests on GraalVM use:

+ +

python tck.py -g <path_to_graalvm>

+ +

To include your own language and TCK provider use:

+ +

python tck.py -g <path_to_graalvm> -cp <path_to_tck_provider_jars> -lp <path_to_language_jars>

+ +

To restrict tests to a certain language, use the language ID as a first unnamed option. +The following example executes tests only for the JavaScript language:

+ +

python tck.py -g <path_to_graalvm> js

+ +

To execute the tests under debugger use the -d or --dbg <port> option:

+ +

python tck.py -d -g <path_to_graalvm>

+ +

The TCK tests can be filtered by test names. To execute just the ScriptTest for the JavaScript TCK provider use:

+ +

python tck.py -g <path_to_graalvm> js default ScriptTest

+ +

The TCK tests can be executed in compile mode in which all calltargets are compiled before they are executed. +To execute JavaScript tests in compile mode use:

+ +

python tck.py -g <path_to_graalvm> js compile

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TraversingCompilationQueue/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TraversingCompilationQueue/index.html new file mode 100644 index 0000000..0b6facd --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TraversingCompilationQueue/index.html @@ -0,0 +1,255 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Approach to the Compilation Queue

+ +

As of version 21.2.0 Truffle has a new approach to compilation queueing. +This document gives motivation and an overview of this approach.

+ +

What is a Compilation queue?

+ +

During execution of guest code each Truffle call target counts how many times it was executed as well as how many loop iterations happened during those executions (i.e. the target’s “call and loop count”). +Once this counter reaches a certain threshold the call target is deemed “hot” and scheduled for compilation. +In order to minimize the impact this has on the execution of the guest code the notion that the target should be compiled is made concrete as a compilation task and placed into a compilation queue to await compilation. +The Truffle runtime spawns several compiler threads (--engine.CompilerThreads) that take tasks from the queue and compile the specified call targets.

+ +

The initial implementation of the compilation queue in Truffle was a straightforward FIFO queue. +This approach has important limitations with respect to warmup characteristics of the guest code execution. +Namely, not all call targets are equally important to compile. +The aim is to identify targets which account for more execution time and compile them first, thus reaching better performance sooner. +Since call targets are queued for compilation when a counter reaches a certain threshold a FIFO queue would compile targets in order of reaching that threshold, which in practise does not correlate to actual execution time.

+ +

Consider the following toy JavaScript example:

+ +
function lowUsage() {
+    for (i = 0; i < COMPILATION_THRESHOLD; i++) {
+        // Do something
+    }
+}
+
+function highUsage() {
+    for (i = 0; i < 100 * COMPILATION_THRESHOLD; i++) {
+        // Do something
+    }
+}
+
+while(true) {
+    lowUsage();
+    highUsage();
+}
+
+ +

Both the lowUsage and the highUsage function will reach a high enough call and loop count threshold even on the first execution, but the lowUsage function will reach it first. +Using a FIFO queue, we would compile the lowUsage function first, even though this example illustrates that the highUsage function should be compiled first in order to reach better performance sooner.

+ +

Traversing Compilation Queue

+ +

The new compilation queue in Truffle, colloquially called “Traversing Compilation Queue”, takes a more dynamic approach to selecting the order in which targets are compiled. +Every time a compiler thread requests the next compilation task the queue will traverse all the entries in the queue and pick the one with the highest priority.

+ +

A task’s priority is determined based on several factors.

+ +

For starters, targets scheduled for first-tier compilation (i.e. first-tier tasks) always have higher priority than second-tier tasks. +The rational behind this is that performance difference between executing code in the interpreter and executing it in first-tier compiled code is much greater then the difference between tier-one and tier-two compiled code, meaning that we get more benefit from compiling these targets sooner. +Also, first-tier compilations are usually take less time, thus one compiler thread can finish multiple first-tier compilations in the same time it takes to complete one second-tier compilation. +This approach has been shown to underperform in certain scenarios and might be improved upon in the coming versions.

+ +

When comparing two tasks of the same tier, we first consider their compilation history and give priority to tasks which were previously compiled with a higher compiler tier. +For example, if a call target get first-tier compiled, then gets invalidated for some reason and then gets queued for a first-tier compilation again, it takes priority over all other first tier targets that have never before been compiled. +The reasoning is that if it was previously compiled, it is obviously important and should not be penalized more than necessary by its invalidation.

+ +

Finally, if the two previous conditions can’t differentiate the priority between two tasks we give priority to the task with the higher “weight”. +The weight is a function of the target’s call and loop count and time. +It is defined as a product of the target’s call and loop count with the rate at which that call and loop count has grown in the past 1ms. +Using the target’s call and loop count as a proxy for amount of time spent executing that call target, this metric aims to balance total time spent executing that call target with the recent growth of that time. +This gives a priority boost to targets that are currently “very hot” when comparing to targets that were “hot” but are not being executed a lot currently.

+ +

For performance reasons the weight for tasks is cached and reused for a period of 1ms. If the cached value is older than 1ms, it is recalculated.

+ +

The traversing compilation queue is on by default as of version 21.2.0 and can be disabled using --engine.TraversingCompilationQueue=false.

+ +

Dynamic Compilation Thresholds

+ +

One problem of the traversing compilation queue is that it needs to traverse all the entries in the queue to get up-to-date weights and choose the highest priority task. +This does not have a significant performance impact as long as the size of the queue remains reasonable. +This means that in order to always choose the highest priority task in a reasonable about of time we need to ensure that the queue does not grow indefinitely.

+ +

This is achieved by an approach we call “dynamic compilation thresholds”. +Simply put, dynamic compilation thresholds means that the compilation threshold (the one each call target’s call and loop count is compared against when determining whether to compile it) may change over time depending on the state of the queue. +If the queue is overloaded we aim to increase the compilation thresholds to reduce the number of incoming compilation tasks, i.e. targets need to be “more hot” to get scheduled for compilation. +On the other hand, if the queue is close to empty, we can reduce the compilation thresholds to allow more targets to get scheduled for compilation, i.e. the compilation threads are in danger of idling so let’s give them even “less hot” targets to compile.

+ +

We call this changing of the thresholds “scaling” as the thresholds are in practice just multiple by a “scale factor” determined by a scale function. +The scale function takes as input the “load” of the queue, which is the number of tasks in the queue divided by the number of compiler threads. +We intentionally control for the number of compiler threads since the raw number of tasks in the queue is not a good proxy of how much compilation pressure there is. +For example, let’s assume that an average compilation takes 100ms and that there are 160 tasks in the queue. +A runtime with 16 threads will finish all the tasks in approximately 10 * 100ms i.e. 1 second. +On the other hand, a runtime with 2 compiler thread will take approximately 80 * 100ms, i.e. 8 seconds.

+ +

The scale function is defined by 3 parameters: --engine.DynamicCompilationThresholdsMinScale, --engine.DynamicCompilationThresholdsMinNormalLoad and DynamicCompilationThresholdsMaxNormalLoad.

+ +

The --engine.DynamicCompilationThresholdsMinScale option defines how low we are willing to scale the thresholds. +It has a default value of 0.1, meaning that the compilation thresholds will never be scaled below 10% of their default value. +This in practice means that, by definition, scale(0) = DynamicCompilationThresholdsMinScale or for default values scale(0) = 0.1

+ +

The --engine.DynamicCompilationThresholdsMinNormalLoad option defines the minimal load at which compilation thresholds will not be scaled. +This means that as long as the load of the queue is above this value the runtime will not scale down the compilation thresholds. +This in practice means that, by definition, scale(DynamicCompilationThresholdsMinScale) = 1 or for default values scale(10) = 1

+ +

The --engine.DynamicCompilationThresholdsMaxNormalLoad option defines the maximal load at which compilation thresholds will not be scaled. +This means that as long as the load of the queue is below this value the runtime will not scale up the compilation thresholds. +This in practice means that, by definition, scale(DynamicCompilationThresholdsMaxScale) = 1 or for default values scale(90) = 1

+ +

So far we’ve defined the scale function at 3 points. +For all values between those points the scale function is a straight line connecting those two points. +This means that for all values between the minimal and maximal normal load the scale function is 1 by definition. +For values between 0 and the minimal normal load the scale function grows linearly between the minimal scale and 1. +Let’s define the slope of this function as s. +Now, for the remainder of the functions domain, i.e. the values greater than the maximum normal load, we define scale to be a linear function with slope s passing through the point (DynamicCompilationThresholdsMaxNormalLoad, 1).

+ +

The following is an ASCII art plot of the scale function which should illustrate how it’s defined.

+ +
          ^ scale
+          |
+          |                                            /
+          |                                           /
+          |                                          /
+          |                                         /
+          |                                        /
+          |                                       /
+        1 |..... ________________________________/
+          |     /.                               .
+          |    / .                               .
+          |   /  .                               .
+          |  /   .                               .
+          | /    .                               .
+MinScale >|/     .                               .
+          |      .                               .
+          |_______________________________________________________> load
+         0       ^                               ^
+              MinNormalLoad                   MaxNormalLoad
+
+ +

The dynamic thresholds only work with the traversing compilation queue and are on by default as of version 21.2.0. +They can be disabled with --engine.DynamicCompilationThresholds=false.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TruffleLibraries/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TruffleLibraries/index.html new file mode 100644 index 0000000..46ddefe --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TruffleLibraries/index.html @@ -0,0 +1,563 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Library Guide

+ +

Truffle Libraries allow language implementations to use polymorphic dispatch for receiver types with support for implementation-specific caching/profiling and automatic support for uncached dispatch. +Truffle Libraries enable modularity and encapsulation for representation types language implementations on top of Truffle. +Read this guide first before using them.

+ +

Getting Started

+ +

This tutorial provides a trace through a use-case on how to use Truffle Libraries. +The full API documentation can be found in the Javadoc. +This document assumes prior knowledge of Truffle APIs and the use of @Specialization with the @Cached annotation.

+ +

Motivating Example

+ +

When implementing arrays in Truffle Languages it is often necessary to use multiple representations for efficiency. +For example, if the array is constructed from an arithmetic sequence of integers (e.g., range(from: 1, step: 2, length: 3)), then it is best represented using the start, stride, and length instead of materializing the full array. +Of course, when an array element is written, then the array needs to be materialized. +In this example we are going to implement an array implementation with two representations:

+ +
    +
  • Buffer: represents a materialized array representation backed by a Java array.
  • +
  • Sequence: represents an arithmetic sequence of numbers represented by start, stride and length: [start, start + 1 * stride, ..., start + (length - 1) * stride].
  • +
+ +

To keep the example simple we will only support int values and we will ignore index bounds error handling. +We will also just implement the read operation and not the typically more complicated write operation.

+ +

To make the example more interesting, we will implement an optimization that will let the compiler allow constant fold sequenced array accesses even if the array receiver value is not constant.

+ +

Assume we have the following code snippet range(start, stride, length)[2]. +In this snippet, the variables start and stride are not known to be constant values, therefore, equivalent code to start + stride * 2 gets compiled. +However, if the start and stride values are known to always be the same then the compiler could constant-fold the entire operation. +This optimization requires the use of caching. +We will later show how this works.

+ +

In the dynamic array implementation of GraalVM’s JavaScript runtime, we use 20 different representations. +There are representations for constant, zero-based, contiguous, holes, and sparse arrays. +Some representations are further specialized for the types byte, int, double, JSObject, and Object. +The source code can be found here. Note: Currently, JavaScript arrays do not use Truffle Libraries yet.

+ +

In the following sections, we discuss multiple implementation strategies for the array representations, ultimately describing how Truffle Libraries can be used to achieve this.

+ +

Strategy 1: Specialization per Representation

+ +

For this strategy, we will start by declaring classes for the two representations BufferArray and SequenceArray.

+ +
final class BufferArray {
+    int length;
+    int[] buffer;
+    /*...*/
+}
+
+final class SequenceArray {
+    final int start;
+    final int stride;
+    final int length;
+    /*...*/
+}
+
+ +

The BufferArray implementation has a mutable buffer and length and is used as the materialized array representation. +The sequence array is represented by the final fields start, stride, and length.

+ +

Now, we specify the basic read operations like this:

+ +
abstract class ExpressionNode extends Node {
+    abstract Object execute(VirtualFrame frame);
+}
+
+@NodeChild @NodeChild
+abstract class ArrayReadNode extends ExpressionNode {
+
+    @Specialization
+    int doBuffer(BufferArray array, int index) {
+        return array.buffer[index];
+    }
+
+    @Specialization
+    int doSequence(SequenceArray seq, int index) {
+        return seq.start + seq.stride * index;
+    }
+}
+
+ +

The array read node specifies two specializations for the buffer version and the sequence. +As mentioned before we are going to ignore error bounds checks for simplicity.

+ +

Now we try to make the array read specialize on the constant-ness of values of the sequence in order to allow the range(start, stride, length)[2] example to fold if start and stride are constant. +To find out whether start and stride are constants we need to profile their value. +To profile these values we need to add another specialization to the array read operation like this:

+ +
@NodeChild @NodeChild
+class ArrayReadNode extends ExpressionNode {
+    /* doBuffer() */
+    @Specialization(guards = {"seq.stride == cachedStride",
+                              "seq.start  == cachedStart"}, limit = "1")
+    int doSequenceCached(SequenceArray seq, int index,
+             @Cached("seq.start")  int cachedStart,
+             @Cached("seq.stride") int cachedStride) {
+        return cachedStart + cachedStride * index;
+    }
+    /* doSequence() */
+}
+
+ +

If the speculation guards of this specialization succeed then the start and stride are effectively constant. +For example, with the values 3 and 2, the compiler would see 3 + 2 * 2 which is 7. +The limit is set to 1 to only try this speculation once. +It would likely be inefficient to increase the limit as this would introduce additional control flow to the compiled code. +If the speculation does not succeed, i.e., if the operation observes multiple start and stride values, we want to fall back to the normal sequence specialization. +To achieve this we change the doSequence specialization by adding replaces = "doSequenceCached" like this:

+ +
@NodeChild @NodeChild
+class ArrayReadNode extends ExpressionNode {
+    /* doSequenceCached() */
+    @Specialization(replaces = "doSequenceCached")
+    int doSequence(SequenceArray seq, int index) {
+        return seq.start + seq.stride * index;
+    }
+}
+
+ +

Now we have achieved the goal of implementing our array representations including additional profiling. +The runnable source code for Strategy 1 can be found here. +This strategy has some nice properties:

+ +
    +
  • The operation is easy to read and all cases are fully enumerated.
  • +
  • The generated code of the read node only requires a single bit per specialization to remember which representation type was observed at runtime.
  • +
+ +

We would already be done with this tutorial if there would not be some problems with this:

+ +
    +
  • New representations cannot be loaded dynamically; they need to be statically known, making the separation of representation types from operations impossible.
  • +
  • Changing or adding representation types often requires the modification of many operations.
  • +
  • Representation classes need to expose most implementation details to operations (no encapsulation).
  • +
+ +

These problems are the primary motivations for Truffle Libraries.

+ +

Strategy 2: Java Interfaces

+ +

Now we will try to address these problems by using Java interfaces. +We start by defining an array interface:

+ +
interface Array {
+    int read(int index);
+}
+
+ +

The implementations can now implement the Array interface and implement the read method in the representation class.

+ +
final class BufferArray implements Array {
+    private int length;
+    private int[] buffer;
+    /*...*/
+    @Override public int read(int index) {
+        return buffer[index];
+    }
+}
+
+final class SequenceArray implements Array {
+    private final int start;
+    private final int stride;
+    private final int length;
+    /*...*/
+    @Override public int read(int index) {
+        return start + (stride * index);
+    }
+}
+
+ +

Finally, we specify the operation node:

+ +
@NodeChild @NodeChild
+abstract class ArrayReadNode extends ExpressionNode {
+    @Specialization
+   int doDefault(Array array, int index) {
+        return array.read(index);
+    }
+}
+
+ +

The problem with this operation implementation is that the partial evaluator does not know which concrete type the array receiver has. +Therefore, it needs to stop partial evaluation and emit a slow interface call for the read method call. +This is not what we want, but we can introduce a polymorphic type cache to resolve it like this:

+ +
class ArrayReadNode extends ExpressionNode {
+    @Specialization(guards = "array.getClass() == arrayClass", limit = "2")
+    int doCached(Array array, int index,
+           @Cached("array.getClass()") Class<? extends Array> arrayClass) {
+        return arrayClass.cast(array).read(index);
+    }
+
+    @Specialization(replaces = "doCached")
+    int doDefault(Array array, int index) {
+        return array.read(index);
+    }
+}
+
+ +

We solved the problem of partially evaluating the implementation, but there is no way to express the extra specialization for the constant stride and start index optimization in this solution.

+ +

This is what we discovered/ solved so far:

+ +
    +
  • Interfaces are existing well-known concept for polymorphism in Java.
  • +
  • New interface implementations can be loaded enabling modularity.
  • +
  • We found a convenient way to use the operations from slow-paths.
  • +
  • Representation types can encapsulate implementation details.
  • +
+ +

But we have introduced new problems:

+ +
    +
  • No representation specific profiling / caching can be performed.
  • +
  • Every interface call requires a polymorphic class cache on the call-site.
  • +
+ +

The runnable source code for Strategy 2 can be found here.

+ +

Strategy 3: Truffle Libraries

+ +

Truffle Libraries work similar to the Java interfaces. +Instead of a Java interface, we create an abstract class extending the Library class and annotate it with @GenerateLibrary. We create abstract methods like with the interface, but we insert a receiver argument in the beginning, in our case of type Object. +Instead of performing interface type checks we use an explicit abstract method in the library typically named is${Type}.

+ +

We do this for our example:

+ +
@GenerateLibrary
+public abstract class ArrayLibrary extends Library {
+
+    public boolean isArray(Object receiver) {
+        return false;
+    }
+
+    public abstract int read(Object receiver, int index);
+}
+
+ +

This ArrayLibrary specifies two messages: isArray and read. +At compile time, the annotation processor generates a package protected class ArrayLibraryGen. +Unlike generated nodes classes, you never need to refer to this class.

+ +

Instead of implementing a Java interface, we export the library using the @ExportLibrary annotation on the representation type. +Message exports are specified using instance methods on the representation and can, therefore, omit the receiver argument of the library.

+ +

The first representation we implement this way is the BufferArray representation:

+ +
@ExportLibrary(ArrayLibrary.class)
+final class BufferArray {
+    private int length;
+    private int[] buffer;
+    /*...*/
+    @ExportMessage boolean isArray() {
+      return true;
+    }
+    @ExportMessage int read(int index) {
+      return buffer[index];
+    }
+}
+
+ +

This implementation is very similar to the interface version, but in addition, we specify the isArray message. +Again, the annotation processor generates the boilerplate code that implements the library abstract class.

+ +

Next, we implement the sequence representation. +We start by implementing it without the optimization for the start and stride value.

+ +
@ExportLibrary(ArrayLibrary.class)
+final class SequenceArray {
+    private final int start;
+    private final int stride;
+    private final int length;
+    /*...*/
+    @ExportMessage int read(int index) {
+        return start + stride * index;
+    }
+}
+
+ +

So far this was equivalent to the interface implementation, but with Truffle Libraries we can now also use specializations in our representations by exporting a message using a class instead of a method. +The convention is that the class is named exactly like the exported message, but with the first letter upper-case.

+ +

Now we implement our stride and start specialization using this mechanism:

+ +
@ExportLibrary(ArrayLibrary.class)
+final class SequenceArray {
+    final int start;
+    final int stride;
+    final int length;
+    /*...*/
+
+    @ExportMessage static class Read {
+        @Specialization(guards = {"seq.stride == cachedStride",
+                                  "seq.start  == cachedStart"}, limit = "1")
+        static int doSequenceCached(SequenceArray seq, int index,
+                 @Cached("seq.start")  int cachedStart,
+                 @Cached("seq.stride") int cachedStride) {
+            return cachedStart + cachedStride * index;
+        }
+
+        @Specialization(replaces = "doSequenceCached")
+        static int doSequence(SequenceArray seq, int index) {
+            return doSequenceCached(seq, index, seq.start, seq.stride);
+        }
+    }
+}
+
+ +

Since the message is declared using an inner class we need to specify the receiver type. +Compared to normal nodes, this class must not extend Node and its methods must be static to allow the annotation processor to generate efficient code for the library subclass.

+ +

Last, we need to use the array library in our read operation. +The Library API provides an annotation called @CachedLibrary that is responsible for dispatching to libraries. +The array read operation now looks like this:

+ +
@NodeChild @NodeChild
+class ArrayReadNode extends ExpressionNode {
+    @Specialization(guards = "arrays.isArray(array)", limit = "2")
+    int doDefault(Object array, int index,
+                  @CachedLibrary("array") ArrayLibrary arrays) {
+        return arrays.read(array, index);
+    }
+}
+
+ +

Similar to the type cache we have seen in Strategy 2 we specialize the library to a particular value. +The first attribute of @CachedLibrary, "array" specifies the value the library is specialized for. +A specialized library can only be used for values they were specialized for. +If they are used with other values then the framework will fail with an assertion error.

+ +

Instead of using the Array type as the parameter type, we use the isArray message in the guard. +Using a specialized library requires us to specify the limit on the specialization. +The limit specifies how many specializations of a library can be instantiated until the operation should rewrite itself to use an uncached version of the library.

+ +

In the array example we have only implemented two array representations. +Therefore it is impossible that the limit is exceeded. +In real array implementations, we are likely to use many more representations. +The limit should be set to a value that is unlikely to be exceeded in representative applications, but at the same time does not produce too much code.

+ +

The uncached or slow-path version of a library can be reached by exceeding the limit of the specialization, but it can also be used manually, e.g., if the array operation needs to be invoked when no node is available. +This is usually the case for parts of the language implementation that are invoked infrequently. +With the interface strategy (Strategy 2), the array read operation could be used by just invoking the interface method.

+ +

With Truffle libraries, we need to lookup an uncached version of the library first. +Every use of @ExportLibrary generates a cached but also an uncached / slow-path library subclass. +The uncached version of the exported library uses the same semantics as @GenerateUncached. +Typically, as with our example, the uncached version can be derived automatically. +The DSL shows an error if it needs further details on how to generate the uncached version. +The uncached version of the library can be invoked like this:

+ +
ArrayLibrary arrays = LibraryFactory.resolve(ArrayLibrary.class).getUncached();
+arrays.read(array, index);
+
+ +

In order to decrease the verbosity of this example, it is recommended that the library class provides the following optional static utilities:

+ +
@GenerateLibrary
+public abstract class ArrayLibrary extends Library {
+    /*...*/
+    public static LibraryFactory<ArrayLibrary> getFactory() {
+        return FACTORY;
+    }
+
+    public static ArrayLibrary getUncached() {
+        return FACTORY.getUncached();
+    }
+
+    private static final LibraryFactory<ArrayLibrary> FACTORY =
+               LibraryFactory.resolve(ArrayLibrary.class);
+}
+
+ +

The verbose example from above can now be simplified as:

+ +
ArrayLibrary.getUncached().readArray(array, index);
+
+ +

The runnable source code for Strategy 3 can be found here.

+ +

Conclusion

+ +

In this tutorial, we have learned that with Truffle Libraries we no longer need to compromise the modularity of representation types by creating a specialization per representation (Strategy 1) and the profiling is no longer blocked by interface calls (Strategy 2). +With Truffle Libraries we now support polymorphic dispatch with type encapsulation but don’t lose the capability of using profiling/caching techniques in representation types.

+ +

What to do next?

+ +
    +
  • +

    Run and debug all the examples here.

    +
  • +
  • +

    Read the interoperability migration guide, as an example of Truffle Libraries usage here.

    +
  • +
  • +

    Read the Truffle Library reference documentation here.

    +
  • +
+ +

FAQ

+ +

Are there any known limitations?

+ +
    +
  • Library exports currently cannot explicitly invoke their super implementation. This makes reflective implementations currently infeasible. See the example here.
  • +
  • Boxing elimination for return values is currently not supported. A message can only have one generic return type. Support for this is planned.
  • +
  • Reflection without static dependencies on the Library class is currently not supported. Support for full dynamic reflection is planned.
  • +
+ +

When should I use Truffle Libraries?

+ +

When to use?

+ +
    +
  • If the representations are modular and cannot be enumerated for an operation (e.g., Truffle Interoperability).
  • +
  • If there is more than one representation of a type and one of the representations needs profiling/caching (e.g., see the motivating example).
  • +
  • If there is a need for a way to proxy all values of a language (e.g., for dynamic taint tracking).
  • +
+ +

When not to use?

+ +
    +
  • For basic types that only have one representation.
  • +
  • For primitive representations that require boxing elimination to speed up the interpreter. Boxing elimination is not supported with Truffle Libraries at the moment.
  • +
+ +

I decided to use a Truffle Library to abstract the language specific types of my language. Should those be exposed to other languages and tools?

+ +

All libraries are accessible to other languages and tools via the ReflectionLibrary. +It is recommended that the language implementation documentation specifies which libraries and messages are intended for external use, and which ones may be subject to breaking changes.

+ +

What happens when a new method is added to a library but a dynamically loaded implementation hasn’t been updated for it?

+ +

If the library method was specified abstract then an AbstractMethodError will be thrown. +Otherwise the default implementation specified by the library method body will be called. +This allows to customize the error in case an abstract method is used. +For example, for Truffle interoperability we often throw an UnsupportedMessageException instead of an AbstractMethodError.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TruffleStrings/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TruffleStrings/index.html new file mode 100644 index 0000000..d3e6e72 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/TruffleStrings/index.html @@ -0,0 +1,697 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Strings Guide

+ +

Truffle Strings is Truffle’s primitive String type, which can be shared between languages. +Language implementers are encouraged to use Truffle Strings as their language’s string type for easier interoperability and better performance.

+ +

TruffleString supports a plethora of string encodings, but is especially optimized for the most commonly used:

+ +
    +
  • UTF-8
  • +
  • UTF-16
  • +
  • UTF-32
  • +
  • US-ASCII
  • +
  • ISO-8859-1
  • +
  • BYTES
  • +
+ +

TruffleString API

+ +

All operations exposed by TruffleString are provided as an inner Node, and as static or instance methods. +Users should use the provided nodes where possible, as the static/instance methods are just shorthands for executing their respective node’s uncached version. +All nodes are named {NameOfOperation}Node, and all convenience methods are named {nameOfOperation}Uncached.

+ +

Some operations support lazy evaluation, such as lazy concatenation or lazy evaluation of certain string properties. +Most of these operations provide a parameter boolean lazy, which allows the user to enable or disable lazy evaluation on a per-callsite basis.

+ +

Operations dealing with index values, such as CodePointAtIndex, are available in two variants: codepoint-based indexing and byte-based indexing. +Byte-based indexing is indicated by the ByteIndex-suffix or prefix in an operation’s name, otherwise indices are based on codepoints. +For example, the index parameter ofCodePointAtIndex is codepoint-based, whereas CodePointAtByteIndex uses a byte-based index.

+ +

The list of currently available operations is listed below and grouped by category.

+ +

Creating a new TruffleString:

+ + + +

Query string properties:

+ +
    +
  • isEmpty: +Check if a string is empty.
  • +
  • CodePointLength: +Get a string’s length in codepoints.
  • +
  • byteLength: +Get a string’s length in bytes.
  • +
  • IsValid: +Check whether a string is encoded correctly.
  • +
  • GetCodeRange: +Get coarse information about the string’s content (are all codepoints in this string from the ASCII/LATIN-1/BMP +range?).
  • +
  • GetByteCodeRange: +Get coarse information about the string’s content, without taking 16/32-bit based encodings into account.
  • +
  • CodeRangeEquals: +Check whether a string’s code range equals the given code range.
  • +
  • isCompatibleTo: +Check if a string is compatible to / can be viewed in a given encoding.
  • +
  • isManaged: +Check if a string is not backed by a native pointer.
  • +
  • isNative: +Check if a string is backed by a native pointer.
  • +
  • isImmutable: +Check if a string is an instance of TruffleString.
  • +
  • isMutable: +Check if a string is an instance of MutableTruffleString.
  • +
+ +

Comparison:

+ +
    +
  • Equal: +Check if two strings are equal. Note that this operation is encoding-sensitive!
  • +
  • RegionEqual: +Check if two strings are equal in a given region defined by a codepoint-based offset and length.
  • +
  • RegionEqualByteIndex: +Check if two strings are equal in a given region defined by a byte-based offset and length.
  • +
  • CompareBytes: +Compare two strings byte-by-byte.
  • +
  • CompareCharsUTF16: +Compare two UTF-16 strings char-by-char.
  • +
  • CompareIntsUTF32: +Compare two UTF-32 strings int-by-int.
  • +
  • HashCode: +Get a string’s hash code. The hash code is based on the string’s bytes, so strings with the same codepoints but +different encodings may have different hash codes.
  • +
+ +

Conversion:

+ +
    +
  • SwitchEncoding: +Convert a string to a given encoding.
  • +
  • ForceEncoding: +Create a string containing the same bytes as the given string, but assigned to the given encoding.
  • +
  • AsTruffleString: +Convert a MutableTruffleString to an immutable TruffleString.
  • +
  • AsManaged: +Convert a TruffleString backed by a native pointer to one backed by a java byte array.
  • +
  • CopyToByteArray: +Copy a string’s content into a byte array.
  • +
  • GetInternalByteArray: +Get a string’s internal byte array.
  • +
  • CopyToNativeMemory: +Copy a string’s content into a native pointer.
  • +
  • GetInternalNativePointer: +Get a native string’s pointer object.
  • +
  • ToJavaString: +Convert a string to a java.lang.String.
  • +
  • ParseInt: +Parse a string’s content as an int value.
  • +
  • ParseLong: +Parse a string’s content as a long value.
  • +
  • ParseDouble: +Parse a string’s content as a double value.
  • +
+ +

Accessing codepoints and bytes:

+ +
    +
  • Materialize: +Use this node to avoid materialization code inside loops iterating over a string’s code points or bytes.
  • +
  • ReadByte: +Read a single byte from a string.
  • +
  • ReadCharUTF16: +Read a single char from a UTF-16 string.
  • +
  • CodePointAtIndex: +Read a single codepoint from a string at a given codepoint-based index.
  • +
  • CodePointAtByteIndex: +Read a single codepoint from a string at a given byte-based index.
  • +
  • CreateCodePointIterator: +Return a TruffleStringIterator object suitable for iterating the string’s codepoints.
  • +
  • CreateBackwardCodePointIterator: +Return a TruffleStringIterator object suitable for iterating the string’s codepoints, starting from the end of the +string.
  • +
  • ByteLengthOfCodePoint: +Return the number of bytes occupied by the codepoint starting at a given byte index.
  • +
  • CodePointIndexToByteIndex: +Convert a given codepoint index to a byte index on a given string.
  • +
  • ByteIndexToCodePointIndex: +Convert a given byte index to a codepoint index on a given string.
  • +
+ +

Search:

+ +
    +
  • ByteIndexOfAnyByte: +Find the first occurrence of any of a set of given bytes in a string and return its byte-based index.
  • +
  • CharIndexOfAnyCharUTF16: +Find the first occurrence of any of a set of given chars in a UTF-16 string and return its char-based index.
  • +
  • IntIndexOfAnyIntUTF32: +Find the first occurrence of any of a set of given ints in a UTF-32 string and return its int-based index.
  • +
  • IndexOfCodePoint: +Find the first occurrence of a given codepoint in a string and return its codepoint-based index.
  • +
  • ByteIndexOfCodePoint: +Find the first occurrence of a given codepoint in a string and return its byte-based index.
  • +
  • ByteIndexOfCodePointSet: +Find the first occurrence of a codepoint contained in a given set in a string and return its byte-based index.
  • +
  • LastIndexOfCodePoint: +Find the last occurrence of a given codepoint in a string and return its codepoint-based index.
  • +
  • LastByteIndexOfCodePoint: +Find the last occurrence of a given codepoint in a string and return its byte-based index.
  • +
  • IndexOfString: +Find the first occurrence of a given substring in a string and return its codepoint-based index.
  • +
  • ByteIndexOfString: +Find the first occurrence of a given substring in a string and return its byte-based index.
  • +
  • LastIndexOfString: +Find the last occurrence of a given substring in a string and return its codepoint-based index.
  • +
  • LastByteIndexOfString: +Find the last occurrence of a given substring in a string and return its byte-based index.
  • +
+ +

Combining:

+ +
    +
  • Concat: +Concatenate two strings.
  • +
  • Substring: +Create a substring from a given string, bounded by a codepoint-based offset and length.
  • +
  • SubstringByteIndex: +Create a substring from a given string, bounded by a byte-based offset and length.
  • +
  • Repeat: +Repeat a given string n times.
  • +
+ +

Instantiation

+ +

A TruffleString can be created from a codepoint, a number, a primitive array or a java.lang.String.

+ +

Strings of any encoding can be created with TruffleString.FromByteArrayNode, which expects a byte array containing the already encoded string. +This operation can be non-copying, by setting the copy parameter to false.

+ +

Important: TruffleStrings will assume the array content to be immutable, do not modify the array after passing it to the non-copying variant of this operation.

+ +
import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.strings.TruffleString;
+
+abstract static class SomeNode extends Node {
+
+    @Specialization
+    static TruffleString someSpecialization(
+            @Cached TruffleString.FromByteArrayNode fromByteArrayNode) {
+        byte[] array = {'a', 'b', 'c'};
+        return fromByteArrayNode.execute(array, 0, array.length, TruffleString.Encoding.UTF_8, false);
+    }
+}
+
+ +

For easier creation of UTF-16 and UTF-32 strings independent of the system’s endianness, TruffleString provides TruffleString.FromCharArrayUTF16Node and TruffleString.FromIntArrayUTF32Node.

+ +

TruffleString may also be created via TruffleStringBuilder, which is TruffleString’s equivalent to java.lang.StringBuilder.

+ +

TruffleStringBuilder provides the following operations:

+ +
    +
  • AppendByte: +Append a single byte to a string builder.
  • +
  • AppendCharUTF16: +Append a single char to a UTF-16 string builder.
  • +
  • AppendCodePoint: +Append a single codepoint to string builder.
  • +
  • AppendIntNumber: +Append an integer number to a string builder.
  • +
  • AppendLongNumber: +Append a long number to a string builder.
  • +
  • AppendString: +Append a TruffleString to a string builder.
  • +
  • AppendSubstringByteIndex: +Append a substring, defined by a byte-based offset and length, to a string builder.
  • +
  • AppendJavaStringUTF16: +Append a Java String substring, defined by a char-based offset and length, to a string builder.
  • +
  • ToString: +Create a new TruffleString from a string builder.
  • +
+ +

See the below example:

+ +
import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleStringBuilder;
+
+abstract static class SomeNode extends Node {
+
+    @Specialization
+    static TruffleString someSpecialization(
+            @Cached TruffleStringBuilder.AppendCharUTF16Node appendCharNode,
+            @Cached TruffleStringBuilder.AppendJavaStringUTF16Node appendJavaStringNode,
+            @Cached TruffleStringBuilder.AppendIntNumberNode appendIntNumberNode,
+            @Cached TruffleStringBuilder.AppendStringNode appendStringNode,
+            @Cached TruffleString.FromCharArrayUTF16Node fromCharArrayUTF16Node,
+            @Cached TruffleStringBuilder.AppendCodePointNode appendCodePointNode,
+            @Cached TruffleStringBuilder.ToStringNode toStringNode) {
+        TruffleStringBuilder sb = TruffleStringBuilder.create(TruffleString.Encoding.UTF_16);
+        sb = appendCharNode.execute(sb, 'a');
+        sb = appendJavaStringNode.execute(sb, "abc", /* fromIndex: */ 1, /* length: */ 2);
+        sb = appendIntNumberNode.execute(sb, 123);
+        TruffleString string = fromCharArrayUTF16Node.execute(new char[]{'x', 'y'}, /* fromIndex: */ 0, /* length: */ 2);
+        sb = appendStringNode.execute(sb, string);
+        sb = appendCodePointNode.execute(sb, 'z');
+        return toStringNode.execute(sb); // string content: "abc123xyz"
+    }
+}
+
+ +

Encodings

+ +

Every TruffleString is encoded in a specific internal encoding, which is set during instantiation.

+ +

TruffleString is fully optimized for the following encodings:

+ +
    +
  • UTF-8
  • +
  • UTF-16
  • +
  • UTF-32
  • +
  • US-ASCII
  • +
  • ISO-8859-1
  • +
  • BYTES
  • +
+ +

Many other encodings are supported, but not fully optimized. +To use them, they must be enabled by setting needsAllEncodings = true in the Truffle language registration.

+ +

A TruffleString’s internal encoding is not exposed. +Instead of querying a string’s encoding, languages should pass an expectedEncoding parameter to all methods where the string’s encoding matters (which is almost all operations). +This allows re-using string objects when converting between encodings, if a string is byte-equivalent in both encodings. +A string can be converted to a different encoding using SwitchEncodingNode, as shown in the following example:

+ +
import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleStringBuilder;
+
+abstract static class SomeNode extends Node {
+
+    @Specialization
+    static void someSpecialization(
+            @Cached TruffleString.FromJavaStringNode fromJavaStringNode,
+            @Cached TruffleString.ReadByteNode readByteNode,
+            @Cached TruffleString.SwitchEncodingNode switchEncodingNode,
+            @Cached TruffleString.ReadByteNode utf8ReadByteNode) {
+
+        // instantiate a new UTF-16 string
+        TruffleString utf16String = fromJavaStringNode.execute("foo", TruffleString.Encoding.UTF_16);
+
+        // read a byte with expectedEncoding = UTF-16.
+        // if the string is not byte-compatible with UTF-16, this method will throw an IllegalArgumentException
+        System.out.printf("%x%n", readByteNode.execute(utf16String, /* byteIndex */ 0, TruffleString.Encoding.UTF_16));
+
+        // convert to UTF-8.
+        // note that utf8String may be reference-equal to utf16String!
+        TruffleString utf8String = switchEncodingNode.execute(utf16String, TruffleString.Encoding.UTF_8);
+
+        // read a byte with expectedEncoding = UTF-8
+        // if the string is not byte-compatible with UTF-8, this method will throw an IllegalArgumentException
+        System.out.printf("%x%n", utf8ReadByteNode.execute(utf8String, /* byteIndex */ 0, TruffleString.Encoding.UTF_8));
+    }
+}
+
+ +

Byte-equivalency between encodings is determined with string compaction on UTF-16 and UTF-32, so e.g. a compacted UTF-16 String is byte-equivalent to ISO-8859-1, and if all of its characters are in the ASCII range (see CodeRange), it is also byte-equivalent to UTF-8.

+ +

To check if your code is switching encodings properly, run your unit tests with the system property truffle.strings.debug-strict-encoding-checks=true. +This disables re-using string objects when switching encodings, and makes encoding checks more strict: all operations working on a single string will enforce an exact match, +whereas operations working on two strings will still allow byte-equivalent re-interpretations.

+ +

All TruffleString operations with more than one string parameter require the strings to be in an encoding compatible with the result encoding. +So either the strings need to be in the same encoding, or the caller must ensure that both Strings are compatible with the resulting encoding. +This enable callers which already know the SwitchEncodingNodes would be noops to just skip them for footprint reasons.

+ +
import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleStringBuilder;
+
+abstract static class SomeNode extends Node {
+
+    @Specialization
+    static boolean someSpecialization(
+            TruffleString a,
+            TruffleString b,
+            @Cached TruffleString.SwitchEncodingNode switchEncodingNodeA,
+            @Cached TruffleString.SwitchEncodingNode switchEncodingNodeB,
+            @Cached TruffleString.EqualNode equalNode) {
+        TruffleString utf8A = switchEncodingNodeA.execute(a, TruffleString.Encoding.UTF_8);
+        TruffleString utf8B = switchEncodingNodeB.execute(b, TruffleString.Encoding.UTF_8);
+        return equalNode.execute(utf8A, utf8B, TruffleString.Encoding.UTF_8);
+    }
+}
+
+ +

String Properties

+ +

TruffleString exposes the following properties:

+ +
    +
  • byteLength: The string’s length in bytes, exposed via the byteLength method.
  • +
  • codePointLength: The string’s length in codepoints, exposed via CodePointLengthNode.
  • +
  • isValid: Can be queried via IsValidNode to check whether the string is encoded correctly.
  • +
  • codeRange: Provides coarse information about the string’s content, exposed via GetCodeRangeNode. This property can +have the following values: +
      +
    • ASCII: All codepoints in this string are part of the Basic Latin Unicode block, also known as ASCII (0x00 - +0x7f).
    • +
    • LATIN-1: All codepoints in this string are part of the ISO-8859-1 character set (0x00 - 0xff), which is +equivalent to the union of the Basic Latin and the Latin-1 Supplement Unicode block. At least one codepoint in the +string is greater than 0x7f. Only applicable to ISO-8859-1, UTF-16 and UTF-32.
    • +
    • BMP: All codepoints in this string are part of the Unicode Basic Multilingual Plane (BMP) (0x0000 - 0xffff). At +least one codepoint in the string is greater than 0xff. Only applicable to UTF-16 and UTF-32.
    • +
    • VALID: This string is encoded correctly, and contains at least one codepoint outside the other applicable code +ranges (e.g. for UTF-8, this means there is one codepoint outside the ASCII range, and for UTF-16 this means that +there is one codepoint outside the BMP range).
    • +
    • BROKEN: This string is not encoded correctly. No further information about its contents can be determined.
    • +
    +
  • +
  • hashCode: The string’s hash code, exposed via HashCodeNode. The hash code is dependent on the string’s encoding; +strings must always be converted to a common encoding before comparing their hash codes!
  • +
+ +

See the below example how to query all properties exposed by TruffleString:

+ +
import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.strings.TruffleString;
+
+abstract static class SomeNode extends Node {
+
+    @Specialization
+    static TruffleString someSpecialization(
+            TruffleString string,
+            @Cached TruffleString.CodePointLengthNode codePointLengthNode,
+            @Cached TruffleString.IsValidNode isValidNode,
+            @Cached TruffleString.GetCodeRangeNode getCodeRangeNode,
+            @Cached TruffleString.HashCodeNode hashCodeNode) {
+        System.out.println("byte length: " + string.byteLength(TruffleString.Encoding.UTF_8));
+        System.out.println("codepoint length: " + codePointLengthNode.execute(string, TruffleString.Encoding.UTF_8));
+        System.out.println("is valid: " + isValidNode.execute(string));
+        System.out.println("code range: " + getCodeRangeNode.execute(string));
+        System.out.println("hash code: " + hashCodeNode.execute(string, TruffleString.Encoding.UTF_8));
+    }
+}
+
+ +

String Equality and Comparison

+ +

TruffleString objects should be checked for equality using EqualNode. +Just like HashCodeNode, the equality comparison is sensitive to the string’s encoding, so before any comparison, strings should always be converted to a common encoding. Object#equals(Object) behaves analogous to EqualNode, but since this method does not have an expectedEncoding parameter, it will determine the string’s common encoding automatically. +If the string’s encodings are not equal, TruffleString will check whether one string is binary-compatible to the other string’s encoding, and if so, match their content. Otherwise, the strings are deemed not equal, no automatic conversion is applied.

+ +

Note that since TruffleString’s hashCode and equals methods are sensitive to string encoding, TruffleString objects must always be converted to a common encoding before, e.g., using them as keys in a HashMap.

+ +

TruffleString also provides three comparison nodes CompareBytesNode, CompareCharsUTF16Node, and CompareIntsUTF32Node, to compare strings respectively byte-by-byte, char-by-char, and int-by-int.

+ +

Concatenation

+ +

Concatenation is done via ConcatNode. +This operation requires both strings to be in expectedEncoding, which is also the encoding of the resulting string. Lazy concatenation is supported via the lazy parameter. +When two strings are concatenated lazily, the allocation and initialization of the new string’s internal array is delayed until another operation requires direct access to that array. +Materialization of such “lazy concatenation strings” can be triggered explicitly with a MaterializeNode. +This is useful to do before accessing a string in a loop, such as in the following example:

+ +
import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.strings.TruffleString;
+
+abstract static class SomeNode extends Node {
+
+    @Specialization
+    static TruffleString someSpecialization(
+            TruffleString utf8StringA,
+            TruffleString utf8StringB,
+            @Cached TruffleString.ConcatNode concatNode,
+            @Cached TruffleString.MaterializeNode materializeNode,
+            @Cached TruffleString.ReadByteNode readByteNode) {
+        // lazy concatenation
+        TruffleString lazyConcatenated = concatNode.execute(utf8StringA, utf8StringB, TruffleString.Encoding.UTF_8, /* lazy */ true);
+
+        // explicit materialization
+        TruffleString materialized = materializeNode.execute(lazyConcatenated, TruffleString.Encoding.UTF_8);
+
+        int byteLength = materialized.byteLength(TruffleString.Encoding.UTF_8);
+        for (int i = 0; i < byteLength; i++) {
+            // string is guaranteed to be materialized here, so no slow materialization code can end up in this loop
+            System.out.printf("%x%n", readByteNode.execute(materialized, i, TruffleString.Encoding.UTF_8));
+        }
+    }
+}
+
+ +

Substrings

+ +

Substrings can be created via SubstringNode and SubstringByteIndexNode, which use codepoint-based and byte-based indices, respectively. +Substrings can also be lazy, meaning that no new array is created for the resulting string, but instead the parent string’s array is re-used and just accessed with the offset and length passed to the substring node. +Currently, a lazy substring’s internal array is never trimmed (i.e. replaced by a new array of the string’s exact length). +Note that this behavior effectively creates a memory leak whenever a lazy substring is created. +An extreme example where this could be problematic: given a string that is 100 megabyte in size, any lazy substring created from this string will keep the 100 megabyte array alive, even when the original string is freed by the garbage collector. +Use lazy substrings with caution.

+ +

Interoperability with java.lang.String

+ +

TruffleString provides FromJavaStringNode for converting a java.lang.String to TruffleString. +To convert from TruffleString to java.lang.String, use a ToJavaStringNode. +This node will internally convert the string to UTF-16, if necessary, and create a java.lang.String from that representation.

+ +

Object#toString() is implemented using the uncached version of ToJavaStringNode and should be avoided on fast paths.

+ +
import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.strings.TruffleString;
+
+abstract static class SomeNode extends Node {
+
+    @Specialization
+    static void someSpecialization(
+            @Cached TruffleString.FromJavaStringNode fromJavaStringNode,
+            @Cached TruffleString.SwitchEncodingNode switchEncodingNode,
+            @Cached TruffleString.ToJavaStringNode toJavaStringNode,
+            @Cached TruffleString.ReadByteNode readByteNode) {
+        TruffleString utf16String = fromJavaStringNode.execute("foo", TruffleString.Encoding.UTF_16);
+        TruffleString utf8String = switchEncodingNode.execute(utf16String, TruffleString.Encoding.UTF_8);
+        System.out.println(toJavaStringNode.execute(utf8String));
+    }
+}
+
+ +

TruffleString also exposes #toStringDebug() for debugging purposes. +Do not use this method for anything other than debugging, as its return value is unspecified and may change at any time.

+ +

Differences to java.lang.String

+ +

The following items should be considered when switching from java.lang.String to TruffleString:

+ +
    +
  • The static overhead of TruffleString instances is larger than that of java.lang.String objects. A TruffleString object +contains 2 pointers fields, 4 int fields and 4 byte fields, which will usually result in a total object size of 40 +bytes (object header of 12 bytes, 4 bytes per pointer with compressed oops, 8-byte memory alignment). A +java.lang.String object contains one pointer field, one int field and one byte field, which in the same conditions +results in a total object size of 24 bytes. This difference in memory footprint may negatively impact some cases where +lots of small strings are generated.
  • +
  • TruffleString does string compaction just like java.lang.String.
  • +
  • If your language needs to convert strings to other encodings, e.g. UTF-8, which is very common in web applications, +TruffleString can turn this operation into a no-op if the string does now contain special characters. For example, +ASCII-only strings can be re-interpreted as almost any encoding, and converting an ASCII-only UTF-16 string to UTF-8 +is a no-op. +In cases where transcoding a string is unavoidable, TruffleStrings will cache the transcoded string in the original +string, so transcoding is only done once per string and encoding.
  • +
  • In order to use 3rd party libraries, TruffleString object will have to be converted to java.lang.String and back. In +order to make this as cheap as possible, TruffleString re-uses Java String’s internal byte arrays when converting from +java.lang.String to TruffleString, and caches Java Strings created from TruffleString objects in the object itself.
  • +
  • TruffleString offers additional features not present in java.lang.String: +
      +
    • Lazy concatenation and string views, which can significantly decrease the amount of array-copy operations your +language may have to do.
    • +
    • String views into native memory, completely avoiding the need to copy native memory into Java arrays before using +it.
    • +
    • String content classification via the codeRange property, which allows specializations on strings that are +ASCII-only et cetera. This can reduce the complexity of some string operations significantly.
    • +
    +
  • +
  • The performance of all TruffleString operations should be on par with or better than their java.lang.String-counterparts.
  • +
+ +

Codepoint Iterators

+ +

TruffleString provides TruffleStringIterator as a means of iterating over a string’s codepoints. +This method should be preferred over using CodePointAtIndexNode in a loop, especially on variable-width encodings such as UTF-8, since CodePointAtIndexNode may have to re-calculate the byte index equivalent of the given codepoint index on every call.

+ +

See the example:

+ +
import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.strings.TruffleString;
+import com.oracle.truffle.api.strings.TruffleStringIterator;
+
+abstract static class SomeNode extends Node {
+
+    @Specialization
+    static void someSpecialization(
+            TruffleString string,
+            @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode,
+            @Cached TruffleStringIterator.NextNode nextNode,
+            @Cached TruffleString.CodePointLengthNode codePointLengthNode,
+            @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode) {
+
+        // iterating over a string's codepoints using TruffleStringIterator
+        TruffleStringIterator iterator = createCodePointIteratorNode.execute(string, TruffleString.Encoding.UTF_8);
+        while (iterator.hasNext()) {
+            System.out.printf("%x%n", nextNode.execute(iterator));
+        }
+
+        // suboptimal variant: using CodePointAtIndexNode in a loop
+        int codePointLength = codePointLengthNode.execute(string, TruffleString.Encoding.UTF_8);
+        for (int i = 0; i < codePointLength; i++) {
+            // performance problem: codePointAtIndexNode may have to calculate the byte index corresponding
+            // to codepoint index i for every loop iteration
+            System.out.printf("%x%n", codePointAtIndexNode.execute(string, i, TruffleString.Encoding.UTF_8));
+        }
+    }
+}
+
+ +

Mutable Strings

+ +

TruffleString also provides a mutable string variant called MutableTruffleString, which is also accepted in all nodes of TruffleString. MutableTruffleString is not thread-safe and allows overwriting bytes in its internal byte array or native pointer via WriteByteNode. +The internal array or native pointer’s content may also be modified externally, but the corresponding MutableTruffleString must be notified of this via notifyExternalMutation(). +MutableTruffleString is not a Truffle interop type, and must be converted to an immutable TruffleString via TruffleString.AsTruffleString before passing a language boundary.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/index.html new file mode 100644 index 0000000..1ac1738 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/index.html @@ -0,0 +1,201 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Truffle Language Implementation Framework

+ +

The Truffle language implementation framework (Truffle) is an open source library for building tools and programming languages implementations as interpreters for self-modifying Abstract Syntax Trees. +Together with the open source Graal compiler, Truffle represents a significant step forward in programming language implementation technology in the current era of dynamic languages.

+ +

The Truffle bits are uploaded to Maven central. +You can use them from your pom.xml file as:

+ +
<dependency>
+    <groupId>org.graalvm.truffle</groupId>
+    <artifactId>truffle-api</artifactId>
+    <version>22.1.0</version> <!-- or any later version -->
+</dependency>
+<dependency>
+    <groupId>org.graalvm.truffle</groupId>
+    <artifactId>truffle-dsl-processor</artifactId>
+    <version>22.1.0<</version>
+    <scope>provided</scope>
+</dependency>
+
+ +

Implement Your Language

+ +

The Truffle framework allows you to run programming languages efficiently on GraalVM. +It simplifies language implementation by automatically deriving high-performance code from interpreters.

+ +

Getting Started

+ +

Information on how to get starting building your language can be found in the Language Implementation Tutorial. +The reference API documentation is available as part of the Truffle Javadoc. +Start with looking at the TruffleLanguage class, which one should subclass to start developing a language. +Truffle comes prebuilt with the Graal Compiler and several language implementations as part of GraalVM.

+ +

A good way to start implementing your language with Truffle is to fork the SimpleLanguage project and start hacking. +SimpleLanguage is a relatively small language implementation, well-documented, and designed to demonstrate most of the Truffle features. +You could also try by looking at code in one of the existing open source languages implementations and experiments.

+ +

Consider reading these publications for a very detailed view into how many of the aspects of Truffle work. However, as with any other software project, the source code is the ground truth.

+ +

Advanced Topics

+ +

Implementing a language using Truffle offers a way to interoperate with other “Truffle” languages. +To learn more about verifying that your language is a valid polyglot citizen, read more about using the Polyglot TCK. +Somewhat related topics worth exploring are Truffle Libraries, as well as how to use them to implement a language interoperability. +Languages implemented with Truffle can also be embedded in Java host applications using the Polyglot API.

+ +

To better understand how to improve the performance of your language please consult the documentation on profiling and optimizing your language. +Also, to better understand how to use Truffle’s automated monomorphization feature (i.e., splitting), look at the related documentation.

+ +

Implement Your Tool

+ +

GraalVM provides a framework for creating language-agnostic tools like debuggers, profilers, and other instrumentations. +In general, GraalVM provides a standardized way to express and run program code enabling cross-language research and the development of tools that can be developed once and then applied to any language.

+ +

The reference API documentation is available as part of the Truffle Javadoc. +Start with looking at the TruffleInstrument class, which – similar to TruffleLanguage – one should subclass to start developing a tool.

+ +

If you want to implement your own “Truffle” tool, a good way to start is to fork the SimpleTool project – like the SimpleLanguage project described above – and start hacking. +SimpleTool is a well-documented, minimalistic code-coverage tool designed to be a starting point for understanding the tool development process using Truffle.

+ +

Since tools, developed with Truffle, instrument the language using the same AST-node-based approach, most of the techniques available to language developers in terms of improving performance are available to the tool developers as well. +This is why it is recommended that you understand how Truffle works from a language developer’s perspective, in order to get the maximum out of your tool.

+ +

Compatibility

+ +

The Truffle API is evolved in a backwards-compatible manner from one version to the next. +When an API is deprecated, then it will stay deprecated for at least two GraalVM releases, and a minimum of one month, before it will be removed.

+ +

As a best practice it is recommended to upgrade Truffle only one version at a time. +This way you can increment the version and fix deprecation warnings before continuing to the next version. +The deprecated Javadoc tags on the deprecated APIs are designed to be a guide on how to upgrade.

+ +

The latest additions and changes can be seen in the changelog.

+ +

Modifying Truffle

+ +

To understand how to modify Truffle, consult this file, and if you would like to contribute to Truffle, consult the contribution documentation.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/Monomorphization/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/Monomorphization/index.html new file mode 100644 index 0000000..167d2c6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/Monomorphization/index.html @@ -0,0 +1,134 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Monomorphization

+ +

Truffle has an automatic approach to monomorphization (also known as “splitting”). For more information about the benefits of monomorphization, continue reading to Monomorphization Use Cases.

+ +

It is controlled by the Splitting engine option and is on by default. +Adding --engine.Splitting=false to your command line will disable it.

+ +

The heuristic relies on information from the language implementation to guide the decisions. To find out more about how to use the new approach in your language implementation, refer to the Reporting Polymorphism guide.

+ +

For more details on how the new approach works, see the Splitting guide.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/MonomorphizationUseCases/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/MonomorphizationUseCases/index.html new file mode 100644 index 0000000..1f602db --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/MonomorphizationUseCases/index.html @@ -0,0 +1,277 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Monomorphization Use Cases

+ +

This guide demonstrates through examples how monomorphization can improve performance of dynamic languages without going into any detail on how monomorphization is implemented (described in the Splitting guide) or how to leverage monomorphization in your language implementation (described in the Reporting Polymorphism guide).

+ +

Monomorphization

+ +

To better illustrate the benefits of monomorphization, consider a small example written in JavaScript:

+ +
function add(arg1, arg2) {
+    return arg1 + arg2;
+}
+
+function callsAdd() {
+    add(1, 2);
+    add("foo", "bar");
+}
+
+var i = 0;
+while (i < 1000) {
+    callsAdd();
+    i++;
+}
+
+ +

As you can see in this example, the add function is called from callsAdd once with integer arguments and once with string arguments. +Once add is executed enough times to be compiled its execution profile will show that the + operator has been executed with both integers and strings and thus handlers (i.e., type checks and execution) for both types will be compiled which has a +performance impact. +This can be avoided by rewriting the example as follows:

+ +
function addInt(arg1, arg2) {
+    return arg1 + arg2;
+}
+
+function addString(arg1, arg2) {
+    return arg1 + arg2;
+
+}
+function callsAdd() {
+    addInt(1, 2);
+    addString("foo", "bar");
+}
+
+i = 0;
+while (i < 1000) {
+    callsAdd();
+    i++;
+}
+
+ +

In this example the add has been duplicated (split) in such a way that each type profile is contained in a separate copy of the function (addInt and addString). +The result is that, come compilation time, only a single type profile is available for each function avoiding potentially costly type checks in the compiled code.

+ +

Automating the detection suitable candidates, as well as their duplication, performed at run time is what we call monomorphization. +It is, in other words, automated run-time monomorphization of polymorphic nodes through AST duplication.

+ +

Example 1 - Monomorphization of Arguments

+ +

This example is an extended version of the illustration example from the previous section. +The add function is still the target for monomorphization and is called from the action function 3 times with 3 sets of different arguments (numbers, strings and arrays). +Execute the action function for 15 seconds in order to have enough time for warmup, and afterwards execute it for 60 seconds keeping track of how long each execution took, reporting finally the average. +Execute this code twice: once with and once without monomorphization enabled and report the output of these two runs as well as the speedup.

+ +
function add(arg1, arg2) {
+    return arg1 + arg2;
+}
+
+var global = 0;
+
+function action() {
+    for (var i = 0; i < 10000; i++) {
+        global = add(1, 2);
+        global = add("foo", "bar");
+        global = add([1,2,3], [4,5,6]);
+    }
+}
+
+
+// Warm up.
+var start = Date.now();
+while ((Date.now() - start) < 15000 /* 15 seconds */) {
+    action();
+}
+// Benchmark
+var iterations = 0;
+var sum = 0;
+var start = Date.now();
+while ((Date.now() - start) < 60000 /* 60 seconds */) {
+    var thisIterationStart = Date.now();
+    action();
+    var thisIterationTime = Date.now() - thisIterationStart;
+    iterations++;
+    sum += thisIterationTime;
+}
+console.log(sum / iterations);
+
+ +

The output without monomorphization is 4.494225288735564. +The output with monomorphization is 4.2421633923. +The speedup is ~5%.

+ +

Example 2 - Monomorphization of Indirect Calls

+ +

This example is slightly more complicated and illustrates how monomorphization benefits higher order functions. In the example, the insertionSort function is defined, which - given an array of items and a function for comparing these items - sorts the array using insertion sort. +Define an array of 1000 random double values between 0 and 1 and sort it four times using 4 different sorting methods (in the action function). +Finally, as with the previous example, warm up the action function for 15 second, and report the average execution time of +this function over the next 60 seconds with and without monomorphization.

+ +
function insertionSort (items, comparator) {
+    for (var i = 0; i < items.length; i++) {
+        let value = items[i];
+        for (var j = i - 1; j >= 0 && comparator(items[j], value); j--) {
+            items[j + 1] = items[j]
+        }
+        items[j + 1] = value
+    }
+}
+
+// Random values in an array
+var array = new Array(1000);
+for (i = 0; i < array.length; i++) {
+    array[i] = Math.random();
+}
+
+
+function action() {
+    insertionSort(array, function (a, b) { return a < b                                      });
+    insertionSort(array, function (a, b) { return a > b                                      });
+    insertionSort(array, function (a, b) { return a.toString().length < b.toString().length; });
+    insertionSort(array, function (a, b) { return a.toString().length > b.toString().length; });
+}
+
+// Warm up.
+var start = Date.now();
+while ((Date.now() - start) < 15000 /* 15 seconds */) {
+    action();
+}
+// Benchmark
+var iterations = 0;
+var sum = 0;
+var start = Date.now();
+while ((Date.now() - start) < 60000 /* 60 seconds */) {
+    var thisIterationStart = Date.now();
+    action();
+    var thisIterationTime = Date.now() - thisIterationStart;
+    iterations++;
+    sum += thisIterationTime;
+}
+console.log(sum / iterations);
+
+ +

The output without monomorphization is 194.05161290322582. +The output with monomorphization is 175.41071428571428. +The speedup is ~10%.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/ReportingPolymorphism/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/ReportingPolymorphism/index.html new file mode 100644 index 0000000..22247fd --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/ReportingPolymorphism/index.html @@ -0,0 +1,311 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Reporting Polymorphic Specializations to Runtime

+ +

This guide gives an overview of what is required of language implementers in order to leverage the monomorphization (splitting) strategy. +More information on how it works can be found in the Splitting guide.

+ +

In simple terms, the monomorphization heuristic relies on the language reporting polymorphic specializations for each node that could potentially be returned to a monomorphic state through splitting. +In this context a polymorphic specialization is any node rewriting which results in the node changing “how polymorphic” it is. This includes, but is not limited to, activating another specialization, increasing the number of instances of an active specialization, excluding a specialization, etc..

+ +

Manual Reporting of Polymorphic Specializations

+ +

To facilitate reporting of polymorphic specializations, a new API was introduced +into the Node class: Node#reportPolymorphicSpecialize. +This method can be used to manually report polymorphic specializations, but only in cases when this cannot be automated by using the DSL.

+ +

Automated Reporting of Polymorphic Specializations

+ +

Since the Truffle DSL automates much of the transitions between specializations, the @ReportPolymorphism annotation for automated reporting of polymorphic specializations was added. +This annotation instructs the DSL to include checks for polymorphism after specializations and to call Node#reportPolymorphicSpecialize if needed.

+ +

For an example on how to use this annotation, consider the com.oracle.truffle.sl.nodes.SLStatementNode. It is the base class for all +SimpleLanguage nodes and, since the ReportPolymorphism annotation is inherited, simply annotating this class will enable reporting of polymorphic specializations for all SimpleLanguage nodes. +Below is the diff of the change that adds this annotation to SLStatementNode:

+ +
diff --git
+a/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java
+b/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java
+index 788cc20..89448b2 100644
+---
+a/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java
++++
+b/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java
+@@ -43,6 +43,7 @@ package com.oracle.truffle.sl.nodes;
+ import java.io.File;
+
+ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
++import com.oracle.truffle.api.dsl.ReportPolymorphism;
+ import com.oracle.truffle.api.frame.VirtualFrame;
+ import com.oracle.truffle.api.instrumentation.GenerateWrapper;
+ import com.oracle.truffle.api.instrumentation.InstrumentableNode;
+@@ -62,6 +63,7 @@ import com.oracle.truffle.api.source.SourceSection;
+  */
+ @NodeInfo(language = "SL", description = "The abstract base node for all SL
+statements")
+ @GenerateWrapper
++@ReportPolymorphism
+ public abstract class SLStatementNode extends Node implements
+InstrumentableNode {
+
+     private static final int NO_SOURCE = -1;
+
+ +

Controlling Automated Reporting of Polymorphic Specializations

+ +

Excluding particular nodes and specializations

+ +

Applying the ReportPolymorphism annotation to all nodes of a language is the simplest way to facilitate the monomorphization, but it could cause reporting of polymorphic specializations in cases where that does not necessarily make sense. +In order to give the language developer more control over which nodes and which specializations are taken into consideration for reporting polymorphism, the @ReportPolymorphism.Exclude annotation was introduced which is applicable to classes (disabling automated reporting for the entire class) or to individual specializations (excluding those specializations from consideration when checking for polymorphism).

+ +

Reporting only on Megamorphic Cases

+ +

As of version 20.3.0 a new annotation was added: ReportPolymorphism.Megamorphic. +This annotation can only be applied to specializations, as marks that specialization as megamorphic as it is intented to be used on expensive “generic” specializations that should be fixed by monomorphization. +The effect of adding this annotation is that, once the annotated specialisation becomes active, the node will report polymorphism to the runtime independant of the state of other specializations.

+ +

This annotation can be used separately from @ReportPolymorphism, i.e., a node does not need to be annotated with @ReportPolymorphism for the megamorphic annotation to work. +If both annotations are used, then both polymorphic and megamorphic activations will be reported as polymorphism.

+ +

Tools Support

+ +

Knowing which nodes should and should not report polymorphic specializations is for the language developer to conclude. +This can be done either through domain knowledge (which nodes of the language are expensive when polymorphic), or through experimentation (measuring the effects of including/excluding particular nodes/specializations). +To aid language developers in better understanding the impact of reporting polymorphic specializations some tools support was provided.

+ +

Tracing individual splits

+ +

Adding the --engine.TraceSplitting argument to the command line when executing your guest language code will, in real time, print information about each split the runtime makes.

+ +

A small part of the output from running one of the JavaScript benchmarks with the flag enabled follows.

+ +
...
+[engine] split   0-37d4349f-1     multiplyScalar |ASTSize      40/   40 |Calls/Thres       2/    3 |CallsAndLoop/Thres       2/ 1000 |Inval#              0 |SourceSection octane-raytrace.js~441-444:12764-12993
+[engine] split   1-2ea41516-1     :anonymous |ASTSize       8/    8 |Calls/Thres       3/    3 |CallsAndLoop/Thres       3/ 1000 |Inval#              0 |SourceSection octane-raytrace.js~269:7395-7446
+[engine] split   2-3a44431a-1     :anonymous |ASTSize      28/   28 |Calls/Thres       4/    5 |CallsAndLoop/Thres       4/ 1000 |Inval#              0 |SourceSection octane-raytrace.js~35-37:1163-1226
+[engine] split   3-3c7f66c4-1     Function.prototype.apply |ASTSize      18/   18 |Calls/Thres       7/    8 |CallsAndLoop/Thres       7/ 1000 |Inval#              0 |SourceSection octane-raytrace.js~36:1182-1219
+...
+
+

Tracing a splitting summary

+ +

Adding the --engine.TraceSplittingSummary argument to the command line when executing your guest language code will, after the execution is complete, print out a summary of the gathered data regarding splitting. +This includes how many splits there were, how large is the splitting budget and how much of it was used, how many splits were forced, a list of split target names and how many times they were split and a list of nodes that reported polymorphic specializations and how many.

+ +

A slightly simplified output of running one of the JavaScript benchmarks with the flag enabled follows.

+ +
[engine] Splitting Statistics
+Split count                             :       9783
+Split limit                             :      15342
+Split count                             :          0
+Split limit                             :        574
+Splits                                  :        591
+Forced splits                           :          0
+Nodes created through splitting         :       9979
+Nodes created without splitting         :      10700
+Increase in nodes                       :     93.26%
+Split nodes wasted                      :        390
+Percent of split nodes wasted           :      3.91%
+Targets wasted due to splitting         :         27
+Total nodes executed                    :       7399
+
+--- SPLIT TARGETS
+initialize                              :         60
+Function.prototype.apply                :        117
+Array.prototype.push                    :          7
+initialize                              :          2
+magnitude                               :         17
+:anonymous                              :        117
+add                                     :          5
+...
+
+--- NODES
+class ANode                             :         42
+class AnotherNode                       :        198
+class YetAnotherNode                    :          1
+...
+
+ +

Tracing polymorphic specializations

+ +

Consider reading the Splitting guide before this section, as the dumped data is directly related to how splitting works.

+ +

To better understand how reporting polymorphism impacts which call targets are considered for splitting one can use the --engine.SplittingTraceEvents option. +This option will print, in real time, a log detailing which nodes are reporting polymorphism and how that is affecting the call targets. +See the following examples.

+ +
Example 1
+ +
[engine] [poly-event] Polymorphic event! Source: JSObjectWriteElementTypeCacheNode@e3c0e40   WorkerTask.run
+[engine] [poly-event] Early return: false callCount: 1, numberOfKnownCallNodes: 1            WorkerTask.run
+
+ +

This log section tells that the JSObjectWriteElementTypeCacheNode in the WorkerTask.run method turned polymorphic and reported it. +It also tells that this is the first time that WorkerTask.run is being executed (callCount: 1), thus you do not mark it as “needs split” (Early return: false)

+ +
Example 2
+ +
[engine] [poly-event] Polymorphic event! Source: WritePropertyNode@50313382                  Packet.addTo
+[engine] [poly-event] One caller! Analysing parent.                                          Packet.addTo
+[engine] [poly-event]   One caller! Analysing parent.                                        HandlerTask.run
+[engine] [poly-event]     One caller! Analysing parent.                                      TaskControlBlock.run
+[engine] [poly-event]       Early return: false callCount: 1, numberOfKnownCallNodes: 1      Scheduler.schedule
+[engine] [poly-event]     Return: false                                                      TaskControlBlock.run
+[engine] [poly-event]   Return: false                                                        HandlerTask.run
+[engine] [poly-event] Return: false                                                          Packet.addTo
+
+ +

In this example the source of the polymorphic specialization is WritePropertyNode in Packet.addTo. +Since this call target has only one known caller, you can analyse its parent in the call tree (i.e., the caller). +This is, in the example, HandlerTask.run and the same applies to it as well, leading to TaskControlBlock.run, and by the same token to Scheduler.schedule. +Scheduler.schedule has a callCount of 1, i.e., this is its first execution, so you do not mark it as “needs split” (Early return: false).

+ +
Example 3
+ +
[engine] [poly-event] Polymorphic event! Source: JSObjectWriteElementTypeCacheNode@3e44f2a5  Scheduler.addTask
+[engine] [poly-event] Set needs split to true                                                Scheduler.addTask
+[engine] [poly-event] Return: true                                                           Scheduler.addTask
+
+ +

In this example the source of the polymorphic specialization is JSObjectWriteElementTypeCacheNode in Scheduler.addTask. +This call target is immediately marked as “needs split”, since all the criteria to do so are met.

+ +
Example 3
+ +
[engine] [poly-event] Polymorphic event! Source: WritePropertyNode@479cbee5                  TaskControlBlock.checkPriorityAdd
+[engine] [poly-event] One caller! Analysing parent.                                          TaskControlBlock.checkPriorityAdd
+[engine] [poly-event]   Set needs split to true                                              Scheduler.queue
+[engine] [poly-event]   Return: true                                                         Scheduler.queue
+[engine] [poly-event] Set needs split to true via parent                                     TaskControlBlock.checkPriorityAdd
+[engine] [poly-event] Return: true                                                           TaskControlBlock.checkPriorityAdd
+
+ +

In this example the source of the polymorphic specialization is WritePropertyNode in TaskControlBlock.checkPriorityAdd. +Since it has only one caller, you look at that caller (Scheduler.queue), and since all the criteria necessary seem to be met, you mark it as “needs split”.

+ +

Dumping polymorphic specializations to IGV

+ +

Consider reading the Splitting guide before this section, as the dumped data is directly related to how splitting works.

+ +

Adding the --engine.SplittingDumpDecisions argument to the command line when executing your guest language code will, every time a call target is marked “needs split”, dump a graph showing a chain of nodes (linked by child connections as well as direct call node to callee root node links) ending in the node that called Node#reportPolymorphicSpecialize.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/Splitting/index.html b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/Splitting/index.html new file mode 100644 index 0000000..0757ecc --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/graalvm-as-a-platform/language-implementation-framework/splitting/Splitting/index.html @@ -0,0 +1,291 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Splitting Algorithm

+ +

This guide gives an overview of the algorithm used in the implementation of Truffle call target splitting.

+ +

The new implementation relies on the language implementations providing information on when a particular node turns polymorphic or increases its “degree” of polymorphism by, for example, adding an entry into an inline cache. +This event is called a “polymorphic specialize”. +This information is provided to the runtime by calling the +Node.reportPolymorphicSpecialize method after the specialization is complete.

+ +

This guide explains what happens after the call to reportPolymorphicSpecialize. +You can find more information on how to correctly report polymorphic specializations in the Reporting Polymorphism guide.

+ +

Approach

+ +

Detecting of suitable splitting candidates relies on the languages reporting polymorphic specializations. +Once the specialization is reported, you can assume that the polymorphism is coming from somewhere in the caller chain of the call target hosting the newly polymorphic node, and that by splitting the right call target (or call targets) you can return this node to a monomorphic state.

+ +

You then identify the call targets for which the splitting could result in monomorphization and mark them as “needs split”. During further execution, if the interpreter is about to execute a direct call to a call target that is marked as “needs split”, that call target will be split (provided there are no outstanding factors preventing it such as the root node not being allowed to be split, +the AST being too big, etc.). +This results in a new call target with a clean profile (i.e., all its nodes are returned to an uninitialized state) to be +re-profiled specifically for this call site, since it is the only call site calling this new call target.

+ +

Following recursive algorithm (expressed as pseudo code) is a simplified version of the approach used to decide which call targets need to be marked “needs split”. +This algorithm is applied to every call target once one of its nodes reports a polymorphic specialization. +The full implementation can be found in org.graalvm.compiler.truffle.runtime.OptimizedCallTarget#maybeSetNeedsSplit.

+ +
setNeedsSplit(callTarget)
+    if callTarget.needsSplit
+        return false
+    if sizeof(knownCallers(callTarget)) == 0
+        return false
+    if callCount(callTarget) == 1
+        return false
+
+    if sizeof(knownCallers(callTarget)) > 1
+        callTarget.needsSplit = true
+    else
+        callTarget.needsSplit = setNeedsSplit(caller(callTarget))
+
+    return callTarget.needsSplit
+
+ +

At the very beginning of the pseudo code you can have early termination conditions. +If the call target is already marked as “needs split”, there is need to continue. +Also, if the call targets has no known callers (e.g., it is the “main” of the execution) splitting is not applicable since splitting is inherently tied to duplicating ASTs for a particular call site. +Finally, if this is happening during the first execution of call target, splitting is pointless since the polymorphic nature of the node is inevitable (i.e., not coming from the callers, but rather an integral property of that call target).

+ +

In the second part of the pseudo code two cases are differentiated:

+ +

1) The call target has multiple known callers - in this case you can assume that the polymorphism is coming from one of these multiple callers. Thus, you mark the call target as “needs split”.

+ +

2) The call target has only one known caller - in this case you know that marking this call target as “needs split” cannot help remove the polymorphism. But, the polymorphism could be coming into this call target from its sole caller, which could have multiple callers and could be a candidate for splitting. Thus, you recursively apply the algorithm to the caller of our call target.

+ +

Ignore for now the return value of our algorithm and its usage, and consider the following SimpleLanguage example to illustrate why this distinction between one and multiple callers is needed:

+ +
function add(arg1, arg2) {
+    return arg1 + arg2;
+}
+
+function double(arg1) {
+    return add(arg1, arg1);
+}
+
+function callsDouble() {
+    double(1);
+    double("foo");
+}
+
+function main() {
+    i = 0;
+    while (i < 1000) {
+        callsDouble();
+    }
+}
+
+ +

In this example, the node representing + in the add function will turn polymorphic once double is called with the string argument "foo" and this will be reported to the runtime and our algorithm will be applied to add. +All of the early return checks will fail (add is not marked “needs split”, it has known callers and this is not its first execution). +Observe that add has only one caller (double), so you apply the algorithm to double. +Early returns all fail, and since double has multiple callers, you mark it as “needs split” and on later iterations calls to double are split resulting in the following code representation of the run time state:

+ +
function add(arg1, arg2) {
+    return arg1 + arg2; // + is polymorphic
+}
+
+function double(arg1) {
+    return add(arg1, arg1);
+}
+
+function doubleSplit1(arg1) {
+    return add(arg1, arg1);
+}
+
+function doubleSplit2(arg1) {
+    return add(arg1, arg1);
+}
+
+function callsDouble() {
+    doubleSplit1(1);
+    doubleSplit2("foo");
+}
+
+function main() {
+    i = 0;
+    while (i < 1000) {
+        callsDouble();
+    }
+}
+
+ +

As you can see, the source of the polymorphism was split, but that did not solve the issue, since both slits still call the same add function and the polymorphism remains. +This is where the algorithms return value comes in to play. +If the algorithm was successful in finding a target to mark than all the transitive callee’s of that target need to be marked “needs split” as well. +With this final step in place, the final run time result of our splitting approach for the previous example can be represent as the following source code:

+ +
function add(arg1, arg2) {
+    return arg1 + arg2; // + is polymorphic
+}
+
+function addSplit1(arg1, arg2) {
+    return arg1 + arg2;
+
+}
+function addSplit2(arg1, arg2) {
+    return arg1 + arg2;
+}
+
+function double(arg1) {
+    return add(arg1, arg1);
+}
+
+function doubleSplit1(arg1) {
+    return addSplit1(arg1, arg1);
+}
+
+function doubleSplit2(arg1) {
+    return addSplit2(arg1, arg1);
+}
+
+function callsDouble() {
+    doubleSplit1(1);
+    doubleSplit2("foo");
+}
+
+function main() {
+    i = 0;
+    while (i < 1000) {
+        callsDouble();
+    }
+}
+
+ +

Final note to observe at this point is that the splitting does not remove the original call targets, and that they still have polymorphism in their profiles. +Thus, even if new calls to these call targets are created, they will also be split. +Consider if the main of the previous example looked as follows.

+ +
function main() {
+    i = 0;
+    while (i < 1000) {
+        callsDouble();
+    }
+    add(1,2); // this line was added
+}
+
+ +

Once the execution reaches the newly added line you do not want it to call the add function with the polymorphic + since the arguments here do not merit the polymorphism. +Luckily, since add was already marked as “needs split”, it will remain so during the entire execution, and this final call to add with cause another split of the add functions.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/guides/index.html b/native-image/spring-boot-webserver/src/main/resources/static/guides/index.html new file mode 100644 index 0000000..2c23e36 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/guides/index.html @@ -0,0 +1,36 @@ +

Guides

+ +

These guides help developers get started with GraalVM Native Image, acquaint them with available features, and describe potential usage scenarios. +Here you will learn how to:

+ + + +

Microservices Frameworks

+ + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/index.html b/native-image/spring-boot-webserver/src/main/resources/static/index.html new file mode 100644 index 0000000..25c0264 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/index.html @@ -0,0 +1,372 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+

GraalVM for JDK 20 Documentation

+ +

Overview

+ + +

Getting Started

+ + +

Native Image

+ + +

Reference Manuals

+ + +

GraalVM as a Platform

+ + +

Security

+ + +

Tools for GraalVM Languages

+ +
+
+
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/native-image/guides/index.html b/native-image/spring-boot-webserver/src/main/resources/static/native-image/guides/index.html new file mode 100644 index 0000000..3bdcea9 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/native-image/guides/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/compiler/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/compiler/index.html new file mode 100644 index 0000000..8878884 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/compiler/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/embed-languages/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/embed-languages/index.html new file mode 100644 index 0000000..c926071 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/embed-languages/index.html @@ -0,0 +1,3233 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Embedding Languages

+ + + +

The GraalVM Polyglot API lets you embed and run code from guest languages in JVM-based host applications.

+ +

Throughout this section, you will learn how to create a host application in Java that runs on GraalVM and directly calls a guest language. +You can use the tabs beneath each code example to choose between JavaScript, R, Ruby, and Python.

+ +

Ensure you set up GraalVM before you begin.

+ +

Compile and Run a Polyglot Application

+ +

GraalVM can run polyglot applications written in any language implemented with the Truffle language implementation framework. +These languages are henceforth referenced as guest languages.

+ +

Complete the steps in this section to create a sample polyglot application that runs on GraalVM and demonstrates programming language interoperability.

+ +

1. Create a hello-polyglot project directory.

+ +

2. In your project directory, add a HelloPolyglot.java file that includes +the following code:

+
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java {file}
+// BEGIN-SNIPPET
+import org.graalvm.polyglot.*;
+import org.graalvm.polyglot.proxy.*;
+// END-SNIPPET
+
+public class hello_polyglot_js {
+
+static
+// BEGIN-SNIPPET
+public class HelloPolyglot {
+    public static void main(String[] args) {
+        System.out.println("Hello Java!");
+        try (Context context = Context.create()) {
+            context.eval("js", "print('Hello JavaScript!');");
+        }
+    }
+}
+// END-SNIPPET
+
+    public static void main(String[] args) {
+        HelloPolyglot.main(null);
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java {file}
+// BEGIN-SNIPPET
+import org.graalvm.polyglot.*;
+import org.graalvm.polyglot.proxy.*;
+// END-SNIPPET
+
+public class hello_polyglot_R {
+
+static
+// BEGIN-SNIPPET
+public class HelloPolyglot {
+    public static void main(String[] args) {
+        System.out.println("Hello Java!");
+        try (Context context = Context.newBuilder()
+                                   .allowAllAccess(true)
+                               .build()) {
+            context.eval("R", "print('Hello R!');");
+        }
+    }
+}
+// END-SNIPPET
+
+    public static void main(String[] args) {
+        HelloPolyglot.main(null);
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java {file}
+// BEGIN-SNIPPET
+import org.graalvm.polyglot.*;
+import org.graalvm.polyglot.proxy.*;
+// END-SNIPPET
+
+public class hello_polyglot_ruby {
+
+static
+// BEGIN-SNIPPET
+public class HelloPolyglot {
+    public static void main(String[] args) {
+        System.out.println("Hello Java!");
+        try (Context context = Context.create()) {
+            context.eval("ruby", "puts 'Hello Ruby!'");
+        }
+    }
+}
+// END-SNIPPET
+
+    public static void main(String[] args) {
+        HelloPolyglot.main(null);
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java {file}
+// BEGIN-SNIPPET
+import org.graalvm.polyglot.*;
+import org.graalvm.polyglot.proxy.*;
+// END-SNIPPET
+
+public class hello_polyglot_python {
+
+static
+// BEGIN-SNIPPET
+public class HelloPolyglot {
+    public static void main(String[] args) {
+        System.out.println("Hello Java!");
+        try (Context context = Context.create()) {
+            context.eval("python", "print('Hello Python!')");
+        }
+    }
+}
+// END-SNIPPET
+
+    public static void main(String[] args) {
+        HelloPolyglot.main(null);
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ + + +
+ +

 In this code:

+
    +
  • import org.graalvm.polyglot.* imports the base API for the Polyglot API.
  • +
  • import org.graalvm.polyglot.proxy.* imports the proxy classes of the Polyglot API, needed in later examples.
  • +
  • Context provides an execution environment for guest languages. +R currently requires the allowAllAccess flag to be set to true to run the example.
  • +
  • eval evaluates the specified snippet of guest language code.
  • +
  • The try with resource statement initializes the Context and ensures that it +is closed after use. Closing the context ensures that all resources including +potential native resources are freed eagerly. Closing a context is optional but +recommended. Even if a context is not closed and no longer referenced it will be +freed by the garbage collector automatically.
  • +
+ +

3. Run javac HelloPolyglot.java to compile HelloPolyglot.java with +GraalVM.

+ +

4. Run java HelloPolyglot to run the application on GraalVM.

+ +

You now have a polyglot application that consists of a Java host application and guest language code that run on GraalVM. +You can use this application with other code examples to demonstrate more advanced capabilities of the Polyglot API.

+ +

To use other code examples in this section, you simply need to do the following:

+ +

1. Add the code snippet to the main method of HelloPolyglot.java.

+ +

2. Compile and run your polyglot application.

+ +

Define Guest Language Functions as Java Values

+ +

Polyglot applications let you take values from one programming language and use them with other languages.

+ +

Use the code example in this section with your polyglot application to show how the Polyglot API can return JavaScript, R, Ruby, or Python functions as Java values.

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.*;
+
+public class function_js {
+    public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.create()) {
+    Value function = context.eval("js", "x => x+1");
+    assert function.canExecute();
+    int x = function.execute(41).asInt();
+    assert x == 42;
+}
+// END-SNIPPET
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.*;
+
+public class function_R {
+    public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.newBuilder()
+                           .allowAllAccess(true)
+                       .build()) {
+    Value function = context.eval("R", "function(x) x + 1");
+    assert function.canExecute();
+    int x = function.execute(41).asInt();
+    assert x == 42;
+}
+// END-SNIPPET
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.*;
+
+public class function_ruby {
+    public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.create()) {
+    Value function = context.eval("ruby", "proc { |x| x + 1 }");
+    assert function.canExecute();
+    int x = function.execute(41).asInt();
+    assert x == 42;
+}
+ // END-SNIPPET
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.*;
+
+public class function_python {
+    public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.create()) {
+    Value function = context.eval("python", "lambda x: x + 1");
+    assert function.canExecute();
+    int x = function.execute(41).asInt();
+    assert x == 42;
+}
+ // END-SNIPPET
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ + + +
+ +

 In this code:

+
    +
  • Value function is a Java value that refers to a function.
  • +
  • The eval call parses the script and returns the guest language function.
  • +
  • The first assertion checks that the value returned by the code snippet can be executed.
  • +
  • The execute call executes the function with the argument 41.
  • +
  • The asInt call converts the result to a Java int.
  • +
  • The second assertion verifies that the result was incremented by one as expected.
  • +
+ +

Access Guest Languages Directly from Java

+ +

Polyglot applications can readily access most language types and are not limited to functions. +Host languages, such as Java, can directly access guest language values embedded in the polyglot application.

+ +

Use the code example in this section with your polyglot application to show how the Polyglot API can access objects, numbers, strings, and arrays.

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.*;
+
+public class access_js_from_java {
+    public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.create()) {
+    Value result = context.eval("js", 
+                    "({ "                   +
+                        "id   : 42, "       +
+                        "text : '42', "     +
+                        "arr  : [1,42,3] "  +
+                    "})");
+    assert result.hasMembers();
+
+    int id = result.getMember("id").asInt();
+    assert id == 42;
+
+    String text = result.getMember("text").asString();
+    assert text.equals("42");
+
+    Value array = result.getMember("arr");
+    assert array.hasArrayElements();
+    assert array.getArraySize() == 3;
+    assert array.getArrayElement(1).asInt() == 42;
+}
+// END-SNIPPET
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.*;
+
+public class access_R_from_java {
+    public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.newBuilder()
+                           .allowAllAccess(true)
+                       .build()) {
+    Value result = context.eval("R", 
+                    "list("                +
+                        "id   = 42, "      +
+                        "text = '42', "    +
+                        "arr  = c(1,42,3)" +
+                    ")");
+    assert result.hasMembers();
+    
+    int id = result.getMember("id").asInt();
+    assert id == 42;
+    
+    String text = result.getMember("text").asString();
+    assert text.equals("42");
+    
+    Value array = result.getMember("arr");
+    assert array.hasArrayElements();
+    assert array.getArraySize() == 3;
+    assert array.getArrayElement(1).asInt() == 42;
+}
+// END-SNIPPET
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.*;
+
+public class access_ruby_from_java {
+    public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.create()) {
+    Value result = context.eval("ruby", 
+                    "o = Struct.new(:id, :text, :arr).new(" +
+                        "42, "       +
+                        "'42', "     +
+                        "[1,42,3] "  +
+                    ")");
+    assert result.hasMembers();
+    
+    int id = result.getMember("id").asInt();
+    assert id == 42;
+    
+    String text = result.getMember("text").asString();
+    assert text.equals("42");
+    
+    Value array = result.getMember("arr");
+    assert array.hasArrayElements();
+    assert array.getArraySize() == 3;
+    assert array.getArrayElement(1).asInt() == 42;
+}
+// END-SNIPPET
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.*;
+
+public class access_python_from_java {
+    public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.create()) {
+    Value result = context.eval("python", 
+                    "type('obj', (object,), {" +
+                        "'id'  : 42, "         +
+                        "'text': '42', "       +
+                        "'arr' : [1,42,3]"     +
+                    "})()");
+    assert result.hasMembers();
+    
+    int id = result.getMember("id").asInt();
+    assert id == 42;
+    
+    String text = result.getMember("text").asString();
+    assert text.equals("42");
+    
+    Value array = result.getMember("arr");
+    assert array.hasArrayElements();
+    assert array.getArraySize() == 3;
+    assert array.getArrayElement(1).asInt() == 42;
+}
+// END-SNIPPET
+    }
+}
+
+  
+ + + + + + +
+ +
+ + +
+ + + +
+ +

 In this code:

+
    +
  • Value result is an Object that contains three members: a number named id, +a string named text, and an array named arr.
  • +
  • The first assertion verifies that the return value can contain members, which +indicates that the value is an object-like structure.
  • +
  • The id variable is initialized by reading the member with the name id from +the resulting object. The result is then converted to a Java int +using asInt().
  • +
  • The next assert verifies that result has a value of 42.
  • +
  • The text variable is initialized using the value of the member text, +which is also converted to a Java String using asString().
  • +
  • The following assertion verifies the result value is equal to the +Java String "42".
  • +
  • Next the arr member that holds an array is read.
  • +
  • Arrays return true for hasArrayElements. R array instances can have +members and array elements at the same time.
  • +
  • The next assertion verifies that the size of the array equals three. The +Polyglot API supports big arrays, so the array length is of type long.
  • +
  • Finally we verify that the array element at index 1 equals 42. Array +indexing with polyglot values is always zero-based, even for languages such as +R where indices start with one.
  • +
+ +

Access Java from Guest Languages

+ +

Polyglot applications offer bi-directional access between guest languages and host languages. +As a result, you can pass Java objects to guest languages.

+ +

Since the Polyglot API is secure by default, access is limited in the default configuration. +To permit guest languages to access any public method or field of a Java object, you have to explicitly specify allowAllAccess(true) when the context is built. +In this mode, the guest language code can access any resource that is accessible to host Java code.

+ +

Use the code example in this section with your polyglot application to show how guest languages can access primitive Java values, objects, arrays, and functional interfaces.

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import java.util.concurrent.Callable;
+import org.graalvm.polyglot.*;
+
+public class access_java_from_js {
+
+// BEGIN-SNIPPET
+public static class MyClass {
+    public int               id    = 42;
+    public String            text  = "42";
+    public int[]             arr   = new int[]{1, 42, 3};
+    public Callable<Integer> ret42 = () -> 42;
+}
+
+public static void main(String[] args) {
+    try (Context context = Context.newBuilder()
+                               .allowAllAccess(true)
+                           .build()) {
+        context.getBindings("js").putMember("javaObj", new MyClass());
+        boolean valid = context.eval("js",
+               "    javaObj.id         == 42"          +
+               " && javaObj.text       == '42'"        +
+               " && javaObj.arr[1]     == 42"          +
+               " && javaObj.ret42()    == 42")
+           .asBoolean();
+        assert valid == true;
+    }
+}
+// END-SNIPPET
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import java.util.concurrent.Callable;
+import org.graalvm.polyglot.*;
+
+public class access_java_from_R {
+
+// BEGIN-SNIPPET
+public static class MyClass {
+    public int               id    = 42;
+    public String            text  = "42";
+    public int[]             arr   = new int[]{1, 42, 3};
+    public Callable<Integer> ret42 = () -> 42;
+}
+
+public static void main(String[] args) {
+    try (Context context = Context.newBuilder()
+                               .allowAllAccess(true)
+                           .build()) {
+        context.getBindings("R").putMember("javaObj", new MyClass());
+        boolean valid = context.eval("R",
+               "    javaObj$id         == 42"   +
+               " && javaObj$text       == '42'" +
+               " && javaObj$arr[[2]]   == 42"   +
+               " && javaObj$ret42()    == 42")
+           .asBoolean();
+        assert valid == true;
+    }
+}
+// END-SNIPPET
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import java.util.concurrent.Callable;
+import org.graalvm.polyglot.*;
+
+public class access_java_from_ruby {
+
+// BEGIN-SNIPPET
+public static class MyClass {
+    public int               id    = 42;
+    public String            text  = "42";
+    public int[]             arr   = new int[]{1, 42, 3};
+    public Callable<Integer> ret42 = () -> 42;
+}
+
+public static void main(String[] args) {
+    try (Context context = Context.newBuilder()
+                               .allowAllAccess(true)
+                           .build()) {
+        context.getPolyglotBindings().putMember("javaObj", new MyClass());
+        boolean valid = context.eval("ruby",
+               "javaObj = Polyglot.import('javaObj')\n" +
+               "    javaObj[:id]         == 42"         +
+               " && javaObj[:text]       == '42'"       +
+               " && javaObj[:arr][1]     == 42"         +
+               " && javaObj[:ret42].call == 42")
+           .asBoolean();
+        assert valid == true;
+    }
+}
+// END-SNIPPET
+}
+ 
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import java.util.concurrent.Callable;
+import org.graalvm.polyglot.*;
+
+public class access_java_from_python {
+
+// BEGIN-SNIPPET
+public static class MyClass {
+    public int               id    = 42;
+    public String            text  = "42";
+    public int[]             arr   = new int[]{1, 42, 3};
+    public Callable<Integer> ret42 = () -> 42;
+}
+
+public static void main(String[] args) {
+    try (Context context = Context.newBuilder()
+                               .allowAllAccess(true)
+                           .build()) {
+        context.getPolyglotBindings().putMember("javaObj", new MyClass());
+        boolean valid = context.eval("python",
+               "import polyglot \n"                            +
+               "javaObj =  polyglot.import_value('javaObj')\n" +
+               "javaObj.id                   == 42"            +
+               " and javaObj.text            == '42'"          +
+               " and javaObj.arr[1]          == 42"            +
+               " and javaObj.ret42() == 42")
+           .asBoolean();
+        assert valid == true;
+    }
+}
+// END-SNIPPET
+}
+
+  
+ + + + + + +
+ +
+ + +
+ + + +
+ +

 In this code:

+
    +
  • The Java class MyClass has four public fields id, text, arr, and +ret42. The fields are initialized with 42, "42", new int[]{1, 42, 3}, and +lambda () -> 42 that always returns an int value of 42.
  • +
  • The Java class MyClass is instantiated and exported with the name javaObj +into the polyglot scope, which allows the host and guest languages to exchange +symbols.
  • +
  • A guest language script is evaluated that imports the javaObj symbol and +assigns it to the local variable which is also named javaObj. To avoid +conflicts with variables, every value in the polyglot scope must be explicitly +imported and exported in the top-most scope of the language.
  • +
  • The next two lines verify the contents of the Java object by comparing it +to the number 42 and the string '42'.
  • +
  • The third verification reads from the second array position and compares it +to the number 42. Whether arrays are accessed using 0-based or 1-based indices +depends on the guest language. Independently of the language, the Java array +stored in the arr field is always accessed using translated 0-based indices. For +example, in the R language, arrays are 1-based so the second array element is +accessible using index 2. In the JavaScript and Ruby languages, the second +array element is at index 1. In all language examples, the Java array is read +from using the same index 1.
  • +
  • The last line invokes the Java lambda that is contained in the field ret42 +and compares the result to the number value 42.
  • +
  • After the guest language script executes, validation takes place to ensure +that the script returns a boolean value of true as a result.
  • +
+ +

Lookup Java Types from Guest Languages

+ +

In addition to passing Java objects to the guest language, it is possible to allow the lookup of Java types in the guest language.

+ +

Use the code example in this section with your polyglot application to show how guest languages lookup Java types and instantiate them.

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.Context;
+
+public class lookup_java_from_js {
+
+
+public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.newBuilder()
+                           .allowAllAccess(true)
+                       .build()) {
+    java.math.BigDecimal v = context.eval("js",
+            "var BigDecimal = Java.type('java.math.BigDecimal');" +
+            "BigDecimal.valueOf(10).pow(20)")
+        .asHostObject();
+    assert v.toString().equals("100000000000000000000");
+}
+// END-SNIPPET
+}
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.Context;
+
+public class lookup_java_from_R {
+
+
+public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.newBuilder()
+                           .allowAllAccess(true)
+                       .build()) {
+    java.math.BigDecimal v = context.eval("R",
+            "BigDecimal = java.type('java.math.BigDecimal');\n" + 
+            "BigDecimal$valueOf(10)$pow(20)")
+        .asHostObject();
+    assert v.toString().equals("100000000000000000000");
+}
+// END-SNIPPET
+}
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.Context;
+
+public class lookup_java_from_ruby {
+
+public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.newBuilder()
+                           .allowAllAccess(true)
+                       .build()) {
+    java.math.BigDecimal v = context.eval("ruby",
+            "BigDecimal = Java.type('java.math.BigDecimal')\n" + 
+            "BigDecimal.valueOf(10).pow(20)")
+        .asHostObject();
+    assert v.toString().equals("100000000000000000000");
+}
+// END-SNIPPET
+}
+}
+ 
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.Context;
+
+public class lookup_java_from_python {
+
+
+public static void main(String[] args) {
+// BEGIN-SNIPPET
+try (Context context = Context.newBuilder()
+                           .allowAllAccess(true)
+                       .build()) {
+    java.math.BigDecimal v = context.eval("python",
+            "import java\n" +
+            "BigDecimal = java.type('java.math.BigDecimal')\n" + 
+            "BigDecimal.valueOf(10).pow(20)")
+        .asHostObject();
+    assert v.toString().equals("100000000000000000000");
+}
+// END-SNIPPET
+}
+}
+
+  
+ + + + + + +
+ +
+ + +
+ + + +
+ +

 In this code:

+
    +
  • A new context is created with all access enabled (allowAllAccess(true)).
  • +
  • A guest language script is evaluated.
  • +
  • The script looks up the Java type java.math.BigDecimal and stores it in a variable named BigDecimal.
  • +
  • The static method BigDecimal.valueOf(long) is invoked to create new +BigDecimals with value 10. In addition to looking up static Java methods, it +is also possible to directly instantiate the returned Java type., e.g., in +JavaScript using the new keyword.
  • +
  • The new decimal is used to invoke the pow instance method with 20 which calculates 10^20.
  • +
  • The result of the script is converted to a host object by calling asHostObject(). The return value is automatically cast to the BigDecimal type.
  • +
  • The result decimal string is asserted to equal to "100000000000000000000".
  • +
+ +

Computed Arrays Using Polyglot Proxies

+ +

The Polyglot API includes polyglot proxy interfaces that let you customize Java interoperability by mimicking guest language types, such as objects, arrays, native objects, or primitives.

+ +

Use the code example in this section with your polyglot application to see how you can implement arrays that compute their values lazily.

+ +
+

Note: The Polyglot API supports polyglot proxies either on the JVM or in Native Image.

+
+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.*;
+import org.graalvm.polyglot.proxy.*;
+
+public class proxy_js {
+
+// BEGIN-SNIPPET
+static class ComputedArray implements ProxyArray {
+    public Object get(long index) {
+        return index * 2;
+    }
+    public void set(long index, Value value) {
+        throw new UnsupportedOperationException();
+    }
+    public long getSize() {
+        return Long.MAX_VALUE;
+    }
+}
+
+public static void main(String[] args) {
+    try (Context context = Context.create()) {
+        ComputedArray arr = new ComputedArray();
+        context.getBindings("js").putMember("arr", arr);
+        long result = context.eval("js",
+                    "arr[1] + arr[1000000000]")
+                .asLong();
+        assert result == 2000000002L;
+    }
+}
+// END-SNIPPET
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Value;
+import org.graalvm.polyglot.proxy.ProxyArray;
+
+public class proxy_R {
+
+// BEGIN-SNIPPET
+static class ComputedArray implements ProxyArray {
+    public Object get(long index) {
+        return index * 2;
+    }
+    public void set(long index, Value value) {
+        throw new UnsupportedOperationException();
+    }
+    public long getSize() {
+        return Long.MAX_VALUE;
+    }
+}
+
+public static void main(String[] args) {
+    try (Context context = Context.newBuilder()
+                               .allowAllAccess(true)
+                           .build()) {
+        ComputedArray arr = new ComputedArray();
+        context.getPolyglotBindings().putMember("arr", arr);
+        long result = context.eval("R",
+               "arr <- import('arr');" +
+               "arr[2] + arr[1000000001]")
+           .asLong();
+        assert result == 2000000002L;
+    }
+}
+// END-SNIPPET
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Value;
+import org.graalvm.polyglot.proxy.ProxyArray;
+
+public class proxy_ruby {
+
+// BEGIN-SNIPPET
+static class ComputedArray implements ProxyArray {
+    public Object get(long index) {
+        return index * 2;
+    }
+    public void set(long index, Value value) {
+        throw new UnsupportedOperationException();
+    }
+    public long getSize() {
+        return Long.MAX_VALUE;
+    }
+}
+
+public static void main(String[] args) {
+    try (Context context = Context.newBuilder()
+                               .allowAllAccess(true)
+                           .build()) {
+        ComputedArray arr = new ComputedArray();
+        context.getPolyglotBindings().putMember("arr", arr);
+        long result = context.eval("ruby",
+               "arr = Polyglot.import('arr') \n" +
+               "arr[1] + arr[1000000000]")
+           .asLong();
+        assert result == 2000000002L;
+    }
+}
+// END-SNIPPET
+}
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Value;
+import org.graalvm.polyglot.proxy.ProxyArray;
+
+public class proxy_python {
+
+// BEGIN-SNIPPET
+static class ComputedArray implements ProxyArray {
+    public Object get(long index) {
+        return index * 2;
+    }
+    public void set(long index, Value value) {
+        throw new UnsupportedOperationException();
+    }
+    public long getSize() {
+        return Long.MAX_VALUE;
+    }
+}
+
+public static void main(String[] args) {
+    try (Context context = Context.newBuilder()
+                               .allowAllAccess(true)
+                           .build()) {
+        ComputedArray arr = new ComputedArray();
+        context.getPolyglotBindings().putMember("arr", arr);
+        long result = context.eval("python",
+               "import polyglot\n" +
+               "arr = polyglot.import_value('arr') \n" +
+               "arr[1] + arr[1000000000]")
+           .asLong();
+        assert result == 2000000002L;
+    }
+}
+// END-SNIPPET
+}
+
+  
+ + + + + + +
+ +
+ + +
+ + + +
+ +

 In this code:

+
    +
  • The Java class ComputedArray implements the proxy interface ProxyArray so +that guest languages treat instances of the Java class like arrays.
  • +
  • ComputedArray array overrides the method get and computes the value +using an arithmetic expression.
  • +
  • The array proxy does not support write access. For this reason, it throws +an UnsupportedOperationException in the implementation of set.
  • +
  • The implementation for getSize returns Long.MAX_VALUE for its length.
  • +
  • The main method creates a new polyglot execution context.
  • +
  • A new instance of the ComputedArray class is then exported using the name arr.
  • +
  • The guest language script imports the arr symbol, which returns the +exported proxy.
  • +
  • The second element and the 1000000000th element is accessed, summed up, and +then returned. Note that array indices from 1-based languages such as R are +converted to 0-based indices for proxy arrays.
  • +
  • The result of the language script is returned as a long value and verified.
  • +
+ +

For more information about the polyglot proxy interfaces, see the Polyglot API JavaDoc.

+ +

Host Access

+ +

The Polyglot API by default restricts access to certain critical functionality, such as file I/O. +These restrictions can be lifted entirely by setting allowAllAccess to true.

+ +
+

Note: The access restrictions are currently only supported with JavaScript.

+
+ +

Controlling Access to Host Functions

+ +

It might be desireable to limit the access of guest applications to the host. +For example, if a Java method is exposed that calls System.exit then the guest application will be able to exit the host process. +In order to avoid accidentally exposed methods, no host access is allowed by default and every public method or field needs to be annotated with @HostAccess.Export explicitly.

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  // COMPILE-CMD: javac {file}
+// RUN-CMD: java -ea {file}
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.HostAccess;
+import org.graalvm.polyglot.PolyglotException;
+
+public class explicit_access_java_from_js {
+
+static
+// BEGIN-SNIPPET
+public class Employee {
+    private final String name;
+    Employee(String name) {this.name = name;}
+
+    @HostAccess.Export
+    public String getName() {
+        return name;
+    }
+}//END-SNIPPET
+static//BEGIN-SNIPPET
+public class Services {
+    @HostAccess.Export
+    public Employee createEmployee(String name) {
+        return new Employee(name);
+    }
+    
+    public void exitVM() {
+        System.exit(1);
+    }
+}
+
+public static void main(String[] args) {
+    try (Context context = Context.create()) {
+        Services services = new Services();
+        context.getBindings("js").putMember("services", services);
+        String name = context.eval("js",
+                "let emp = services.createEmployee('John Doe');" + 
+                "emp.getName()").asString();
+        assert name.equals("John Doe");
+        
+        try {
+            context.eval("js", "services.exitVM()");
+            assert false;
+        } catch (PolyglotException e) {
+            assert e.getMessage().endsWith(
+                    "Unknown identifier: exitVM");
+        }
+    }
+}
+// END-SNIPPET
+}
+
+  
+ + + + + + +
+ +
+ + + + + +
+ + + +
+ +

 In this code:

+
    +
  • The class Employee is declared with a field name of type String. Access to the getName method is explicitly allowed by annotating the method with @HostAccess.Export.
  • +
  • The Services class exposes two methods, createEmployee and exitVM. The createEmployee method takes the name of the employee as an argument and creates a new Employee instance. The createEmployee method is annotated with @HostAccess.Export and therefore accessible to the guest application. The exitVM method is not explicitly exported and therefore not accessible.
  • +
  • The main method first creates a new polyglot context in the default configuration, disallowing host access except for methods annotated with @HostAccess.Export.
  • +
  • A new Services instance is created and put into the context as global variable services.
  • +
  • The first evaluated script creates a new employee using the services object and returns its name.
  • +
  • The returned name is asserted to equal the expected name John Doe.
  • +
  • A second script is evaluated that calls the exitVM method on the services object. This fails with a PolyglotException as the exitVM method is not exposed to the guest application.
  • +
+ +

Host access is fully customizable by creating a custom HostAccess policy.

+ +

Controlling Host Callback Parameter Scoping

+ +

By default, a Value lives as long as the corresponding Context. +However, it may be desireable to change this default behavior and bind a value to a scope, such that when execution leaves the scope, the value is invalidated. +An example for such a scope are guest-to-host callbacks, where a Value may be passed as a callback parameter. +We have already seen above how passing callback parameters works with the default HostAccess.EXPLICIT:

+ +
public class Services {
+    Value lastResult;
+
+    @HostAccess.Export
+    public void callback(Value result) {
+        this.lastResult = result;
+    }
+
+    String getResult() {
+        return this.lastResult.asString();
+    }
+}
+
+public static void main(String[] args) {
+    Services s = new Services()
+    try (Context context = Context.newBuilder().allowHostAccess(HostAccess.EXPLICIT).build()) {
+        context.getBindings("js").putMember("services", s);
+        context.eval("js", "services.callback('Hello from JS');");
+        System.out.println(s.getResult());
+    }
+}
+
+ +

In this example, lastResult maintains a reference to the value from the guest that is stored on the host and remains accessible also after the scope of callback() has ended.

+ +

However, this is not always desireable, as keeping the value alive may block resources unnecessarily or not reflect the behavior of ephemeral values correctly. +For these cases, HostAccess.SCOPED can be used, which changes the default behavior for all callbacks, such that values that are passed as callback parameters are only valid for the duration of the callback.

+ +

To make the above code work with HostAccess.SCOPED, individual values passed as a callback parameters can be pinned to extend their validity until after the callback returns:

+
public class Services {
+    Value lastResult;
+
+    @HostAccess.Export
+    void callback(Value result, Value notneeded) {
+        this.lastResult = result;
+        this.lastResult.pin();
+    }
+
+    String getResult() {
+        return this.lastResult.asString();
+    }
+}
+
+public static void main(String[] args) {
+    Services s = new Services()
+    try (Context context = Context.newBuilder().allowHostAccess(HostAccess.SCOPED).build()) {
+        context.getBindings("js").putMember("services", s);
+        context.eval("js", "services.callback('Hello from JS', 'foobar');");
+        System.out.println(services.getResult());
+    }
+}
+
+ +

Alternatively, the entire callback method can opt out from scoping if annotated with @HostAccess.DisableMethodScope, maintaining regular semantics for all parameters of the callback:

+
public class Services {
+    Value lastResult;
+    Value metaInfo;
+
+    @HostAccess.Export
+    @HostAccess.DisableMethodScope
+    void callback(Value result, Value metaInfo) {
+        this.lastResult = result;
+        this.metaInfo = metaInfo;
+    }
+
+    String getResult() {
+        return this.lastResult.asString() + this.metaInfo.asString();
+    }
+}
+
+public static void main(String[] args) {
+    Services s = new Services()
+    try (Context context = Context.newBuilder().allowHostAccess(HostAccess.SCOPED).build()) {
+        context.getBindings("js").putMember("services", s);
+        context.eval("js", "services.callback('Hello from JS', 'foobar');");
+        System.out.println(services.getResult());
+    }
+}
+
+ +

Access Privilege Configuration

+ +

It is possible to configure fine-grained access privileges for guest applications. +The configuration can be provided using the Context.Builder class when constructing a new context. +The following access parameters may be configured:

+ +
    +
  • Allow access to other languages using allowPolyglotAccess.
  • +
  • Allow and customize access to host objects using allowHostAccess.
  • +
  • Allow and customize host lookup to host types using allowHostClassLookup. Allows the guest application to look up the host application classes permitted by the lookup predicate. For example, a Javascript context can create a Java ArrayList, provided that ArrayList is allowlisted by the classFilter and access is permitted by the host access policy: context.eval("js", "var array = Java.type('java.util.ArrayList')")
  • +
  • Allow host class loading using allowHostClassLoading. Classes are only accessible if access to them is granted by the host access policy.
  • +
  • Allow the creation of threads using allowCreateThread.
  • +
  • Allow access to native APIs using allowNativeAccess.
  • +
  • Allow access to IO using allowIO and proxy file accesses using fileSystem.
  • +
+ +
+

Note: Granting access to class loading, native APIs, or host I/O effectively grants all access, as these privileges can be used to bypass other access restrictions.

+
+ +

Build Native Executables from Polyglot Applications

+ +

Polyglot embeddings can also be compiled ahead-of-time using Native Image. +By default, no language is included if the Polyglot API is used. +To enable guest languages, the --language:<languageId> (e.g., --language:js) option needs to be specified. +All examples on this page can be converted to native executables with the native-image builder.

+ +

The following example shows how a simple HelloPolyglot JavaScript application can be built using native-image.

+ +
javac HelloPolyglot.java
+native-image --language:js -cp . HelloPolyglot
+./hellopolyglot
+
+ +

Please note that some languages (e.g. Python, Ruby) need their language home directories to work without limitations. +If the polyglot application runs on a JVM (e.g. here), the language homes are discovered automatically. +However, for native images, paths to language homes have to be stored in the image or specified at runtime.

+ +

By default, the native-image builder copies the necessary language homes to the resources directory located in the same directory as the produced image. +The paths to the copied homes are written to the image’s build artifacts file and also stored in the image itself so that the homes are automatically discovered as long as their relative paths with respect to the image file stay the same. +That means that the resources directory should be always distributed together with the image file.

+ +
native-image --language:python -cp . HelloPolyglot
+./hellopolyglot
+
+ +

In case an installed GraalVM is available, it is possible to use language homes from the GraalVM home directory. A GraalVM home can be specified at runtime using the option -Dorg.graalvm.home=$GRAALVM_HOME, assuming the environment variable GRAALVM_HOME is populated with an absolute path to the GraalVM home directory. +Language homes are automatically discovered in the specified directory. For example:

+ +
./hellopolyglot -Dorg.graalvm.home=$GRAALVM_HOME
+
+ +
+

Note: The -Dorg.graalvm.home option has precedence over any relative language home paths stored in the image.

+
+ +
+

Note: The version of GraalVM the home of which is specified at runtime must match the version of GraalVM used to build the native executable/library.

+
+ +

Excluding the JIT compiler

+ +

It is possible to include a guest language in the native executable, but exclude the JIT compiler by passing the -Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime option to the builder. +Be aware, the flag -Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime has to placed after all the Truffle language/tool options, so that it will override the default settings.

+ +

The following example shows a native image build command that creates an image that will only contain the Truffle language interpreter (the Graal compiler will not be included in the image).

+
native-image --language:js -Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime -cp . HelloPolyglotInterpreter
+
+ +

Configuring Native Host Reflection

+ +

Accessing host Java code from the guest application requires Java reflection in order to work. +When reflection is used within a native executable, the reflection configuration file is required.

+ +

For this example we use JavaScript to show host access with native executables. +Copy the following code in a new file named AccessJavaFromJS.java.

+ +
import org.graalvm.polyglot.*;
+import org.graalvm.polyglot.proxy.*;
+import java.util.concurrent.*;
+
+public class AccessJavaFromJS {
+
+    public static class MyClass {
+        public int               id    = 42;
+        public String            text  = "42";
+        public int[]             arr   = new int[]{1, 42, 3};
+        public Callable<Integer> ret42 = () -> 42;
+    }
+
+    public static void main(String[] args) {
+        try (Context context = Context.newBuilder()
+                                   .allowAllAccess(true)
+                               .build()) {
+            context.getBindings("js").putMember("javaObj", new MyClass());
+            boolean valid = context.eval("js",
+                   "    javaObj.id         == 42"          +
+                   " && javaObj.text       == '42'"        +
+                   " && javaObj.arr[1]     == 42"          +
+                   " && javaObj.ret42()    == 42")
+               .asBoolean();
+            System.out.println("Valid " + valid);
+        }
+    }
+}
+
+ +

Copy the following code into reflect.json:

+ +
[
+  { "name": "AccessJavaFromJS$MyClass", "allPublicFields": true },
+  { "name": "java.util.concurrent.Callable", "allPublicMethods": true }
+]
+ +

Now you can create a native executable that supports host access:

+ +
javac AccessJavaFromJS.java
+native-image --language:js -H:ReflectionConfigurationFiles=reflect.json -cp . AccessJavaFromJS
+./accessjavafromjs
+
+ +

Note that in case assertions are needed in the image, the -H:+RuntimeAssertions option can be passed to native-image. +For production deployments, this option should be omitted.

+ +

Code Caching Across Multiple Contexts

+ +

The GraalVM Polyglot API allows code caching across multiple contexts. +Code caching allows compiled code to be reused and allows sources to be parsed only once. +Code caching can often reduce memory consumption and warm-up time of the application.

+ +

By default, code is cached within a single context instance only. +To enable code caching between multiple contexts, an explicit engine needs to be specified. +The engine is specified when creating the context using the context builder. +The scope of code sharing is determined by the engine instance. +Code is only shared between contexts associated with one engine instance.

+ +

All sources are cached by default. +Caching may be disabled explicitly by setting cached(boolean cached) to false. Disabling caching may be useful in case the source is known to only be evaluated once.

+ +

Consider the following code snippet as an example:

+ +
public class Main {
+    public static void main(String[] args) {
+        try (Engine engine = Engine.create()) {
+            Source source = Source.create("js", "21 + 21");
+            try (Context context = Context.newBuilder()
+                .engine(engine)
+                .build()) {
+                    int v = context.eval(source).asInt();
+                    assert v == 42;
+            }
+            try (Context context = Context.newBuilder()
+                .engine(engine)
+                .build()) {
+                    int v = context.eval(source).asInt();
+                    assert v == 42;
+            }
+        }
+    }
+}
+
+ +

In this code:

+
    +
  • import org.graalvm.polyglot.* imports the base API for the Polyglot API.
  • +
  • Engine.create() creates a new engine instance with the default configuration.
  • +
  • Source.create() creates a source object for the expression “21 + 21” +with “js” language, which is the language identifier for JavaScript.
  • +
  • Context.newBuilder().engine(engine).build() builds a new context with +an explicit engine assigned to it. All contexts associated with an engine share the code.
  • +
  • context.eval(source).asInt() evaluates the source and returns the result as Value instance.
  • +
+ +

Embed Guest Languages in Java

+ +

The GraalVM Polyglot API can be used from within a guest language using Java interoperability. +This can be useful if a script needs to run isolated from the parent context. +In Java as a host language a call to Context.eval(Source) returns an instance of Value, but since we executing this code as part of a guest language we can use the language-specific interoperability API instead. +It is therefore possible to use values returned by contexts created inside of a language, like regular values of the language. +In the example below we can conveniently write value.data instead of value.getMember("data"). +Please refer to the individual language documentation for details on how to interoperate with foreign values. +More information on value sharing between multiple contexts can be found here.

+ +

Consider the following code snippet as an example:

+ +
import org.graalvm.polyglot.*;
+
+public class Main {
+    public static void main(String[] args) {
+        try (Context outer = Context.newBuilder()
+                                   .allowAllAccess(true)
+                               .build()) {
+            outer.eval("js", "inner = Java.type('org.graalvm.polyglot.Context').create()");
+            outer.eval("js", "value = inner.eval('js', '({data:42})')");
+            int result = outer.eval("js", "value.data").asInt();
+            outer.eval("js", "inner.close()");
+
+            System.out.println("Valid " + (result == 42));
+        }
+    }
+}
+
+ +

In this code:

+
    +
  • Context.newBuilder().allowAllAccess(true).build() builds a new outer context with all privileges.
  • +
  • outer.eval evaluates a JavaScript snippet in the outer context.
  • +
  • inner = Java.type('org.graalvm.polyglot.Context').create() the first JS script line looks up the Java host type Context and creates a new inner context instance with no privileges (default).
  • +
  • inner.eval('js', '({data:42})'); evaluates the JavaScript code ({data:42}) in the inner context and returns stores the result.
  • +
  • "value.data" this line reads the member data from the result of the inner context. Note that this result can only be read as long as the inner context is not yet closed.
  • +
  • context.eval("js", "c.close()") this snippet closes the inner context. Inner contexts need to be closed manually and are not automatically closed with the parent context.
  • +
  • Finally the example is expected to print Valid true to the console.
  • +
+ +

Build a Shell for Many Languages

+ +

With just a few lines of code, the GraalVM Polyglot API lets you build applications that integrate with any guest language supported by GraalVM.

+ +

This shell implementation is agnostic to any particular guest language.

+ +
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
+PrintStream output = System.out;
+Context context = Context.newBuilder().allowAllAccess(true).build();
+Set<String> languages = context.getEngine().getLanguages().keySet();
+output.println("Shell for " + languages + ":");
+String language = languages.iterator().next();
+for (;;) {
+    try {
+        output.print(language + "> ");
+        String line = input.readLine();
+        if (line == null) {
+            break;
+        } else if (languages.contains(line)) {
+            language = line;
+            continue;
+        }
+        Source source = Source.newBuilder(language, line, "<shell>")
+                        .interactive(true).buildLiteral();
+        context.eval(source);
+    } catch (PolyglotException t) {
+        if(t.isExit()) {
+            break;
+        }
+        t.printStackTrace();
+    }
+}
+
+ +

Step Through with Execution Listeners

+ +

The GraalVM Polyglot API allows users to instrument the execution of guest languages through ExecutionListener class. +For example, it lets you attach an execution listener that is invoked for every statement of the guest language program. +Execution listeners are designed as simple API for polyglot embedders and may become handy in, e.g., single-stepping through the program.

+ +
import org.graalvm.polyglot.*;
+import org.graalvm.polyglot.management.*;
+
+public class ExecutionListenerTest {
+    public static void main(String[] args) {
+        try (Context context = Context.create("js")) {
+            ExecutionListener listener = ExecutionListener.newBuilder()
+                      .onEnter((e) -> System.out.println(
+                              e.getLocation().getCharacters()))
+                      .statements(true)
+                      .attach(context.getEngine());
+            context.eval("js", "for (var i = 0; i < 2; i++);");
+            listener.close();
+        }
+    }
+}
+
+ +

In this code:

+
    +
  • The Context.create() call creates a new context for the guest language.
  • +
  • Create an execution listener builder by invoking ExecutionListeners.newBuilder().
  • +
  • Set onEnter event to notify when element’s execution is entered and consumed. At least one event consumer and one filtered source element needs to be enabled.
  • +
  • To complete the listener attachment, attach() needs to be invoked.
  • +
  • The statements(true) filters execution listeners to statements only.
  • +
  • The context.eval() call evaluates a specified snippet of guest language code.
  • +
  • The listener.close() closes a listener earlier, however execution listeners are automatically closed with the engine.
  • +
+ +

Polyglot Isolates

+ +

On Oracle GraalVM, a Polyglot engine can be configured to run in a dedicated native-image isolate. +This feature is enabled with the --engine.SpawnIsolate option. +An engine running in this mode executes within a VM-level fault domain with its own garbage collector and JIT compiler. +The fact that an engine runs within an isolate is completely transparent with respect to the Polyglot API and interoperability:

+ +
import org.graalvm.polyglot.*;
+
+public class PolyglotIsolate {
+  public static void main(String[] args) {
+    Context context = Context.newBuilder("js")
+      .allowHostAccess(HostAccess.SCOPED)
+      .option("engine.SpawnIsolate", "true").build();
+    Value function = context.eval("js", "x => x+1")
+    assert function.canExecute();
+    int x = function.execute(41).asInt();
+    assert x == 42;
+  }
+}
+
+ +

Since the host’s GC and the isolate’s GC are not aware of one another, cyclic references between objects on both heaps may occur. +We thus strongly recommend to use scoped parameters for host callbacks to avoid cyclic references.

+ +

Multiple contexts can be spawned in the same isolated engine by sharing engines:

+ +
public class PolyglotIsolateMultipleContexts {
+    public static void main(String[] args) {
+        try (Engine engine = Engine.newBuilder()
+                .option("engine.SpawnIsolate", "js").build()) {
+            Source source = Source.create("js", "21 + 21");
+            try (Context context = Context.newBuilder()
+                .engine(engine)
+                .build()) {
+                    int v = context.eval(source).asInt();
+                    assert v == 42;
+            }
+            try (Context context = Context.newBuilder()
+                .engine(engine)
+                .build()) {
+                    int v = context.eval(source).asInt();
+                    assert v == 42;
+            }
+        }
+    }
+}
+
+ +

Note how you need to specify the language for the isolated engine as a parameter to --engine.SpawnIsolate in this case. +The reason is that an isolated engine needs to know which set of languages should be available. +Behind the scenes, GraalVM will then locate the corresponding Native Image language library. +If only a single language is selected, then the library for the language will be loaded. +If multiple languages are selected, then libpolyglot, the library containing all Truffle languages shipped with GraalVM, will be loaded. +If a matching library is not available, creation of the engine will fail.

+ +

Only one language library can be loaded during GraalVM’s lifetime. +This means that the first isolated engine that is created sets the default for the remainder of the execution: if an isolated engine with solely JavaScript was created first, only JavaScript will be available in isolated engines.

+ +

Setting the Heap Size

+ +

Passing Native Image Runtime Options

+ +

Engines running in an isolate can make use of Native Image runtime options by passing --engine.IsolateOption.<option> to the engine builder. +For example, this can be used to limit the maximum heap memory used by an engine by setting the maximum heap size for the isolate via --engine.IsolateOption.MaxHeapSize=128m:

+ +
import org.graalvm.polyglot.*;
+
+public class PolyglotIsolateMaxHeap {
+  public static void main(String[] args) {
+    try {
+      Context context = Context.newBuilder("js")
+        .allowHostAccess(HostAccess.SCOPED)
+        .option("engine.SpawnIsolate", "true")
+        .option("engine.IsolateOption.MaxHeapSize", "64m").build()
+      context.eval("js", "var a = [];while (true) {a.push('foobar');}");
+    } catch (PolyglotException ex) {
+      if (ex.isResourceExhausted()) {
+        System.out.println("Resource exhausted");
+      }
+    }
+  }
+}
+
+

Exceeding the maximum heap size will automatically close the context and raise a PolyglotException.

+ +

Ensuring Host Callback Stack Headroom

+ +

With Polyglot Isolates, the --engine.HostCallStackHeadRoom option can require a minimum stack size that is guaranteed when performing a host callback. +If the available stack size drops below the specified threshold, the host callback fails.

+ +

Memory Protection

+ +

In Linux environments that support Memory Protection Keys, the --engine.MemoryProtection=true option can be used to isolate the heaps of Polyglot Isolates at the hardware level. +If an engine is created with this option, a dedicated protection key will be allocated for the isolated engine’s heap. +GraalVM will only enable access to the engine’s heap when executing code of the Polyglot Isolate.

+ +

Dependency Setup

+ +

To best make use of the GraalVM Embedding API (i.e. org.graalvm.polyglot.*), your project should use a GraalVM as JAVA_HOME. +In addition to that, you should specify the graal-sdk.jar (which is included in GraalVM) as a provided dependency to your projects. +This is mainly to provide IDEs and other tools with the information that the project uses this API. +For example, add the following to the pom.xml file of your Maven project:

+ +
<dependency>
+    <groupId>org.graalvm.sdk</groupId>
+    <artifactId>graal-sdk</artifactId>
+    <version>${graalvm.version}</version>
+    <scope>provided</scope>
+</dependency>
+
+ +

Additionally, when using Java modules, your module-info.java file should require org.graalvm.sdk.

+ +
module com.mycompany.app {
+  requires org.graalvm.sdk;
+
+}
+
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/embed-languages/sandbox-resource-limits/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/embed-languages/sandbox-resource-limits/index.html new file mode 100644 index 0000000..bea29f0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/embed-languages/sandbox-resource-limits/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/graalvm-updater/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/graalvm-updater/index.html new file mode 100644 index 0000000..c592643 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/graalvm-updater/index.html @@ -0,0 +1,387 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

GraalVM Updater

+ + + +

GraalVM Updater, gu, is a command-line tool for installing and managing optional GraalVM language runtimes and utilities. +It is available in the GraalVM JDK. +To assist you with the installation, language runtimes and utilities are pre-packaged as JAR files and referenced in the documentation as “components”. +GraalVM Updater can be also used to update your local GraalVM installation to a newer version or upgrade from GraalVM Community Edition to Oracle GraalVM. +Read more in Upgrade GraalVM.

+ +

Check Available Components

+ +

To check what components are already shipped with your GraalVM installation or what you have already installed, run the list command:

+
gu list
+
+ +

To check what components are available for your GraalVM version to install, run the gu available command:

+
gu available
+Downloading: Component catalog from ...
+ComponentId              Version            Component name                
+-----------------------------------------------------------------------------
+espresso                 <version>          Java on Truffle               
+espresso-llvm            <version>          Java on Truffle LLVM Java library
+js                       <version>          JavaScript
+llvm                     <version>          LLVM
+llvm-toolchain           <version>          LLVM.org toolchain                  
+nodejs                   <version>          Graal.nodejs                  
+python                   <version>          Graal.Python                  
+R                        <version>          FastR                         
+ruby                     <version>          TruffleRuby                   
+wasm                     <version>          GraalWasm                     
+
+ +

Note down the ComponentId value for the component you would like to install.

+ +

GraalVM Updater verifies whether or not the version of a component is appropriate for your current GraalVM installation. +A component may require other components as prerequisites for its operation. +GraalVM Updater verifies such requirements and will either attempt to download the required dependencies, or abort the installation if the component’s requirements are not met. +Components intended for Oracle GraalVM cannot be installed on GraalVM Community Edition.

+ +

Generic support for Node.js, R, Ruby, Python, and WebAssembly will work out of the box in most cases. +It is recommended to fine-tune system-dependent configurations, following the recommendations in the component post-installation messages.

+ +

Install Components

+ +

You can install a component by component’s ID using GraalVM Updater: gu install ComponentId.

+ +
    +
  1. Get a list of components available for your GraalVM version and their descriptive names: +
     gu available
    +
    +
  2. +
  3. Install a component package using the ComponentId value. For example, js: +
     gu install js
    +
    +

    The installation starts, displaying progress.

    +
  4. +
+ +

To see more verbose output during the installation, as the download progress bar, print versions, and dependency information, use the -v (--verbose) switch.

+ +

If a component is installed that depends on another component, GraalVM Updater will search for the appropriate dependency and install it as well. +If a required component cannot be found, the installation will fail.

+ +

Install Components Manually

+ +

You can install a component from a local file, in other words, manually.

+ +
    +
  1. +

    Download a component in consideration of the operating system, the Java version, and architecture (if applicable) from:

    + + +
  2. +
  3. +

    Having downloaded the appropriate JAR file, install it with this command:

    + +
     gu -L install component.jar
    +
    +

    The -L option, equivalent to --local-file or --file, installs a component from a downloaded JAR. + However, a component may depend on other components (e.g., Ruby depends on the LLVM toolchain). + For example, gu -L install component.jar will fail if the required components are not yet installed. + If all dependencies are downloaded into the same directory, you can run:

    + +
     gu -L install -D
    +
    +
  4. +
+ +

Install Components from Local Collection

+ +

Components can be downloaded manually in advance to a local file folder, or to a folder shared on the local network. +GraalVM Updater can then use that folder instead of the catalog. +Specify the directory to use for the components collection:

+
gu install -C /path/to/downloads/directory ComponentId
+
+ +

Uninstall Components

+ +

Components may be uninstalled from GraalVM when no longer needed. +To uninstall a specific component, use its ComponentId. Run gu list to find out the exact ComponentId.

+ +

The command to uninstall the component is:

+
gu remove ComponentId
+
+ +

If more components end with, for example, ruby, the installer will print an error message that a component’s full name is required (org.graalvm.ruby).

+ +

Note that the LLVM toolchain component may fail uninstallation if its dependent component(s) remains installed. In this case, remove the dependent component first, or add the -D option, which would remove dependent components in addition to those explicitly selected:

+
gu -D remove llvm-toolchain
+
+ +

Rebuild Images

+ +

Language runtime components for GraalVM may change. For example:

+
    +
  • polyglot native libraries become out of sync;
  • +
  • removed languages runtimes may cause the native binary to fail on missing resources or libraries.
  • +
+ +

To rebuild and refresh the native binaries (language launchers), use the following command:

+
gu rebuild-images [--verbose] polyglot|libpolyglot|js|llvm|python|ruby|R... [custom native-image args]...
+
+ +

Replace Components and Files

+ +

A component may be only installed once. +GraalVM Updater refuses to install a component if a component with the same ID is already installed. +However, the installed component can be replaced. +GraalVM Updater first uninstalls the component, and then installs a new one.

+ +

To replace a component, use the -r option and the -L (--local-file or --file) option to treat parameters as the local filename of a packaged component:

+
gu install -L -r component.jar
+gu install -r ruby
+
+ +

The process is the same as if gu remove is run first and gu install next.

+ +

GraalVM Updater also refuses to overwrite existing files if the to-be-installed and existing versions differ. +There are cases when refreshing file contents may be needed, such as if they were modified or damaged. +In this case, use the -o option:

+
gu install -L -o component.jar
+gu install -o ruby
+
+ +

GraalVM Updater will then instruct the user to replace the contained files of a component. +By default, it will not alter anything. +Alternatively, use the -f (--force) option, which disables most of the checks and allows the user to install non-matching versions.

+ +

Configure Proxies

+ +

If GraalVM Updater needs to reach the component catalog, or download a component, it may need to pass through the HTTP/HTTPS proxy, if the network uses one. +On macOS, the proxy settings are automatically obtained from the OS. +On Linux, ensure that the http_proxy and https_proxy environment variables are set appropriately before launching the gu tool. +Refer to the distribution and/or desktop environment documentation for the details.

+ +

GraalVM Updater intentionally does not support an option to disable certificate or hostname verification, for security reasons. +A user may try to add a proxy’s certificate to the GraalVM default security trust store. +You can also download a component manually to a directory, and then run gu -L install /path/to/file to install from a local filesystem (see Install Components Manually).

+ +

Working without Internet Access

+ +

If your machine cannot access and download the catalog and components from the Internet, GraalVM Updater can install components from a local directory, or a directory on an accessible network share.

+ +

You need to prepare a directory, download all components that you want to install and their dependencies (in case they require other GraalVM components to work) into that directory.

+ +

Then you can use gu -L install /path/to/file (where the -L option instructs to use local files, equivalent to --local-file or --file). +Adding the -D option will instruct GraalVM Updater to look for potential dependencies in the directory next to the installable file. +Additionally, gu -C /path/to/download/dir install component can be used, with the specified directory contents acting as a catalog of components.

+ +

Note that with gu -L you need to specify the component’s file name, but when using gu -C <dir>, you need to specify a component ID (ComponentId):

+
# Specify file location
+gu -LD install /tmp/installables/ruby.jar
+
+# Specify component name
+gu -C /tmp/instalables install ruby
+
+ +

Configure Installation

+ +

The installation command of GraalVM Updater accepts multiple options and parameters: + shell + gu install [-0CcDfiLMnosruvyxY] param [param ...] +

+ +

The following options are currently supported:

+
    +
  • -0, --dry-run: dry run, do not change anything
  • +
  • -c, --catalog: treat parameters as component IDs from the GraalVM components catalog. This is the default
  • +
  • -C, --custom-catalog <url>: use a specific catalog URL to locate components
  • +
  • -L, --local-file: treat parameters as local filenames of packaged components
  • +
  • -M: force gu to ignore dependencies of installed components
  • +
  • -f, --force: force overwrite, bypass version checks
  • +
  • -i, --fail-existing: fail on an existing component
  • +
  • -n, --no-progress: do not display the downloading progress
  • +
  • -o, --overwrite: overwrite different files
  • +
  • -r, --replace: replace existing components
  • +
  • -s, --no-verify-jars: skip integrity verification of component archives
  • +
  • -u, --url: interpret parameters as URLs of packaged components
  • +
  • -v, --verbose: be verbose. Prints versions and dependency information
  • +
  • -x, --ignore: ignore failures
  • +
  • -y, --only-validate: do not install, just check compatibility and conflicting files
  • +
  • -Y, --validate-before: download, verify, and check file conflicts before any disk change is made
  • +
+ +

GraalVM Updater Commands Overview

+ +

Command-line help is available by running gu or gu -h. Run gu <command> -h to get help specific for the particular command. For example, gu install -h.

+ +

GraalVM Updater usage options:

+
    +
  • gu info [-cClLnprstuvV] <param>: print the information about specific component (from file, URL, or catalog)
  • +
  • gu available [-aClvV] <expr>: list components available in the catalog
  • +
  • gu install [--0CcDfiLMnosruvyxY] <param>: install a component package
  • +
  • gu list [-clv] <expression>: list installed components or components from catalog
  • +
  • gu remove [-0DfMxv] <id>: uninstall a component
  • +
  • gu upgrade [-cCnLsuxSd] [<ver>] [<cmp>]: upgrade to the recent GraalVM version
  • +
  • gu rebuild-images: rebuild the native launchers. Use -h for detailed usage
  • +
+ +

GraalVM Updater common options:

+
    +
  • -A, --auto-yes: say YES or ACCEPT to a question
  • +
  • -c, --catalog: treat parameters as component IDs from the catalog of GraalVM components. This is the default
  • +
  • -C, --custom-catalog <url>: use user-supplied catalog at URL
  • +
  • -e, --debug: enable debugging and print stacktraces
  • +
  • -E, --no-catalog-errors: do not stop if at least one catalog is working
  • +
  • -h, --help: print help
  • +
  • -L, --local-file, --file: treat parameters as local filenames of packaged components
  • +
  • -N, --non-interactive: enable non-interactive mode. Fail when input is required
  • +
  • --show-version: print version information and continue
  • +
  • -u, --url: interpret parameters as URLs of packaged components
  • +
  • -v, --verbose: enable verbose output. Print versions and dependency information
  • +
  • --version: print version
  • +
+ +

Additonal options:

+ +
    +
  • --email <address>: print an e-mail address used for generating a download token
  • +
  • --config <path>: provide the path to a download token
  • +
  • --show-ee-token: print a saved download token
  • +
  • -k, --public-key <path>: provide path to custom GPG public key for verification
  • +
  • -U, --username <username>: enter a username for login to Oracle component repository
  • +
+ + + +

Check the GraalVM’s components availability and support per platform.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/index.html new file mode 100644 index 0000000..3a4909e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/index.html @@ -0,0 +1,153 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

GraalVM Reference Manuals

+ +

Here you will find the in-depth documentation for technologies offered by GraalVM. +These manuals are aimed at software engineers and developers who already work with GraalVM, or are considering it as an environment for their workloads.

+ +

Technologies

+ +

Native Image - learn in detail about Native Image: GraalVM’s innovative technology that can ahead-of-time compile Java code to a self-contained native executable.

+ +

Java on Truffle - learn how to run Java via a Java bytecode interpreter, implemented with the Truffle framework.

+ +

GraalVM Updater - learn how to add more capabilities and upgrade the core GraalVM installation.

+ +

Polyglot Programming - learn how to write polyglot applications and allow languages to directly interoperate with each other in the same memory space.

+ +

Embedding Languages - learn how to embed polyglot applications in Java host applications or native images.

+ +

Specific Languages

+ +

If you are mostly interested in the GraalVM support for a specific language, here you can find the most extensive documentation:

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/demos/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/demos/index.html new file mode 100644 index 0000000..3b90226 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/demos/index.html @@ -0,0 +1,312 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Running Demo Applications

+ +

Java on Truffle is an implementation of the Java Virtual Machine Specification, which offers some interesting capabilities in addition to being able to run applications in Java or other JVM languages. +For example, the enhanced HotSwap capabilities boosts developer productivity by enabling unlimited hot code reloading. +Moreover, to illustrate what Java on Truffle can do, consider the following short examples.

+ +

Mixing AOT and JIT for Java

+ +

GraalVM Native Image technology allows compiling applications ahead-of-time (AOT) to executable native binaries which:

+
    +
  • are standalone
  • +
  • start instantly
  • +
  • have lower memory usage
  • +
+ +

The main trade off for using Native Image is that the analysis and compilation of your program happens under the closed world assumption, meaning the static analysis needs to process all bytecode which will ever be executed in the application. +This makes using some language features like dynamic class loading or reflection tricky.

+ +

Java on Truffle is a JVM implementation of a JVM bytecode interpreter, built on the Truffle framework. +It is essentially a Java application, as are the Truffle framework itself and the GraalVM JIT compiler. +All three of them can be compiled ahead-of-time with native-image. +Using Java on Truffle for some parts of your application makes it possible to isolate the required dynamic behaviour and still use the native executable on the rest of your code.

+ +

Consider a canonical Java Shell tool (JShell) as an example command line application. +It is a REPL capable of evaluating Java code and consists of two parts:

+
    +
  • the UI - CLI app handling input-output
  • +
  • the backend processor for running code you enter into Shell.
  • +
+ +

This design naturally fits the point we are trying to illustrate. We can build a native executable of the JShell’s UI part, and make it include Java on Truffle to run the code dynamically specified at run time.

+ +

Prerequisites:

+ + +
    +
  1. Clone the project with the demo applications and navigate to the espresso-jshell directory:
  2. +
+ +
git clone https://github.com/graalvm/graalvm-demos.git
+cd graalvm-demos/espresso-jshell
+
+ +

The JShell implementation is actually the normal JShell launcher code, which only accepts Java on Truffle implementation (the project code-name is “Espresso”) of the execution engine.

+ +

The “glue” code that binds the part which is AOT compiled with the component that dynamically evaluates the code is located in the EspressoExecutionControl class. +It loads the JShell classes within the Java on Truffle context and delegate the input to them:

+ +
    protected final Lazy<Value> ClassBytecodes = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$ClassBytecodes"));
+    protected final Lazy<Value> byte_array = Lazy.of(() -> loadClass("[B"));
+    protected final Lazy<Value> ExecutionControlException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$ExecutionControlException"));
+    protected final Lazy<Value> RunException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$RunException"));
+    protected final Lazy<Value> ClassInstallException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$ClassInstallException"));
+    protected final Lazy<Value> NotImplementedException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$NotImplementedException"));
+    protected final Lazy<Value> EngineTerminationException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$EngineTerminationException"));
+    protected final Lazy<Value> InternalException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$InternalException"));
+    protected final Lazy<Value> ResolutionException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$ResolutionException"));
+    protected final Lazy<Value> StoppedException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$StoppedException"));
+    protected final Lazy<Value> UserException = Lazy.of(() -> loadClass("jdk.jshell.spi.ExecutionControl$UserException"));
+
+ +

There is more code to pass the values correctly and transform the exceptions. +To try it out, build the espresso-jshell binary using the provided script, which will:

+
    +
  1. Build the Java sources to the bytecode
  2. +
  3. Build the JAR file
  4. +
  5. Build a native executable
  6. +
+ +

The most important configuration line in the native-image command is --language:java which instructs to include the Java on Truffle implementation into the binary. +After the build you can observe the resulting binary file (file and ldd are Linux commands)

+
file ./espresso-jshell
+ldd ./espresso-jshell
+
+ +

It is indeed a binary file not depending on the JVM, and you can run it noticing how fast it starts:

+
./espresso-jshell
+|  Welcome to JShell -- Version 11.0.10
+|  For an introduction type: /help intro
+
+jshell> 1 + 1
+1 ==> 2
+
+ +

Experiment with loading new code into JShell and see how Java on Truffle executes it.

+ +

Watch a video version of the mixing AOT and JIT compiled code with Java on Truffle demo.

+ +
+
+
+ video_1 + watch video +
+
+
+ + +


+ +

GraalVM Tools with Java on Truffle

+ +

Java on Truffle is a proper part of the GraalVM ecosystem, and like other GraalVM-supported languages gets the support of developer tooling by default. The Truffle framework integrates with the tools like the debugger, profiler, memory analyser, the Instrumentation API. +The interpreter for a language needs to mark the AST nodes with some annotations to support those tools.

+ +

For example, to be able to use a profiler, a language interpreter needs to mark the root nodes. +For the debugger purposes, the language expressions should be marked as instrumental, the scopes for the variables specified, and so on. The language interpreter does not need to integrate with the tools itself. +As a result, you can profile a Java on Truffle program out of the box using either the CPU Sampler or Memory Tracer tools.

+ +

For example, if we have a class like the following one computing the prime numbers:

+
import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+import java.util.stream.LongStream;
+
+public class Main {
+
+    public static void main(String[] args) {
+        Main m = new Main();
+
+        for (int i = 0; i < 100_000; i++) {
+            System.out.println(m.random(100));
+        }
+    }
+
+    private Random r = new Random(41);
+    public List<Long> random(int upperbound) {
+        int to = 2 + r.nextInt(upperbound - 2);
+        int from = 1 + r.nextInt(to - 1);
+        return primeSequence(from, to);
+    }
+    public static List<Long> primeSequence(long min, long max) {
+        return LongStream.range(min, max)
+                .filter(Main::isPrime)
+                .boxed()
+                .collect(Collectors.toList());
+    }
+    public static boolean isPrime(long n) {
+        return LongStream.rangeClosed(2, (long) Math.sqrt(n))
+                .allMatch(i -> n % i != 0);
+    }
+}
+
+ +

Build this program, and run it with the --cpusampler option.

+
javac Main.java
+java -truffle --cpusampler Main > output.txt
+
+ +

At the end of the output.txt file you will find the profiler output, the histogram of the methods, and how much time the execution took. +You can also try an experiment with the --memtracer option, to see where the allocations in this program are happening.

+
java -truffle --experimental-options --memtracer Main > output.txt
+
+ +

Other tools that GraalVM offers are Chrome Debugger, Code Coverage, and GraalVM Insight.

+ +

Having the “out-of-the-box” support for the developer tooling makes Java on Truffle an interesting choice of the JVM.

+ +

Watch a short demonstration of GraalVM built-in tools for Java on Truffle.

+ +
+
+
+ video_1 + watch video +
+
+
+ + +


+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/faq/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/faq/index.html new file mode 100644 index 0000000..f6e2578 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/faq/index.html @@ -0,0 +1,167 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Frequently Asked Questions

+ +

Does Java running on Truffle implement the Java language running as a Truffle interpreter?

+ +

Not quite: it implements the Java Virtual Machine running as a Truffle interpreter. +That means it can only run a Java program once it has been compiled to Java bytecode (classes, JARs, etc.) with your favorite Java compiler (e.g., javac) or a build tool (Maven, Gradle, etc.). +In the GraalVM family, this is similar to WebAssembly or the LLVM interpreter: while both can run C programs, they have to be complied by a C compiler first.

+ +

Does Java running on Truffle run on HotSpot too?

+

Like other languages implemented with the Truffle framework, it can run both as a native executable or on top of HotSpot. +Running on top of HotSpot is currently only possible on Linux. +We plan to extend this capability to macOS and Windows platforms also.

+ +

Does running Java on Truffle require HotSpot?

+ +

No, it doesn’t, it works fine as a native executable. +Java on Truffle does require a standard core Java library (the rt.jar library for Java 8 or the lib/modules file for Java 11 and Java 17 as well as the associated native libraries: libjava, libnio, etc.)

+ +

Running Java on GraalVM already brings the highest level of optimization, what benefits will Java on Truffle give me?

+
    +
  • Java on Truffle will inherit the extensive tooling provided by the Truffle framework. This means that for the things like code coverage and profiling you would no longer need to rely on external tools.
  • +
  • Another important aspect is that Java on Truffle comes with improved isolation of the host Java VM and the Java program running on Truffle.
  • +
  • Moreover, Java on Truffle can run in the context of a native executable while still allowing dynamically-loaded bytecodes!
  • +
  • Finally, you can enjoy the benefits of enhanced HotSwap capabilities which will help boost your productivity.
  • +
+ +

What is the license for Java on Truffle?

+

Java on Truffle is an implementation of the Java Virtual Machine. It is open source and is offered as free software under the GNU General Public License version two (GPLv2).

+ +

Can I run Java on Truffle in production?

+

Yes, you can. Java on Truffle is still a prototype, but it already passes the Java Compatibility Kit (JCK or TCK for Java SE) 8, 11 and 17 runtimes. +It may undergo significant improvements.

+ +

What performance can I expect from executing Java on Truffle?

+

Performance is currently 2-3x slower than HotSpot. +It does not match the speed offered by GraalVM yet for sure, but having created a fully working Java on Truffle runtime, the development team is now focusing on making it as performant as the GraalVM JIT.

+ +

Can I embed Java running on Truffle in my application?

+

Yes, you can use GraalVM’s Polyglot API to run Java bytecodes in a separate context from the host Java VM. +You can even embed a Java 8 context in a Java 11 or Java 17 application!

+ +

Why do I see “Unrecognized option: -javaagent:…/idea_rt.jar…” when I try to run my app from the IDE?

+

Java on Truffle does not yet support attaching Java agents. For the time being add: -XX:+IgnoreUnrecognizedVMOptions to the VM options too.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/hotswap-plugin/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/hotswap-plugin/index.html new file mode 100644 index 0000000..e73df43 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/hotswap-plugin/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/hotswap/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/hotswap/index.html new file mode 100644 index 0000000..864d08a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/hotswap/index.html @@ -0,0 +1,396 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Enhanced HotSwap Capabilities with Java on Truffle

+ +

With Java on Truffle you can benefit from enhanced HotSwap capabilites that allow the code to evolve naturally during development without the need for restarting a running application. +You do not have to configure anything specific besides launching your app in debug mode and attaching a standard IDE debugger to gain the advantages of enhanced HotSwap.

+ +

Debugging with Java on Truffle

+ +

You can use your favorite IDE debugger to debug Java applications running in the Java on Truffle runtime. +For example, starting a debugger session from IntelliJ IDEA is based on the Run Configurations. +To ensure you attach the debugger to your Java application in the same environment, navigate in the main menu to Run, Debug…, Edit Configurations, expand Environment, check the JRE value and VM options values. +It should show GraalVM as project’s JRE and VM options should include -truffle -XX:+IgnoreUnrecognizedVMOptions. It is necessary to specify -XX:+IgnoreUnrecognizedVMOptions because Intellij automatically adds a -javaagent argument which is not supported yet. +Press Debug.

+ +

This will run the application and start a debugger session in the background.

+ +

Using HotSwap during a Debugging Session

+ +

Once you have your debugger session running, you will be able to apply extensive code changes (HotSwap) without needing to restart the session. +Feel free to try this out on your own applications or by following these instructions:

+ +
    +
  1. Create a new Java application.
  2. +
  3. +

    Use the following main method as a starting point:

    + +
           public class HotSwapDemo {
    +
    +           private static final int ITERATIONS = 100;
    +
    +           public static void main(String[] args) {
    +               HotSwapDemo demo = new HotSwapDemo();
    +               System.out.println("Starting HotSwap demo with Java on Truffle: 'java.vm.name' = " + System.getProperty("java.vm.name"));
    +               // run something in a loop
    +               for (int i = 1; i <= ITERATIONS; i++) {
    +                   demo.runDemo(i);
    +               }
    +               System.out.println("Completed HotSwap demo with Java on Truffle");
    +           }
    +
    +           public void runDemo(int iteration) {
    +               int random = new Random().nextInt(iteration);
    +               System.out.printf("\titeration %d ran with result: %d\n", iteration, random);
    +           }
    +       }
    +
    +
  4. +
  5. Check that the java.vm.name property says you’re running on Espresso.
  6. +
  7. Place a line breakpoint on the first line in runDemo().
  8. +
  9. +

    Setup the Run configurations to run with Java on Truffle and press Debug. You will see:

    + +

    +
  10. +
  11. +

    While paused at the breakpoint, extract a method from the body of runDemo():

    + +

    +
  12. +
  13. +

    Reload the changes by navigating to Run -> Debugging Actions -> Reload Changed Classes:

    + +

    +
  14. +
  15. +

    Verify that the change was applied by noticing the <obsolete>:-1 current frame in the Debug -> Frames view:

    + +

    +
  16. +
  17. +

    Place a breakpoint on the first line of the new extracted method and press Resume Program. The breakpoint will hit:

    + +

    +
  18. +
  19. +

    Try to change the access modifiers of printRandom() from private to public static. Reload the changes. Press Resume Program to verify the change was applied:

    + +

    +
  20. +
+ +

Watch the video version of the enhanced HotSwap capabilities with Java on Truffle demo.

+ +

Supported Changes

+ +

The enhanced HotSwap of Java on Truffle is almost feature complete. +The following changes are supported:

+ +
    +
  • Add and remove methods
  • +
  • Add and remove constructors
  • +
  • Add and remove methods from interfaces
  • +
  • Change access modifiers of methods
  • +
  • Change access modifiers of constructors
  • +
  • Add and remove fields
  • +
  • Change field type
  • +
  • Move field in hierarchy and preserve state (see note below)
  • +
  • Changes to class access modifiers, e.g. abstract and final modifiers
  • +
  • Changes to Lambdas
  • +
  • Add new anonymous inner classes
  • +
  • Remove anonymous inner classes
  • +
  • Changing the superclass
  • +
  • Changing implemented interfaces
  • +
+ +

Note: When instance fields are moved in the class hierarchy the state is preserved whenever possible. +Examples include the Pull Up Field refactoring where all existing instances of the origin subclass will be able to read the previously stored value from the super class field. +On the other hand, for unrelated subclass instances where the field was not present prior to the change, the new field value will be the language default (i.e. null for object-type fields, 0 for int etc.).

+ +

As of GraalVM 22.1.0, the following limitations remain:

+
    +
  • Changes to Enums
  • +
+ +

HotSwap Plugin API

+ +

With Java on Truffle you can benefit from enhanced HotSwap capabilities that allow the code to evolve naturally during development without the need for restarting a running application. +While code reloading (HotSwap) is a powerful tool, it is not sufficient to reflect all kinds of changes, e.g., changes to annotations, framework-specific changes such as implemented services or beans. +For these things the code often needs to be executed to reload configurations or contexts before the changes are fully reflected in the running instance. +This is where the Truffle on Java HotSwap Plugin API comes in handy. +The Truffle on Java HotSwap Plugin API is meant for framework developers by setting up appropriate hooks to reflect changes in response to source code edits in your IDE. +The main design principle is that you can register various HotSwap listeners that will be fired on specified HotSwap events. +Examples include the ability to re-run a static initializer, a generic post HotSwap callback and hooks when implementations for a certain service provider changes.

+ +

Note: The HotSwap Plugin API is under development and more fine-grained registration of HotSwap listeners are likely to be added upon requests from the community. +You are welcomed to send enhancement requests to help shape the API through our community support channels.

+ +

Review the HotSwap Plugin API by going through a running example that will enable more powerful reloading support on Micronaut.

+ +

Micronaut HotSwap Plugin

+ +

The Micronaut HotSwap plugin example implementation is hosted as a fork of Micronaut-core. +The following instructions are based on a macOS X setup and only minor variations are needed for Windows. +To get started:

+ +
    +
  1. Clone the repository: +
      git clone git@github.com:javeleon/micronaut-core.git
    +
    +
  2. +
  3. Build and publish to local Maven repository: +
      cd micronaut-core
    +  ./gradlew publishMavenPublicationToMavenLocal
    +
    +
  4. +
+ +

Now you will have a HotSwap-ready version of Micronaut. +Before setting up a sample application that uses the enhanced version of Micronaut, look at what the plugin does under the hood.

+ +

The interesting class is MicronautHotSwapPlugin which holds on to an application context that can be reloaded when certain changes are made to the application source code. +The class looks like this:

+ +
final class MicronautHotSwapPlugin implements HotSwapPlugin {
+
+    private final ApplicationContext context;
+    private boolean needsBeenRefresh = false;
+
+    MicronautHotSwapPlugin(ApplicationContext context) {
+        this.context = context;
+        // register class re-init for classes that provide annotation metadata
+        EspressoHotSwap.registerClassInitHotSwap(
+                AnnotationMetadataProvider.class,
+                true,
+                () -> needsBeenRefresh = true);
+        // register ServiceLoader listener for declared bean definitions
+        EspressoHotSwap.registerMetaInfServicesListener(
+                BeanDefinitionReference.class,
+                context.getClassLoader(),
+                () -> reloadContext());
+        EspressoHotSwap.registerMetaInfServicesListener(
+                BeanIntrospectionReference.class,
+                context.getClassLoader(),
+                () -> reloadContext());
+    }
+
+    @Override
+    public String getName() {
+        return "Micronaut HotSwap Plugin";
+    }
+
+    @Override
+    public void postHotSwap(Class<?>[] changedClasses) {
+        if (needsBeenRefresh) {
+            reloadContext();
+        }
+        needsBeenRefresh = false;
+    }
+
+    private void reloadContext() {
+        if (Micronaut.LOG.isInfoEnabled()) {
+            Micronaut.LOG.info("Reloading app context");
+        }
+        context.stop();
+        context.flushBeanCaches();
+        context.start();
+
+        // fetch new embedded application bean which will re-wire beans
+        Optional<EmbeddedApplication> bean = context.findBean(EmbeddedApplication.class);
+        // now restart the embedded app/server
+        bean.ifPresent(ApplicationContextLifeCycle::start);
+    }
+}
+
+ +

The logic regarding the HotSwap API sits in the constructor of this class. +Micronaut is architected around compile-time annotation processing where annotation metadata is gathered and stored into static fields in generated classes. +Whenever a developer makes a change to a Micronaut-annotated class, the corresponding metadata classes are re-generated. +Since standard HotSwap does not (and it should not) re-run static initializers, with HotSwap Plugin static initializer are re-run for all classes that provide metadata (the Micronaut-generated classes). Thus, this API method EspressoHotSwap.registerClassInitHotSwap is used:

+ +
public static boolean registerClassInitHotSwap(Class<?> klass, boolean onChange, HotSwapAction action)
+
+ +

This will register a listener on Class changes for the specific class and importantly any subclass thereof. +The onChange variable instructs if static initializers should only be re-run if the code within changed. +The action parameter is a hook for firing a specific action whenever a static initializer has been re-run. +Here we pass a function for setting the needsBeenRefresh field to true whenever an static initializer is re-run. +Upon completion of a HotSwap action the plugin receives a postHotSwap call that, in response to a true needsBeenRefresh, executes the Micronaut-specific code to reload the application context in the reloadContext method.

+ +

Detecting and Injecting New Classes

+ +

HotSwap is designed to enable classes to be HotSwap’ed in a running application. +However, if a developer introduces an entirely new class, e.g., a new @Controller class in Micronaut, HotSwap does not magically inject a new class, as doing so would require knowledge about internal classloading logic at the very least.

+ +

A standard way in which classes are discovered by a framework is through the ServiceLoader mechanism. +The Truffle on Java HotSwap API has built-in support for registering service implementation change listeners by means of the method EspressoHotSwap.registerMetaInfServicesListener:

+ +
public static boolean registerMetaInfServicesListener(Class<?> serviceType, ClassLoader loader, HotSwapAction action)
+
+ +

The current support is limited to listening for implementation changes for class path based service deployment in META-INF/services. +Whenever there is a change to the set of service implementations for the registered class type, the action is fired. +In the Micronaut HotSwap plugin, reloadContext is executed which will then pickup the changes automatically.

+ +

Note: HotSwap actions caused by changes to service implementation changes are fired indepent of HotSwap. As a developer, you do not need to perform a HotSwap from your IDE to see the new functionality in the running application.

+ +

Next-Level HotSwap for Micronaut

+ +

Now that you know how the Micronaut HotSwap plugin works, use this feature in a real application. +Here is a sample application created from the tutorial “Creating your first Micronaut Graal Application”. +Example’s sources can be downloaded as a ready-made Gradle project from here. +Download, unzip and open the project in your IDE.

+ +

Before you proceed, make sure that you have Java on Truffle installed and set the GraalVM as the project SDK.

+ +
    +
  1. In your IDE navigate to the root build.gradle within the sample project. Add: +
    run.jvmArgs+="-truffle"
    +
    +
  2. +
  3. Also add maven local repository where we previously published the enhanced Micronaut framework. For example: +
    repositories {
    +  mavenLocal()
    +  ...
    +}
    +
    +
  4. +
  5. In gradle.properties update the Micronaut version that you published. For example: +
    micronautVersion=2.5.8-SNAPSHOT
    +
    +

    Now you are all setup.

    +
  6. +
  7. +

    Executeassemble task and create a run configuration using the defined run gradle task.

    +
  8. +
  9. +

    Press the Debug button to start the application in debugging mode, which enables enhanced HotSwap support.

    +
  10. +
  11. +

    Once the application is started, verify that you get a response from the ConferenceController by going to http://localhost:8080/conferences/random.

    +
  12. +
  13. Try to make various changes to the classes within the sample app, e.g., change the @Controller mapping to a different value, or add a new @Getannotated method and apply HotSwap to see the magic. In case you define a new @Controller class, all you need is compiling the class and once the change is picked up by the file system watch, you will see the reload without the need for explicitly HotSwap.
  14. +
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/implementation/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/implementation/index.html new file mode 100644 index 0000000..80baa89 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/implementation/index.html @@ -0,0 +1,161 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Implementation Details

+ +

Java on Truffle operates, like other languages implemented with Truffle, both as a native executable or on top of HotSpot (currently possible on Linux only). +In the first case, when the Java on Truffle runtime is compiled to a native executable, it does not require HotSpot to run Java. +However it requires a standard core Java library (the rt.jar library for Java 8 or the lib/modules file for Java 11 as well as the associated native libraries: libjava, libnio, etc.).

+ +

Java on Truffle is a minified Java VM that implements all core components of a VM including:

+
    +
  • Bytecode interpreter
  • +
  • Bytecode verifier
  • +
  • Single Java Class File parser
  • +
  • Simple object model
  • +
  • Java Native Interface (JNI) implementation in Java
  • +
  • Virtual Machine Implementation in Java
  • +
  • Java Debug Wire Protocol (JDWP)
  • +
+ +

Java on Truffle reuses all JARs and native libraries from GraalVM. +All native libraries and methods are loaded/accessed/called via Truffle Native Function Interface (JNI). +JNI handles are implemented in Java on Truffle, e.g., all Truffle NFI methods only receive and return primitives. +Some methods are substituted for performance, e.g., Math.sqrt, System.arraycopy, avoiding the expensive transition to native.

+ +

Some native libraries might contain static data that would conflict if were used from multiple Java on Truffle contexts or even from both Java on Truffle and Java running on HotSpot. +On Linux, Java on Truffle uses the capability of Truffle NFI to try to load libraries in isolated namespaces (dlmopen). This is only available on Linux with glibc and has many limitations. +This mode is not used when running in a native executable since there will be no conflict with HotSpot.

+ +

Current Limitations

+ +
    +
  • Java on Truffle does not implement the JVM Tool Interface (JVMTI). As a result, it does not support the -agentlib, or -agentpath VM options.
  • +
  • Java on Truffle does not implement the java.lang.instrument interface. As a result it does not support the -javaagent VM option.
  • +
  • Java on Truffle currently uses the standard native libraries from the Java core library. This requires allowing a polyglot Context native access. Because of the way these libraries are loaded (via Truffle NFI), running on top of HotSpot only works on Linux (with glibc). Running as part of a native executable works on Linux, Windows, and macOS but it currently limited to one context.
  • +
  • Support for Java Management Extensions (JMX) is partial and some methods might return partial data.
  • +
  • The Debugger Protocol Implementation (JDWP) lacks some capabilities compared to HotSpot. It will correctly report the supported capabilities. In particular actions that require to enumerate all Java objects are not supported. However it does support a few hot reloading cases that HotSpot does not.
  • +
  • When the java.MultiThreaded option is set to “false”, reference processing will not happen. Depending on the application, this could create resource leaks. Note that this option is set to “false” automatically if Java on Truffle runs in a context where a single-threaded language is enabled (e.g., JavaScript).
  • +
  • Java on Truffle does not support the Polyglot API yet. However, it provides a guest Java Polyglot API, described in polyglot.jar. For more information, see Interoperability with Truffle Languages.
  • +
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/index.html new file mode 100644 index 0000000..a8be818 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/index.html @@ -0,0 +1,273 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Java on Truffle

+ +

Using GraalVM, you can run Java applications normally on the JVM, in Native Image, and now on Truffle. +Java on Truffle is an implementation of the Java Virtual Machine Specification, Java SE 8 and Java SE 11, built upon GraalVM as a Truffle interpreter. +It is a minified Java VM that includes all core components of a VM, implements the same API as the Java Runtime Environment library (libjvm.so), and reuses all JARs and native libraries from GraalVM. +See the Implementation Details for more information. +The project name behind this implementation is “Espresso”. +Its open source version is available on GitHub.

+ +

The Java on Truffle execution mode runs Java via a Java bytecode interpreter, implemented with the Truffle framework – an open-source library for writing interpreters for programming languages. +Now Java can be executed by the same principle as other languages in the GraalVM ecosystem (JavaScript, Ruby, Python, R), directly interoperate with those languages, and pass data back and forth in the same memory space. +Besides complete language interoperability, with Java on Truffle you can:

+ +
    +
  • run Java bytecode in a separate context from the host Java VM.
  • +
  • run either a Java 8, Java 11, Java 17 guest JVM, allowing to embed e.g. a Java 8 context in a Java 11 application, by using GraalVM’s Polyglot API
  • +
  • leverage the whole stack of tools provided by the Truffle framework, not previously available for Java.
  • +
  • have an improved isolation of the host Java VM and the Java program running on Truffle, so you can run less trusted guest code.
  • +
  • run in the context of a native executable while still allowing dynamically-loaded bytecodes.
  • +
+ +

Java on Truffle passes the Java Compatibility Kit (JCK or TCK for Java SE).

+ +

Install Java on Truffle

+ +

The Java on Truffle runtime is not available by default, but can be easily added to GraalVM using the GraalVM Updater tool.

+
gu install espresso
+
+ +

The installable’s name, espresso, is the project code-name, it is used to avoid ambiguity with the other ways Java code can run on GraalVM. +It installs the jvm runtime library under the $JAVA_HOME/lib/truffle/ location.

+ +

Run Java on Truffle

+ +

You can run a Java application on Truffle by passing the -truffle flag to the standard java launcher. +This is similar to how you used to switch between the -client and -server JVMs.

+ +

To execute a class file:

+
java -truffle [options] class
+
+

To execute a JAR file:

+
java -truffle [options] -jar jarfile
+
+ +

Using Java 11 based GraalVM distribution, you can also run a Java application from the main class in a module, or run a single source-file program:

+
java -truffle [options] -m module[/<mainclass>]
+java -truffle [options] sourcefile
+
+ +

By default, Java on Truffle runs within GraalVM by reusing all GraalVM’s JARs and native libraries, but it is possible to “cross-version” and specify a different Java installation directory (java.home). +It will automatically switch versions regardless of the host JVM.

+
java -truffle --java.JavaHome=/path/to/java/home -version
+
+ +

Performance Considerations

+ +

The startup time will not match the speed offered by the regular GraalVM just-in-time (JIT) execution yet, but having created a fully working Java on Truffle runtime, the development team is now focusing on performance. +You can still influence the performance by passing the following options to java -truffle:

+
    +
  • --engine.MultiTier=true to enable multi-tier compilation;
  • +
  • --engine.Inlining=false in combination with --java.InlineFieldAccessors=true to make the compilation faster, in exchange for slower performance.
  • +
+ +

The --vm.XX: syntax ensures the option is passed to the underlying Native Image VM. +When using the -XX: syntax, the VM first checks if there is such an option in the Java on Truffle runtime. +If there is none, it will try to apply this option to the underlying Native Image VM. +This might be important for options such as MaxDirectMemorySize which can be set independently at both levels: -XX:MaxDirectMemorySize=256M controls how much native memory can be reserved by the Java program running on Truffle (the guest VM), while --vm.XX:MaxDirectMemorySize=256M controls how much native memory can be reserved by Native Image (the host VM).

+ +

Start Running Applications

+ +

From Command Line

+ +

To ensure you have successfully installed Java on Truffle, verify its version:

+
java -truffle -version
+
+ +

Taking this HelloWorld.java example, compile it and run from the command line:

+
public class HelloWorld {
+  public static void main(String[] args) {
+    System.out.println("Hello, World!");
+  }
+}
+
+ +
$JAVA_HOME/bin/javac HelloWorld.java
+$JAVA_HOME/bin/java -truffle HelloWorld
+HelloWorld.java!
+
+ +

Taking some real-world applications, try running Spring PetClinic - a sample web application that demonstrates the use of Spring Boot with Spring MVC and Spring Data JPA.

+ +
    +
  1. Clone the project and navigate to the project’s directory: +
    git clone https://github.com/spring-projects/spring-petclinic.git
    +cd spring-petclinic
    +
    +
  2. +
  3. Build a JAR file (Spring PetClinic is built with Maven): +
    ././mvnww package
    +
    +
  4. +
  5. Then run it from the command line by selecting the -truffle runtime: +
    java -truffle -jar target/spring-petclinic-<version>-SNAPSHOT.jar
    +
    +
  6. +
  7. When the application starts, access it on localhost:8080.
  8. +
+ +

From IDE

+ +

To run a Java project on Truffle from an IDE requires setting the Java on Truffle enabled GraalVM as a project’s default JDK. +For exampe, to run the Spring PetClinic project using Intellij IDEA, you need to:

+ +

1. Navigate to File, then to Project Structure. Click Project, and then click Project SDK. Expand the drop down, press Add JDK and open the folder where you installed GraalVM. For macOS users, JDK home path will be /Library/Java/JavaVirtualMachines/{graalvm}/Contents/Home. Give it a name, and press Apply.

+ +

+ +

2. Generate sources and update folders for the project. In the Maven sidebar, click on the folder with the spinner icon:

+ +

+ +

3. Enable the Java on Truffle execution mode. From the main menu select Run, then Run…. Click Edit Configurations and choose Environment. Put the -truffle -XX:+IgnoreUnrecognizedVMOptions command in VM options and press Apply.

+ +

+ +

It is necessary to specify -XX:+IgnoreUnrecognizedVMOptions because Intellij automatically adds a -javaagent argument which is not supported yet.

+ +

4. Press Run.

+ +

Debugging

+ +

You do not have to configure anything special to debug applications running Java on Truffle from your favorite IDE debugger. +For example, starting a debugger session from IntelliJ IDEA is based on the Run Configurations. +To ensure you attach the debugger to your Java application in the same environment, navigate in the main menu to Run -> Debug… -> Edit Configurations, expand Environment, check the JRE value and VM options values. +It should show GraalVM as project’s JRE and VM options should include -truffle -XX:+IgnoreUnrecognizedVMOptions: -truffle to run Java on Truffle, and -XX:+IgnoreUnrecognizedVMOptions as a temporary workaround since the Java on Truffle runtime does not yet support attaching Java agents.

+ +

+ + + +

Java on Truffle enables a seamless Java interoperability with other languages in the GraalVM ecosystem. +Check the Interoperability with Truffle Languages guide to learn how to load code written in foreign languages, export and import objects between languages, how to use Java-on-Truffle objects from a foreign language and vice versa to create powerful polyglot programs.

+ +

To learn about the implementation approach, project’s current status, and known limitations proceed to Implementation Details.

+ +

You can already run some large applications like the Eclipse IDE, Scala or other languages REPLs, etc. in the Java on Truffle execution mode. +We recommend having a look at the collection of Demo Applications.

+ +

If you have a question, check the available FAQs, or reach us directly over the #espresso channel in GraalVM Slack.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/interoperability/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/interoperability/index.html new file mode 100644 index 0000000..b12230f --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java-on-truffle/interoperability/index.html @@ -0,0 +1,447 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Interoperability with Truffle Languages

+ +

Java on Truffle allows you to interface other “Truffle” languages (languages which interpreters are implemented with the Truffle framework) to create polyglot programs – programs written in more than one language.

+ +

This guide describes how to load code written in foreign languages, how to export and import objects between languages, how to use Java on Truffle objects from a foreign language, how to use foreign objects from Java on Truffle, and how to embed in host Java.

+ +

To avoid confusion, the terms host and guest are used to differentiate the different layers where Java is executed. Java on Truffle refers to the guest layer.

+ +

You pass polyglot options to the java -truffle launcher. +If you are using the native configuration, you will need to use the --polyglot flag to get access to other languages.

+ +

Foreign objects must “inhabit” a guest Java type when flowing into Java on Truffle. +How this type is attached to foreign objects is an implementation detail.

+ +

Polyglot

+ +

Java on Truffle provides a guest Java Polyglot API, described in polyglot.jar. +This JAR is automatically injected on guest Java contexts but can be excluded with --java.Polyglot=false.

+ +

You can import the Polyglot class to interact with other guest languages:

+ +
// guest java
+import com.oracle.truffle.espresso.polyglot.Polyglot;
+
+int two = Polyglot.eval(int.class, "js", "1+1");
+
+ +

You can determine if an object is foreign:

+
// guest java
+Object foreign = Polyglot.eval("js", "[2, 0, 2, 1]");
+Object local = new int[]{2, 0, 2, 1};
+System.out.println(Polyglot.isForeignObject(foreign)); // prints true
+System.out.println(Polyglot.isForeignObject(local));   // prints false
+
+ +

You can cast foreign objects to guest Java types:

+
// guest java
+Object foreignArray = Polyglot.eval("js", "['a string', 42, 3.14159, null]");
+Object[] objects = Polyglot.cast(Object[].class, foreignArray);
+
+assert objects.length == 4;
+String elem0 = Polyglot.cast(String.class, objects[0]);   // eager conversion
+Integer elem1 = Polyglot.cast(Integer.class, objects[1]); // preserves identity
+int elem1_ = Polyglot.cast(int.class, objects[1]);        // eager conversion
+
+double elem2 = Polyglot.cast(double.class, objects[2]);   // eager conversion
+Object elem3 = objects[3];
+assert elem3 == null;
+
+ +

The Polyglot.cast(targetClass, obj) method is an augmented Java cast, e.g., targetClass.cast(obj):

+
    +
  • Java cast succeeds ⇒ Polyglot.cast succeeds.
  • +
  • Java cast does not succeeds, Polyglot.cast can “re-type” foreign objects, e.g., to cast to Integer, the foreign object must fitsInInt.
  • +
  • If Polyglot.cast fails, it will throw ClassCastException similar to Class#cast.
  • +
+ +

Polyglot.cast supports a natural mapping from common interop “kinds” to Java types, summarized below:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Interop “kind”Allowed typesPreserves identity
isBooleanBoolean/booleanYes* (boxed type)
fitsInByteByte/byteYes* (boxed type)
fitsInShortShort/shortYes* (boxed type)
fitsInIntInteger/intYes* (boxed type)
fitsInLongLong/longYes* (boxed type)
fitsInFloatFloat/floatYes* (boxed type)
fitsInDoubleDouble/doubleYes* (boxed type)
isString & 1-characterCharacter/charYes* (boxed type)
isStringStringNo (eager conversion)
isException & Polyglot.isForeignObjectForeignExceptionYes
hasArrayElementsObject[]Yes
isNull*Yes
*ObjectYes
+ +

You can access the polyglot bindings:

+
// guest java
+Object foreignObject = Polyglot.importObject("foreign_object");
+
+// Also typed imports
+String userName = Polyglot.importObject("user_name", String.class);
+int year = Polyglot.importObject("year", int.class);
+
+// And exports
+Polyglot.exportObject("data", new double[]{56.77, 59.23, 55.67, 57.50, 64.44, 61.37);
+Polyglot.exportObject("message", "Hello, Espresso!");
+
+ +

Interop Protocol

+ +

Java on Truffle provides an explicit guest API to access the Interop protocol. +It contains methods mimicking the interop protocol messages. +This API can be used on guest Java objects as well.

+ +
// guest java
+import com.oracle.truffle.espresso.polyglot.Interop;
+
+Object foreignArray = Polyglot.eval("js", "[2, 0, 2, 1]");
+System.out.println(Interop.hasArrayElements(foreignArray)); // prints true
+System.out.println(Interop.getArraySize(foreignArray));     // prints 4
+
+Object elem0 = Interop.readArrayElement(foreignArray, 0);
+System.out.println(Interop.fitsInInt(elem0)); // prints true
+System.out.println(Interop.asInt(elem0));     // prints 2
+
+ +

Embedding in Host Java

+ +

Java on Truffle is embedded via the Polyglot API, which is part of GraalVM.

+ +
// host java
+import org.graalvm.polyglot.*;
+
+class Embedding {
+    public static void main(String[] args) {
+        Context polyglot = Context.newBuilder().allowAllAccess(true).build();
+
+        // Class loading is exposed through language bindings, with class
+        // names using the same format as Class#forName(String).
+        Value intArray = polyglot.getBindings("java").getMember("[I");
+        Value objectArray = polyglot.getBindings("java").getMember("[Ljava.lang.Object;")
+
+        Value java_lang_Math = polyglot.getBindings("java").getMember("java.lang.Math");
+        double sqrt2 = java_lang_Math.invokeMember("sqrt", 2).asDouble();
+        double pi = java_lang_Math.getMember("PI").asDouble();
+        System.out.println(sqrt2);
+        System.out.println(pi);
+    }
+}
+
+ +

A number of useful context option can be set with contextBuilder.option(key, value):

+
    +
  • Java properties can be added by settings java.Properties.property.name to the desired value (in this case this would set the property.name).
  • +
  • java.Properties.java.class.path can be used to set the classpath of the Java on Truffle context.
  • +
  • java.Properties.java.library.path can be used to set the native library path of the Java on Truffle context.
  • +
  • java.EnableAssertions can be set to true to enable assertions.
  • +
  • java.EnableSystemAssertions can be set to true to enable assertions in the Java standard library.
  • +
  • java.Verify can be set to none, remove, or all to control whether bytecode verification does not happen, only happens on user code, or happens for all classes.
  • +
  • java.JDWPOptions can be set to setup and enable debugging over JDWP. For example, it could be set to transport=dt_socket,server=y,address=localhost:8080,suspend=y.
  • +
  • java.Polyglot can be set to true or false to allow or deny access to the polyglot features from the com.oracle.truffle.espresso.polyglot package.
  • +
  • java.PolyglotTypeConverters can be set to declare a type conversion function that maps a meta qualified name to a type converter class. Please refer to more details in a dedicated section below.
  • +
  • java.PolyglotInterfaceMappings can be set to a semicolon-separated list of 1:1 interface type mappings to automatically construct guest proxies for host objects that implement declared interfaces in the list. Please refer to more details in a dedicated section below.
  • +
+ +

*Java on Truffle does not support evaluation (.eval) of Java sources.

+ +

In Java, methods can be overloaded, e.g., several methods can share the same name, with different signatures. +To remove ambiguity, Java on Truffle allows to specify the method descriptor in the methodName/methodDescriptor form:

+ +
// host java
+Value java_lang_String = polyglot.getBindings("java").getMember("java.lang.String");
+// String#valueOf(int)
+String valueOf = String.format("%s/%s", "valueOf", "(I)Ljava/lang/String;");
+Value fortyTwo = java_lang_String.invokeMember(valueOf, 42);
+assert "42".equals(fortyTwo.asString());
+
+ +

Class<?> instance vs. static class accessor (Klass):

+

The static class accessor allows to access (public) static fields and call (public) static methods.

+ +
// Class loading through language bindings return the static class accessor.
+Value java_lang_Number = polyglot.getBindings("java").getMember("java.lang.Number");
+Value java_lang_Class = polyglot.getBindings("java").getMember("java.lang.Class");
+
+// Class#forName(String) returns the Class<Integer> instance.
+Value integer_class = java_lang_Class.invokeMember("forName", "java.lang.Integer");
+
+// Static class accessor to Class<?> instance and viceversa.
+assert integer_class.equals(java_lang_Integer.getMember("class"));
+assert java_lang_Integer.equals(integer_class.getMember("static"));
+
+// Get Integer super class.
+assert java_lang_Number.equals(java_lang_Integer.getMember("super"));
+
+ +

Converting host objects to guest types using type converters

+ +

Since version 22.3.0 Java on Truffle has built-in support for declaring type conversion of host objects to proper guest-typed objects. This is done via context builder options as described above. The main idea is to allow transparent flow of objects from host to guest without having to perform guest type checks when host objects enter an embedded Java on Truffle context. Specifically the follwoing options can be set to control type conversion for an embedded context:

+ +

java.PolyglotTypeConverters

+

This option takes precedence over java.PolyglotInterfaceMappings and thus, if a dedicated type converter function is defined, no other automatic interface mapping proxies are generated by Java on Truffle.

+ +

Note: Declared type converters must implement the GuestTypeConversion interface located in the com.oracle.truffle.espresso.polyglot package in polyglor.jar.

+
package com.oracle.truffle.espresso.polyglot;
+
+public interface GuestTypeConversion<T> {
+    T toGuest(Object polyglotInstance);
+}
+
+ +

For each type converter declared use one option call like this:

+ +
// host java
+Context polyglot = Context.newBuilder().allowAllAccess(true).
+        option("java.PolyglotTypeConverters.java.math.BigDecimal", "guest.context.path.BigDecimalConverter").
+        build();
+...
+
+// guest java
+package guest.context.path;
+
+import com.oracle.truffle.espresso.polyglot.GuestTypeConversion;
+import com.oracle.truffle.espresso.polyglot.Interop;
+import com.oracle.truffle.espresso.polyglot.InteropException;
+
+import java.math.BigDecimal;
+
+public class BigDecimalConverter implements GuestTypeConversion<BigDecimal> {
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public BigDecimal toGuest(Object polyglotInstance) {
+        try {
+            return new BigDecimal(Interop.asString(Interop.invokeMember(polyglotInstance, "toString")));
+        } catch (InteropException e) {
+            throw new ClassCastException("polyglot instance cannot be cast to java.math.BigDecimal");
+        }
+    }
+}
+
+
+

The java.math.Bigdecimal part of the option declares the fully qualified meta name of a host object entering Java on Truffle.

+ +

java.PolyglotInterfaceMappings

+ +

If there are no dedicated java.PolyglotTypeConverters for a host object flowing into an embedded Java on Truffle context, automatic interface type mapping kicks in. java.PolyglotInterfaceMappings enables seamless interface type sharing between the host and the embedded context.

+ +

The following example shows how this option can be used to allow passing common JDK collection types by interface to an embedded Java on Truffle context:

+ +
// host java
+builder.option("java.PolyglotInterfaceMappings", getInterfaceMappings());
+
+
+private static String getInterfaceMappings(){
+    return "java.lang.Iterable;"+
+    "java.util.Collection;"+
+    "java.util.List;"+
+    "java.util.Set;"+
+    "java.util.Map;"+
+    "java.util.Iterator;"+
+    "java.util.Spliterator;";
+}
+
+
+ +

Multithreading

+ +

Java on Truffle is designed to be a multi-threaded language and much of the ecosystem expects threads to be available. +This may be incompatible with other Truffle languages which do not support threading, so you can disable the creation of multiple threads with the option --java.MultiThreaded=false.

+ +

When this option is enabled, finalizers will not run, neither the ReferenceQueue notification mechanism. +Both these features would require starting new threads. Note that the garbage-collection of weakly reachable objects remains unaffected.

+ +

Instead, reference processing can be manually triggered through a special command, only available in single-threaded environments.

+ +
// Host Java
+// Will trigger Reference processing and run finalizers
+polyglot.eval("java", "<ProcessReferences>");
+
+ +

Note that this command might trigger arbitrary cleaner and finalizer code. As such, this should ideally be run with as few guest java frames on the stack as possible.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/compiler/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/compiler/index.html new file mode 100644 index 0000000..3c97f89 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/compiler/index.html @@ -0,0 +1,213 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Graal Compiler

+ + + +

The Graal compiler is a dynamic just-in-time (JIT) compiler, written in Java, that transforms bytecode into machine code. +The Graal compiler integrates with the Java HotSpot VM, which supports a compatible version of the JVM Compiler Interface (JVMCI). +JVMCI is a privileged, low-level interface to the JVM, enabling a compiler written in Java to be used by the JVM as a dynamic compiler (see JEP 243). +It can read metadata from the VM, such as method bytecode, and install machine code into the VM. +GraalVM includes a version of the HotSpot JVM that supports JVMCI.

+ +

Compiler Advantages

+ +

The Graal compiler provides optimized performance for programs running on the JVM through unique approaches to code analysis and optimization. +It includes multiple optimization algorithms (called “Phases”), like aggressive inlining, polymorphic inlining, and others. +Find some of the platform-independent compiler optimizations in GraalVM Community Edition here.

+ +

The Graal compiler can bring performance advantages for highly-abstracted programs. For example, it includes a partial-escape-analysis optimization that can remove costly allocations of certain objects. +This optimization determines when a new object is accessible outside a compilation unit and only allocates it on paths that “escape” the compilation unit (e.g. the object is passed as a parameter, stored in a field, or returned from a method). This can greatly improve performance of an application by reducing the number of heap allocations. +Code using more modern Java features like Streams or Lambdas will see greater speedups as this type of code involves a significant number of such non- or partially-escaping objects. Code that is bound by things like I/O or memory allocations that cannot be removed by the compiler will see less improvement. +For more information on performance tuning, refer to Compiler Configuration on JVM.

+ +

Graph Compilation

+ +

To run guest programming languages, namely JavaScript, Ruby, R, Python, and WebAssembly in the same runtime as the host JVM-based language, the compiler should work with a language-independent intermediate representation (IR) between the source language and the machine code to be generated. +A graph was selected for this role.

+ +

The graph can represent similar statements of different languages in the same way, like “if” statements or loops, which makes it possible to mix languages in the same program. +The Graal compiler can then perform language-independent optimization and generate machine code on this graph.

+ +

GraalVM also includes the Truffle language implementation framework – a library, written in Java – to build interpreters for programming languages, which then run on GraalVM. +These languages can consequently benefit from the optimization possibilities of the Graal compiler. +The pipeline for such compilation is:

+ +
    +
  • The Truffle framework code and data (Abstract Syntax Trees) is partially evaluated to produce a compilation graph. When such an Abstract Syntax Tree (AST) is hot (i.e., called many times), it is scheduled for compilation by the compiler.
  • +
  • The compilation graph is optimized by the Graal compiler to produce machine code.
  • +
  • JVMCI installs this machine code in the VM’s code cache.
  • +
  • The AST will automatically redirect execution to the installed machine code once it is available.
  • +
+ +

Ahead-of-time Compilation

+ +

Besides the Truffle framework, GraalVM incorporates its optimizing compiler into an advanced ahead-of-time (AOT) compilation technology – Native Image – which translates Java and JVM-based code into a native platform executable. +These native executables start nearly instantaneously, are smaller, and consume less resources of the same Java application, making them ideal for cloud deployments and microservices. +For more information about the AOT compilation, go to Native Image.

+ +

Compiler Operating Modes

+ +

There are two operating modes of the Graal compiler when used as the HotSpot JIT compiler: as pre-compiled machine code (“libgraal”), or as dynamically executed Java bytecode (“jargraal”).

+ +

libgraal: the Graal compiler is compiled ahead-of-time into a native shared library. +In this operating mode, the shared library is loaded by the HotSpot VM. +The compiler uses memory separate from the HotSpot heap. +It runs fast from the start since it does not need to warm up. +This is the default and recommended mode of operation.

+ +

jargraal: the Graal compiler goes through the same warm-up phase that the rest of the Java application does. +That is, it is first interpreted before its hot methods are compiled. +This mode is selected with the -XX:-UseJVMCINativeLibrary command line option.

+ +

Diagnostic Data

+ +

If an uncaught exception is thrown by the compiler, the compilation is simply discarded and execution continues. +The Graal compiler can instead produce diagnostic data (such as immediate representation graphs) that can be submitted along with a bug report. +This is enabled with -Dgraal.CompilationFailureAction=Diagnose. +The default location of the diagnostics output is in graal_dumps/ under the current working directory of the process but can be changed with the -Dgraal.DumpPath option. +During the VM shutdown, the location of the archive containing the diagnostic data is printed to the console.

+ +

Furthermore, diagnostic data can be produced for any compilation performed by the Graal compiler with the -Dgraal.Dump option. +This will produce diagnostic data for every method compiled by the compiler. +To refine the set of methods for which diagnostic data is produced, use the -Dgraal.MethodFilter=<class>.<method> option. +For example, -Dgraal.MethodFilter=java.lang.String.*,HashMap.get will produce diagnostic data only for methods in the java.lang.String class as well as methods named get in a class whose non-qualified name is HashMap.

+ +

Instead of being written to a file, diagnostic data can also be sent over the network to the Ideal Graph Visualizer. +This requires the -Dgraal.PrintGraph=Network option, upon which the compiler will try to send diagnostic data to 127.0.0.1:4445. +This network endpoint can be configured with the -Dgraal.PrintGraphHost and -Dgraal.PrintGraphPort options.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/index.html new file mode 100644 index 0000000..ef33630 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/index.html @@ -0,0 +1,158 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Java and JVM

+ +

Any JVM-based application that runs on Java HotSpot VM can run on GraalVM. +GraalVM is based on Java HotSpot VM, but integrates an advanced just-in-time (JIT) compiler, written in Java - the Graal compiler. +At runtime, the application is loaded and executed normally on the JVM. +The JVM passes bytecode to the Graal compiler, which compiles that to the machine code and returns it to the JVM.

+ +

GraalVM’s dynamic compiler can improve the efficiency and the speed of applications written in Java, Scala, Kotlin, or other JVM languages through unique approaches to code analysis and optimization. +For example, it assures performance advantages for highly abstracted programs due to its ability to remove costly object allocations. +To learn more, go to the Graal compiler page. +The open source compiler’s code is available on GitHub.

+ +

Compiler Operating Modes

+ +

There are two operating modes of Graal when used as a HotSpot JIT compiler:

+
    +
  • +

    libgraal: the Graal compiler is compiled ahead-of-time into a native shared library. +In this operating mode, the shared library is loaded by the HotSpot VM. +The compiler uses memory separate from the HotSpot heap and runs fast from the start since it does not need to warmup. +This is the default and recommended mode of operation.

    +
  • +
  • +

    jargraal: the Graal compiler goes through the same warm-up phase that the rest of the Java application does. That is, it is first interpreted before its hot methods are compiled. +This mode is selected with the -XX:-UseJVMCINativeLibrary command-line option. +This will delay the time to reach peak performance as the compiler itself needs to be compiled before it produces code quickly. +This mode allows you to debug the Graal compiler with a Java debugger.

    +
  • +
+ +

Interoperability

+ +

In addition to running JVM-based languages on GraalVM, you can also call any other language implemented with the Truffle language implementation framework directly from Java. +See the Polyglot Programming and Embedding Languages guides for more information about interoperability with other programming languages.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/operations/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/operations/index.html new file mode 100644 index 0000000..f029ad0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/operations/index.html @@ -0,0 +1,276 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

JVM Operations Manual

+ +

Running the Graal compiler in Native Image vs on the JVM

+ +

When running the Graal compiler on the JVM, it goes through the same warm-up phase that the rest of the Java application does. +That is, it is first interpreted before its hot methods are compiled. +This can translate into slightly longer times until the application reaches peak performance when compared to the native compilers in the JVM such as C1 and C2.

+ +

To address the issue of taking longer to reach to peak performance, libgraal was introduced – a shared library, produced using Native Image to ahead-of-time compile the compiler itself. +That means the Oracle GraalVM compiler is deployed as a native shared library.

+ +

In this mode, the compiler uses memory separate from the HotSpot heap, and it runs compiled from the start. +Therefore it has execution properties similar to other native HotSpot compilers such as C1 and C2. +Currently, this is the default mode of operation. +It can be disabled with -XX:-UseJVMCINativeLibrary.

+ +

Measuring Performance

+ +

The first thing to be sure of when measuring performance is to ensure the JVM is using the Oracle GraalVM compiler. +In the GraalVM binary, the JVM is configured to use the Graal compiler as the top tier compiler by default. +You can confirm this by adding -Dgraal.ShowConfiguration=info to the command line. +It will produce a line of output similar to the one below when the compiler is initialized:

+ +
Using "Graal Community compiler" loaded from class files
+
+ +
+

Note: The Graal compiler is only initialized on the first top-tier JIT compilation request so if your application is short-lived, you may not see this output.

+
+ +

Optimizing JVM-based applications is a science in itself. +The compilation may not even be a factor in the case of poor performance as the problem may lie in any other part of the VM (I/O, garbage collection, threading, etc), or in +a poorly written application or 3rd party library code. For this reason, it is worth utilizing the JDK Mission Control tool chain to diagnose the application behavior.

+ +

You can also compare performance against the native top-tier compiler in the JVM by adding -XX:-UseJVMCICompiler to the command line.

+ +

If you observe a significant performance regression when using the Graal compiler, please open an issue on GitHub. +Attaching a Java Flight Recorder log and instructions to reproduce the issue makes investigation easier and thus the chances of a fix higher. +Even better is if you can submit a JMH benchmark that represents the hottest parts of your application (as identified by a profiler). +This allows us to very quickly pinpoint missing optimization opportunities or to offer suggestions on how to restructure the code to avoid or reduce performance bottlenecks.

+ +

Troubleshooting the Graal compiler

+ +

Like all software, the Graal compiler is not guaranteed to be bug free so it is useful to know how to diagnose and submit useful bug reports if you encounter issues.

+ +

If you spot a security vulnerability, please do not report it via GitHub Issues or the public mailing lists, but via the process outlined at Reporting Vulnerabilities guide.

+ +

Compilation Exceptions

+ +

One advantage of the compiler being written in Java is that runtime exceptions during compilation are not fatal VM errors. +Instead, each compilation has an exception handler that takes action based on the graal.CompilationFailureAction property.

+ +

The default value is Silent. Specifying Diagnose causes failing compilations to be retried with extra diagnostics enabled. +In this case, just before the VM exits, all diagnostic output captured during retried compilations is written to a .zip file and its location is printed on the console:

+
Graal diagnostic output saved in /Users/demo/graal-dumps/1499768882600/graal_diagnostics_64565.zip
+
+ +

You can then attach the .zip file to an issue on GitHub.

+ +

Apart from Silent and Diagnose, the following values for graal.CompilationFailureAction +are also supported:

+
    +
  • Print: prints a message and stack trace to the console but does not perform the re-compilation.
  • +
  • ExitVM: same as Diagnose but the VM process exits after the re-compilation.
  • +
+ +

Code Generation Errors

+ +

The other type of error you might encounter with compilers is the production of incorrect machine code. +This error can cause a VM crash, which should produce a file that starts with hs_err_pid in the current working directory of the VM process. +In most cases, there is a section in the file that shows the stack at the time of the crash, including the type of code for each frame in the stack, as in the following example:

+ +
Stack: [0x00007000020b1000,0x00007000021b1000],  sp=0x00007000021af7a0,  free space=1017k
+Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
+J 761 JVMCI org.graalvm.compiler.core.gen.NodeLIRBuilder.matchComplexExpressions(Ljava/util/List;)V (299 bytes) @ 0x0000000108a2fc01 [0x0000000108a2fac0+0x141] (null)
+j  org.graalvm.compiler.core.gen.NodeLIRBuilder.doBlock(Lorg/graalvm/compiler/nodes/cfg/Block;Lorg/graalvm/compiler/nodes/StructuredGraph;Lorg/graalvm/compiler/core/common/cfg/BlockMap;)V+211
+j  org.graalvm.compiler.core.LIRGenerationPhase.emitBlock(Lorg/graalvm/compiler/nodes/spi/NodeLIRBuilderTool;Lorg/graalvm/compiler/lir/gen/LIRGenerationResult;Lorg/graalvm/compiler/nodes/cfg/Block;Lorg/graalvm/compiler/nodes/StructuredGraph;Lorg/graalvm/compiler/core/common/cfg/BlockMap;)V+65
+
+ +

This example shows that the top frame was compiled (J) by the JVMCI compiler, which is the Graal compiler. +The crash occurred at offset 0x141 in the machine code produced for:

+
org.graalvm.compiler.core.gen.NodeLIRBuilder.matchComplexExpressions(Ljava/util/List;)V
+
+ +

The next two frames in the stack were executed in the interpreter (j). +The location of the crash is also often indicated near the top of the file with something like this:

+
# Problematic frame:
+# J 761 JVMCI org.graalvm.compiler.core.gen.NodeLIRBuilder.matchComplexExpressions(Ljava/util/List;)V (299 bytes) @ 0x0000000108a2fc01 [0x0000000108a2fac0+0x141] (null)
+
+ +

In this example, there is likely an error in the code produced by the Graal compiler for NodeLIRBuilder.matchComplexExpressions.

+ +

When filing an issue on GitHub for such a crash, you should first attempt to reproduce the crash with extra diagnostics enabled for the compilation of the problematic method. +In this example, you would add the following to your command line:

+
-Dgraal.MethodFilter=NodeLIRBuilder.matchComplexExpressions, -Dgraal.Dump=:2
+
+ +

These options are described in more detail here. +In brief, these options tell the compiler to capture snapshots of the compiler state at verbosity level 2 while compiling any method named matchComplexExpressions in a class with a simple name of NodeLIRBuilder. +The complete format of the MethodFilter option is described in the output of java -XX:+JVMCIPrintProperties.

+ +

Quite often, the crash location does not exist directly in the problematic method mentioned in the crash log but comes from an inlined method.

+ +

In such a case, simply filtering for the problematic method might not capture an erroneous compilation causing a crash.

+ +

To improve the likelihood of capturing an erroneous compilation, you need to broaden the MethodFilter value. +To guide this, add -Dgraal.PrintCompilation=true when trying to reproduce the crash so you can see what was compiled just before the crash.

+ +

The following shows sample output from the console:

+
HotSpotCompilation-1218        Lorg/graalvm/compiler/core/amd64/AMD64NodeLIRBuilder;                  peephole                                      (Lorg/graalvm/compiler/nodes/ValueNode;)Z           |   87ms   428B   447B  1834kB
+HotSpotCompilation-1212        Lorg/graalvm/compiler/lir/LIRInstructionClass;                         forEachState                                  (Lorg/graalvm/compiler/lir/LIRInstruction;Lorg/graalvm/compiler/lir/InstructionValueProcedure;)V  |  359ms    92B   309B  6609kB
+HotSpotCompilation-1221        Lorg/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator;          getResult                                     ()Lorg/graalvm/compiler/hotspot/HotSpotLIRGenerationResult;  |   54ms    18B   142B  1025kB
+#
+# A fatal error has been detected by the Java Runtime Environment:
+#
+#  SIGSEGV (0xb) at pc=0x000000010a6cafb1, pid=89745, tid=0x0000000000004b03
+#
+# JRE version: OpenJDK Runtime Environment (8.0_121-b13) (build 1.8.0_121-graalvm-olabs-b13)
+# Java VM: OpenJDK 64-Bit GraalVM (25.71-b01-internal-jvmci-0.30 mixed mode bsd-amd64 compressed oops)
+# Problematic frame:
+# J 1221 JVMCI org.graalvm.compiler.hotspot.amd64.AMD64HotSpotLIRGenerator.getResult()Lorg/graalvm/compiler/hotspot/HotSpotLIRGenerationResult; (18 bytes) @ 0x000000010a6cafb1 [0x000000010a6caf60+0x51] (null)
+#
+# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
+
+

Here we see that the crash happened in a different method than the first crash. +As such, we expand the filter argument to be -Dgraal.MethodFilter=NodeLIRBuilder.matchComplexExpressions,AMD64HotSpotLIRGenerator.getResult and run again.

+ +

When the VM crashes in this way, it does not execute the shutdown code that archives the Graal compiler diagnostic output or delete the directory it was written to. +This must be done manually after the crash.

+ +

By default, the directory is $PWD/graal-dumps/<timestamp>; for example, ./graal-dumps/1499938817387. +However, you can set the directory with -Dgraal.DumpPath=<path>.

+ +

A message, such as the following, is printed to the console when this directory is first used by the compiler:

+
Dumping debug output in /Users/demo/graal-dumps/1499768882600
+
+ +

This directory should contain content related to the crashing method, such as:

+
ls -l /Users/demo/graal-dumps/1499768882600
+-rw-r--r--  1 demo  staff    144384 Jul 13 11:46 HotSpotCompilation-1162[AMD64HotSpotLIRGenerator.getResult()].bgv
+-rw-r--r--  1 demo  staff     96925 Jul 13 11:46 HotSpotCompilation-1162[AMD64HotSpotLIRGenerator.getResult()].cfg
+-rw-r--r--  1 demo  staff  12600725 Jul 13 11:46 HotSpotCompilation-791[NodeLIRBuilder.matchComplexExpressions(List)].bgv
+-rw-r--r--  1 demo  staff   1727409 Jul 13 11:46 HotSpotCompilation-791[NodeLIRBuilder.matchComplexExpressions(List)].cfg
+
+

You should attach a .zip of this directory to an issue on GitHub.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/options/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/options/index.html new file mode 100644 index 0000000..856f6e5 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/java/options/index.html @@ -0,0 +1,280 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Compiler Configuration on JVM

+ +

The options for configuring the Graal compiler on the JVM are in 3 categories.

+ +

General Options

+ +

These are general options for setting/getting configuration details.

+ +
    +
  • -XX:-UseJVMCICompiler: This disables use of the Graal compiler as the top tier JIT. +This is useful when wanting to compare performance of the Graal compiler against the native JIT compilers.
  • +
  • +

    -Dgraal.CompilerConfiguration=<name>: Selects the Graal compiler configuration to use. If omitted, the compiler +configuration with the highest auto-selection priority is used. To see the set +of available configurations, supply the value help to this option.

    + +

    The current configurations and their semantics are:

    +
      +
    • enterprise: To produce highly optimized code with a possible trade-off to compilation time. [Oracle GraalVM]
    • +
    • community: To produce reasonably optimized code with a faster compilation time.
    • +
    • economy: To compile as fast as possible with less optimal throughput of the generated code.
    • +
    +
  • +
  • +

    -Dgraal.ShowConfiguration=none: Prints information about the Graal compiler configuration selected. + This option only produces output when the compiler is initialized. By default, the Graal compiler is + initialized on the first top-tier compilation. For this reason, the way to use this option + is as follows: java -XX:+EagerJVMCI -Dgraal.ShowConfiguration=info -version.

    + +

    The accepted values for this option are:

    +
      +
    • none: To show no information.
    • +
    • info: To print one line of output describing the compiler configuration in use + and whether it is loaded from a Native Image (“libgraal”) or from class files (“jargraal”).
    • +
    • verbose: To print detailed compiler configuration information.
    • +
    +
  • +
  • +

    -Dgraal.MitigateSpeculativeExecutionAttacks=None: Selects a strategy to mitigate speculative + execution attacks (e.g., SPECTRE).

    + +

    Accepted values are:

    +
      +
    • None: No mitigations are used in JIT compiled code.
    • +
    • AllTargets: All branches are protected against speculative attacks. This has a large +performance impact.
    • +
    • GuardTargets: Only branches that preserve Java memory safety are protected. This has +reduced performance impact.
    • +
    • NonDeoptGuardTargets: Same as GuardTargets except that branches which deoptimize are +not protected since they can not be executed repeatedly.
    • +
    +
  • +
+ +

Performance Tuning Options

+ +
    +
  • -Dgraal.UsePriorityInlining=true: This can be used to disable use of the advanced inlining +algorithm that favours throughput over compilation speed. [Oracle GraalVM]
  • +
  • -Dgraal.Vectorization=true: This can be used to disable the auto vectorization optimization. +[Oracle GraalVM]
  • +
  • -Dgraal.OptDuplication=true: This can be used to disable the path duplication optimization. [Oracle GraalVM]
  • +
  • -Dgraal.TuneInlinerExploration=0: This can be used to try tune for better peak performance or faster warmup. +It automatically adjusts values governing the effort spent during inlining. The value of the option is +a float clamped between -1 and 1 inclusive. Anything below +0 reduces inlining effort and anything above 0 increases +inlining effort. In general, peak performance is improved with more inlining effort +while less inlining effort improves warmup (albeit to a lower peak). Note that this +option is only a heuristic and the optimal value can differ from application to application. [Oracle GraalVM]
  • +
  • +

    -Dgraal.TraceInlining=false: Enables tracing of inlining decisions. This can be used + for advanced tuning where it may be possible to change the source code of the program. + The output format is shown below:

    + +
    compilation of 'Signature of the compilation root method':
    +at 'Sig of the root method' ['Bytecode index']: <'Phase'> 'Child method signature': 'Decision made about this callsite'
    +  at 'Signature of the child method' ['Bytecode index']:
    +     |--<'Phase 1'> 'Grandchild method signature': 'First decision made about this callsite'
    +     \--<'Phase 2'> 'Grandchild method signature': 'Second decision made about this callsite'
    +  at 'Signature of the child method' ['Bytecode index']: <'Phase'> 'Another grandchild method signature': 'The only decision made about this callsite.'
    +
    + +

    For example:

    +
    compilation of java.lang.Character.toUpperCaseEx(int):
    +at java.lang.Character.toUpperCaseEx(Character.java:7138) [bci: 22]:
    +   ├──<GraphBuilderPhase> java.lang.CharacterData.of(int): no, bytecode parser did not replace invoke
    +   └──<PriorityInliningPhase> java.lang.CharacterData.of(int): yes, worth inlining according to the cost-benefit analysis.
    +at java.lang.Character.toUpperCaseEx(Character.java:7138) [bci: 26]:
    +   ├──<GraphBuilderPhase> java.lang.CharacterDataLatin1.toUpperCaseEx(int): no, bytecode parser did not replace invoke
    +   └──<PriorityInliningPhase> java.lang.CharacterDataLatin1.toUpperCaseEx(int): yes, worth inlining according to the cost-benefit analysis.
    +  at java.lang.CharacterDataLatin1.toUpperCaseEx(CharacterDataLatin1.java:223) [bci: 4]:
    +     ├──<GraphBuilderPhase> java.lang.CharacterDataLatin1.getProperties(int): no, bytecode parser did not replace invoke
    +     └──<PriorityInliningPhase> java.lang.CharacterDataLatin1.getProperties(int): yes, worth inlining according to the cost-benefit analysis.
    +
    +
  • +
+ +

Diagnostic Options

+ +
    +
  • +

    -Dgraal.CompilationFailureAction=Silent: Specifies the action to take when compilation fails by + throwing an exception.

    + +

    The accepted values are:

    +
      +
    • Silent: Print nothing to the console.
    • +
    • Print: Print a stack trace to the console.
    • +
    • Diagnose: Retry the compilation with extra diagnostics enabled. On VM exit, the collected + diagnostics are saved to a zip file that can be submitted along with a bug report. A message + is printed to the console describing where the diagnostics file is saved: +
      Graal diagnostic output saved in /Users/graal/graal_dumps/1549459528316/graal_diagnostics_22774.zip
      +
      +
    • +
    • ExitVM: Same as Diagnose except that the VM process exits after retrying.
    • +
    + +

    For all values except for ExitVM, the VM continues executing.

    +
  • +
  • -Dgraal.CompilationBailoutAsFailure=false: The compiler may not complete compilation of a method due + to some property or code shape in the method (e.g., exotic uses of the jsr and ret bytecodes). In this + case the compilation bails out. If you want to be informed of such bailouts, this option makes GraalVM + treat bailouts as failures and thus be subject to the action specified by the + -Dgraal.CompilationFailureAction option.
  • +
  • -Dgraal.PrintCompilation=false: Prints an informational line to the console for each completed compilation. +For example: +
    HotSpotCompilation-11  Ljava/lang/Object;                            wait          ()V       |  591ms    12B    92B  4371kB
    +HotSpotCompilation-175 Ljava/lang/String;                            lastIndexOf   (II)I     |  590ms   126B   309B  4076kB
    +HotSpotCompilation-184 Ljava/util/concurrent/ConcurrentHashMap;      setTabAt      ([Ljava/util/concurrent/ConcurrentHashMap$Node;ILjava/util/concurrent/ConcurrentHashMap$Node;)V  |  591ms    38B    67B  3411kB
    +HotSpotCompilation-136 Lsun/nio/cs/UTF_8$Encoder;                    encode        ([CII[B)I |  591ms   740B   418B  4921
    +
    +
  • +
+ +

Setting Compiler Options with Language Launchers

+ +

The Graal compiler properties above are usable with some other GraalVM launchers such as +node, js and lli. The prefix for specifying the properties is slightly different. +For example:

+
java -XX:+EagerJVMCI -Dgraal.ShowConfiguration=info -version
+
+ +

Becomes:

+
js --jvm --vm.Dgraal.ShowConfiguration=info -version
+
+ +
+

Note the -D prefix is replaced by --vm.D.

+
+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/FAQ/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/FAQ/index.html new file mode 100644 index 0000000..2f9fb91 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/FAQ/index.html @@ -0,0 +1,431 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Frequently Asked Questions

+ +

Below are the most frequently asked questions and answers about JavaScript running on GraalVM.

+ +

Compatibility

+ +

Is GraalVM compatible with the JavaScript language?

+

GraalVM is compatible with the ECMAScript 2022 specification and is further developed alongside the 2023 draft specification. +The compatibility of GraalVM’s JavaScript runtime is verified by external sources, like the Kangax ECMAScript compatibility table.

+ +

GraalVM JavaScript is tested against a set of test engines, like the official test suite of ECMAScript, test262, as well as tests published by V8 and Nashorn, Node.js unit tests, and GraalVM’s own unit tests.

+ +

For a reference of the JavaScript APIs that GraalVM supports, see GRAAL.JS-API.

+ +

Is GraalVM compatible with the original node implementation?

+

Node.js based on GraalVM is largely compatible with the original Node.js (based on the V8 engine). +This leads to a high number of npm-based modules being compatible with GraalVM. +In fact, out of the 100k npm modules we test, more than 94% of them pass all tests. +Still, several sources of differences have to be considered:

+ +
    +
  • Setup: +GraalVM mostly mimicks the original setup of Node, including the node executable, npm, and similar. +However, not all command-line options are supported (or behave exactly identically). +Modules might require that native modules are (re-)compiled against the v8.h file.
  • +
+ +

Since GraalVM 21.1, Node.js and all related executables (e.g., node, npm, etc.) are not included by default in the GraalVM binary. +Node.js support is now packaged in a separate component that can be installed with the GraalVM Updater using $JAVA_HOME/bin/gu install nodejs.

+ +
    +
  • +

    Internals: +GraalVM is implemented on top of a JVM, and thus has a different internal architecture than Node.js based on V8. +This implies that some internal mechanisms behave differently and cannot exactly replicate V8 behaviour. +This will hardly ever affect user code, but might affect modules implemented natively, depending on V8 internals.

    +
  • +
  • +

    Performance: +Due to GraalVM being implemented on top of a JVM, performance characteristics vary from the original native implementation. +While GraalVM’s peak performance can match V8 on many benchmarks, it will typically take longer to reach the peak (known as warmup). +Be sure to give the GraalVM compiler some extra time when measuring (peak) performance.

    +
  • +
  • +

    Compatiblity: +GraalVM uses the following approaches to check and retain compatibility with Node.js code:

    +
  • +
  • node-compat-table: GraalVM is compared against other engines using the node-compat-table module, highlighting incompatibilities that might break Node.js code.
  • +
  • automated mass-testing of modules using mocha: in order to test a large set of modules, GraalVM is tested against 95k modules that use the mocha test framework. Using mocha allows automating the process of executing the test and comprehending the test result.
  • +
  • manual testing of popular modules: a select list of npm modules is tested in a manual test setup. These highly-relevant modules are tested in a more sophisticated manner.
  • +
+ +

My application used to run on Nashorn, it does not work on GraalVM JavaScript?

+

Reason:

+
    +
  • GraalVM JavaScript tries to be compatible with the ECMAScript specification, as well as competing engines (including Nashorn). In some cases, this is a contradicting requirement; then, ECMAScript is given precedence. Also, there are cases where GraalVM Javascript is not exactly replicating Nashorn features intentionally, e.g., for security reasons.
  • +
+ +

Solution:

+
    +
  • In many cases, enabling GraalVM’s Nashorn compatibility mode enables features not enabled by default. Note that this can have negative effects on application security! See the Nashorn Migration Guide for details.
  • +
+ +

Specific applications:

+
    +
  • For JSR 223 ScriptEngine, you might want to set the system property polyglot.js.nashorn-compat to true in order to use the Nashorn compatibility mode.
  • +
  • For ant, use ANT_OPTS="-Dpolyglot.js.nashorn-compat=true" ant when using GraalVM JavaScript via ScriptEngine.
  • +
+ +

Builtin functions like array.map() or fn.apply() are not available on non-JavaScript objects like ProxyArrays from Java

+ +

Reason:

+
    +
  • Java objects provided to JavaScript are treated as close as possible to their JavaScript counterpart. For instance, Java arrays provided to JavaScript are treated like JavaScript Array exotic objects (JavaScript arrays) whenever possible; the same is true for functions. One obvious difference is that such object’s prototype is null. This means that while you can, e.g., read the length or read and write the values of a Java array in JavaScript code, you cannot call sort() on it, as the Array.prototype is not provided by default.
  • +
+ +

Solution:

+
    +
  • While the objects do not have the methods of the prototype assigned, you can explicitly call them, e.g., Array.prototype.call.sort(myArray).
  • +
  • We offer the option js.foreign-object-prototype. When enabled, objects on the JavaScript side get the most prototype (e.g. Array.prototype, Function.prototype, Object.prototype) set and can thus behave more similarly to native JavaScript objects of the respective type. Normal JavaScript precedence rules apply here, e.g., own properties (of the Java object in that case) take precedence over and hide properties from the prototype.
  • +
+ +

Note that while the JavaScript builtin functions. e.g., from Array.prototype can be called on the respective Java types, those functions expect JavaScript semantics. +This for instance means that operations might fail (typically with a TypeError: Message not supported) when an operation is not supported in Java. +Consider Array.prototype.push as an example: while arrays can grow in size in JavaScript, they are fixed-size in Java, thus pushing a value is semantically not possible and will fail. +In such cases, you can wrap the Java object and handle that case explicitly. +Use the interfaces ProxyObject and ProxyArray for that purpose.

+ +

How can one verify GraalVM works on their application?

+

If your module ships with tests, execute them with GraalVM. Of course, this will only test your application, but not its dependencies. +You can use the Compatibility tool to find whether the module you are interested in is tested on GraalVM, and whether the tests pass successfully. +Additionally, you can upload your package-lock.json or package.json file into that tool and it will analyze all your dependencies at once.

+ +

Performance

+ +

My application is slower on GraalVM JavaScript than on another engine

+

Reason:

+
    +
  • Ensure your benchmark considers warmup. During the first few iterations, GraalVM JavaScript will be slower than natively implemented engines, while on peak performance, this difference should level out.
  • +
  • GraalVM JavaScript is shipped in two different modes: native (default) and JVM. While the default of native offers fast startup, it might show slower peak performance once the application is warmed up. In the JVM mode, the application might need a few hundred milliseconds more to start, but typically shows better peak performance.
  • +
  • Repeated execution of code via newly created org.graalvm.polyglot.Context is slow, despite the same code being executed every time.
  • +
+ +

Solution:

+
    +
  • Use proper warmup in your benchmark, and disregard the first few iterations where the application still warms up.
  • +
  • Use the --jvm option for slower startup, but higher peak performance.
  • +
  • Double-check you have no flags set that might lower your performance, e.g., -ea/-esa.
  • +
  • Try to minify the problem to the root cause and file an issue so the GraalVM team can have a look.
  • +
  • When running code via org.graalvm.polyglot.Context, make sure that one org.graalvm.polyglot.Engine object is shared and passed to each newly created Context. Use org.graalvm.polyglot.Source objects and cache them when possible. Then, GraalVM makes sure already compiled code can be shared across the Contexts, leading to improved performance. See Reference Manual: Code Caching across multiple Contexts for more details and an example.
  • +
+ +

How to achieve the best peak performance?

+

Here are a few tips you can follow to analyse and improve peak performance:

+ +
    +
  • When measuring, ensure you have given the GraalVM compiler enough time to compile all hot methods before starting to measure peak performance. A useful command line option for that is --engine.TraceCompilation=true – this outputs a message whenever a (JavaScript) method is compiled. As long as this still prints frequently, measurement should not yet start.
  • +
  • Compare the performance between Native Image and the JVM mode if possible. Depending on the characteristics of your application, one or the other might show better peak performance.
  • +
  • The Polyglot API comes with several tools and options to inspect the performance of your application: +
      +
    • --cpusampler and --cputracer will print a list of the hottest methods when the application is terminated. Use that list to figure out where most time is spent in your application.
    • +
    • --experimental-options --memtracer can help you understand the memory allocations of your application. Refer to the Profiling Command Line Tool reference for more detail.
    • +
    +
  • +
+ +

What is the difference between running GraalVM’s JavaScript in Native Image compared to the JVM?

+

In essence, the JavaScript engine of GraalVM is a plain Java application. +Running it on any JVM (JDK 11 or higher) is possible, but, for a better result, it should be GraalVM or a compatible JVMCI-enabled JDK using the GraalVM compiler. +This mode gives the JavaScript engine full access to Java at runtime, but also requires the JVM to first (just-in-time) compile the JavaScript engine when executed, just like any other Java application.

+ +

Running in Native Image means that the JavaScript engine, including all its dependencies from, e.g., the JDK, is pre-compiled into a native executable. +This will tremendously speed up the startup of any JavaScript application, as GraalVM can immediately start to compile JavaScript code, without itself requiring to be compiled first. +This mode, however, will only give GraalVM access to Java classes known at the time of image creation. +Most significantly, this means that the JavaScript-to-Java interoperability features are not available in this mode, as they would require dynamic class loading and execution of arbitrary Java code at runtime.

+ +

Can npm packages be installed globally?

+

Node packages can be installed globally using npm and the -g option, both with the original Node.js implementation and GraalVM.

+ +

While the original Node.js implementation has one main folder (NODE/bin) to put binaries and globally installed packages and their commandline tools, GraalVM has several: the main GRAALVM/bin folder, and separate folders for each language, e.g. GRAALVM/jre/languages/js/bin. +When installing npm packages globally in GraalVM, links to the executables e.g. for command line interface tools are put to the JavaScript-specific folder. +In order for globally installed packages to function properly, you might need to add GRAALVM/jre/languages/js/bin to your $PATH.

+ +

Another option is to specify the global installation folder of npm by setting the $PREFIX environment variable, or by specifying the --prefix option when running npm install.

+ +

For more details, see Installing npm Packages Globally.

+ +

Errors

+ +

TypeError: Access to host class com.myexample.MyClass is not allowed or does not exist

+

Reason:

+
    +
  • You are trying to access a Java class that is not known to the js or node process, or is not among the allowed classes your code can access.
  • +
+ +

Solution:

+
    +
  • Ensure there is no typo in the class name.
  • +
  • Ensure the class is on the classpath. Use the --vm.cp=<classpath> option of the launchers.
  • +
  • Ensure access to the class is permitted, by having @HostAccess.Export on your class and/or the Context.Builder.allowHostAccess() set to a permissive setting. See JavaDoc of org.graalvm.polyglot.Context.
  • +
+ +

TypeError: UnsupportedTypeException

+
TypeError: execute on JavaObject[Main$$Lambda$63/1898325501@1be2019a (Main$$Lambda$63/1898325501)] failed due to: UnsupportedTypeException
+
+ +

Reason:

+
    +
  • GraalVM JavaScript in some cases does not allow concrete callback types when calling from JavaScript to Java. A Java function expecting, e.g., a Value object, might fail with the quoted error message due to that.
  • +
+ +

Solution:

+
    +
  • Change the signature in the Java callback method.
  • +
+ +

Status:

+ + +

Example:

+
import java.util.function.Function;
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Value;
+import org.graalvm.polyglot.HostAccess;
+
+public class Minified {
+  public static void main(String ... args) {
+    //change signature to Function<Object, String> to make it work
+    Function<Value, String> javaCallback = (test) -> {
+      return "passed";
+    };
+    try(Context ctx = Context.newBuilder()
+    .allowHostAccess(HostAccess.ALL)
+    .build()) {
+      Value jsFn = ctx.eval("js", "f => function() { return f(arguments); }");
+      Value javaFn = jsFn.execute(javaCallback);
+      System.out.println("finished: "+javaFn.execute());
+    }
+  }
+}
+
+ +

TypeError: Message not supported

+
TypeError: execute on JavaObject[Main$$Lambda$62/953082513@4c60d6e9 (Main$$Lambda$62/953082513)] failed due to: Message not supported.
+
+ +

Reason:

+
    +
  • You are trying to execute an operation (a message) on a polyglot object that this object does not handle. E.g., you are calling Value.execute() on a non-executable object.
  • +
  • A security setting (e.g., org.graalvm.polyglot.HostAccess) might prevent the operation.
  • +
+ +

Solution:

+
    +
  • Ensure the object (type) in question does handle the respective message.
  • +
  • Specifically, ensure the JavaScript operation you try to execute on a Java type is possible semantically in Java. For instance, while you can push a value to an array in JavaScript and thus automatically grow the array, arrays in Java are of fixed length and trying to push to them will result in a Message not supported failure. You might want to wrap Java objects for such cases, e.g., as a ProxyArray.
  • +
  • Ensure access to the class is permitted, by having @HostAccess.Export on your class and/or the Context.Builder.allowHostAccess() set to a permissive setting. See JavaDoc of org.graalvm.polyglot.Context.
  • +
  • Are you trying to call a Java Lambda expression or Functional Interface? Annotating the proper method with @HostAccess.Export can be a pitfall. While you can annotate the method the functional interface refers to, the interface itself (or the Lambda class created in the background) fails to be properly annotated and recognized as exported. See below for examples highlighting the problem and a working solution.
  • +
+ +

An example that triggers a Message not supported error with certain HostAccess settings, e.g., HostAccess.EXPLICIT:

+
{
+  ...
+  //a JS function expecting a function as argument
+  Value jsFn = ...;
+  //called with a functional interface as argument
+  jsFn.execute((Function<Integer, Integer>)this::javaFn);
+  ...
+}
+
+@Export
+public Object javaFn(Object x) { ... }
+
+@Export
+public Callable<Integer> lambda42 = () -> 42;
+
+ +

In the example above, the method javaFn is seemingly annotated with @Export, but the functional interface passed to jsFn is not, as the functional interface behaves like a wrapper around javaFn, thus hiding the annotation. +Neither is lambda42 properly annotated - that pattern annotates the field lambda42, not its executable function in the generated lambda class.

+ +

In order to add the @Export annotation to a functional interface, use this pattern instead:

+ +
import java.util.function.Function;
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.Value;
+import org.graalvm.polyglot.HostAccess;
+
+public class FAQ {
+  public static void main(String[] args) {
+    try(Context ctx = Context.newBuilder()
+    .allowHostAccess(HostAccess.EXPLICIT)
+    .build()) {
+      Value jsFn = ctx.eval("js", "f => function() { return f(arguments); }");
+      Value javaFn = jsFn.execute(new MyExportedFunction());
+      System.out.println("finished: " + javaFn.execute());
+    }
+  }
+
+  @FunctionalInterface
+  public static class MyExportedFunction implements Function<Object, String> {
+    @Override
+    @HostAccess.Export
+    public String apply(Object s) {
+      return "passed";
+    }
+  };
+}
+
+ +

Another option is to allow access to java.function.Function’s apply method. +However, note that this allows access to ALL instances of this interface - in most production setups, this will be too permissive and open potential security holes.

+ +
HostAccess ha = HostAccess.newBuilder(HostAccess.EXPLICIT)
+  //warning: too permissive for use in production
+  .allowAccess(Function.class.getMethod("apply", Object.class))
+  .build();
+
+ +

Warning: Implementation does not support runtime compilation.

+ +

If you get the following warning, you are not running on GraalVM or a JVMCI-enabled JVM using the GraalVM compiler:

+
[engine] WARNING: The polyglot context is using an implementation that does not support runtime compilation.
+The guest application code will therefore be executed in interpreted mode only.
+Execution only in interpreted mode will strongly impact the guest application performance.
+To disable this warning the '--engine.WarnInterpreterOnly=false' option or use the '-Dpolyglot.engine.WarnInterpreterOnly=false' system property.
+
+ +

To resolve this, use GraalVM or see the Run GraalVM JavaScript on a Stock JDK guide for instructions how to set up the Graal compiler on a compatible JVMCI-enabled stock JDK.

+ +

Nevertheless, if this is intentional, you can disable the warning and continue to run with degraded performance by setting the above mentioned option, either via the command line or using the Context.Builder, e.g.:

+
try (Context ctx = Context.newBuilder("js")
+    .option("engine.WarnInterpreterOnly", "false")
+    .build()) {
+  ctx.eval("js", "console.log('Greetings!');");
+}
+
+

Note that when using an explicit polyglot engine, the option has to be set on the Engine, e.g.:

+
try (Engine engine = Engine.newBuilder()
+    .option("engine.WarnInterpreterOnly", "false")
+    .build()) {
+  try (Context ctx = Context.newBuilder("js").engine(engine).build()) {
+    ctx.eval("js", "console.log('Greetings!');");
+  }
+}
+
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/JavaInteroperability/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/JavaInteroperability/index.html new file mode 100644 index 0000000..8e9fc7c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/JavaInteroperability/index.html @@ -0,0 +1,524 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Java Interoperability

+ +

GraalVM includes a JavaScript language execution runtime and allows interoperability with Java code. +This document describes the features and usage of this JavaScript-to-Java interoperability feature.

+ +

For a reference of GraalVM public API, see JavaScript Compatibility. +See the Embedding Reference on how to interact with a guest language like JavaScript from a Java host application. +The Polyglot Programming guide can be of additional help in that area. +Specific migration guides for Rhino and Nashorn are also available.

+ +

Both JavaScript and Node.js are separately installable components of GraalVM. +See the README for details on how to use the GraalVM Updater tool, gu, to install JavaScript and Node.js. +After a successfull installation, the respective native launchers js and node from the $GRAALVM/bin directory can be used.

+ +

Although other builds are possible, the following examples assume this setup is used.

+ +

Enabling Java Interoperability

+

In GraalVM, the js and node launchers are started in an ahead-of-time compiled native mode by default. In that mode, Java interoperability is not available.

+ +

To enable Java interoperability, the --jvm option has to be provided to the native launcher. +This way, GraalVM JavaScript is executed on a traditional JVM and allows full Java interoperability.

+ +

Classpath

+

In order to load Java classes you need to have them on the Java classpath. +You can specify the classpath with the --vm.classpath=<classpath> option (or short: --vm.cp=<classpath>):

+
node --jvm --vm.cp=/my/class/path
+js --jvm --vm.cp=/my/class/path
+
+

The method Java.addToClasspath() can be used to programmatically add to the classpath at runtime.

+ +

Polyglot Context

+

The preferred method of launching GraalVM JavaScript with Java interop support is via polyglot Context. +For that, a new org.graalvm.polyglot.Context is built with the hostAccess option allowing access and a hostClassLookup predicate defining the Java classes you allow access to:

+ +
Context context = Context.newBuilder("js")
+    .allowHostAccess(HostAccess.ALL)
+    //allows access to all Java classes
+    .allowHostClassLookup(className -> true)
+    .build();
+context.eval("js", jsSourceCode);
+
+ +

See the Embedding Reference on how to interact with a guest language like JavaScript from a Java host application. +The Polyglot Programming guide can also be of help in that area.

+ +

ScriptEngine (JSR 223)

+

The org.graalvm.polyglot.Context is the preferred execution method for interoperability with GraalVM’s languages and tools. +In addition, JavaScript running on GraalVM is fully compatible with JSR 223 and supports the ScriptEngine API. +Internally, the GraalVM’s JavaScript ScriptEngine wraps a polyglot context instance:

+ +
ScriptEngine eng = new ScriptEngineManager()
+    .getEngineByName("graal.js");
+Object fn = eng.eval("(function() { return this; })");
+Invocable inv = (Invocable) eng;
+Object result = inv.invokeMethod(fn, "call", fn);
+
+ +

See the ScriptEngine guide for more details on how to use it from GraalVM JavaScript.

+ +

Access Java from JavaScript

+

GraalVM provides a set of features to allow interoperability from JavaScript to Java. +While Rhino, Nashorn, and GraalVM JavaScript have a mostly comparable overall feature set, they differ in exact syntax, and, partly, semantics.

+ +

Class Access

+

To access a Java class, GraalVM JavaScript supports the Java.type(typeName) function:

+ +
var FileClass = Java.type('java.io.File');
+
+ +

By default, Java classes are not automatically mapped to global variables, e.g., there is no java global property in GraalVM JavaScript. +Existing code accessing, e.g., java.io.File, should be rewritten to use the Java.type(name) function:

+ +
//GraalVM JavaScript compliant syntax
+var FileClass = Java.type("java.io.File");
+//backwards-compatible syntax
+var FileClass = java.io.File;
+
+ +

GraalVM JavaScript provides Packages, java, and similar global properties for compatibility. +However, explicitly accessing the required class with Java.type is preferred whenever possible for two reasons:

+
    +
  1. It allows resolving the class in one step rather than trying to resolve each property as a class.
  2. +
  3. Java.type immediately throws a TypeError if the class cannot be found or is not accessible, rather than silently treating an unresolved name as a package.
  4. +
+ +

The js.java-package-globals flag can be used to deactivate the global fields of Java packages (set false to avoid creation of the fields; default is true).

+ +

Constructing Java Objects

+

Java objects can be constructed with JavaScript’s new keyword:

+ +
var FileClass = Java.type('java.io.File');
+var file = new FileClass("myFile.md");
+
+ +

Field and Method Access

+

Static fields of a Java class, or fields of a Java object, can be accessed like JavaScript properties:

+ +
var JavaPI = Java.type('java.lang.Math').PI;
+
+ +

Java methods can be called like JavaScript functions:

+ +
var file = new (Java.type('java.io.File'))("test.md");
+var fileName = file.getName();
+
+ +

Conversion of Method Arguments

+

JavaScript is defined to operate on the double number type. +GraalVM JavaScript might internally use additional Java data types for performance reasons (e.g., the int type).

+ +

When calling Java methods, a value conversion might be required. +This happens when the Java method expects a long parameter, and an int is provided from GraalVM JavaScript (type widening). +If this conversion causes a lossy conversion, a TypeError is thrown:

+ +
//Java
+void longArg   (long arg1);
+void doubleArg (double arg2);
+void intArg    (int arg3);
+
+
//JavaScript
+javaObject.longArg(1);     //widening, OK
+javaObject.doubleArg(1);   //widening, OK
+javaObject.intArg(1);      //match, OK
+
+javaObject.longArg(1.1);   //lossy conversion, TypeError!
+javaObject.doubleArg(1.1); //match, OK
+javaObject.intArg(1.1);    //lossy conversion, TypeError!
+
+ +

Note how the argument values have to fit into the parameter types. +You can override this behavior using custom target type mappings.

+ +

Selection of Method

+

Java allows overloading of methods by argument types. +When calling from JavaScript to Java, the method with the narrowest available type that the actual argument can be converted to without loss is selected:

+ +
//Java
+void foo(int arg);
+void foo(short arg);
+void foo(double arg);
+void foo(long arg);
+
+
//JavaScript
+javaObject.foo(1);              // will call foo(short);
+javaObject.foo(Math.pow(2,16)); // will call foo(int);
+javaObject.foo(1.1);            // will call foo(double);
+javaObject.foo(Math.pow(2,32)); // will call foo(long);
+
+ +

To override this behavior, an explicit method overload can be selected using the javaObject['methodName(paramTypes)'] syntax. +Parameter types need to be comma-separated without spaces, and Object types need to be fully qualified (e.g., 'get(java.lang.String,java.lang.String[])'). +Note that this is different from Nashorn which allows extra spaces and simple names. +In the example above, one might always want to call, e.g., foo(long), even when foo(short) can be reached with lossless conversion (foo(1)):

+ +
javaObject['foo(int)'](1);
+javaObject['foo(long)'](1);
+javaObject['foo(double)'](1);
+
+ +

Note that the argument values still have to fit into the parameter types. +You can override this behavior using custom target type mappings.

+ +

An explicit method selection can also be useful when the method overloads are ambiguous and cannot be automatically resolved as well as when you want to override the default choice:

+ +
//Java
+void sort(List<Object> array, Comparator<Object> callback);
+void sort(List<Integer> array, IntBinaryOperator callback);
+void consumeArray(List<Object> array);
+void consumeArray(Object[] array);
+
+
//JavaScript
+var array = [3, 13, 3, 7];
+var compare = (x, y) => (x < y) ? -1 : ((x == y) ? 0 : 1);
+
+// throws TypeError: Multiple applicable overloads found
+javaObject.sort(array, compare);
+// explicitly select sort(List, Comparator)
+javaObject['sort(java.util.List,java.util.Comparator)'](array, compare);
+
+// will call consumeArray(List)
+javaObject.consumeArray(array);
+// explicitly select consumeArray(Object[])
+javaObject['consumeArray(java.lang.Object[])'](array);
+
+ +

Note that there is currently no way to explicitly select constructor overloads. +Future versions of GraalVM JavaScript might lift that restriction.

+ +

Package Access

+

GraalVM JavaScript provides a Packages global property:

+ +
> Packages.java.io.File
+JavaClass[java.io.File]
+
+ +

Array Access

+

GraalVM JavaScript supports the creation of Java arrays from JavaScript code. +Both the patterns suggested by Rhino and Nashorn are supported:

+ +
//Rhino pattern
+var JArray = Java.type('java.lang.reflect.Array');
+var JString = Java.type('java.lang.String');
+var sarr = JArray.newInstance(JString, 5);
+
+//Nashorn pattern
+var IntArray = Java.type("int[]");
+var iarr = new IntArray(5);
+
+ +

The arrays created are Java types, but can be used in JavaScript code:

+ +
iarr[0] = iarr[iarr.length] * 2;
+
+ +

Map Access

+

In GraalVM JavaScript you can create and access Java Maps, e.g., java.util.HashMap:

+ +
var HashMap = Java.type('java.util.HashMap');
+var map = new HashMap();
+map.put(1, "a");
+map.get(1);
+
+ +

GraalVM JavaScript supports iterating over such maps similar to Nashorn:

+ +
for (var key in map) {
+    print(key);
+    print(map.get(key));
+}
+
+ +

List Access

+

In GraalVM JavaScript you can create and access Java Lists, e.g., java.util.ArrayList:

+ +
var ArrayList = Java.type('java.util.ArrayList');
+var list = new ArrayList();
+list.add(42);
+list.add("23");
+list.add({});
+
+for (var idx in list) {
+    print(idx);
+    print(list.get(idx));
+}
+
+ +

String Access

+

GraalVM JavaScript can create Java strings with Java interoperability. +The length of the string can be queried with the length property (note that length is a value property and cannot be called as a function):

+ +
var javaString = new (Java.type('java.lang.String'))("Java");
+javaString.length === 4;
+
+ +

Note that GraalVM JavaScript uses Java strings internally to represent JavaScript strings, so the above code and the JavaScript string literal "Java" are actually not distinguishable.

+ +

Iterating Properties

+

Properties (fields and methods) of Java classes and Java objects can be iterated with a JavaScript for..in loop:

+ +
var m = Java.type('java.lang.Math')
+for (var i in m) { print(i); }
+> E
+> PI
+> abs
+> sin
+> ...
+
+ +

Access to JavaScript Objects from Java

+

JavaScript objects are exposed to Java code as instances of com.oracle.truffle.api.interop.java.TruffleMap. +This class implements Java’s Map interface.

+ +

JavaImporter

+

The JavaImporter feature is available only in Nashorn compatibility mode (js.nashorn-compat option).

+ +

Console Output of Java Classes and Java Objects

+

GraalVM JavaScript provides both print and console.log.

+ +

GraalVM JavaScript provides a print built-in function compatible with Nashorn.

+ +

The console.log is provided by Node.js directly. +It does not provide special treatment of interop objects. +Note that the default implementation of console.log on GraalVM JavaScript is just an alias for print, and Node’s implementation is only available when running on Node.js.

+ +

Exceptions

+

Exceptions thrown in Java code can be caught in JavaScript code. +They are represented as Java objects:

+ +
try {
+    Java.type('java.lang.Class')
+    .forName("nonexistent");
+} catch (e) {
+    print(e.getMessage());
+}
+
+ +

Promises

+ +

GraalVM JavaScript provides support for interoperability between JavaScript Promise objects and Java. +Java objects can be exposed to JavaScript code as thenable objects, allowing JavaScript code to await Java objects. +Moreover, JavaScript Promise objects are regular JavaScript objects, and can be accessed from Java using the mechanisms described in this document. +This allows Java code to be called back from JavaScript when a JavaScript promise is resolved or rejected.

+ +

Creating JavaScript Promise Objects That Can Be Resolved from Java

+

JavaScript applications can create Promise objects delegating to Java the resolution of the Promise instance. +This can be achieved from JavaScript by using a Java object as the “executor” function of the JavaScript Promise. +For example, Java objects implementing the following functional interface can be used to create new Promise objects:

+
@FunctionalInterface
+public interface PromiseExecutor {
+    void onPromiseCreation(Value onResolve, Value onReject);
+}
+
+ +

Any Java object implementing PromiseExecutor can be used to create a JavaScript Promise:

+
// `javaExecutable` is a Java object implementing the `PromiseExecutor` interface
+var myPromise = new Promise(javaExecutable).then(...);
+
+ +

JavaScript Promise objects can be created not only using functional interfaces, but also using any other Java object that can be executed by the GraalVM JavaScript engine (for example, any Java object implementing the Polyglot ProxyExecutable interface). +More detailed example usages are available in the GraalVM JavaScript unit tests.

+ +

Using await with Java Objects

+

JavaScript applications can use the await expression with Java objects. +This can be useful when Java and JavaScript have to interact with asynchronous events. +To expose a Java object to GraalVM JavaScript as a thenable object, the Java object should implement a method called then() having the following signature:

+
void then(Value onResolve, Value onReject);
+
+ +

When await is used with a Java object implementing then(), the GraalVM JavaScript runtime will treat the object as a JavaScript Promise. +The onResolve and onReject arguments are executable Value objects that should be used by the Java code to resume or abort the JavaScript await expression associated with the corresponding Java object. +More detailed example usages are available in the GraalVM JavaScript unit tests.

+ +

Using JavaScript Promises from Java

+

Promise objects created in JavaScript can be exposed to Java code like any other JavaScript object. +Java code can access such objects like normal Value objects, with the possibility to register new promise resolution functions using the Promise’s default then() and catch() functions. +As an example, the following Java code registers a Java callback to be executed when a JavaScript promise resolves:

+
Value jsPromise = context.eval(ID, "Promise.resolve(42);");
+Consumer<Object> javaThen = (value)
+    -> System.out.println("Resolved from JavaScript: " + value);
+jsPromise.invokeMember("then", javaThen);
+
+

More detailed example usages are available in the GraalVM JavaScript unit tests.

+ +

Multithreading

+ +

GraalVM JavaScript supports multithreading when used in combination with Java. More details about the GraalVM JavaScript multithreading model can be found in the Multithreading documentation.

+ +

Extending Java classes

+ +

In the JVM mode (--jvm), GraalVM JavaScript provides support for extending Java classes and interfaces using the Java.extend function. +Note that host access has to be enabled in the polyglot context for this feature to be available.

+ +

Java.extend

+

Java.extend(types...) returns a generated adapter Java class object that extends the specified Java class and/or interfaces. +For example:

+ +
var Ext = Java.extend(Java.type("some.AbstractClass"),
+                      Java.type("some.Interface1"),
+                      Java.type("some.Interface2"));
+var impl = new Ext({
+  superclassMethod: function() {/*...*/},
+  interface1Method: function() {/*...*/},
+  interface2Method: function() {/*...*/},
+  toString() {return "MyClass";}
+});
+impl.superclassMethod();
+
+ +

Super methods can be called via Java.super(adapterInstance). +See a combined example:

+ +
var sw = new (Java.type("java.io.StringWriter"));
+var FilterWriterAdapter = Java.extend(Java.type("java.io.FilterWriter"));
+var fw = new FilterWriterAdapter(sw, {
+    write: function(s, off, len) {
+        s = s.toUpperCase();
+        if (off === undefined) {
+            fw_super.write(s, 0, s.length)
+        } else {
+            fw_super.write(s, off, len)
+        }
+    }
+});
+var fw_super = Java.super(fw);
+fw.write("abcdefg");
+fw.write("h".charAt(0));
+fw.write("**ijk**", 2, 3);
+fw.write("***lmno**", 3, 4);
+print(sw); // ABCDEFGHIJKLMNO
+
+ +

Note that in the nashorn-compat mode, you can also extend interfaces and abstract classes using a new operator on a type object of an interface or an abstract class:

+ +
// --experimental-options --js.nashorn-compat
+var JFunction = Java.type('java.util.function.Function');
+ var sqFn = new JFunction({
+   apply: function(x) { return x * x; }
+});
+sqFn.apply(6); // 36
+
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/JavaScriptCompatibility/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/JavaScriptCompatibility/index.html new file mode 100644 index 0000000..56dd105 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/JavaScriptCompatibility/index.html @@ -0,0 +1,595 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

JavaScript Compatibility

+ +

GraalVM provides an ECMAScript-compliant JavaScript language runtime. +This document explains the public API it presents for user applications written in JavaScript.

+ + + +

ECMAScript Language Compliance

+ +

GraalVM JavaScript implements JavaScript as prescribed in the ECMAScript (ECMA-262) specification. +It is fully compatible with the ECMAScript 2022 specification (sometimes referred to as the 13th edition or “ES13”). +New features are frequently added to GraalVM when they are confirmed to be part of ECMAScript 2023, see the CHANGELOG.md for details. +Older versions starting from ECMAScript 5 can be enabled with a config flag (by number: --js.ecmascript-version=5 or by year: --js.ecmascript-version=2019). +In a production setup you might consider specifying a fixed ECMAScript version to be used, as future versions of GraalVM JavaScript will use newer versions of the specification once available.

+ +

GraalVM JavaScript provides the following function objects in the global scope as specified by ECMAScript, representing the JavaScript core library: +Array, ArrayBuffer, Boolean, DataView, Date, Error, Function, JSON, Map, Math, Number, Object, Promise, Proxy, Reflect, RegExp, Set, SharedArrayBuffer, String, Symbol, TypedArray, WeakMap, and WeakSet.

+ +

Additional objects are available under flags, for instance Intl (flag: --js.intl-402). +Run js --help or js --help:languages for the list of available flags.

+ +

Several of these function objects and some of their members are only available when a certain version of the specification is selected for execution. +For a list of methods provided, inspect the ECMAScript specification. +Extensions to the specification are specified below.

+ +

Internationalization API (ECMA-402)

+ +

Internationalization API implementation (see https://tc39.github.io/ecma402) can be activated using the following flag: --js.intl-402=true. +If you run in native mode (default option), you also need to specify the path to your ICU data directory using the following option: --vm.Dcom.ibm.icu.impl.ICUBinary.dataPath=$GRAAL_VM_DIR/jre/languages/js/icu4j/icudt, +where $GRAAL_VM_DIR refers to your GraalVM installation directory. +If you run in the JVM mode (the --jvm flag is used), you do not need to specify where your ICU data are located, although you can do it with the above option.

+ +

Once you activate the Internationalization API, you can use the following built-ins:

+ +
    +
  • Intl.NumberFormat
  • +
  • Intl.DateTimeFormat
  • +
  • Intl.Collator
  • +
  • Intl.PluralRules
  • +
+ +

The functionality of a few other built-ins is then also updated according to the specification linked above.

+ +

JavaScript Modules

+ +

GraalVM JavaScript supports modules as defined by ECMAScript 6 and later. +Be aware that the support for this feature grew and still grows over time. Be sure to use the latest ECMAScript version for the all the latest features.

+ +

When loading modules via a polyglot Source, you can use the inofficial application/javascript+module mime type to specify you are loading a module. +When loading with JavaScript code from a file, make sure the module is loaded from a file with the .mjs extension. +Loading with the import keyword is not limited by that, and can import from a file of any extension.

+ +

Compatibility Extensions

+ +

The following objects and methods are available in GraalVM JavaScript for compatibility with other JavaScript execution engines. +Note that the behavior of such methods might not strictly match the semantics of those methods in all existing engines.

+ +

Language Features

+ +

Conditional Catch Clauses

+ +

GraalVM JavaScript supports conditional catch clauses if the js.syntax-extensions option is enabled:

+ +
try {
+    myMethod(); // can throw
+} catch (e if e instanceof TypeError) {
+    print("TypeError caught");
+} catch (e) {
+    print("another Error caught");
+}
+
+ +

Global Properties

+ +

load(source)

+ +
    +
  • loads (parses and executes) the specified JavaScript source code
  • +
+ +

Source can be of type:

+ +
    +
  • a String: the path of the source file or a URL to execute.
  • +
  • java.lang.URL: the URL is queried for the source code to execute if the js.load-from-url option is set to true.
  • +
  • java.io.File: the file is read for the source code to execute.
  • +
  • a JavaScript object: the object is queried for a name and a script property, which represent the source name and code, respectively.
  • +
  • all other types: the source is converted to a String.
  • +
+ +

load is available by default and can be deactivated by setting the js.load option to false.

+ +

print(...arg) and printErr(...arg)

+ +
    +
  • prints the arguments on the console (stdout and stderr, respectively)
  • +
  • provides a best-effort human readable output
  • +
+ +

print and printErr are available by default and can be deactivated by setting the js.print option to false.

+ +

Methods of the console Global Object

+ +

A global console object is provided that offers several methods for debugging purposes. +These methods strive to provide similar functionality as provided in other engines, but do not guarantee identical results.

+ +

Note that those methods behave differently when GraalVM JavaScript is executed in Node.js mode (i.e., the node executable is started instead of js). +Node.js provides its own implementation that is used instead.

+ +
    +
  • console.log, console.info, and console.debug: an alias for print(...arg)
  • +
  • console.error, and console.warn: similar to print, but using the error IO stream
  • +
  • console.assert(check, message): prints message when check is falsy
  • +
  • console.clear: clears the console window if possible
  • +
  • console.count(), and console.countReset(): counts and print how many times it has been called, or resets this counter
  • +
  • console.group, and console.groupEnd: increases or decreases the indentation for succeeding outputs to the console
  • +
  • console.time(), console.timeLog(), and console.timeEnd(): starts a timer, prints the duration the timer has been active, or prints the duration and stops the timer, respectively
  • +
+ +

The console object is available by default and can be deactivated by setting the option js.console to false.

+ +

Additional Global Functions in the js Shell

+ +

quit(status)

+ +
    +
  • exits the engine and returns the specified status code
  • +
+ +

read(file)

+ +
    +
  • reads the content of file
  • +
+ +

The result is returned as a String.

+ +

The argument file can be of type:

+
    +
  • java.io.File: the file is used directly.
  • +
  • all other types: file is converted to a String and interpreted as a file name.
  • +
+ +

readbuffer(file)

+ +
    +
  • reads the content of file similar to the read function
  • +
+ +

The result is returned as a JavaScript ArrayBuffer object.

+ +

readline()

+ +
    +
  • reads one line of input from the input stream
  • +
+ +

The result is returned as a String.

+ +

Object

+ +

Object.prototype.__defineGetter__(prop, func)

+ +
    +
  • defines the prop property of this to be the getter function func
  • +
+ +

This functionality is deprecated in most JavaScript engines. +In recent ECMAScript versions, getters and setters are natively supported by the language.

+ +

Object.prototype.__defineSetter__(prop, func)

+ +
    +
  • defines the prop property of this to be the setter function func
  • +
+ +

This functionality is deprecated in most JavaScript engines. +In recent ECMAScript versions, getters and setters are natively supported by the language.

+ +

Object.prototype.__lookupGetter__(prop)

+ +
    +
  • returns the getter function for property prop of the object as set by __defineGetter__
  • +
+ +

This functionality is deprecated in most JavaScript engines. +In recent ECMAScript versions, getters and setters are natively supported by the language.

+ +

Object.prototype.__lookupSetter__(prop)

+ +
    +
  • returns the setter function for property prop of the object as set by __defineSetter__
  • +
+ +

This functionality is deprecated in most JavaScript engines. +In recent ECMAScript versions, getters and setters are natively supported by the language.

+ +

Nashorn Scripting Mode

+

GraalVM JavaScript provides a scripting mode compatible with the one provided by the Nashorn engine. +It is enabled with the js.scripting option. Make sure to have --experimental-options set:

+
js --experimental-options --js.scripting=true
+
+ +

In scripting mode, several properties and functions are added to the global object, including readFully, readLine, $ARG, $ENV, and $EXEC.

+ +

There are migration guides available for code previously targeted to the Nashorn or Rhino engines.

+ +

GraalVM JavaScript Extensions

+ +

Graal Object

+ +

The Graal object is provided as a property of the global object. +It provides Graal-specific information. +The existence of the property can be used to identify whether the GraalVM JavaScript engine is the current language engine:

+ +
if (typeof Graal != 'undefined') {
+    print(Graal.versionECMAScript);
+    print(Graal.versionGraalVM);
+    print(Graal.isGraalRuntime());
+}
+
+ +

The Graal object is available in GraalVM JavaScript by default, unless deactivated by an option (js.graal-builtin=false).

+ +

Graal.versionECMAScript

+ +
    +
  • provides the version number (year value) of GraalVM JavaScript’s ECMAScript compatibility mode.
  • +
+ +

Graal.versionGraalVM

+ +
    +
  • provides the version of GraalVM, if the current engine is executed on GraalVM
  • +
+ +

Graal.isGraalRuntime()

+ +
    +
  • provides whether GraalVM JavaScript is executed on a GraalVM-enabled runtime
  • +
  • If true, hot code is compiled by the GraalVM compiler, resulting in high peak performance.
  • +
  • If false, GraalVM JavaScript will not be optimized by the GraalVM Compiler, typically resulting in lower performance.
  • +
+ +

Graal.setUnhandledPromiseRejectionHandler(handler)

+ +
    +
  • provides the unhandled promise rejection handler when using option (js.unhandled-rejections=handler).
  • +
  • the handler is called with two arguments: (rejection, promise).
  • +
  • Graal.setUnhandledPromiseRejectionHandler can be called with null, undefined, or empty args to clear the handler.
  • +
+ +

Java

+ +

The Java object is only available when the engine is started in JVM mode (--jvm flag).

+ +

Note that some functions require a Nashorn compatibility mode flag to be set. +On GraalVM, this flag can be set with:

+
js --jvm --experimental-options --js.nashorn-compat=true
+
+ +

Java.type(className)

+ +
    +
  • loads the specified Java class and provides it as an object
  • +
  • fields of this object can be read directly from it, and new instances can be created with the JavaScript new keyword: +
    var BigDec = Java.type('java.math.BigDecimal');
    +var bd = new BigDec("0.1");
    +console.log(bd.add(bd).toString());
    +
    +
  • +
+ +

Java.from(javaData)

+ +
    +
  • creates a shallow copy of the Java datastructure (Array, List) as a JavaScript array
  • +
+ +

In many cases, this is not necessary; you can typically use the Java datastructure directly from JavaScript.

+ +

Java.to(jsData, toType)

+ +
    +
  • converts the argument to a Java dataype
  • +
+ +

The source object jsData is expected to be a JavaScript array, or an object with a length property. +The target toType can either be a String (e.g. "int[]") or a type object (e.g., Java.type("int[]")). +Valid target types are Java arrays. +When no target type is provided, Object[] is assumed:

+
var jsArr = ["a", "b", "c"];
+var strArrType = Java.type("java.lang.String[]");
+var javaArr = Java.to(jsArr, strArrType);
+assertEquals('class java.lang.String[]', String(javaArr.getClass()));
+
+ +

The conversion methods as defined by ECMAScript (e.g., ToString and ToDouble) are executed when a JavaScript value has to be converted to a Java type. +Lossy conversion is disallowed and results in a TypeError.

+ +

Java.isJavaObject(obj)

+ +
    +
  • returns whether obj is an object of the Java language
  • +
  • returns false for native JavaScript objects, as well as for objects of other polyglot languages
  • +
+ +

Java.isType(obj)

+ +
    +
  • returns true if obj is an object representing the constructor and static members of a Java class, as obtained (for example) by Java.type()
  • +
  • returns false for all other arguments
  • +
+ +

Java.typeName(obj)

+ +
    +
  • returns the Java Class name of obj when obj represents a Java type (isType(obj) === true) or Java Class instance
  • +
  • returns undefined otherwise
  • +
+ +

Java.isJavaFunction(fn)

+ +
    +
  • returns whether fn is an object of the Java language that represents a Java function
  • +
  • returns false for all other types, including native JavaScript function, and functions of other polyglot languages
  • +
+ +

This function requires the Nashorn compatibility mode flag.

+ +

Java.isScriptObject(obj)

+ +
    +
  • returns whether obj is an object of the JavaScript language
  • +
  • returns false for all other types, including objects of Java and other polyglot languages
  • +
+ +

This function requires the Nashorn compatibility mode flag.

+ +

Java.isScriptFunction(fn)

+ +
    +
  • returns whether fn is a JavaScript function
  • +
  • returns false for all other types, including Java function, and functions of other polyglot languages
  • +
+ +

This function requires the Nashorn compatibility mode flag.

+ +

Java.addToClasspath(location)

+ +
    +
  • adds the specified location (file name or path name, as String) to Java’s classpath
  • +
+ +

Polyglot

+ +

The functions of the Polyglot object allow to interact with values from other polyglot languages.

+ +

The Polyglot object is available by default, unless deactivated by setting the js.polyglot-builtin option to false.

+ +

Polyglot.export(key, value)

+ +
    +
  • exports the JavaScript value under the name key (a string) to the polyglot bindings: +
    function helloWorld() { print("Hello, JavaScript world"); }
    +Polyglot.export("helloJSWorld", helloWorld);
    +
    +
  • +
+ +

If the polyglot bindings already had a value identified by key, it is overwritten with the new value. The value may be any valid Polyglot value.

+ +
    +
  • throws a TypeError if key is not a String or is missing
  • +
+ +

Polyglot.import(key)

+ +
    +
  • imports the value identified by key (a string) from the polyglot bindings and returns it: +
    var rubyHelloWorld = Polyglot.import("helloRubyWorld");
    +rubyHelloWorld();
    +
    +
  • +
+ +

If no language has exported a value identified by key, undefined is returned.

+ +
    +
  • throws a TypeError if key is not a string or missing
  • +
+ +

Polyglot.eval(languageId, sourceCode)

+ +
    +
  • parses and evaluates the sourceCode with the interpreter identified by languageId
  • +
+ +

The value of sourceCode is expected to be a String (or convertable to one).

+ +
    +
  • returns the evaluation result, depending on the sourceCode and/or the semantics of the language evaluated: +
    var rArray = Polyglot.eval('R', 'runif(1000)');
    +
    +
  • +
+ +

Exceptions can occur when an invalid languageId is passed, when the sourceCode cannot be evaluated by the language, or when the executed program throws one.

+ +

Polyglot.evalFile(languageId, sourceFileName)

+ +
    +
  • parses the file sourceFileName with the interpreter identified by languageId
  • +
+ +

The value of sourceFileName is expected to be a String (or convertable to one), representing a file reachable by the current path.

+ +
    +
  • returns an executable object, typically a function: +
    var rFunc = Polyglot.evalFile('R', 'myExample.r');
    +var result = rFunc();
    +
    +
  • +
+ +

Exceptions can occur when an invalid languageId is passed, when the file identified by sourceFileName cannot be found, or when the language throws an exception during parsing (parse time errors, e.g. syntax errors). +Exceptions thrown by the evaluated program are only thrown once the resulting function is evaluated.

+ +

The Polyglot.evalFile function is available by default when the Polyglot builtin is available, unless deactivated by setting the js.polyglot-evalfile option to false. +It is also available when js.debug-builtin is activated.

+ +

Debug

+ +
    +
  • requires starting the engine with the js.debug-builtin flag
  • +
+ +

Debug is a GraalVM JavaScript specific function object that provides functionality for debugging JavaScript code and the GraalVM JavaScript compiler. +This API might change without notice. Do not use for production purposes.

+ +

Global Functions

+ +

printErr(...arg)

+ +
    +
  • behaves identical to print
  • +
+ +

The only difference is that the error stream is used to print to, instead of the default output stream.

+ +

loadWithNewGlobal(source, arguments)

+ +
    +
  • behaves similarly to load function
  • +
+ +

The relevant difference is that the code is evaluated in a new global scope (Realm, as defined by ECMAScript).

+ +

Source can be of type:

+ +
    +
  • java.lang.URL: the URL is queried for the source code to execute.
  • +
  • a JavaScript object: the object is queried for a name and a script property.
  • +
  • all other types: the source is converted to a String.
  • +
+ +

The value of arguments is provided to the loaded code upon execution.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/Modules/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/Modules/index.html new file mode 100644 index 0000000..dcf0f2e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/Modules/index.html @@ -0,0 +1,334 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Using JavaScript Modules and Packages in GraalVM JavaScript

+ +

GraalVM JavaScript is compatible with the latest ECMAScript standard, and can be executed in a variety of embedding scenarios such as Java-based applications or Node.js. +Depending on the GraalVM’s JavaScript embedding scenario, JavaScript packages and modules may be used in different ways.

+ +

Node.js

+ +

GraalVM ships with a specific Node.js version that it is compatible with. +Applications can therefore freely import and use NPM packages compatible with the supported Node.js version, including CommonJS, ES modules, and modules that use native bindings. +To check and verify the Node.js version supported by GraalVM, simply run bin/node --version.

+ +

Java-based Applications (Context API)

+ +

When embedded in a Java application (using the Context API), GraalVM JavaScript can execute JavaScript applications and modules that do not depend on Node.js’ built-in modules such as 'fs', 'events', or 'http' or Node.js-specific functions such as setTimeout() or setInterval(). +On the other hand, modules that depend on such Node.js builtins cannot be loaded in a GraalVM polyglot Context.

+ +

Supported NPM packages can be used in a GraalVM JavaScript Context using one of the following approaches:

+ +
    +
  1. Using a package bundler. +For example, to combine multiple NPM packages in a single JavaScript Source file.
  2. +
  3. Using ES modules on the local FileSystem. +Optionally, a custom Truffle FileSystem can be used to configure how files are resolved.
  4. +
+ +

By default, a Java Context does not support loading modules using the CommonJS require() function. +This is because require() is a Node.js built-in function, and is not part of the ECMAScript specification. +Experimental support for CommonJS modules can be enabled through the js.commonjs-require option as described below.

+ +

ECMAScript Modules (ESM)

+ +

GraalVM JavaScript supports the full ES modules specification, including import statements, dynamic import of modules using import(), and advanced features such as top-level await. +ECMAScript modules can be loaded in a Context simply by evaluating the module sources. +GraalVM JavaScript loads ECMAScript modules based on their file extension. +Therefore, any ECMAScript module should have file name extension .mjs. +Alternatively, the module Source should have Mime type "application/javascript+module".

+ +

As an example, let’s assume that you have a file named foo.mjs containing the following simple ES module:

+
export class Foo {
+
+    square(x) {
+        return x * x;
+    }
+}
+
+ +

The ES module can be loaded in a polyglot Context in the following way:

+
public static void main(String[] args) throws IOException {
+
+    String src = "import {Foo} from '/path/to/foo.mjs';" +
+                 "const foo = new Foo();" +
+                 "console.log(foo.square(42));";
+
+    Context cx = Context.newBuilder("js")
+                .allowIO(true)
+                .build();
+
+	cx.eval(Source.newBuilder("js", src, "test.mjs").build());
+}
+
+ +

Note that the ES module file has .mjs extension. +Also note that the allowIO() option is provided to enable IO access. +More examples of ES modules usage are available here.

+ +

Experimental module namespace exports

+ +

The --js.esm-eval-returns-exports experimental option can be used to expose the ES module namespace exported object to a Polyglot Context. +This can be handy when an ES module is used directly from Java:

+
public static void main(String[] args) throws IOException {
+
+    String code = "export const foo = 42;";
+
+    Context cx = Context.newBuilder("js")
+                .allowIO(true)
+                .option("js.esm-eval-returns-exports", "true")
+                .build();
+
+    Source source = Source.newBuilder("js", code)
+                .mimeType("application/javascript+module")
+                .build();
+
+    Value exports = cx.eval(source);
+    // now the `exports` object contains the ES module exported symbols.
+    System.out.println(exports.getMember("foo").toString()); // prints `42`
+}
+
+

The option is disabled by default.

+ +

Truffle FileSystem

+ +

By default, GraalVM JavaScript uses the built-in FileSystem of the polyglot Context to load and resolve ES modules. +A FileSystem can be used to customize the ES modules loading process. +For example, a custom FileSystem can be used to resolve ES modules using URLs:

+ +
Context cx = Context.newBuilder("js").fileSystem(new FileSystem() {
+
+	private final Path TMP = Paths.get("/some/tmp/path");
+
+    @Override
+    public Path parsePath(URI uri) {
+    	// If the URL matches, return a custom (internal) Path
+    	if ("http://localhost/foo".equals(uri.toString())) {
+        	return TMP;
+		} else {
+        	return Paths.get(uri);
+        }
+    }
+
+	@Override
+    public SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
+    	if (TMP.equals(path)) {
+        	String moduleBody = "export class Foo {" +
+                            "        square(x) {" +
+                            "            return x * x;" +
+                            "        }" +
+                            "    }";
+            // Return a dynamically-generated file for the ES module.
+            return createByteChannelFrom(moduleBody);
+        }
+    }
+
+    /* Other FileSystem methods not shown */
+
+}).allowIO(true).build();
+
+String src = "import {Foo} from 'http://localhost/foo';" +
+             "const foo = new Foo();" +
+             "console.log(foo.square(42));";
+
+cx.eval(Source.newBuilder("js", src, "test.mjs").build());
+
+ +

In this simple example, a custom FileSystem is used to load a dynamically-generated ES module when an application attempts to import the http://localhost/foo URL.

+ +

A complete example of a custom Truffle FileSystem to load ES modules can be found here.

+ +

CommonJS Modules (CJS)

+ +

By default, the Context API does not support CommonJS modules, and has no built-in require() function. +In order to be loaded and used from a Context in Java, a CommonJS module needs to be bundled into a self-contained JavaScript source file. +This can be done using one of the many popular open-source bundling tools such as Parcel, Browserify, and Webpack. +Experimental support for CommonJS modules can be enabled through the js.commonjs-require option as described below.

+ +

Experimental support for CommonJS NPM modules in the Context API

+ +

The js.commonjs-require option provides a built-in require() function that can be used to load NPM-compatible CommonJS modules in a JavaScript Context. +Currently, this is an experimental feature not for production usage.

+ +

To enable CommonJS support, a JavaScript context can be created in the following way:

+
Map<String, String> options = new HashMap<>();
+// Enable CommonJS experimental support.
+options.put("js.commonjs-require", "true");
+// (optional) folder where the NPM modules to be loaded are located.
+options.put("js.commonjs-require-cwd", "/path/to/root/folder");
+// (optional) Node.js built-in replacements as a comma separated list.
+options.put("js.commonjs-core-modules-replacements",
+            "buffer:buffer/," +
+            "path:path-browserify");
+// Create context with IO support and experimental options.
+Context cx = Context.newBuilder("js")
+                            .allowExperimentalOptions(true)
+                            .allowIO(true)
+                            .options(options)
+                            .build();
+// Require a module
+Value module = cx.eval("js", "require('some-module');");
+
+ +

The "js.commonjs-require-cwd" option can be used to specify the main folder where NPM packages have been installed. +As an example, this can be the folder where the npm install command was executed, or the folder containing your main node_modules folder. +Any NPM module will be resolved relative to that folder, including any built-in replacement specified using "js.commonjs-core-modules-replacements".

+ +
Differences with Node.js built-in require() function
+ +

The Context built-in require() function can load regular NPM modules implemented in JavaScript, but cannot load native NPM modules. +The built-in require() relies on the FileSystem, therefore I/O access needs to be enabled at context creation time using the allowIO option. +The built-in require() aims to be largely compatible with Node.js, and we expect it to work with any NPM module that would work in a browser (e.g., created using a package bundler).

+ +
Installing an NPM module to be used via the Context API
+ +

In order to be used from a JavaScript Context, an NPM module needs to be installed to a local folder. +This can be done using GraalVM JavaScript’s npm install command like one would normally do for Node.js applications. +At runtime, the option js.commonjs-require-cwd can be used to specify the main installation folder for NPM packages. +The require() built-in function will resolve packages according to the default Node.js’ package resolution protocol starting from the directory specified via js.commonjs-require-cwd. +When no directory is provided with the option, the current working directory of the application will be used.

+ +
Node.js core modules mockups
+ +

Some JavaScript applications or NPM modules might need functionalities that are available in Node.js’ built-in modules (e.g., 'fs' and 'buffer', etc.). +Such modules are not available in the Context API. +Thankfully, the Node.js community has developed high-quality JavaScript implementations for many Node.js core modules (e.g., the ‘buffer’ module for the browser). +Such alternative module implementations can be exposed to a JavaScript Context using the js.commonjs-core-modules-replacements option, in the following way:

+
options.put("js.commonjs-core-modules-replacements", "buffer:my-buffer-implementation");
+
+ +

As the code suggests, the option instructs the GraalVM JavaScript runtime to load a module called my-buffer-implementation when an application attempts to load the Node.js 'buffer' built-in module using require('buffer').

+ +
Global symbols pre-initialization
+ +

An NPM module or a JavaScript application might expect certain global properties to be defined in the global scope. +For example, applications or modules might expect the Buffer global symbol to be defined in the JavaScript global object. +To this end, the application user code can use globalThis to patch the application’s global scope:

+
// define an empty object called 'process'
+globalThis.process = {};
+// define the 'Buffer' global symbol
+globalThis.Buffer = require('some-buffer-implementation').Buffer;
+// import another module that might use 'Buffer'
+require('another-module');
+
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/Multithreading/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/Multithreading/index.html new file mode 100644 index 0000000..f759c5d --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/Multithreading/index.html @@ -0,0 +1,172 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Multithreading

+ +

Running JavaScript on GraalVM supports multithreading. +Depending on the usage scenario, threads can be used to execute parallel JavaScript code using multiple Context objects, or multiple Worker threads.

+ +

Multithreading with Java and JavaScript

+ +

Multithreading is supported when running JavaScript in the context of Java interoperability. +The basic model of multi-threaded execution supported by GraalVM is a “share-nothing” model that should be familiar to any JavaScript developer:

+ +
    +
  1. An arbitrary number of JavaScript Contexts can be created, but they should be used by one thread at a time.
  2. +
  3. Concurrent access to JavaScript objects is not allowed: any JavaScript object cannot be accessed by more than one thread at a time.
  4. +
  5. Concurrent access to Java objects is allowed: any Java object can be accessed by any Java or JavaScript thread, concurrently.
  6. +
+ +

A JavaScript Context cannot be accessed by two or more threads, concurrently, but it is possible to access the same Context from multiple threads using proper syncronization, to ensure that concurrent access never happens.

+ +

Examples

+ +

The GraalVM JavaScript unit tests contain several examples of multi-threaded Java/JavaScript interactions. +The most notable ones describe how:

+ +
    +
  1. Multiple Context objects can be executed in multiple threads.
  2. +
  3. JavaScript values created by one thread can be used from another thread when proper synchronization is used.
  4. +
  5. A Context can be accessed from multiple threads when proper synchronization is used.
  6. +
  7. Java concurrency can be used from JavaScript.
  8. +
  9. Java objects can be accessed by multiple JavaScript threads, concurrently.
  10. +
+ +

Multithreading with Node.js

+ +

The basic multithreading model of GraalVM JavaScript applies to Node.js applications as well. +In Node.js, a Worker thread can be created to execute JavaScript code in parallel, but JavaScript objects cannot be shared between Workers. +On the contrary, a Java object created with GraalVM Java interoperability (e.g., using Java.type()) can be shared between Node.js Workers. +This allows multi-threaded Node.js applications to share Java objects.

+ +

Examples

+ +

The GraalVM Node.js unit tests contain several examples of multi-threaded Node.js applications. +The most notable examples show how:

+ +
    +
  1. Node.js worker threads can execute Java code.
  2. +
  3. Java objects can be shared between Node.js worker threads.
  4. +
  5. JavaScript Promise objects can be used to await on messages from workers, using Java objects to bind promises to worker messages.
  6. +
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/NashornMigrationGuide/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/NashornMigrationGuide/index.html new file mode 100644 index 0000000..aa58f45 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/NashornMigrationGuide/index.html @@ -0,0 +1,358 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Migration Guide from Nashorn to GraalVM JavaScript

+ +

This guide serves as a migration guide for code previously targeted to the Nashorn engine. +See the Java Interoperability guide for an overview of supported Java interoperability features.

+ +

The Nashorn engine has been deprecated in JDK 11 as part of JEP 335 and +and has been removed from JDK15 as part of JEP 372.

+ +

GraalVM can step in as a replacement for JavaScript code previously executed on the Nashorn engine. +GraalVM provides all the features for JavaScript previously provided by Nashorn. +Many are available by default, some are behind flags, and others require minor modifications to your source code.

+ +

Both Nashorn and GraalVM JavaScript support a similar set of syntax and semantics for Java interoperability. +One notable difference is that GraalVM JavaScript takes a secure by default approach, meaning some features need to be explicitly enabled that were available by default on Nashorn. +The most important differences relevant for migration are listed here.

+ +

Nashorn features available by default (dependent on security settings):

+
    +
  • Java.type, Java.typeName
  • +
  • Java.from, Java.to
  • +
  • Java.extend, Java.super
  • +
  • Java package globals: Packages, java, javafx, javax, com, org, edu
  • +
+ +

Nashorn Compatibility Mode

+ +

GraalVM JavaScript provides a Nashorn compatibility mode. +Some of the functionality necessary for Nashorn compatibility is only available when the js.nashorn-compat option is enabled. +This is the case for Nashorn-specific extensions that GraalVM JavaScript does not want to expose by default.

+ +

Note that you have to enable experimental options to use this flag. +Further note that setting this flag defeats the secure by default approach of GraalVM JavaScript in some cases, e.g., when operating on a legacy ScriptEngine.

+ +

When you use the Nashorn compatibility mode, by default, ECMAScript 5 is set as compatibility level. +You can specify a different ECMAScript version using the js.ecmascript-version flag; note that this might conflict with full Nashorn compatibilty. +A code example how to set the flag is given near the end of this section.

+ +

The js.nashorn-compat option can be set: +1. by using a command line option:

+
js --experimental-options --js.nashorn-compat=true
+
+ +

2. by using the Polyglot API:

+
import org.graalvm.polyglot.Context;
+
+try (Context context = Context.newBuilder().allowExperimentalOptions(true).option("js.nashorn-compat", "true").build()) {
+    context.eval("js", "print(__LINE__)");
+}
+
+ +

3. by using a system property when starting a Java application (remember to enable allowExperimentalOptions on the Context.Builder in your application as well):

+
java -Dpolyglot.js.nashorn-compat=true MyApplication
+
+ +

Functionality only available under the nashorn-compat flag includes:

+
    +
  • Java.isJavaFunction, Java.isJavaMethod, Java.isScriptObject, Java.isScriptFunction
  • +
  • new Interface|AbstractClass(fn|obj)
  • +
  • JavaImporter
  • +
  • JSAdapter
  • +
  • java.lang.String methods on string values
  • +
  • load("nashorn:parser.js"), load("nashorn:mozilla_compat.js")
  • +
  • exit, quit
  • +
+ +

The js.ecmascript-version option can be set in similar fashion. +As this is a supported option, there is no need to provide the experimental-options flag just for setting the ecmascript-version: +1. by using a command line option:

+
js --js.ecmascript-version=2020
+
+ +

Nashorn Syntax Extensions

+ +

Nashorn syntax extensions can be enabled using the js.syntax-extensions experimental option. +They are also enabled by default in the Nashorn compatibility mode (js.nashorn-compat).

+ +

GraalVM JavaScript vs Nashorn

+ +

GraalVM JavaScript differs from Nashorn in some aspects that were intentional design decisions.

+ +

Secure by Default

+

GraalVM JavaScript takes a secure by default approach. +Unless explicitly permitted by the embedder, JavaScript code cannot access Java classes or access the file system, among other restrictions. +Several features of GraalVM JavaScript, including Nashorn compatibility features, are only available when the relevant security settings are permissive enough. +Make sure you understand the security implications of any change that lifts the secure default limits to your application and the host system.

+ +

For a full list of available settings, see Context.Builder. +Those flags can be defined when building the context with GraalVM Polyglot API.

+ +

Flags frequently required to enable features of GraalVM JavaScript are:

+
    +
  • allowHostAccess(): configure which public constructors, methods or fields of public classes are accessible by guest applications. Use HostAccess.EXPLICIT or a custom HostAccess policy to selectively enable access. Set to HostAccess.ALL to allow unrestricted access.
  • +
  • allowHostClassLookup(): set a filter that specifies the Java host classes that can be looked up by the guest application. Set to the Predicate className -> true to allow lookup of all classes.
  • +
  • allowIO(): allow the guest language to perform unrestricted IO operations on the host system, required, e.g., to load() from the file system. Set to true to enable IO.
  • +
+ +

If you run code on the legacy ScriptEngine, see Setting options via Bindings regarding how to set them there.

+ +

Finally, note that the nashorn-compat mode enables the relevant flags when executing code on the ScriptEngine (but not on Context), to provide better compatibilty with Nashorn in that setup.

+ +

Launcher Name js

+

GraalVM JavaScript comes with a binary launcher named js. +Note that, depending on the build setup, GraalVM might still ship Nashorn and its jjs launcher.

+ +

ScriptEngine Name graal.js

+

GraalVM JavaScript is shipped with support for ScriptEngine. +It registers under several names, including “graal.js”, “JavaScript”, and “js”. +Be sure to activate the Nashorn compatibility mode as described above if you need full Nashorn compatibility. +Depending on the build setup, GraalVM might still ship Nashorn and provide it via ScriptEngine. +For more details, see ScriptEngine Implementation.

+ +

ClassFilter

+

GraalVM JavaScript supports a class filter when starting with a polyglot Context. +See Context.Builder.hostClassFilter.

+ +

Fully Qualified Names

+

GraalVM Javascript requires the use of Java.type(typename). +It does not support accessing classes just by their fully qualified class name by default. +Java.type brings more clarity and avoids the accidental use of Java classes in JavaScript code. +For instance, look at this pattern:

+
var bd = new java.math.BigDecimal('10');
+
+ +

It should be expressed as:

+
var BigDecimal = Java.type('java.math.BigDecimal');
+var bd = new BigDecimal('10');
+
+ +

Lossy Conversion

+

GraalVM JavaScript does not allow lossy conversions of arguments when calling Java methods. +This could lead to bugs with numeric values that are hard to detect.

+ +

GraalVM JavaScript will always select the overloaded method with the narrowest possible argument types that can be converted to without loss. +If no such overloaded method is available, GraalVM JavaScript throws a TypeError instead of lossy conversion. +In general, this affects which overloaded method is executed.

+ +

Custom targetTypeMappings can be used to customize behaviour. See HostAccess.Builder#targetTypeMapping.

+ +

ScriptObjectMirror Objects

+

GraalVM JavaScript does not provide objects of the class ScriptObjectMirror. +Instead, JavaScript objects are exposed to Java code as objects implementing Java’s Map interface.

+ +

Code referencing ScriptObjectMirror instances can be rewritten by changing the type to either an interface (Map or List) or the polyglot Value class which provides similar capabilities.

+ +

Multithreading

+ +

Running JavaScript on GraalVM supports multithreading by creating several Context objects from Java code. +Contexts can be shared between threads, but each context must be accessed by a single thread at a time. +Multiple JavaScript engines can be created from a Java application, and can be safely executed in parallel on multiple threads:

+
Context polyglot = Context.create();
+Value array = polyglot.eval("js", "[1,2,42,4]");
+
+
+ +

GraalVM JavaScript does not allow the creation of threads from JavaScript applications with access to the current Context. +Moreover, GraalVM JavaScript does not allow concurrent threads to access the same Context at the same time. +This could lead to unmanagable synchronization problems like data races in a language that is not prepared for multithreading. For example:

+
new Thread(function() {
+    print('printed from another thread'); // throws Exception due to potential synchronization problems
+}).start();
+
+ +

JavaScript code can create and start threads with Runnables implemented in Java. +The child thread may not access the Context of the parent thread or of any other polyglot thread. +In case of violations, an IllegalStateException will be thrown. +A child thread may create a new Context instance, though.

+
new Thread(aJavaRunnable).start(); // allowed on GraalVM JavaScript
+
+ +

With proper synchronization in place, multiple contexts can be shared between different threads. The example Java applications using GraalVM JavaScript Contexts from multiple threads can be found here.

+ +

Extensions Only Available in Nashorn Compatibility Mode

+ +

The following extensions to JavaScript available in Nashorn are deactivated in GraalVM JavaScript by default. +They are provided in GraalVM’s Nashorn compatibility mode. +It is highly recommended not to implement new applications based on those features, but only to use it as a means to migrate existing applications to GraalVM.

+ +

String length Property

+

GraalVM JavaScript does not treat the length property of a String specially. +The canonical way of accessing the String length is reading the length property:

+
myJavaString.length;
+
+ +

Nashorn allows users to access length as both a property and a function. +Existing function calls length() should be expressed as property access. +Nashorn behavior is mimicked in the Nashorn compatibility mode.

+ +

Java Packages in the JavaScript Global Object

+

GraalVM JavaScript requires the use of Java.type instead of fully qualified names. +In the Nashorn compatibility mode, the following Java packages are added to the JavaScript global object: java, javafx, javax, com, org, and edu.

+ +

JavaImporter

+

The JavaImporter feature is available only in the Nashorn compatibility mode.

+ +

JSAdapter

+

The use of the non-standard JSAdapter feature is discouraged and should be replaced with the equivalent standard Proxy feature. +For compatibility, JSAdapter is still available in the Nashorn compatibility mode.

+ +

Java.* Methods

+

Several methods provided by Nashorn on the Java global object are available only in the Nashorn compatibility mode, or currently not supported by GraalVM JavaScript. +Available in the Nashorn compatibility mode are: Java.isJavaFunction, Java.isJavaMethod, Java.isScriptObject, and Java.isScriptFunction. Java.asJSONCompatible is currently not supported.

+ +

Accessors

+

In the Nashorn compatibility mode, GraalVM JavaScript allows users to access getters and setters just by using the names as properties, while omitting get, set, or is:

+ +
var Date = Java.type('java.util.Date');
+var date = new Date();
+
+var myYear = date.year; // calls date.getYear()
+date.year = myYear + 1; // calls date.setYear(myYear + 1);
+
+ +

GraalVM JavaScript mimics the behavior of Nashorn regarding the ordering of the access:

+
    +
  • In case of a read operation, GraalVM JavaScript will first try to call a getter with the name get and the property name in camel case. If that is not available, a getter with the name is and the property name in camel case is called. In the second case, unlike Nashorn, the resulting value is returned even if it is not of type boolean. Only if both methods are not available, the property itself will be read.
  • +
  • In case of a write operation, GraalVM JavaScript will try to call a setter with the name set and the property name in camel case, providing the value as argument to that function. If the setter is not available, the property itself will be written.
  • +
+ +

Note that Nashorn (and thus, GraalVM JavaScript) makes a clear distinction between property read/writes and function calls. +When the Java class has both a field and a method of the same name publicly available, obj.property will always read the field (or the getter as discussed above), while obj.property() will always call the respective method.

+ +

Additional Aspects to Consider

+ +

Features of GraalVM JavaScript

+

GraalVM JavaScript supports features of the newest ECMAScript specification and some extensions to it. +See JavaScript Compatibility. +Note that this example adds objects to the global scope that might interfere with existing source code unaware of those extensions.

+ +

Console Output

+

GraalVM JavaScript provides a print builtin function compatible with Nashorn.

+ +

Note that GraalVM JavaScript also provides a console.log function. +This is an alias for print in pure JavaScript mode, but uses an implementation provided by Node.js when running in Node mode. +The behaviour around Java objects differs for console.log in Node mode as Node.js does not implement special treatment for such objects.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/NodeJS/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/NodeJS/index.html new file mode 100644 index 0000000..5cef216 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/NodeJS/index.html @@ -0,0 +1,198 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Node.js Runtime

+ +

GraalVM can run unmodified Node.js applications. +Applications can freely import and use NPM packages, including native ones.

+ +

For the differences between running the node native launcher and accessing Node.js/npm modules/ECMAScript modules from a Java Context, see NodeJSVSJavaScriptContext.

+ +

Installing Node.js Component

+ +

Since GraalVM 21.1, the Node.js support is packaged in a separate GraalVM component. +It can be installed with the GraalVM Updater.

+ +
$JAVA_HOME/bin/gu install nodejs
+
+ +

This installs the node and npm binaries in the $JAVA_HOME/bin directory.

+ +

Polyglot Support in Node.js

+ +

The Node.js component is able to use the polyglot language interoperability (flag: --polyglot) with other installed polyglot languages. +This feature is available by default in JVM mode (flag: --jvm). +For polyglot access to the Ruby language, you can e.g. use this command:

+ +
$JAVA_HOME/bin/node --jvm --polyglot -e 'var array = Polyglot.eval("ruby", "[1,2,42,4]"); console.log(array[2]);'
+
+ +

To use the polyglot capabilities of node in the native mode (flag: --native), the libpolyglot needs to be rebuilt first. +For this, the native-image component and the other languages need to be installed first, before the image can be rebuilt:

+ +
$JAVA_HOME/bin/gu install native-image
+$JAVA_HOME/bin/gu rebuild-images libpolyglot
+
+ +

After a successfull rebuild, the polyglot access is also available in the --native mode:

+ +
$JAVA_HOME/bin/node --native --polyglot -e 'var array = Polyglot.eval("ruby", "[1,2,42,4]"); console.log(array[2]);'
+
+ +

Running Node.js Applications

+ +

To run Node.js-based applications, use the node launcher in the GraalVM distribution:

+
$JAVA_HOME/bin/node [options] [filename] [args]
+
+ +

GraalVM’s Node.js runtime is based on a recent version of Node.js, and runs the +GraalVM JavaScript engine instead of Google V8. Thus, some internal features (e.g., VM-internal statistics, configuration, profiling, debugging, etc.) are unsupported, or supported with potentially different behavior.

+ +

The node command is largely compatible with Node.js, and features additional GraalVM-specific functionalities (e.g., interoperability with Java and all other GraalVM languages). +A list of available options can be obtained with node --help.

+ +

Installing Packages Using npm

+ +

To install a Node.js package, you can use the npm launcher from the GraalVM’s /bin folder. +The npm command is equivalent to the default NPM command, and supports most of its options.

+ +

An NPM package can be installed with:

+
$JAVA_HOME/bin/npm install <package>
+
+ +

As the npm command of GraalVM is largely compatible with NPM, packages will be installed in the node_modules folder, as expected.

+ +

Installing npm Packages Globally

+ +

Node packages can be installed globally using npm and the -g option. +By default, npm installs global packages (links to their executables) in the path where the node executable is installed, typically NODE/bin. +In GraalVM, while there is a node executable in JAVA_HOME/bin, this is just a link to the actual executable in the JAVA_HOME/jre/languages/js/bin folder. +That folder is where global packages are installed. +You might want to add that directory to your $PATH if you regularly use globally installed packages, especially their command line interfaces.

+ +

Another option is to specify the global installation folder of npm by setting the $PREFIX environment variable, or by specifying the --prefix option when running npm install. +For example, the following command will install global packages in the /foo/bar folder:

+
$JAVA_HOME/bin/npm install --prefix /foo/bar -g <package>
+
+

More details about prefix can be found in the official NPM documentation.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/NodeJSvsJavaScriptContext/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/NodeJSvsJavaScriptContext/index.html new file mode 100644 index 0000000..60a31fb --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/NodeJSvsJavaScriptContext/index.html @@ -0,0 +1,200 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Differences Between Node.js and Java Embeddings

+ +

GraalVM provides a fully-compliant ECMAScript 2022 JavaScript language runtime. +As such, it can run JavaScript code in a variety of embedding scenarios, including Oracle Database, any Java-based application, and Node.js.

+ +

Depending on the GraalVM’s JavaScript embedding scenario, applications have access to different built-in capabilities. +For example, Node.js applications executed using GraalVM’s bin/node executable have access to all of Node.js’ APIs, including built-in Node.js modules such as fs, http, etc. +Conversely, JavaScript code embedded in a Java application has access to limited capabilities, as specified through the Context API, and do not have access to Node.js built-in modules.

+ +

This guide describes the main differences between a Node.js application and a GraalVM JavaScript application embedded in Java.

+ +

Context Creation

+ +

JavaScript code in GraalVM can be executed using a GraalVM execution Context.

+ +

In a Java application, a new context can be created using the Context API. +New contexts can be configured in multiple ways, and configuration options include exposing access to Java classes, allowing access to IO, etc. +A list of context creation options can be found in the API documentation. +In this scenario, Java classes can be exposed to JavaScript by using GraalVM’s polyglot Bindings.

+ +

In a Node.js application, the GraalVM Context executing the application is pre-initialized by the Node.js runtime, and cannot be configured by the user application. +In this scenario, Java classes can be exposed to the Node.js application by using the --vm.cp= command line option of the bin/node command, as described below.

+ +

Java Interoperability

+ +

JavaScript applications can interact with Java classes using the Java built-in object. +The object is not available by default, and can be enabled in the following way:

+ +
    +
  1. In Node.js mode, start GraalVM using the bin/node --jvm command.
  2. +
  3. In Java, create a GraalVM context using the withHostInterop() option, e.g.: +
    Context.create("js").withHostInterop()
    +
    +

    More details on the Java interoperability capabilities of GraalVM JavaScript are available in Java Interoperability.

    +
  4. +
+ +

Multithreading

+ +

A GraalVM context running JavaScript enforces a “share-nothing” model of parallelism: no JavaScript values can be accessed by two concurrent Java threads at the same time. +In order to leverage parallel execution, multiple contexts have to be created and executed from multiple threads:

+ +
    +
  1. In Node.js mode, multiple contexts can be created using Node.js’ Worker threads API. +The Worker threads API ensures that no sharing can happen between two parallel contexts.
  2. +
  3. In Java, multiple contexts can be executed from multiple threads. +As long as a context is not accessed by two threads at the same time, parallel execution happens safely.
  4. +
+ +

More details on parallel execution in GraalVM JavaScript are available in this blog post.

+ +

Java Libraries

+ +

Java libraries can be accessed from JavaScript in GraalVM through the Java built-in object. +In order for a Java library to be accessible from a Context, its jar files need to be added to the GraalVM classpath. This can be done in the following way:

+ +
    +
  1. In Node.js mode, the classpath can be modified using the --jvm.cp option.
  2. +
  3. In Java, the default Java’s -cp option can be used.
  4. +
+ +

More details on GraalVM command line options are available in Options.

+ +

JavaScript Packages and Modules

+ +

Many popular JavaScript modules such as those available on the npm package registry can be used from Node.js as well as from Java:

+ +
    +
  1. In Node.js mode, JavaScript modules are handled by the Node.js runtime. +Therefore, GraalVM JavaScript supports all modules supported by Node.js (including ES modules, CommonJS modules, and native modules).
  2. +
  3. In Java mode, GraalVM JavaScript can execute any JavaScript module or package that does not depend on native Node.js built-in modules (such as 'fs', 'http', etc.) +Modules can be loaded using a package bundler, or using the available built-in mechanisms for ES modules. +CommonJS modules are supported in Java mode under an experimental flag.
  4. +
+ +

More details on JavaScript modules are available in Modules.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/OperatorOverloading/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/OperatorOverloading/index.html new file mode 100644 index 0000000..9ab7971 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/OperatorOverloading/index.html @@ -0,0 +1,282 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Operator Overloading

+ +

GraalVM JavaScript provides an early implementation of the ECMAScript operator overloading proposal. +This lets you overload the behavior of JavaScript’s operators on your JavaScript classes.

+ +

If you want to experiment with this feature, you will first need to enable it. +Since both the proposal and our implementation of it are in early stages, you will need to set the following experimental option.

+ +
js --experimental-options --js.operator-overloading
+
+ +

After setting the option, you will see a new builtin in the global namespace, the Operators function. +You can call this function, passing it a JavaScript object as an argument. +The object should have a property for every operator you wish to overload, with the key being the name of the operator and the value being a function which implements it. +The return value of the Operators function is a constructor that you can then subclass when defining your type. +By subclassing this constructor, you get a class whose objects will all inherit the overloaded operator behavior that you defined in your argument to the Operators function.

+ +

Basic Example

+ +

Let’s look at an example from the original proposal featuring vectors:

+ +
const VectorOps = Operators({
+  "+"(a, b) {
+    return new Vector(a.contents.map((elt, i) => elt + b.contents[i]));
+  },
+  "=="(a, b) {
+    return a.contents.length === b.contents.length &&
+           a.contents.every((elt, i) => elt == b.contents[i]);
+  },
+});
+
+class Vector extends VectorOps {
+  contents;
+  constructor(contents) {
+    super();
+    this.contents = contents;
+  }
+}
+
+ +

Here we define overloads for two operators, + and ==. +Calling the Operators function with the table of overloaded operators yields the VectorOps class. +We then define our Vector class as a subclass of VectorOps.

+ +

If we create instances of Vector, we can observe that they follow our overloaded operator definitions:

+ +
> new Vector([1, 2, 3]) + new Vector([4, 5, 6]) == new Vector([5, 7, 9])
+true
+
+ +

Example with Mixed Types

+ +

It is also possible to overload operators between values of different types, allowing, for example, multiplication of vectors by numbers.

+ +
const VectorOps = Operators({
+    "+"(a, b) {
+        return new Vector(a.contents.map((elt, i) => elt + b.contents[i]));
+    },
+    "=="(a, b) {
+        return a.contents.length === b.contents.length &&
+            a.contents.every((elt, i) => elt == b.contents[i]);
+    },
+}, {
+    left: Number,
+    "*"(a, b) {
+        return new Vector(b.contents.map(elt => elt * a));
+    }
+});
+
+class Vector extends VectorOps {
+    contents;
+    constructor(contents) {
+        super();
+        this.contents = contents;
+    }
+}
+
+ +

To define mixed-type operators, we need to pass additional objects to the Operators function. +These extra tables should each have either a left property or a right property, depending on whether we are overloading the behavior of operators with some other type on the left or on the right side of the operator. +In our case, we are overloading the * operator for cases when there is a Number on the left and our type, Vector, on the right. +Each extra table can have either a left property or a right property and then any number of operator overloads which will apply to that particular case.

+ +

Let’s see this in action:

+ +
> 2 * new Vector([1, 2, 3]) == new Vector([2, 4, 6])
+true
+
+ +

Reference

+ +

The function Operators(table, extraTables...) returns a class with overloaded operators. +Users should define their own class which extends that class.

+ +

The table argument must be an object with one property for every overloaded operator. +The property key must be the name of the operator. +These are the names of operators which can be overloaded:

+ +
    +
  • binary operators: "+", "-", "*", "/", "%", "**", "&", "^", "|", "<<", ">>", ">>>", "==", "<"
  • +
  • unary operators: "pos", "neg", "++", "--", "~"
  • +
+ +

The "pos" and "neg" operator names correspond to unary + and unary -, respectively. +Overloading "++" works both for pre-increments ++x and post-increments x++, the same goes for "--". +The overload for "==" is used both for equality x == y and inequality x != y tests. +Similarly, the overload for "<" is used for all comparison operators (x < y, x <= y, x > y, x >= y) by swapping the arguments and/or negating the result.

+ +

The value assigned to an operator name must be a function of two arguments in the case of binary operators or a function of one argument in the case of unary operators.

+ +

The table argument can also have an open property. +If so, the value of that property must be an array of operator names. +These are the operators which future classes will be able to overload on this type (e.g. a Vector type might declare "*" to be open so that later a Matrix type might overload the operations Vector * Matrix and Matrix * Vector). +If the open property is missing, all operators are considered to be open for future overloading with other types.

+ +

Following the first argument table are optional arguments extraTables. +Each of these must also be an object. +Each extra table must have either a left property or a right property, not both. +The value of that property must be one of the following JavaScript constructors:

+ +
    +
  • Number
  • +
  • BigInt
  • +
  • String
  • +
  • any class with overloaded operators (i.e. extended from a constructor returned by Operators)
  • +
+ +

The other properties of the extra table should be operator overloads as in the first table argument (operator name as key, function implementing the operator as value).

+ +

These extra tables define the behavior of operators when one of the operand types is of a type other than the one being defined. +If the extra table has a left property, its operator definitions will apply to cases when the left operand is of the type named by the left property and the right operand is of the type whose operators are being defined. +Similarly for the right property, if the extra table has a right property, the table’s operator definitions will apply when the right operand has the named type and the left operand has the type whose operators are being defined.

+ +

Note that you are free to overload any of the binary operators between your custom type and the JavaScript numeric types Number and BigInt. +However, the only operators you are allowed to overload between your custom type and the String type are "==" and "<".

+ +

The Operators function will return a constructor that you will usually want to extend in your own class. +Instances of that class will respect your overloaded operator definitions. +Whenever you use an operator on an object with overloaded operators, the following happens:

+ +

1) Every operand that does not have overloaded operators is coerced to a primitive. + 2) If there is an applicable overload for this pairing of operands, it is called. Otherwise, a TypeError is thrown.

+ +

Notably, your objects with overloaded operators will not be coerced to primitives when applying operators and you can get TypeErrors when applying undefined operators to them. +There are two exceptions to this:

+ +

1) If you are using the + operator and one of the arguments is a string (or an object without overloaded operators that coerces to a string via ToPrimitive), then the result will be a concatenation of the ToString values of the two operands. + 2) If you are using the == operator and there is no applicable overload found, the two operands are assumed to be different (x == y will return false and x != y will return true).

+ +

Differences from the Proposal

+ +

There a few differences between the proposal (as defined by its specification and prototype implementation) and our implementation in GraalVM JavaScript:

+ +
    +
  • You do not have to use the with operators from construction to enable the use of overloaded operators. When you overload operators for a class, those operators can then be used anywhere without using with operators from. Furthermore, our parser will not accept the with operators from clause as valid JavaScript.
  • +
  • You cannot use decorators to define overloaded operators. At the time of implementing this proposal, GraalVM JavaScript does not support decorators (these are still an in-progress proposal).
  • +
  • You cannot overload the "[]" and "[]=" operators for reading and writing integer-indexed elements. These two operators require more complex treatment and are not currently supported.
  • +
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/Options/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/Options/index.html new file mode 100644 index 0000000..0dfec11 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/Options/index.html @@ -0,0 +1,232 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Options

+ +

Running JavaScript on GraalVM can be configured with several options.

+ +

GraalVM JavaScript Launcher Options

+ +

These options are to control the behaviour of the js launcher:

+
    +
  • -e, --eval CODE : evaluate the JavaScript source code, then exit the engine. +
    js -e 'print(1+2);'
    +
    +
  • +
  • -f, --file FILE: load and execute the provided script file. Note that the -f flag is optional and can be omitted in most cases, as any additional argument to js will be interpreted as file anyway. +
    js -f myfile.js
    +
    +
  • +
  • --version: print the version information of GraalVM JavaScript, then exit.
  • +
  • --strict: execute the engine in JavaScript’s strict mode.
  • +
+ +

GraalVM JavaScript Engine Options

+ +

There are several options to configure the behavior of the GraalVM JavaScript engine. +Depending on how the engine is started, the options can be passed either to the launcher or programmatically.

+ +

For a full list of options of the JavaScript engine, pass the --help:js flag to the js launcher (available from GraalVM 22.1., for older releases use --help:languages). +To include internal options, use --help:js:internal. +Note that those lists both include stable, supported options and experimental options.

+ +

Provide Options to the Launcher

+

To the launcher, the options are passed with --js.<option-name>=<value>:

+
js --js.ecmascript-version=6
+
+ +

Provide Options Programmatically Using the Context API

+

When started from Java using GraalVM’s Polyglot API, the options are passed programmatically to the Context object:

+
Context context = Context.newBuilder("js")
+                         .option("js.ecmascript-version", "6")
+                         .build();
+context.eval("js", "42");
+
+ +

See the Polyglot Programming reference for information on how to set options programmatically.

+ +

Stable and Experimental Options

+ +

The available options are distinguished in stable and experimental options. +If an experimental option is used, an extra flag has to be provided upfront.

+ +

In the native launchers (js and node), --experimental-options has to be passed before all experimental options. +When using a Context, the option allowExperimentalOptions(true) has to be called on the Context.Builder. +See ScriptEngine Implementation on how to use experimental options with a ScriptEngine.

+ +

Frequently Used Stable Options

+

The following stable options are frequently relevant:

+
    +
  • --js.ecmascript-version: emulate a specific ECMAScript version. Integer value (5, 6, etc., 2015-2022), "latest" (latest supported version of the spec, including finished proposals), or "staging" (latest version including supported unfinished proposals), default is "latest".
  • +
  • --js.foreign-object-prototype: provide JavaScript’s default prototype to foreign objects that mimic JavaScript’s own types (foreign Arrays, Objects and Functions). Boolean value, default is false.
  • +
  • --js.intl-402: enable ECMAScript Internationalization API. Boolean value, default is false.
  • +
  • --js.regexp-static-result: provide static RegExp properties containing the results of the last successful match, e.g., RegExp.$1 (legacy). Boolean value, default is true.
  • +
  • --js.strict: enable strict mode for all scripts. Boolean value, default is false.
  • +
+ +

For a complete list, use js --help:js:internal

+ +

ECMAScript Version

+ +

This option provides compatibility to a specific version of the ECMAScript specification. +It expects an integer value, where both the counting version numbers (5, 6, …) and the publication years (starting from 2015) are supported. +As of GraalVM 21.2, latest, staging are supported, too. +The default in GraalVM 22.x is the ECMAScript 2022 specification. +GraalVM JavaScript implements some features of the future draft specification and of open proposals, if you explicitly select that version and/or enable specific experimental flags. +For production settings, it is recommended to set the ecmascript-version to a released, finalized version of the specification (e.g., 2022).

+ +

Available versions are:

+
    +
  • 5 for ECMAScript 5.x
  • +
  • 6 or 2015 for ECMAScript 2015
  • +
  • 7 or 2016 for ECMAScript 2016
  • +
  • 8 or 2017 for ECMAScript 2017
  • +
  • 9 or 2018 for ECMAScript 2018
  • +
  • 10 or 2019 for ECMAScript 2019
  • +
  • 11 or 2020 for ECMAScript 2020
  • +
  • 12 or 2021 for ECMAScript 2021 (default in 21.3)
  • +
  • 13 or 2022 for ECMAScript 2022 (default in 22.0+, latest released version, ECMAScript 2022 specification)
  • +
  • latest for the latest supported language version (the default version)
  • +
  • staging for the latest supported language features including experimental unstable, unfinished proposals (do not use in production!)
  • +
+ +

intl-402

+ +

This option enables ECMAScript’s Internationalization API. +It expects a Boolean value and the default is false.

+ +

Strict Mode

+ +

This option enables JavaScript’s strict mode for all scripts. +It expects a Boolean value and the default is false.

+ +

Frequently Used Experimental Options

+

Note that these options are experimental and are not guaranteed to be maintained or supported in the future. +To use them, the --experimental-options flag is required or the experimental options have to be enabled on the Context, see above.

+ +
    +
  • --js.nashorn-compat: provide compatibility mode with the Nashorn engine. Sets ECMAScript version to 5 by default. Might conflict with newer ECMAScript versions. Boolean value, default is false.
  • +
  • --js.timezone: set the local time zone. String value, default is the system default.
  • +
  • --js.v8-compat: provide better compatibility with Google’s V8 engine. Boolean value, default is false.
  • +
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/RhinoMigrationGuide/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/RhinoMigrationGuide/index.html new file mode 100644 index 0000000..dc466f6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/RhinoMigrationGuide/index.html @@ -0,0 +1,154 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Migration Guide from Rhino to GraalVM JavaScript

+ +

This document serves as a migration guide for code previously targeted to the Rhino engine. +See the Java Interoperability guide for an overview of supported features.

+ +

Both Rhino and GraalVM JavaScript support a similar set of syntax and semantics for Java interoperability. +The most important differences relevant for migration are listed here.

+ +

Java.type(typename) instead of java.a.b.c.typename

+

GraalVM JavaScript does not put available Java classes in the JavaScript scope. +You have to explicitly load the classes using Java.type(typename). +GraalVM JavaScript supports the Packages global object, but loading the classes explicitly is still encouraged. +The following Java package globals are available in Nashorn compatibility mode (js.nashorn-compat option): java, javafx, javax, com, org, edu.

+ +

Console Output of Java Classes and Java Objects

+

GraalVM JavaScript provides a print builtin function. +It tries to special-case its behavior on Java classes and Java objects to provide the most useful output.

+ +

Note that GraalVM JavaScript also provides a console.log function. +This is an alias for print in pure JavaScript mode, but uses an implementation provided by Node.js when in Node mode. +The behavior around interop objects differs for console.log in Node mode as it does not implement special treatment for such objects.

+ +

JavaScript vs Java Strings

+

GraalVM JavaScript uses Java strings internally to represent JavaScript strings. +This makes it impossible to differentiate whether a specific string was created by JavaScript or by Java code. +In GraalVM JavaScript, the JavaScript properties take precedence over Java fields or methods. +For instance, you can query the length property (of JavaScript) but you cannot call the length function (of Java) on JavaScript strings - length behaves like a data property, not like a function.

+ +

JavaImporter

+

The JavaImporter feature is available only in Nashorn compatibility mode (js.nashorn-compat option).

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/RunOnJDK/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/RunOnJDK/index.html new file mode 100644 index 0000000..8da5dba --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/RunOnJDK/index.html @@ -0,0 +1,190 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Run GraalVM JavaScript on a Stock JDK

+ +

GraalVM JavaScript is optimized for execution as part of GraalVM, or in an embedding scenario built on GraalVM. +This guarantees best possible performance by using the GraalVM compiler as the optimizing compiler, and potentially Native Image to ahead-of-time compile the engine into a native binary.

+ +

As GraalVM JavaScript is a Java application, it is possible to execute it on a stock Java VM like OpenJDK. +When executed without the GraalVM compiler, JavaScript performance will be significantly worse. +While the JIT compilers available on stock JVMs can execute and JIT-compile the GraalVM JavaScript codebase, they cannot optimize it to its full performance potential. +This document describes how to run GraalVM JavaScript on stock Java VMs, and shows how you can use the GraalVM compiler as a JIT compiler to guarantee the best possible performance.

+ +

GraalVM JavaScript on Maven Central

+

GraalVM JavaScript is open source and regularly pushed to Maven Central Repository by the community. +You can find it as package org.graalvm.js.

+ +

There is an example Maven project for GraalVM JavaScript on JDK11 (or later) using the GraalVM compiler at graal-js-jdk11-maven-demo. +The example contains a Maven project for a JavaScript benchmark (a prime number generator). +It allows a user to compare the performance of GraalVM JavaScript running with or without the GraalVM compiler as the optimizing compiler. +Running with the GraalVM compiler will siginificantly improve the execution performance of any relatively large JavaScript codebase.

+ +

In essence, the example POM file activates JVMCI to install additional JIT compilers, and configures the JIT compiler to be the GraalVM compiler by providing it on --module-path and --upgrade-module-path.

+ +

GraalVM JavaScript on JDK 11+

+

The Maven example given above is the preferred way to start on JDK 11 (or newer). +Working without Maven, you can provide the JAR files manually to the java command. +Using --upgrade-module-path executes GraalVM JavaScript with the GraalVM compiler, guaranteeing the best performance. +The GraalVM JAR files can be downloaded from org.graalvm at Maven, and the ICU4J library from org.ibm.icu at Maven.

+ +

On Linux and MacOS

+
JARS=/path/to/JARs
+JDK=/path/to/JDK
+$JDK/bin/java -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=$JARS/graal-sdk-22.2.0.jar:$JARS/truffle-api-22.2.0.jar --upgrade-module-path=$JARS/compiler-22.2.0.jar:$JARS/compiler-management-22.2.0.jar -cp $JARS/launcher-common-22.2.0.jar:$JARS/js-launcher-22.2.0.jar:$JARS/js-22.2.0.jar:$JARS/truffle-api-22.2.0.jar:$JARS/graal-sdk-22.2.0.jar:$JARS/js-scriptengine-22.2.0.jar:$JARS/regex-22.2.0.jar:$JARS/icu4j-71.1.jar com.oracle.truffle.js.shell.JSLauncher
+
+ +

On Windows - similar to the Linux/MacOS command but adapted to the syntax of Window’s shell:

+
set JARs=c:\path\to\jars
+set JDK=c:\path\to\jdk
+%JDK%\bin\java -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler --module-path=%JARS%\graal-sdk-22.2.0.jar;%JARS%\truffle-api-22.2.0.jar --upgrade-module-path=%JARS%\compiler-22.2.0.jar;%JARS%\compiler-management-22.2.0.jar -cp %JARS%\launcher-common-22.2.0.jar;%JARS%\js-launcher-22.2.0.jar;%JARS%\js-22.2.0.jar;%JARS%\truffle-api-22.2.0.jar;%JARS%\graal-sdk-22.2.0.jar;%JARS%\js-scriptengine-22.2.0.jar;%JARS%\regex-22.2.0.jar;%JARS\icu4j-71.1.jar com.oracle.truffle.js.shell.JSLauncher
+
+ +

To start a Java application instead and launch GraalVM JavaScript via GraalVM SDK’s Context (encouraged) or a ScriptEngine (supported, but discouraged), launcher-common-*.jar and js-launcher-*.jar can be omitted.

+ +

ScriptEngine JSR 223

+

GraalVM JavaScript can be started via ScriptEngine when js-scriptengine.jar is included on the classpath. +The engine registers under several different names, e.g., Graal.js. +Note that the Nashorn engine might be available under its names as well, if available on the JDK.

+ +

To start GraalVM JavaScript from ScriptEngine, the following code can be used:

+ +
new ScriptEngineManager().getEngineByName("graal.js");
+
+ +

To list all available engines:

+ +
List<ScriptEngineFactory> engines = (new ScriptEngineManager()).getEngineFactories();
+for (ScriptEngineFactory f: engines) {
+    System.out.println(f.getLanguageName()+" "+f.getEngineName()+" "+f.getNames().toString());
+}
+
+ +

Inspecting the Setup - Is the GraalVM Compiler Used as a JIT Compiler?

+

The --engine.TraceCompilation flag enables a debug output whenever a JavaScript method is compiled by the GraalVM compiler. +JavaScript source code with long-enough run time will trigger the compilation and print a log output:

+ +
> function add(a,b) { return a+b; }; for (var i=0;i<1000*1000;i++) { add(i,i); }
+[truffle] opt done         add <opt> <split-c0875dd>                                   |ASTSize       7/    7 |Time    99(  90+9   )ms |DirectCallNodes I    0/D    0 |GraalNodes    22/   71 |CodeSize          274 |CodeAddress 0x7f76e4c1fe10 |Source    <shell>:1:1
+
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/ScriptEngine/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/ScriptEngine/index.html new file mode 100644 index 0000000..89dec62 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/ScriptEngine/index.html @@ -0,0 +1,189 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

ScriptEngine Implementation

+ +

GraalVM provides a JSR-223 compliant javax.script.ScriptEngine implementation for running JavaScript. +Note that this feature is provided for legacy reasons in order to allow easier migration for implementations currently based on a ScriptEngine. +We strongly encourage users to use the org.graalvm.polyglot.Context interface in order to control many of the settings directly and benefit from finer-grained security settings in GraalVM.

+ +

Setting Options via Bindings

+

The ScriptEngine interface does not provide a default way to set options. +As a workaround, GraalJSScriptEngine supports setting some Context options +through Bindings. +These options are:

+
    +
  • polyglot.js.allowHostAccess <boolean>
  • +
  • polyglot.js.allowNativeAccess <boolean>
  • +
  • polyglot.js.allowCreateThread <boolean>
  • +
  • polyglot.js.allowIO <boolean>
  • +
  • polyglot.js.allowHostClassLookup <boolean or Predicate<String>>
  • +
  • polyglot.js.allowHostClassLoading <boolean>
  • +
  • polyglot.js.allowAllAccess <boolean>
  • +
  • polyglot.js.nashorn-compat <boolean>
  • +
  • polyglot.js.ecmascript-version <String>
  • +
+ +

These options control the sandboxing rules applied to evaluated JavaScript code and are set to false by default, unless the application was started in the Nashorn compatibility mode (--js.nashorn-compat=true).

+ +

Note that using ScriptEngine implies allowing experimental options. +This is an exhaustive list of allowed options to be passed via Bindings; in case you need to pass additional options to GraalVM JavaScript, you need to manually create a Context as shown below.

+ +

To set an option via Bindings, use Bindings.put(<option name>, true) before the engine’s script context is initialized. Note that +even a call to Bindings#get(String) may lead to context initialization. +The following code shows how to enable polyglot.js.allowHostAccess via Bindings:

+
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
+Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
+bindings.put("polyglot.js.allowHostAccess", true);
+bindings.put("polyglot.js.allowHostClassLookup", (Predicate<String>) s -> true);
+bindings.put("javaObj", new Object());
+engine.eval("(javaObj instanceof Java.type('java.lang.Object'));"); // it will not work without allowHostAccess and allowHostClassLookup
+
+

This example will not work if the user calls, e.g., engine.eval("var x = 1;"), before calling bindings.put("polyglot.js.allowHostAccess", true);, since +any call to eval forces context initialization.

+ +

Setting Options via System Properties

+

Options to the JavaScript engine can be set via system properties before starting the JVM by prepending polyglot.:

+
java -Dpolyglot.js.ecmascript-version=2022 MyApplication
+
+ +

Or, options to the JavaScript engine can be set programmatically from within Java before creating ScriptEngine. This, however, only works for the options passed to the JavaScript engine (like js.ecmascript), not for the six options mentioned above that can be set via the Bindings. +Another caveat is that those system properties are shared by all concurrently executed ScriptEngines.

+ +

Manually Creating Context for More Flexibility

+

Context options can also be passed to GraalJSScriptEngine directly, via an instance of Context.Builder:

+
ScriptEngine engine = GraalJSScriptEngine.create(null,
+        Context.newBuilder("js")
+        .allowHostAccess(HostAccess.ALL)
+        .allowHostClassLookup(s -> true)
+        .option("js.ecmascript-version", "2022"));
+engine.put("javaObj", new Object());
+engine.eval("(javaObj instanceof Java.type('java.lang.Object'));");
+
+ +

This allows setting all options available in GraalVM JavaScript. +It does come at the cost of a hard dependency on GraalVM JavaScript, e.g., the GraalJSScriptEngine and Context classes.

+ +

Supported File Extensions

+

The GraalVM JavaScript implementation of javax.script.ScriptEngine supports the js file extension for JavaScript source files, as well as the mjs extension for ES modules.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/index.html new file mode 100644 index 0000000..9093171 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/js/index.html @@ -0,0 +1,302 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

GraalVM JavaScript Implementation

+ +

GraalVM provides an ECMAScript-compliant runtime to execute JavaScript and Node.js applications. +It is fully standard compliant, executes applications with high performance, and provides all benefits from the GraalVM stack, including language interoperability and common tooling. +This reference documentation provides information on available JavaScript engine configurations, the Node.js runtime, the javax.script.ScriptEngine implementation, multithreading support details, possible embedding scenarios, and more. +To migrate the code previously targeted to the Nashorn or Rhino engines, migration guides are available.

+ +

Running JavaScript

+

Since GraalVM 22.2, the JavaScript support is packaged in a separate GraalVM component. +It can be installed with the GraalVM Updater:

+ +
$JAVA_HOME/bin/gu install js
+
+ +

Alternatively, you can download the JavaScript component and install it from a file, e.g.:

+ +
$JAVA_HOME/bin/gu install --file ~/Downloads/js-...-.jar
+
+

This installs js in the $JAVA_HOME/bin directory. +When the JavaScript component is installed, GraalVM can run plain JavaScript (ECMAScript) code:

+
$JAVA_HOME/bin/js [options] [filename...] -- [args]
+
+ +

For information about the compatibility of GraalVM JavaScript with existing standards and engines, see JavaScriptCompatibility.

+ +

Running Node.js

+

GraalVM is capable of executing unmodified Node.js applications. +Applications can import npm modules, including native ones. +Since GraalVM 21.1, the Node.js support is packaged in a separate GraalVM component. +It can be installed with the GraalVM Updater:

+ +
$JAVA_HOME/bin/gu install nodejs
+
+ +

Alternatively, you can download the Node.js component and install it from a file, e.g.:

+ +
$JAVA_HOME/bin/gu install --file ~/Downloads/nodejs-installable-...-.jar
+
+ +

This installs node and npm launchers in the $JAVA_HOME/bin directory. +Use the node utility to execute Node.js applications:

+
node [options] [filename] [args]
+
+ +

To install a Node.js package, use the npm launcher from $JAVA_HOME/bin. +The npm command is equivalent to the default Node.js command and supports all Node.js APIs.

+ +
    +
  1. Install the colors and ansispan packages using npm install as follows: +
     npm install colors ansispan
    +
    +

    After the packages are installed, you can use them from your application.

    +
  2. +
  3. Add the following code snippet to a file named app.js and save it in the same directory where you installed the Node.js packages: +
     const http = require("http");
    + const span = require("ansispan");
    + require("colors");
    +
    + http.createServer(function (request, response) {
    +     response.writeHead(200, {"Content-Type": "text/html"});
    +     response.end(span("Hello Graal.js!".green));
    + }).listen(8000, function() { console.log("Graal.js server running at http://127.0.0.1:8000/".red); });
    +
    + setTimeout(function() { console.log("DONE!"); process.exit(); }, 2000);
    +
    +
  4. +
  5. Execute it on GraalVM using the node command as follows: +
     node app.js
    +
    +
  6. +
+ +

For more information about running Node.js, continue to Node.js Runtime. +Node.js functionality is available when an application is started from the node binary launcher. +Certain limits apply when launching a Node.js application or accessing npm packages from a Java context, see Node.js vs. Java Script Context.

+ +

Interoperability

+ +

GraalVM supports several other programming languages like Ruby, R, Python, and LLVM languages. +While GraalVM is designed to run Node.js and JavaScript applications, it also provides interoperability between those languages and lets you execute code from or call methods in any of those languages using GraalVM Polyglot APIs.

+ +

To enable Node.js or JavaScript interoperability with other languages, pass the --jvm and --polyglot options. For example:

+ +
node --jvm --polyglot
+Welcome to Node.js v16.14.2.
+Type ".help" for more information.
+> var array = Polyglot.eval("python", "[1,2,42,4]")
+> console.log(array[2]);
+42
+> console.log(Polyglot.eval('R', 'runif(100)')[0]);
+0.8198353068437427
+
+ +

For more information about interoperability with other programming languages, see Polyglot Programming for a general description.

+ +

Interoperability with Java

+ +

To access Java from JavaScript, use Java.type, as in the following example:

+
node --jvm
+> var BigInteger = Java.type('java.math.BigInteger');
+> console.log(BigInteger.valueOf(2).pow(100).toString(16));
+10000000000000000000000000
+
+ +

Vice versa, you can execute JavaScript from Java by embedding the JavaScript context in the Java program:

+
import org.graalvm.polyglot.*;
+import org.graalvm.polyglot.proxy.*;
+
+public class HelloPolyglot {
+
+    static String JS_CODE = "(function myFun(param){console.log('hello '+param);})";
+
+    public static void main(String[] args) {
+        System.out.println("Hello Java!");
+        try (Context context = Context.create()) {
+            Value value = context.eval("js", JS_CODE);
+            value.execute(args[0]);
+        }
+    }
+}
+
+

By wrapping the function definition (()), you return the function immediately. +The source code unit can be represented with a String, as in the example, a file, read from URL, and other means.

+ +

Run the above Java program:

+
javac HelloPolyglot.java
+java HelloPolyglot JavaScript
+
+

This way you can evaluate JavaScript context embedded in Java, but you will not be able to call a function and set parameters in the function directly from the Java code. +The Node.js runtime cannot be embedded into a JVM but has to be started as a separate process.

+ +

For example, save this code as app.js:

+
var HelloPolyglot = Java.type("HelloPolyglot");
+
+HelloPolyglot.main(["from node.js"]);
+
+console.log("done");
+
+

Then start node with the --jvm option to enable interoperability with Java:

+
node --jvm --vm.cp=. app.js
+Hello Java!
+hello from node.js
+done
+
+

By setting the classpath, you instruct node to start a JVM properly. +Both Node.js and JVM then run in the same process and the interoperability works using the same Value classes as above.

+ +

Learn more about language interoperability in the Java Interoperability guide.

+ +

Further documentation

+ +

For additional information, refer to those documentation pages on specific topics around GraalVM JavaScript:

+ + + +

Using GraalVM JavaScript:

+ + +

Legacy environments:

+ + +

Node.js support:

+ + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/languages/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/languages/index.html new file mode 100644 index 0000000..dd9e5cc --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/languages/index.html @@ -0,0 +1,140 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Languages References

+ +

GraalVM provides runtimes for JavaScript, Ruby, Python, and a number of other popular languages. +GraalVM’s polyglot capabilities make it possible to mix multiple programming languages in a single application while eliminating any foreign language call costs.

+ +

If you are mostly interested in GraalVM’s support for a specific language, here you can find the most extensive documentation:

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Compatibility/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Compatibility/index.html new file mode 100644 index 0000000..02a2333 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Compatibility/index.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

LLVM Compatibility

+ +

GraalVM supports LLVM bitcode versions 4.0 to 12.0.1. +It is recommended to use the LLVM toolchain shipped with GraalVM.

+ +

Optimizations Flags

+ +

In contrast to the static compilation model of LLVM languages, in GraalVM the machine code is not directly produced from the LLVM bitcode. +There is an additional dynamic compilation step by the Graal compiler.

+ +

First, the LLVM frontend (e.g., clang) performs optimizations on the bitcode level, and then the Graal compiler does its own optimizations on top of that during dynamic compilation. +Some optimizations are better when done ahead-of-time on bitcode, while other optimizations are better left for the dynamic compilation of the Graal compiler, when profiling information is available.

+ +

The LLVM toolchain that is shipped with GraalVM automatically selects the recommended flags by default.

+ +

Generally, all optimization levels should work, but for a better result, it is recommended to compile the bitcode with the optimization level -O1.

+ +

For cross-language interoperability, the -mem2reg optimization is required. +There are two ways to get that: either compile with at least -O1, or use the opt tool to apply the -mem2reg optimization manually.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Compiling/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Compiling/index.html new file mode 100644 index 0000000..41acaff --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Compiling/index.html @@ -0,0 +1,256 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Compiling to LLVM Bitcode

+ +

GraalVM can execute C/C++, Rust, and other languages that can be compiled to LLVM bitcode. +As the first step, you have to compile a program to LLVM bitcode using some LLVM compiler front end, for example, clang for C and C++, rust for the Rust programing language, etc.

+ +

File Format

+ +

While the GraalVM LLVM runtime can execute plain bitcode files, the preferred format is a native executable with embedded bitcode. +The executable file formats differ on Linux and macOS. +Linux by default uses ELF files. +The bitcode is stored in a section called .llvmbc. +The macOS platform uses Mach-O files. +The bitcode is in the __bundle section of the __LLVM segment.

+ +

Using native executables with embedded bitcode offers two advantages over plain bitcode files. +First, build systems for native projects, for example a Makefile, expect the result to be an executable. +Embedding the bitcode instead of changing the output format improves compatibility with existing projects. +Second, executables allow specifying library dependencies which is not possible with LLVM bitcode. +The GraalVM LLVM runtime utilizes this information to find and load dependencies.

+ +

LLVM Toolchain for Compiling C/C++

+ +

To simplify compiling C/C++ to executables with embedded bitcode, GraalVM comes with a pre-built LLVM toolchain. +The toolchain contains compilers such as clang for C or clang++ for C++, but also other tools that are needed +for building native projects such as a linker (ld), or an archiver (ar) for creating static libraries.

+ +

The LLVM toolchain can be added to GraalVM on demand with the GraalVM Updater tool:

+
$JAVA_HOME/bin/gu install llvm-toolchain
+
+ +

To get the location of the toolchain, use the --print-toolchain-path argument of lli:

+
export LLVM_TOOLCHAIN=$($JAVA_HOME/bin/lli --print-toolchain-path)
+
+ +

See the content of the toolchain path for a list of available tools:

+
ls $LLVM_TOOLCHAIN
+
+ +

Use those tools just as you would for native compilation. For example, save this C code in a file named hello.c:

+
#include <stdio.h>
+
+int main() {
+    printf("Hello from GraalVM!\n");
+    return 0;
+}
+
+ +

Then you can compile hello.c to an executable with embedded LLVM bitcode as follows:

+
$LLVM_TOOLCHAIN/clang hello.c -o hello
+
+ +

The resulting executable, hello, can be executed on GraalVM using lli:

+
$JAVA_HOME/bin/lli hello
+
+ +

External Library Dependencies

+ +

If the bitcode file depends on external libraries, GraalVM will automatically pick up the dependencies from the binary headers. +For example:

+
#include <unistd.h>
+#include <ncurses.h>
+
+int main() {
+    initscr();
+    printw("Hello, Curses!");
+    refresh();
+    sleep(1);
+    endwin();
+    return 0;
+}
+
+ +

This hello-curses.c file can be then compiled and run with:

+
$LLVM_TOOLCHAIN/clang hello-curses.c -lncurses -o hello-curses
+lli hello-curses
+
+ +

Running C++

+ +

For running C++ code, the GraalVM LLVM runtime requires the libc++ standard library from the LLVM project. +The LLVM toolchain shipped with GraalVM automatically links against libc++. +For example, save this code as a hello-c++.cpp file:

+
#include <iostream>
+
+int main() {
+    std::cout << "Hello, C++ World!" << std::endl;
+}
+
+ +

Compile it with clang++ shipped with GraalVM and execute:

+
$LLVM_TOOLCHAIN/clang++ hello-c++.cpp -o hello-c++
+lli hello-c++
+Hello, C++ World!
+
+ +

Running Rust

+ +

The LLVM toolchain, bundled with GraalVM, does not come with the Rust compiler. +To install Rust, run the following in your command prompt, then follow the onscreen instructions:

+
curl https://sh.rustup.rs -sSf | sh
+
+ +

Save this example Rust code in a hello-rust.rs file:

+
fn main() {
+    println!("Hello Rust!");
+}
+
+ +

This can be then compiled to bitcode with the --emit=llvm-bc flag:

+
rustc --emit=llvm-bc hello-rust.rs
+
+ +

To run the Rust program, we have to tell GraalVM where to find the Rust standard libraries:

+
lli --lib $(rustc --print sysroot)/lib/libstd-* hello-rust.bc
+Hello Rust!
+
+ +

Since the Rust compiler is not using the LLVM toolchain shipped with GraalVM, depending on the local Rust installation, an error similar to one of the following might happen:

+
Mismatching target triple (expected x86_64-unknown-linux-gnu, got x86_64-pc-linux-gnu)
+Mismatching target triple (expected x86_64-apple-macosx10.11.0, got x86_64-apple-darwin)
+
+ +

This indicates that the Rust compiler used a different target triple than the LLVM toolchain shipped with GraalVM. +In this particular case, the differences are just different naming conventions across Linux distributions or MacOS versions, there is no real difference. +In that case, the error can be safely ignored:

+ +
lli --experimental-options --llvm.verifyBitcode=false --lib $(rustc --print sysroot)/lib/libstd-* hello-rust.bc
+
+ +

This option should only be used after manually verifying that the target triples are really compatible, i.e., the architecture, operating system, and C library all match. +For example, x86_64-unknown-linux-musl and x86_64-unknown-linux-gnu are really different, the bitcode is compiled for a different C library. +The --llvm.verifyBitcode=false option disables all checks, GraalVM will then try to run the bitcode regardless, which might randomly fail in unexpected ways.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Debugging/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Debugging/index.html new file mode 100644 index 0000000..a7e843e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Debugging/index.html @@ -0,0 +1,160 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Debugging on the GraalVM LLVM Runtime

+ +

The GraalVM LLVM runtime supports source-level (e.g., the C language) debugging with the Chrome Developer Tools using GraalVM’s Chrome Inspector implementation. +This includes support for single-stepping, breakpoints, and inspection of local and global variables.

+ +

To use this feature, make sure to compile your program with debug information enabled by specifying the -g argument when compiling with clang (the LLVM toolchain shipped with GraalVM will automatically enable debug information). +This gives you the ability to step through the program’s source code and set breakpoints in it.

+ +

To start debugging, run lli with the --inspect option:

+
$JAVA_HOME/bin/lli --inspect <bitcode file>
+
+ +

When launched, the inspector will suspend execution at the first instruction of the program and print a link to the console. +Pasting this link into Chrome’s address bar will open the developer tools for you.

+ +

Breakpoints

+ +

Breakpoints can only be set in functions that have already been parsed. +GraalVM defaults to parsing functions in LLVM bitcode files only when they are first being executed. +To instead parse functions eagerly, and be able to set breakpoints also in functions not yet executed, you can use the option lli --llvm.lazyParsing=false.

+ +

Program-defined Breakpoints Using __builtin_debugtrap()

+ +

Program-defined breakpoints using the __builtin_debugtrap function enables you to mark locations in the program at which you explicitly want GraalVM to halt the program and switch to the debugger. +The debugger automatically halts at each call to this function as if a breakpoint were set on the call. +You can use this feature to quickly reach the code you are actually trying to debug without having to first find and set a breakpoint on it after launching your application. +You can also instruct Chrome Inspector not to suspend your program at the first source-level statement being executed. +When doing so, GraalVM will instead execute your program until it reaches a call to __builtin_debugtrap() before invoking the debugger. +To enable this behavior you need to pass the arguments lli --inspect.Suspend=false --inspect.WaitAttached=true.

+ +

Locating Source Files

+ +

Debug information in LLVM bitcode files contains absolute search paths to identify the location of source code. +If the source files did not move, it should be found automatically.

+ +

If the source files moved, or were compiled on a different machine, a search path can be specified using the --inspect.SourcePath=<path> option (multiple paths can be separated by :).

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Interoperability/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Interoperability/index.html new file mode 100644 index 0000000..df8d5f8 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Interoperability/index.html @@ -0,0 +1,269 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Interoperability

+ +

GraalVM supports several other programming languages including JavaScript, Python, Ruby, and R. +While GraalVM’s implementation of lli is designed to run LLVM bitcode, it also provides the API for programming language interoperability that lets you execute code from any other GraalVM-supported language.

+ +

Dynamic languages like JavaScript usually access object members by name. +Since normally names are not preserved in LLVM bitcode, it must be compiled with debug information enabled (the LLVM toolchain shipped with GraalVM will automatically enable debugging information).

+ +

The following example demonstrates how you can use the API for interoperability with other programming languages.

+ +

Define a C struct for points and implement allocation functions in a file named cpart.c:

+ +
// cpart.c
+#include <graalvm/llvm/polyglot.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+struct Point {
+    double x;
+    double y;
+};
+
+POLYGLOT_DECLARE_STRUCT(Point)
+
+void *allocNativePoint() {
+    struct Point *ret = malloc(sizeof(*ret));
+    return polyglot_from_Point(ret);
+}
+
+void *allocNativePointArray(int length) {
+    struct Point *ret = calloc(length, sizeof(*ret));
+    return polyglot_from_Point_array(ret, length);
+}
+
+void freeNativePoint(struct Point *p) {
+    free(p);
+}
+
+void printPoint(struct Point *p) {
+    printf("Point<%f,%f>\n", p->x, p->y);
+}
+
+ +

Make sure LLVM_TOOLCHAIN resolves to the GraalVM LLVM toolchain (lli --print-toolchain-path), and then compile cpart.c (the graalvm-llvm library defines the Polyglot API functions used in the example):

+
$LLVM_TOOLCHAIN/clang -shared cpart.c -lgraalvm-llvm -o cpart.so
+
+ +

You can then access this C/C++ code from other languages. For example, save this JavaScript code in the jspart.js file:

+
// Load and parse the LLVM bitcode into GraalVM
+var cpart = Polyglot.evalFile("llvm" ,"cpart.so");
+
+// Allocate a light-weight C struct
+var point = cpart.allocNativePoint();
+
+// Access it as if it was a JS object
+point.x = 5;
+point.y = 7;
+
+// Pass it back to a native function
+cpart.printPoint(point);
+
+// Allocate an array of structs
+var pointArray = cpart.allocNativePointArray(15);
+
+// Access this array like it was a JS array
+for (var i = 0; i < pointArray.length; i++) {
+    var p = pointArray[i];
+    p.x = i;
+    p.y = 2*i;
+}
+
+cpart.printPoint(pointArray[3]);
+
+// Additionally, pass a JS object to a native function
+cpart.printPoint({x: 17, y: 42});
+
+// Free the unmanaged data objects
+cpart.freeNativePoint(point);
+cpart.freeNativePoint(pointArray);
+
+ +

Finally, run this JavaScript file:

+
js --polyglot jspart.js
+Point<5.000000,7.000000>
+Point<3.000000,6.000000>
+Point<17.000000,42.000000>
+
+ +

Polyglot C API

+ +

There are also lower level API functions for directly accessing polyglot values from C. +See the Polyglot Programming reference and the comments in polyglot.h for more details.

+ +

For example, this program allocates and accesses a Java array from C:

+
#include <stdio.h>
+#include <graalvm/llvm/polyglot.h>
+
+int main() {
+    void *arrayType = polyglot_java_type("int[]");
+    void *array = polyglot_new_instance(arrayType, 4);
+    polyglot_set_array_element(array, 2, 24);
+    int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
+    printf("%d\n", element);
+    return element;
+}
+
+ +

Compile it to LLVM bitcode:

+
$LLVM_TOOLCHAIN/clang polyglot.c -lgraalvm-llvm -o polyglot
+
+ +

Then run it, using the --jvm argument to run in the JVM mode, since there is a Java type used:

+
lli --jvm polyglot
+24
+
+ +

Embedding in Java

+ +

GraalVM can also be used to embed LLVM bitcode in Java host programs.

+ +

For example, write a Java class Polyglot.java that embeds GraalVM to +run the previous example:

+
import java.io.*;
+import org.graalvm.polyglot.*;
+
+class Polyglot {
+    public static void main(String[] args) throws IOException {
+        Context polyglot = Context.newBuilder().
+        		               allowAllAccess(true).build();
+        File file = new File("polyglot");
+        Source source = Source.newBuilder("llvm", file).build();
+        Value cpart = polyglot.eval(source);
+        cpart.execute();
+    }
+}
+
+ +

Compile it and run:

+
javac Polyglot.java
+java Polyglot
+24
+
+ +

See the Embedding Languages reference for more information.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/NativeExecution/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/NativeExecution/index.html new file mode 100644 index 0000000..1971794 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/NativeExecution/index.html @@ -0,0 +1,209 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Interaction of GraalVM with Native Code

+ +

The GraalVM LLVM runtime allows users to run code written in languages that traditionally compile directly to native code. +These languages typically do not require any managed runtime or VM to run. +Therefore, special care is needed to consider the interaction of this code with the managed runtime of GraalVM, in particular if the code is using certain low-level features.

+ +

Limited Access to Low-level System Calls

+ +
    +
  • Signal handling is performed based on the following assumptions: +
      +
    • The managed runtime assumes that it has full control of handling all signals.
    • +
    • Installed signal handlers might behave differently than on native execution.
    • +
    +
  • +
  • Process control and threading is done based on the following aspects: +
      +
    • GraalVM assumes it has full control over threading.
    • +
    • Multi-threading is supported via the pthreads library (for example, pthread_create).
    • +
    • Directly using process related syscalls like clone, fork, vfork, etc. is not supported.
    • +
    • The exec function family is not supported.
    • +
    +
  • +
+ +

Memory Layout

+ +

The memory and stack layout of processes running on GraalVM is different than with direct native execution. +In particular, no assumptions are possible about the relative positions of globals, stack variables and so on.

+ +

Walking the stack is only possible using the GraalVM APIs. +There is a strict separation between code and data. +Self-modifying code will not work. +Reads, writes and pointer arithmetic on pointers to code are not supported.

+ +

Interaction with System Libraries in Native Mode

+ +

In the native execution mode (the default mode), code running on the GraalVM LLVM runtime can do calls to real native libraries (for example, system libraries). +These calls behave similar to JNI calls in Java: they temporarily leave the managed execution environment.

+ +

Since the code executed in these libraries is not under the control of GraalVM, that code can essentially do anything. +In particular, no multicontext isolation applies, and GraalVM APIs like the virtual filesystem are bypassed.

+ +

Note that this applies in particular to most of the standard C library.

+ +

Managed Execution Mode

+ +

The managed mode (enabled with the --llvm.managed option) is a special execution mode where the LLVM runtime runs purely in managed mode, similar to all other GraalVM supported languages.

+ +
+

Note: The managed mode is only available in Oracle GraalVM.

+
+ +

In this mode, by design, it is not allowed to call native code and access native memory. +All memory is managed by the garbage collector, and all code that should be run needs to be compiled to bitcode.

+ +

Pointer arithmetic is only possible to the extent allowed by the C standard. +In particular, overflows are prevented, and it is not possible to access different allocations via out-of-bounds access. +All such invalid accesses result in runtime exceptions rather than in undefined behavior.

+ +

In managed mode, GraalVM simulates a virtual Linux/AMD64 operating system, with musl libc and libc++ as the C/C++ standard libraries. +All code needs to be compiled for that system, and can then be used to run on any architecture or operating system supported by GraalVM. +Syscalls are virtualized and routed through appropriate GraalVM APIs.

+ +

Polyglot Interaction Between Native Code and Managed Languages

+ +

When using polyglot interoperability between LLVM languages (e.g., C/C++) and managed languages (e.g., JavaScript, Python, Ruby), some care must be taken with the manual memory management. +Note that this section only applies to the native mode of execution (the default). +In managed mode (enabled with the --llvm.managed option and only available in Oracle GraalVM), the LLVM runtime itself behaves like a managed language, and the polyglot interaction is the same as between other managed languages.

+ +
    +
  • Garbage collection policies to be considered: +
      +
    • Pointers to objects of managed languages are managed by a garbage collector, therefore they do not need to be freed manually.
    • +
    • On the other hand, pointers to allocations from the LLVM code (e.g., malloc) are not under control of the garbage collector, so they need to be deallocated manually.
    • +
    +
  • +
  • Unmanaged heap policies to be considered: +
      +
    • Native memory (e.g., malloc, data sections, thread locals) is not under the control of a garbage collector.
    • +
    • Pointers to foreign objects controlled by the garbage collector can not be stored in native memory directly.
    • +
    • There are handle functions available to work around this limitation (see graalvm/llvm/handles.h).
    • +
    +
  • +
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Options/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Options/index.html new file mode 100644 index 0000000..5d342c9 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/Options/index.html @@ -0,0 +1,177 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

LLI Command Options

+ +

The syntax to execute programs in LLVM bitcode format with GraalVM is:

+
lli [LLI options] [GraalVM options] [polyglot options] <bitcode file> [program args]
+
+

Here, <bitcode file> is a compiled program with embedded LLVM bitcode.

+ +

The following options to lli are available:

+ +
    +
  • +

    --llvm.managed: enable a managed execution mode for LLVM IR code, which means memory +allocations from LLVM bitcode are done on the managed heap. Learn more from Limitations and Differences to Native Execution. Note: The managed execution mode for LLVM bitcode is not available in GraalVM Community Edition.

    +
  • +
  • +

    --print-toolchain-path: print the path of the LLVM toolchain bundled with GraalVM. +This directory contains compilers and tools that can be used to compile C/C++ programs +to LLVM bitcode for execution on GraalVM.

    +
  • +
  • +

    --print-toolchain-api-tool <tool>: print the path of a tool from the LLVM toolchain. +Valid values for <tool> are CC, CXX, LD, AR, NM, OBJCOPY, OBJDUMP, +RANLIB, READELF, READOBJ, or STRIP.

    +
  • +
  • +

    --print-toolchain-api-paths <path>: print a search path for the LLVM toolchain. +Valid values for <path> are PATH and LD_LIBRARY_PATH.

    +
  • +
  • +

    --print-toolchain-api-identifier: print a unique identifier of the LLVM toolchain. +Different modes of the LLVM runtime (e.g., --llvm.managed) might require compilation +of bitcode with a different LLVM toolchain. This identifier can be used as a stable +directory name to store build outputs for different modes.

    +
  • +
  • +

    -L <path>/--llvm.libraryPath=<path>: a list of paths where GraalVM will search for +library dependencies. Paths are delimited by :.

    +
  • +
  • +

    --lib <libs>/--llvm.libraries=<libs>: a list of libraries to load in addition to +the dependencies of the main binary. Files with a relative path are looked up relative +to llvm.libraryPath. Entries are delimited by :.

    +
  • +
  • +

    --version: print the version and exit.

    +
  • +
  • +

    --version:graalvm: print the GraalVM version information and exit.

    +
  • +
+ +

Expert and Diagnostic Options

+

Use --help and --help:<topic> to get a full list of options.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/index.html new file mode 100644 index 0000000..a6dea46 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/llvm/index.html @@ -0,0 +1,189 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

GraalVM LLVM Runtime

+ +

The GraalVM LLVM runtime can execute programming languages that can be transformed to LLVM bitcode. +This includes languages like C/C++, Fortran and others.

+ +

In contrast to static compilation that is normally used for LLVM-based languages, GraalVM’s implementation of the lli tool first interprets LLVM bitcode and then dynamically compiles the hot parts of the program using the Graal compiler. +This allows seamless interoperability with the dynamic languages supported by GraalVM.

+ +

Install LLVM Runtime

+ +

Since GraalVM 22.2, the LLVM runtime is packaged in a separate GraalVM component. It can be installed with GraalVM Updater:

+ +
$JAVA_HOME/bin/gu install llvm
+
+ +

This installs GraalVM’s implementation of lli in the $JAVA_HOME/bin directory. +With the LLVM runtime installed, you can execute programs in LLVM bitcode format on GraalVM.

+ +

Additionally to installing the LLVM runtime, you can add the LLVM toolchain:

+ +
gu install llvm-toolchain
+export LLVM_TOOLCHAIN=$(lli --print-toolchain-path)
+
+ +

Now you can compile C/C++ code to LLVM bitcode using clang shipped with GraalVM via a prebuilt LLVM toolchain.

+ +

Run LLVM Bitcode on GraalVM

+ +

To run LLVM-based languages on GraalVM, the binaries need to be compiled with embedded bitcode. +The Compiling guide provides information on how to compile a program to LLVM bitcode and what file format is expected.

+ +

The syntax to execute programs in LLVM bitcode format on GraalVM is:

+
lli [LLI options] [GraalVM options] [polyglot options] <bitcode file> [program args]
+
+ +

Here, <bitcode file> is a compiled program with embedded LLVM bitcode. +See LLI Command Options or use lli --help for options explanations.

+ +

For example, put this C code into a file named hello.c:

+
#include <stdio.h>
+
+int main() {
+    printf("Hello from GraalVM!\n");
+    return 0;
+}
+
+ +

Then compile hello.c to an executable hello with embedded LLVM bitcode and run it as follows:

+
$LLVM_TOOLCHAIN/clang hello.c -o hello
+lli hello
+
+ +

Note: LLVM bitcode is platform-dependent. +The program must be compiled to bitcode for an appropriate platform.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ARM64/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ARM64/index.html new file mode 100644 index 0000000..7ac23d0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ARM64/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Agent/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Agent/index.html new file mode 100644 index 0000000..892bb28 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Agent/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Build-Overview/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Build-Overview/index.html new file mode 100644 index 0000000..65d8e76 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Build-Overview/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/BuildConfiguration/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/BuildConfiguration/index.html new file mode 100644 index 0000000..216d10e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/BuildConfiguration/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/BuildOutput/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/BuildOutput/index.html new file mode 100644 index 0000000..d818196 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/BuildOutput/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/C-API/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/C-API/index.html new file mode 100644 index 0000000..24b28b8 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/C-API/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/CertificateManagement/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/CertificateManagement/index.html new file mode 100644 index 0000000..dd17bc5 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/CertificateManagement/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ClassInitialization/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ClassInitialization/index.html new file mode 100644 index 0000000..f7deace --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ClassInitialization/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/CodeStyle/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/CodeStyle/index.html new file mode 100644 index 0000000..8159290 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/CodeStyle/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/DebugInfo/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/DebugInfo/index.html new file mode 100644 index 0000000..b2aa447 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/DebugInfo/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/DynamicProxy/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/DynamicProxy/index.html new file mode 100644 index 0000000..bc23ea0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/DynamicProxy/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ExperimentalAgentOptions/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ExperimentalAgentOptions/index.html new file mode 100644 index 0000000..1269469 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ExperimentalAgentOptions/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/FAQ/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/FAQ/index.html new file mode 100644 index 0000000..1ca047f --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/FAQ/index.html @@ -0,0 +1,182 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+
+
+
+ +

This documentation is for an old GraalVM version. See the latest version.

+
+
+
+
+
+
+ Table of Contents + +

Frequently Asked Questions

+ +

How is GraalVM Native Image licensed?

+ +

The Native Image technology is distributed as a separate installable to GraalVM. +Native Image for GraalVM Community Edition is licensed under the GPL 2 with Classpath Exception.

+ +

Native Image for GraalVM Enterprise Edition is licensed under the Oracle Technology Network License Agreement for GraalVM Enterprise Edition.

+ +

What is the licence for artifacts produced by Native Image? For example, compiling an AWT application produces an awt.dll/so file. Under what license can I ship it?

+ +

Everything that is included in a product (GraalVM Native Image) or produced by a product (a native executable, a native shared library, etc.) is covered by the same licence. That is Oracle Technology Network License Agreement for GraalVM Enterprise Edition.

+ +

Native Image doesn’t work on my Windows 10?

+ +

Make sure you execute the native-image command from the x64 Native Tools Command Prompt. +This is the prerequisite for Windows: installing Visual Studio and Microsoft Visual C++ (MSVC) with the Windows 10 SDK. +You can use Visual Studio 2017 version 15.9 or later.

+ +

Check this link for more information and step-by-step instructions.

+ + + +

A fully static native executable gives you the most flexibility to choose a base image - it can run on anything including a FROM scratch image. +A mostly static native executable requires a container image that provides glibc, but has no additional requirements. +In both cases, choosing the base image mostly depends on what your particular executable needs without having to worry about run-time library dependencies.

+ +

Does AWS provide support by Native Image? OR Can I deploy Native Image in AWS Lambda?

+ +

Yes, you can. AWS SDK for Java 2.x (version 2.16.1 or later) has out-of-the-box support for GraalVM Native Image compilation.

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/HostedvsRuntimeOptions/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/HostedvsRuntimeOptions/index.html new file mode 100644 index 0000000..889495c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/HostedvsRuntimeOptions/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ImplementingNativeMethodsInJavaWithSVM/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ImplementingNativeMethodsInJavaWithSVM/index.html new file mode 100644 index 0000000..6c01625 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/ImplementingNativeMethodsInJavaWithSVM/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/JCASecurityServices/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/JCASecurityServices/index.html new file mode 100644 index 0000000..c25c963 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/JCASecurityServices/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/JFR/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/JFR/index.html new file mode 100644 index 0000000..3efef88 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/JFR/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/JNI/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/JNI/index.html new file mode 100644 index 0000000..9c34b99 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/JNI/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/LLVMBackend/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/LLVMBackend/index.html new file mode 100644 index 0000000..d29e660 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/LLVMBackend/index.html @@ -0,0 +1,193 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

LLVM Backend for Native Image

+ +

Native Image provides an alternative backend that uses the LLVM intermediate representation and the LLVM compiler to produce native executables. +This LLVM backend enables users to target alternative architectures that are not directly supported by GraalVM Native Image. However, this approach introduces some performance costs.

+ +

Installing and Usage

+ +

The LLVM Backend is shipped as a separate component to Native Image and can be installed with GraalVM Updater:

+
gu install native-image-llvm-backend
+
+ +

To enable the LLVM backend, pass the -H:CompilerBackend=llvm option to the native-image command.

+ +

Code Generation Options

+ +
    +
  • -H:+SpawnIsolates: enables isolates. (These are disabled by default when using the LLVM backend because they incur a performance penalty.)
  • +
  • -H:+BitcodeOptimizations: enables aggressive optimizations at the LLVM bitcode level. This is experimental and may cause bugs.
  • +
+ +

Debugging Options

+ +
    +
  • -H:TempDirectory=: specifies where the files generated by Native Image will be saved. The LLVM files are saved under SVM-<timestamp>/llvm in this folder.
  • +
  • -H:LLVMMaxFunctionsPerBatch=: specifies the maximum size of a compilation batch*. Setting it to 1 compiles every function separately, 0 compiles everything as a single batch.
  • +
  • -H:DumpLLVMStackMap=: specifies a file in which to dump debugging information, including a mapping between compiled functions and the name of the corresponding bitcode file.
  • +
+ +

About batches: LLVM compilation happens in four phases:

+
    +
  1. LLVM bitcode files (named f0.bc, f1.bc, etc.,) are created for each function.
  2. +
  3. The bitcode files are linked into batches (named b0.bc, b1.bc, etc.). This phase is skipped when -H:LLVMMaxFunctionsPerBatch=1 is specified.
  4. +
  5. The batches are optimized (into b0o.bc, b1o.bc, etc.,) and then compiled (into b0.o, b1.o, etc.).
  6. +
  7. The compiled batches are linked into a single object file (llvm.o), which is then linked into the final executable.
  8. +
+ +

How to Add a Target Architecture to GraalVM Using LLVM Backend

+ +

An interesting use case for the LLVM backend is to target a new architecture without having to implement a completely new backend for Native Image. +The following are the necessary steps to achieve this at the moment.

+ +

Target-Specific LLVM Settings

+ +

There are a few instances where the GraalVM code has to go deeper than the target-independent nature of LLVM. +These are most notably inline assembly snippets to implement direct register accesses and direct register jumps (for trampolines), as well as precisions about the structure of the stack frames of the code emitted by LLVM. +This represents less than a dozen simple values to be set for each new target.

+ +

(Complete set of values for AArch64)

+ +

LLVM Statepoint Support

+ +

While the LLVM backend uses mostly common, well-supported features of LLVM, garbage collection support implies the use of statepoint intrinsics, an experimental feature of LLVM. +This means that supporting a new architecture will require the implementation of statepoints in LLVM for the requested target. +As most of the statepoint logic is handled at the bitcode level, i.e., at a target-independent stage, this is mostly a matter of emitting the right type of calls to lower the statepoint intrinsics.

+ +

(Implementation of statepoints for AArch64)

+ +

Object File Support

+ +

The data section for programs created with the LLVM backend of the Graal compiler is emitted independently from the code, which is handled by LLVM. +This means that the Graal compiler needs an understanding of object file relocations for the target architecture to be able to link the LLVM-compiled code with the GraalVM-generated data section.

+ +

(see ELFMachine$ELFAArch64Relocations for an example)

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Limitations/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Limitations/index.html new file mode 100644 index 0000000..7ac23d0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Limitations/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Logging/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Logging/index.html new file mode 100644 index 0000000..724427e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Logging/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/MemoryManagement/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/MemoryManagement/index.html new file mode 100644 index 0000000..7f84937 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/MemoryManagement/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/NativeImageHeapdump/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/NativeImageHeapdump/index.html new file mode 100644 index 0000000..a6c6c1c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/NativeImageHeapdump/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Options/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Options/index.html new file mode 100644 index 0000000..ea47374 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Options/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/PGO/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/PGO/index.html new file mode 100644 index 0000000..9c24ea6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/PGO/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Properties/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Properties/index.html new file mode 100644 index 0000000..5b9a1b8 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Properties/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Reflection/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Reflection/index.html new file mode 100644 index 0000000..ba32ef2 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Reflection/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Reports/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Reports/index.html new file mode 100644 index 0000000..c1a85b3 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Reports/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Resources/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Resources/index.html new file mode 100644 index 0000000..846018c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/Resources/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/StaticImages/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/StaticImages/index.html new file mode 100644 index 0000000..c277882 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/StaticImages/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/URLProtocols/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/URLProtocols/index.html new file mode 100644 index 0000000..40729ef --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/URLProtocols/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/basics/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/basics/index.html new file mode 100644 index 0000000..b3a96e3 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/basics/index.html @@ -0,0 +1,313 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Basics

+ +

Native Image is written in Java and takes Java bytecode as input to produce a standalone binary (an executable, or a shared library). +During the process of producing a binary, Native Image can run user code. +Finally, Native Image links compiled user code, parts of the Java runtime (for example, the garbage collector, threading support), and the results of code execution into the binary.

+ +

We refer to this binary as a native executable, or a native image. +We refer to the utility that produces the binary as the native-image builder, or the native-image generator.

+ +

To clearly distinguish between code executed during the native image build, and code executed during the native image execution, we refer to the difference between the two as build time and run time.

+ +

To produce a minimal image, Native Image employs a process called static analysis.

+ +

Table of Contents

+ + + +

Build Time vs Run Time

+ +

During the image build, Native Image may execute user code. +This code can have side effects, such as writing a value to a static field of a class. +We say that this code is executed at build time. +Values written to static fields by this code are saved in the image heap. +Run time refers to code and state in the binary when it is executed.

+ +

The easiest way to see the difference between these two concepts is through configurable class initialization. +In Java, a class is initialized when it is first used. +Every Java class used at build time is said to be build-time initialized. +Note that merely loading a class does not necessarily initialize it. +The static class initializer of build-time initialized classes executes on the JVM running the image build. +If a class is initialized at build time, its static fields are saved in the produced binary. +At run time, using such a class for the first time does not trigger class initialization.

+ +

Users can trigger class initialization at build time in different ways:

+
    +
  • By passing --initialize-at-build-time=<class> to the native-image builder.
  • +
  • By using a class in the static initializer of a build-time initialized class.
  • +
+ +

Native Image will initialize frequently used JDK classes at image build time, for example, java.lang.String, java.util.**, etc. +Note that build-time class initialization is an expert feature. +Not all classes are suitable for build-time initialization.

+ +

The following example demonstrates the difference between build-time and run-time executed code:

+
public class HelloWorld {
+    static class Greeter {
+        static {
+            System.out.println("Greeter is getting ready!");
+        }
+        
+        public static void greet() {
+          System.out.println("Hello, World!");
+        }
+    }
+
+  public static void main(String[] args) {
+    Greeter.greet();
+  }
+}
+
+ +

Having saved the code in a file named HelloWorld.java, we compile and run the application on the JVM:

+ +
javac HelloWorld.java
+java HelloWorld 
+Greeter is getting ready!
+Hello, World!
+
+ +

Now we build a native image of it, and then execute:

+
native-image HelloWorld
+========================================================================================================================
+GraalVM Native Image: Generating 'helloworld' (executable)...
+========================================================================================================================
+...
+Finished generating 'helloworld' in 14.9s.
+
+
./helloworld 
+Greeter is getting ready!
+Hello, World!
+
+

HelloWorld started up and invoked Greeter.greet. +This caused Greeter to initialize, printing the message Greeter is getting ready!. +Here we say the class initializer of Greeter is executed at image run time.

+ +

What would happen if we tell native-image to initialize Greeter at build time?

+ +
native-image HelloWorld --initialize-at-build-time=HelloWorld\$Greeter
+========================================================================================================================
+GraalVM Native Image: Generating 'helloworld' (executable)...
+========================================================================================================================
+Greeter is getting ready!
+[1/7] Initializing...                                                                                    (3.1s @ 0.15GB)
+ Version info: 'GraalVM dev Java 11 EE'
+ Java version info: '11.0.15+4-jvmci-22.1-b02'
+ C compiler: gcc (linux, x86_64, 9.4.0)
+ Garbage collector: Serial GC
+...
+Finished generating 'helloworld' in 13.6s.
+./helloworld 
+Hello, World!
+
+ +

We saw Greeter is getting ready! printed during the image build. +We say the class initializer of Greeter executed at image build time. +At run time, when HelloWorld invoked Greeter.greet, Greeter was already initialized. +The static fields of classes initialized during the image build are stored in the image heap.

+ +

Native Image Heap

+ +

The Native Image heap, also called the image heap, contains:

+
    +
  • Objects created during the image build that are reachable from application code.
  • +
  • java.lang.Class objects of classes used in the native image.
  • +
  • Object constants embedded in method code.
  • +
+ +

When native image starts up, it copies the initial image heap from the binary.

+ +

One way to include objects in the image heap is to initialize classes at build time:

+
class Example {
+    private static final String message;
+    
+    static {
+        message = System.getProperty("message");
+    }
+
+    public static void main(String[] args) {
+        System.out.println("Hello, World! My message is: " + message);
+    }
+}
+
+ +

Now we compile and run the application on the JVM:

+
javac Example.java
+java -Dmessage=hi Example
+Hello, World! My message is: hi
+
+
java -Dmessage=hello Example 
+Hello, World! My message is: hello
+
+
java Example
+Hello, World! My message is: null
+
+ +

Now examine what happens when we build a native image in which the Example class is initialized at build time:

+
native-image Example --initialize-at-build-time=Example -Dmessage=native
+================================================================================
+GraalVM Native Image: Generating 'example' (executable)...
+================================================================================
+...
+Finished generating 'example' in 19.0s.
+
+
./example 
+Hello, World! My message is: native
+
+
./example -Dmessage=aNewMessage
+Hello, World! My message is: native
+
+ +

The class initializer of the Example class was executed at image build time. +This created a String object for the message field and stored it inside the image heap.

+ +

Static Analysis

+ +

Static analysis is a process that determines which program elements (classes, methods and fields) are used by an application. +These elements are also referred to as reachable code. +The analysis itself has two parts:

+
    +
  • Scanning the bytecodes of a method to determine what other elements are reachable from it.
  • +
  • Scanning the root objects in the native image heap (i.e., static fields) to determine which classes are reachable from them. +It starts from the entry points of the application (i.e., the main method). +The newly discovered elements are iteratively scanned until further scanning yields no additional changes in element’s reachability.
  • +
+ +

Only reachable elements are included in the final image. +Once a native image is built, no new elements can be added at run time, for example, through class loading. +We refer to this constraint as the closed-world assumption.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/contributing/CodeStyle/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/contributing/CodeStyle/index.html new file mode 100644 index 0000000..6317612 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/contributing/CodeStyle/index.html @@ -0,0 +1,184 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Code Style

+ +

To ensure the quality of code and that all contributors follow the same standards, we established a ruleset that grew over time and proved to be useful. Note that the rules are open for discussion!

+ +

Source Code Formatting

+ +

The IDE projects generated with mx ideinit are configured with strict formatting rules. +In Eclipse, when a file is saved, it is automatically formatted according to these rules. +The configuration includes special comments which can be used to relax checks in particular regions of code.

+ +

Source code formatting can be disabled with special comments:

+
//@formatter:off
+
+//@formatter:on
+
+

Comment reformatting can be disabled like this:

+
/*-
+ *
+ */
+
+ +

Checks with Checkstyle

+ +

Checkstyle is used to verify adherence to the style rules. +It can be run manually with mx checkstyle.

+ +

The default Checkstyle rules are defined in src/com.oracle.svm.core/.checkstyle_checks.xml and define various special comments, including

+
//Checkstyle: stop method name check
+
+//Checkstyle: resume method name check
+
+

and similar commands for other checks that can be disabled (including general stop and resume commands). +Of course, ensuring a reasonable use of these comments is a matter for code review.

+ +

If a project requires a different set of Checkstyle rules, this can be specified in mx.substratevm/suite.py by changing the value of the project’s checkstyle attribute (which, by default, references com.oracle.svm.core). +Specific code files can be excluded from checkstyle on a directory granularity with a file src/<project name>/.checkstyle.exclude. +Such an exclusion file must contain one directory per line, with paths relative to the project root. +The file must be explicitly added with git add because git will ignore it by default.

+ +

When pulling a changeset which adds or removes Checkstyle XML files, the IDE might show inappropriate style warnings or errors. +This is resolved by running mx ideinit and cleaning the affected projects.

+ +

IDE Integration

+ +

IDE plugins can be helpful in adhering to style rules. +Some examples are:

+ + + +

See the documentation on IDE integration for further suggestions.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/contributing/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/contributing/index.html new file mode 100644 index 0000000..bae0d5d --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/contributing/index.html @@ -0,0 +1,141 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Contributing to Native Image

+ +

GraalVM is an open source project, so is Substrate VM - the codename for the Native Image technology. +We welcome contributors to the core!

+ +

There are two common ways to contribute:

+ + + +

If you want to contribute changes to Native Image core, you must adhere to the project’s standards of quality. For more information, see Native Image Code Style.

+ +

If you would like to ensure complete compatibility of your library with Native Image, consider contributing your library metadata to the GraalVM Reachability Metadata Repository. +Follow contributing rules for this repository. +Using this open source repository, users can share the burden of maintaining metadata for third-party dependencies.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/DebugInfo/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/DebugInfo/index.html new file mode 100644 index 0000000..f3f326e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/DebugInfo/index.html @@ -0,0 +1,1086 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Debug Info Feature

+ +

To add debug information to a generated native image, provide the -g option to the native-image builder:

+
native-image -g Hello
+
+ +

The -g flag instructs native-image to generate debug information. +The resulting image will contain debug records in a format the GNU Debugger (GDB) understands. +Additionally, you can pass -O0 to the builder which specifies that no compiler optimizations should be performed. +Disabling all optimizations is not required, but in general it makes the debugging experience better.

+ +

Debug information is not just useful to the debugger. It can also be used by the Linux performance profiling tools perf and valgrind to correlate execution statistics such as CPU utilization or cache misses with specific, named Java methods and even link them to individual lines of Java code in the original Java source file.

+ +

By default, debug info will only include details of some of the values of parameters and local variables. +This means that the debugger will report many parameters and local variables as being undefined. If you pass -O0 to the builder then full debug information will be included. +If you +want more parameter and local variable information to be included when employing higher +levels of optimization (-O1 or, the default, -O2) you need to pass an extra command +line flag to the native-image command

+ +
native-image -g -H:+SourceLevelDebug Hello
+
+ +

Enabling debuginfo with flag -g does not make any difference to how a generated +native image is compiled and does not affect how fast it executes nor how much memory it uses at runtime. +However, it can significantly increase the size of the generated image on disk. Enabling full parameter +and local variable information by passing flag -H:+SourceLevelDebug can cause a program to be compiled +slightly differently and for some applications this can slow down execution.

+ +

The basic perf report command, which displays a histogram showing percentage execution time in each Java method, only requires passing flags -g and -H:+SourceLevelDebug to the native-image command. +However, more sophisticated uses of perf (i.e. perf annotate) and use of +valgrind requires debug info to be supplemented with linkage symbols identifying compiled Java methods. +Java method symbols are omitted from the generated native image by default but they can be retained achieved by passing one extra flag to the native-image command

+ +
native-image -g -H:+SourceLevelDebug -H:-DeleteLocalSymbols Hello
+
+ +

Use of this flag will result in a small increase in the size of the +resulting image file.

+ +
+

Note: Native Image debugging currently works on Linux with initial support for macOS. The feature is experimental.

+
+ +
+

Note: Debug info support for perf and valgrind on Linux is an experimental feature.

+
+ +

Table of Contents

+ + + +

Source File Caching

+ +

The -g option also enables caching of sources for any JDK runtime classes, GraalVM classes, and application classes which can be located when generating a native executable. +By default, the cache is created alongside the generated binary in a subdirectory named sources. +If a target directory for the native executable is specified using option -H:Path=... then the cache is also relocated under that same target. +Use a command line option to provide an alternative path to sources and to configure source file search path roots for the debugger. +Files in the cache are located in a directory hierarchy that matches the file path information included in the debug records of the native executable. +The source cache should contain all the files needed to debug the generated binary and nothing more. +This local cache provides a convenient way of making just the necessary sources available to the debugger or IDE when debugging a native executable.

+ +

The implementation tries to be smart about locating source files. +It uses the current JAVA_HOME to locate the JDK src.zip when searching for JDK runtime sources. +It also uses entries in the classpath to suggest locations for GraalVM source files and application source files (see below for precise details of the scheme used to identify source locations). +However, source layouts do vary and it may not be possible to find all sources. +Hence, users can specify the location of source files explicitly on the command line using option DebugInfoSourceSearchPath:

+ +
javac --source-path apps/greeter/src \
+    -d apps/greeter/classes org/my/greeter/*Greeter.java
+javac -cp apps/greeter/classes \
+    --source-path apps/hello/src \
+    -d apps/hello/classes org/my/hello/Hello.java
+native-image -g \
+    -H:-SpawnIsolates \
+    -H:DebugInfoSourceSearchPath=apps/hello/src \
+    -H:DebugInfoSourceSearchPath=apps/greeter/src \
+    -cp apps/hello/classes:apps/greeter/classes org.my.hello.Hello
+
+ +

The DebugInfoSourceSearchPath option can be repeated as many times as required to notify all the target source locations. +The value passed to this option can be either an absolute or relative path. +It can identify either a directory, a source JAR, or a source ZIP file. +It is also possible to specify several source roots at once using a comma separator:

+ +
native-image -g \
+    -H:DebugInfoSourceSearchPath=apps/hello/target/hello-sources.jar,apps/greeter/target/greeter-sources.jar \
+    -cp apps/target/hello.jar:apps/target/greeter.jar \
+    org.my.Hello
+
+ +

By default, the cache of application, GraalVM, and JDK sources is created in a directory named sources. +The DebugInfoSourceCacheRoot option can be used to specify an alternative path, which can be absolute or relative. +In the latter case the path is interpreted relative to the target directory for the generated executable specified via option -H:Path (which defaults to the current working directory). +As an example, the following variant of the previous command specifies an absolute temporary directory path constructed using the current process id:

+ +
SOURCE_CACHE_ROOT=/tmp/$$/sources
+native-image -g \
+    -H:-SpawnIsolates \
+    -H:DebugInfoSourceCacheRoot=$SOURCE_CACHE_ROOT \
+    -H:DebugInfoSourceSearchPath=apps/hello/target/hello-sources.jar,apps/greeter/target/greeter-sources.jar \
+    -cp apps/target/hello.jar:apps/target/greeter.jar \
+    org.my.Hello
+
+

The resulting cache directory will be something like /tmp/1272696/sources.

+ +

If the source cache path includes a directory that does not yet exist, it will be created during population of the cache.

+ +

Note that in all the examples above the DebugInfoSourceSearchPath options are actually redundant. +In the first case, the classpath entries for apps/hello/classes and apps/greeter/classes will be used to derive the default search roots apps/hello/src and apps/greeter/src. +In the second case, the classpath entries for apps/target/hello.jar and apps/target/greeter.jar will be used to derive the default search roots apps/target/hello-sources.jar and apps/target/greeter-sources.jar.

+ +

Supported Features

+ +

The currently supported features include:

+ +
    +
  • break points configured by file and line, or by method name
  • +
  • single stepping by line including both into and over function calls
  • +
  • stack backtraces (not including frames detailing inlined code)
  • +
  • printing of primitive values
  • +
  • structured (field by field) printing of Java objects
  • +
  • casting/printing objects at different levels of generality
  • +
  • access through object networks via path expressions
  • +
  • reference by name to methods and static field data
  • +
  • reference by name to values bound to parameter and local vars
  • +
  • reference by name to class constants
  • +
+ +

Note that single stepping within a compiled method includes file and line number info for inlined code, including inlined GraalVM methods. +So, GDB may switch files even though you are still in the same compiled method.

+ +

Special considerations for debugging Java from GDB

+ +

GDB does not currently include support for Java debugging. +In consequence, debug capability has been implemented by generating debug info that models the Java program as an equivalent C++ program. +Java class, array and interface references are actually pointers to records that contain the relevant field/array data. +In the corresponding C++ model the Java name is used to label the underlying C++ (class/struct) layout types and Java references appear as pointers.

+ +

So, for example in the DWARF debug info model java.lang.String identifies a C++ class. +This class layout type declares the expected fields like hash of type int and value of type byte[] and methods like String(byte[]), charAt(int), etc. However, the copy constructor which appears in Java as String(String) appears in gdb with the signature String(java.lang.String *).

+ +

The C++ layout class inherits fields and methods from class (layout) type java.lang.Object using C++ public inheritance. +The latter in turn inherits standard oop (ordinary object pointer) header fields from a special struct class named _objhdr which includes two fields. The first field is called +hub and its type is java.lang.Class * i.e. it is a pointer to the object’s +class. The second field is called idHash and has type int. It stores an +identity hashcode for the object.

+ +

The ptype command can be used to print details of a specific type. +Note that the Java type name must be specified in quotes because to escape the embedded . characters.

+ +
(gdb) ptype 'java.lang.String'
+type = class java.lang.String : public java.lang.Object {
+  private:
+    byte [] *value;
+    int hash;
+    byte coder;
+
+  public:
+    void String(byte [] *);
+    void String(char [] *);
+    void String(byte [] *, java.lang.String *);
+    . . .
+    char charAt(int);
+    . . .
+    java.lang.String * concat(java.lang.String *);
+    . . .
+}
+
+ +

The ptype command can also be used to identify the static type of a Java +data value. The current example session is for a simple hello world +program. Main method Hello.main is passed a single parameter +args whose Java type is String[]. If the debugger is stopped at +entry to main we can use ptype to print the type of args.

+ +
(gdb) ptype args
+type = class java.lang.String[] : public java.lang.Object {
+  public:
+    int len;
+    java.lang.String *data[0];
+} *
+
+ +

There are a few details worth highlighting here. Firstly, the debugger +sees a Java array reference as a pointer type, as it does every Java object +reference.

+ +

Secondly, the pointer points to a structure, actually a C++ class, +that models the layout of the Java array using an integer length field +and a data field whose type is a C++ array embedded into the block of +memory that models the array object.

+ +

Elements of the array data field are references to the base type, in +this case pointers to java.lang.String. The data array has a nominal +length of 0. However, the block of memory allocated for the String[] +object actually includes enough space to hold the number of pointers +determined by the value of field len.

+ +

Finally, notice that the C++ class java.lang.String[] inherits from +the C++ class java.lang.Object. So, an array is still also an object. +In particular, as we will see when we print the object contents, this +means that every array also includes the object header fields that all +Java objects share.

+ +

The print command can be used to display the object reference as a memory +address.

+ +
(gdb) print args
+$1 = (java.lang.String[] *) 0x7ffff7c01130
+
+ +

It can also be used to print the contents of the object field by field. This +is achieved by dereferencing the pointer using the * operator.

+ +
(gdb) print *args
+$2 = {
+  <java.lang.Object> = {
+    <_objhdr> = {
+      hub = 0xaa90f0,
+      idHash = 0
+    }, <No data fields>}, 
+  members of java.lang.String[]:
+  len = 1,
+  data = 0x7ffff7c01140
+}
+
+ +

The array object contains embedded fields inherited from class +_objhdr via parent class Object. _objhdr is a synthetic type +added to the deubg info to model fields that are present at the start +of all objects. They include hub which is a reference to the object’s +class and hashId a unique numeric hash code.

+ +

Clearly, the debugger knows the type (java.lang.String[]) and location +in memory (0x7ffff7c010b8) of local variable args. It also knows about +the layout of the fields embedded in the referenced object. This means +it is possible to use the C++ . and -> operators in debugger commands +to traverse the underlying object data structures.

+ +
(gdb) print args->data[0]
+$3 = (java.lang.String *) 0x7ffff7c01160
+(gdb) print *args->data[0]
+$4 = {
+   <java.lang.Object> = {
+     <_objhdr> = {
+      hub = 0xaa3350
+     }, <No data fields>},
+   members of java.lang.String:
+   value = 0x7ffff7c01180,
+   hash = 0,
+   coder = 0 '\000'
+ }
+(gdb) print *args->data[0]->value
+$5 = {
+  <java.lang.Object> = {
+    <_objhdr> = {
+      hub = 0xaa3068,
+      idHash = 0
+    }, <No data fields>}, 
+  members of byte []:
+  len = 6,
+  data = 0x7ffff7c01190 "Andrew"
+}
+
+ +

Returning to the hub field in the object header it was +mentioned before that this is actually a reference to the object’s +class. This is actually an instance of Java type java.lang.Class. +Note that the field is typed by gdb using a pointer +to the underlying C++ class (layout) type.

+ +
(gdb) print args->hub
+$6 = (java.lang.Class *) 0xaa90f0
+
+ +

All classes, from Object downwards inherit from a common, automatically generated header type _objhdr. +It is this header type which includes the hub field:

+ +
(gdb) ptype _objhdr
+type = struct _objhdr {
+    java.lang.Class *hub;
+    int idHash;
+}
+
+(gdb) ptype 'java.lang.Object'
+type = class java.lang.Object : public _objhdr {
+  public:
+    void Object(void);
+    . . .
+
+ +

The fact that all objects have a common header pointing to a class +makes it possible to perform a simple test to decide if an address +is an object reference and, if so, what the object’s class is. +Given a valid object reference it is always possible to print the +contents of the String referenced from the hub’s name field.

+ +

Note that as a consequence, this allows every object observed by the debugger +to be downcast to its dynamic type. i.e. even if the debugger only sees the static +type of e.g. java.nio.file.Path we can easily downcast to the dynamic type, which +might be a subtype such as jdk.nio.zipfs.ZipPath, thus making it possible to inspect +fields that we would not be able to observe from the static type alone. +First the value is cast to an object reference. +Then a path expression is used to dereference through the the hub field and the hub’s name field to the byte[] value array located in the name String.

+ +
(gdb) print/x ((_objhdr *)$rdi)
+$7 = (_objhdr *) 0x7ffff7c01130
+(gdb) print *$7->hub->name->value
+$8 = {
+  <java.lang.Object> = {
+    <_objhdr> = {
+      hub = 0xaa3068,
+      idHash = 178613527
+    }, <No data fields>}, 
+   members of byte []:
+   len = 19,
+  data = 0x8779c8 "[Ljava.lang.String;"
+ }
+
+ +

The value in register rdi is obviously a reference to a String array. +Indeed, this is no coincidence. The example session has stopped at a break +point placed at the entry to Hello.main and at that point the value for +the String[] parameter args will be located in register rdi. Looking +back we can see that the value in rdi is the same value as was printed by +command print args.

+ +

A simpler command which allows just the name of the hub object to be printed is as follows:

+ +
(gdb) x/s $7->hub->name->value->data
+798:	"[Ljava.lang.String;"
+
+ +

Indeed it is useful to define a gdb command hubname_raw to execute this operation on an arbitrary raw memory address.

+ +
define hubname_raw
+  x/s (('java.lang.Object' *)($arg0))->hub->name->value->data
+end
+
+(gdb) hubname_raw $rdi
+0x8779c8:	"[Ljava.lang.String;"
+
+ +

Attempting to print the hub name for an invalid reference will fail +safe, printing an error message.

+ +
(gdb) p/x $rdx
+$5 = 0x2
+(gdb) hubname $rdx
+Cannot access memory at address 0x2
+
+ +

If gdb already knows the Java type for a reference it can be printed without casting using a simpler version of the hubname command. +For example, the String array retrieved above as $1 has a known type.

+ +
(gdb) ptype $1
+type = class java.lang.String[] : public java.lang.Object {
+    int len;
+    java.lang.String *data[0];
+} *
+
+define hubname
+  x/s (($arg0))->hub->name->value->data
+end
+
+(gdb) hubname $1
+0x8779c8:	"[Ljava.lang.String;"
+
+ +

The native image heap contains a unique hub object (i.e. instance of +java.lang.Class) for every Java type that is included in the +image. It is possible to refer to these class constants using the +standard Java class literal syntax:

+ +
(gdb) print 'Hello.class'
+$6 = {
+  <java.lang.Object> = {
+    <_objhdr> = {
+      hub = 0xaabd00,
+      idHash = 1589947226
+    }, <No data fields>}, 
+  members of java.lang.Class:
+  typeCheckStart = 13,
+  name = 0xbd57f0,
+  ...
+
+ +

Unfortunately it is necessary to quote the class constant literal to +avoid gdb interpreting the embedded . character as a field access.

+ +

Note that the type of a class constant literal is java.lang.Class +rather than java.lang.Class *.

+ +

Class constants exist for Java instance classes, interfaces, array +classes and arrays, including primitive arrays:

+ +
(gdb)  print 'java.util.List.class'.name
+$7 = (java.lang.String *) 0xb1f698
+(gdb) print 'java.lang.String[].class'.name->value->data
+$8 = 0x8e6d78 "[Ljava.lang.String;"
+(gdb) print 'long.class'.name->value->data
+$9 = 0xc87b78 "long"
+(gdb) x/s  'byte[].class'.name->value->data
+0x925a00:	"[B"
+(gdb) 
+
+ +

Interface layouts are modelled as C++ union types. +The members of the union include the C++ layout types for all Java classes which implement the interface.

+ +
(gdb) ptype 'java.lang.CharSequence'
+type = union java.lang.CharSequence {
+    java.nio.CharBuffer _java.nio.CharBuffer;
+    java.lang.AbstractStringBuilder _java.lang.AbstractStringBuilder;
+    java.lang.String _java.lang.String;
+    java.lang.StringBuilder _java.lang.StringBuilder;
+    java.lang.StringBuffer _java.lang.StringBuffer;
+}
+
+ +

Given a reference typed to an interface it can be resolved to the relevant class type by viewing it through the relevant union element.

+ +

If we take the first String in the args array we can ask gdb to cast it to interface CharSequence.

+ +
(gdb) print args->data[0]
+$10 = (java.lang.String *) 0x7ffff7c01160
+(gdb) print ('java.lang.CharSequence' *)$10
+$11 = (java.lang.CharSequence *) 0x7ffff7c01160
+
+ +

The hubname command will not work with this union type because it is only objects of the elements of the union that include the hub field:

+ +
(gdb) hubname $11
+There is no member named hub.
+
+ +

However, since all elements include the same header any one of them can be passed to hubname in order to identify the actual type. +This allows the correct union element to be selected:

+ +
(gdb) hubname $11->'_java.nio.CharBuffer'
+0x95cc58:	"java.lang.String`\302\236"
+(gdb) print $11->'_java.lang.String'
+$12 = {
+  <java.lang.Object> = {
+    <_objhdr> = {
+      hub = 0xaa3350,
+      idHash = 0
+    }, <No data fields>},
+  members of java.lang.String:
+  hash = 0,
+  value = 0x7ffff7c01180,
+  coder = 0 '\000'
+}
+
+ +

Notice that the printed class name for hub includes some trailing characters. +That is because a data array storing Java String text is not guaranteed to be zero-terminated.

+ +

The debugger does not just understand the name and type of local and +parameter variables. It also knows about method names and static field +names.

+ +

The following command places a breakpoint on the main entry point for class Hello. +Note that since GDB thinks this is a C++ method it uses the :: separator to separate the method name from the class name.

+ +
(gdb) info func ::main
+All functions matching regular expression "::main":
+
+File Hello.java:
+	void Hello::main(java.lang.String[] *);
+(gdb) x/4i Hello::main
+=> 0x4065a0 <Hello::main(java.lang.String[] *)>:	sub    $0x8,%rsp
+   0x4065a4 <Hello::main(java.lang.String[] *)+4>:	cmp    0x8(%r15),%rsp
+   0x4065a8 <Hello::main(java.lang.String[] *)+8>:	jbe    0x4065fd <Hello::main(java.lang.String[] *)+93>
+   0x4065ae <Hello::main(java.lang.String[] *)+14>:	callq  0x406050 <Hello$Greeter::greeter(java.lang.String[] *)>
+(gdb) b Hello::main
+Breakpoint 1 at 0x4065a0: file Hello.java, line 43.
+
+ +

An example of a static field containing Object data is provided by the static field powerCache in class BigInteger.

+ +
(gdb) ptype 'java.math.BigInteger'
+type = class _java.math.BigInteger : public _java.lang.Number {
+  public:
+    int [] mag;
+    int signum;
+  private:
+    int bitLengthPlusOne;
+    int lowestSetBitPlusTwo;
+    int firstNonzeroIntNumPlusTwo;
+    static java.math.BigInteger[][] powerCache;
+    . . .
+  public:
+    void BigInteger(byte [] *);
+    void BigInteger(java.lang.String *, int);
+    . . .
+}
+(gdb) info var powerCache
+All variables matching regular expression "powerCache":
+
+File java/math/BigInteger.java:
+	java.math.BigInteger[][] *java.math.BigInteger::powerCache;
+
+ +

The static variable name can be used to refer to the value stored in this field. +Note also that the address operator can be used identify the location (address) of the field in the heap.

+ +
(gdb) p 'java.math.BigInteger'::powerCache
+$13 = (java.math.BigInteger[][] *) 0xced5f8
+(gdb) p &'java.math.BigInteger'::powerCache
+$14 = (java.math.BigInteger[][] **) 0xced3f0
+
+ +

The debugger dereferences through symbolic names for static fields to access the primitive value or object stored in the field.

+ +
(gdb) p *'java.math.BigInteger'::powerCache
+$15 = {
+  <java.lang.Object> = {
+    <_objhdr> = {
+    hub = 0xb8dc70,
+    idHash = 1669655018
+    }, <No data fields>},
+  members of _java.math.BigInteger[][]:
+  len = 37,
+  data = 0xced608
+}
+(gdb) p 'java.math.BigInteger'::powerCache->data[0]@4
+$16 = {0x0, 0x0, 0xed5780, 0xed5768}
+(gdb) p *'java.math.BigInteger'::powerCache->data[2]
+$17 = {
+  <java.lang.Object> = {
+    <_objhdr> = {
+    hub = 0xabea50,
+    idHash = 289329064
+    }, <No data fields>},
+  members of java.math.BigInteger[]:
+  len = 1,
+  data = 0xed5790
+}
+(gdb) p *'java.math.BigInteger'::powerCache->data[2]->data[0]
+$18 = {
+  <java.lang.Number> = {
+    <java.lang.Object> = {
+      <_objhdr> = {
+        hub = 0xabed80
+      }, <No data fields>}, <No data fields>},
+  members of java.math.BigInteger:
+  mag = 0xcbc648,
+  signum = 1,
+  bitLengthPlusOne = 0,
+  lowestSetBitPlusTwo = 0,
+  firstNonzeroIntNumPlusTwo = 0
+}
+
+ +

Identifying Source Code Location

+ +

One goal of the implementation is to make it simple to configure the debugger so that it can identify the relevant source file when it stops during program execution. The native-image tool tries to achieve this by accumulating the relevant sources in a suitably structured file cache.

+ +

The native-image tool uses different strategies to locate source files for JDK runtime classes, GraalVM classes, and application source classes for inclusion in the local sources cache. +It identifies which strategy to use based on the package name of the class. +So, for example, packages starting with java.* or jdk.* are JDK classes; packages starting with org.graal.* or com.oracle.svm.* are GraalVM classes; any other packages are regarded as application classes.

+ +

Sources for JDK runtime classes are retrieved from the src.zip found in the JDK release used to run the native image generation process. +Retrieved files are cached under subdirectory sources, using the module name (for JDK11) and package name of the associated class to define the directory hierarchy in which the source is located.

+ +

For example, on Linux the source for class java.util.HashMap will be cached in file sources/java.base/java/util/HashMap.java. +Debug info records for this class and its methods will identify this source file using the relative directory path java.base/java/util and file name HashMap.java. On Windows things will be the same modulo use of \ rather than / as the file separator.

+ +

Sources for GraalVM classes are retrieved from ZIP files or source directories derived from entries in the classpath. +Retrieved files are cached under subdirectory sources, using the package name of the associated class to define the directory hierarchy in which the source is located (e.g., class com.oracle.svm.core.VM has its source file cached at sources/com/oracle/svm/core/VM.java).

+ +

The lookup scheme for cached GraalVM sources varies depending upon what is found in each classpath entry. +Given a JAR file entry like /path/to/foo.jar, the corresponding file /path/to/foo.src.zip is considered as a candidate ZIP file system from which source files may be extracted. +When the entry specifies a directory like /path/to/bar, then directories /path/to/bar/src and /path/to/bar/src_gen are considered as candidates. +Candidates are skipped when the ZIP file or source directory does not exist, or it does not contain at least one subdirectory hierarchy that matches one of the the expected GraalVM package hierarchies.

+ +

Sources for application classes are retrieved from source JAR files or source directories derived from entries in the classpath. +Retrieved files are cached under subdirectory sources, using the package name of the associated class to define the directory hierarchy in which the source is located (e.g., class org.my.foo.Foo has its source file cached as sources/org/my/foo/Foo.java).

+ +

The lookup scheme for cached application sources varies depending upon what is found in each classpath entry. +Given a JAR file entry like /path/to/foo.jar, the corresponding JAR /path/to/foo-sources.jar is considered as a candidate ZIP file system from which source files may be extracted. +When the entry specifies a dir like /path/to/bar/classes or /path/to/bar/target/classes then one of the directories +/path/to/bar/src/main/java, /path/to/bar/src/java or /path/to/bar/src is selected as a candidate (in that order of preference). +Finally, the current directory in which the native executable is being run is also considered as a candidate.

+ +

These lookup strategies are only provisional and may need extending in the future. +However, it is possible to make missing sources available by other means. +One option is to unzip extra app source JAR files, or copy extra app source trees into the cache. +Another is to configure extra source search paths.

+ +

Configuring Source Paths in GNU Debugger

+ +

By default, GDB will employ the local directory root sources to locate the source files for your application classes, GraalVM classes, and JDK runtime classes. +If the sources cache is not located in the directory in which you run GDB, you can configure the required paths using the following command:

+ +
(gdb) set directories /path/to/sources/
+
+ +

The argument to the set directories command should identify the location of the sources cache as an absolute path or a relative path from the working directory of the gdb session.

+ +

Note that the current implementation does not yet find some sources for the GraalVM JIT compiler in the org.graalvm.compiler* package subspace.

+ +

You can supplement the files cached in sources by unzipping application source JAR files or copying application source trees into the cache. +You will need to ensure that any new subdirectory you add to sources corresponds to the top level package for the classes whose sources are being included.

+ +

You can also add extra directories to the search path using the set directories command:

+
(gdb) set directories /path/to/my/sources/:/path/to/my/other/sources
+
+

Note that the GNU Debugger does not understand ZIP format file systems so any extra entries you add must identify a directory tree containing the relevant sources. +Once again, top level entries in the directory added to the search path must correspond to the top level package for the classes whose sources are being included.

+ +

Checking Debug Info on Linux

+ +

Note that this is only of interest to those who want to understand how the debug info implementation works or want to troubleshoot problems encountered during debugging that might relate to the debug info encoding.

+ +

The objdump command can be used to display the debug info embedded into a native executable. +The following commands (which all assume the target binary is called hello) can be used to display all generated content:

+
objdump --dwarf=info hello > info
+objdump --dwarf=abbrev hello > abbrev
+objdump --dwarf=ranges hello > ranges
+objdump --dwarf=decodedline hello > decodedline
+objdump --dwarf=rawline hello > rawline
+objdump --dwarf=str hello > str
+objdump --dwarf=loc hello > loc
+objdump --dwarf=frames hello > frames
+
+ +

The info section includes details of all compiled Java methods.

+ +

The abbrev section defines the layout of records in the info section that describe Java files (compilation units) and methods.

+ +

The ranges section details the start and end addresses of method code segments.

+ +

The decodedline section maps subsegments of method code range segments to files and line numbers. +This mapping includes entries for files and line numbers for inlined methods.

+ +

The rawline segment provides details of how the line table is generated using DWARF state machine instructions that encode file, line, and address transitions.

+ +

The loc section provides details of address ranges within +which parameter and local variables declared in the info section +are known to have a determinate value. The details identify where +the value is located, either in a machine register, on the stack or +at a specific address in memory.

+ +

The str section provides a lookup table for strings referenced from records in the info section.

+ +

The frames section lists transition points in compiled methods where a (fixed size) stack frame is pushed or popped, allowing the debugger to identify each frame’s current and previous stack pointers and its return address.

+ +

Note that some of the content embedded in the debug records is generated by the C compiler and belongs to code that is either in libraries or the C lib bootstrap code that is bundled in with the Java method code.

+ +

Currently Supported Targets

+ +

The prototype is currently implemented only for the GNU Debugger on Linux:

+ +
    +
  • +

    Linux/x86_64 support has been tested and should work correctly

    +
  • +
  • +

    Linux/AArch64 support is present but has not yet been fully verified (break points should work ok but stack backtraces may be incorrect)

    +
  • +
+ +

Windows support is still under development.

+ +

Debugging with Isolates

+ +

Enabling the use of isolates, by passing command line option -H:-SpawnIsolates to the native-image builder, affects the way ordinary object pointers (oops) are encoded. +In turn, that means the debug info generator has to provide gdb with information about how to translate an encoded oop to the address in memory, where the object data is stored. +This sometimes requires care when asking gdb to process encoded oops vs decoded raw addresses.

+ +

When isolates are disabled, oops are essentially raw addresses pointing directly at the object contents. +This is generally the same whether the oop is embedded in a static/instance field or is referenced from a local or parameter variable located in a register or saved to the stack. +It is not quite that simple because the bottom 3 bits of some oops may be used to hold “tags” that record certain transient properties of an object. +However, the debug info provided to gdb means that it will remove these tag bits before dereferencing the oop as an address.

+ +

By contrast, when isolates are enabled, oops references stored in static or instance fields are actually relative addresses, offsets from a dedicated heap base register (r14 on x86_64, r29 on AArch64), rather than direct addresses (in a few special cases the offset may also have some low tag bits set). +When an “indirect” oop of this kind gets loaded during execution, it is almost always immediately converted to a “raw” address by adding the offset to the heap base register value. +So, oops which occur as the value of local or parameter vars are actually raw addresses.

+ +
+

Note that on some operating systems enabling isolates causes problems with printing of objects when using a gdb release version 10 or earlier. It is currently recommended to disable use of isolates, by passing command line option -H:-SpawnIsolates, when generating debug info if your operating system includes one of these earlier releases. Alternatively, you may be able to upgrade your debugger to a later version.

+
+ +

The DWARF info encoded into the image, when isolates are enabled, tells gdb to rebase indirect oops whenever it tries to dereference them to access underlying object data. +This is normally automatic and transparent, but it is visible in the underlying type model that gdb displays when you ask for the type of objects.

+ +

For example, consider the static field we encountered above. +Printing its type in an image that uses isolates shows that this static field has a different type to the expected one:

+ +
(gdb) ptype 'java.math.BigInteger'::powerCache
+type = class _z_.java.math.BigInteger[][] : public java.math.BigInteger[][] {
+} *
+
+

The field is typed as _z_.java.math.BigInteger[][] which is an empty wrapper class that inherits from the expected type java.math.BigInteger[][]. +This wrapper type is essentially the same as the original but the DWARF info record that defines it includes information that tells gdb how to convert pointers to this type.

+ +

When gdb is asked to print the oop stored in this field it is clear that it is an offset rather than a raw address.

+ +
(gdb) p/x 'java.math.BigInteger'::powerCache
+$1 = 0x286c08
+(gdb) x/x 0x286c08
+0x286c08:	Cannot access memory at address 0x286c08
+
+ +

However, when gdb is asked to dereference through the field, it applies the necessary address conversion to the oop and fetches the correct data.

+ +
(gdb) p/x *'java.math.BigInteger'::powerCache
+$2 = {
+  <java.math.BigInteger[][]> = {
+    <java.lang.Object> = {
+      <_objhdr> = {
+        hub = 0x1ec0e2,
+        idHash = 0x2f462321
+      }, <No data fields>},
+    members of java.math.BigInteger[][]:
+    len = 0x25,
+    data = 0x7ffff7a86c18
+  }, <No data fields>}
+
+ +

Printing the type of the hub field or the data array shows that they are also modelled using indirect types:

+ +
(gdb) ptype $1->hub
+type = class _z_.java.lang.Class : public java.lang.Class {
+} *
+(gdb) ptype $2->data
+type = class _z_.java.math.BigInteger[] : public java.math.BigInteger[] {
+} *[0]
+
+ +

The debugger still knows how to dereference these oops:

+ +
(gdb) p $1->hub
+$3 = (_z_.java.lang.Class *) 0x1ec0e2
+(gdb) x/x $1->hub
+0x1ec0e2:	Cannot access memory at address 0x1ec0e2
+(gdb) p *$1->hub
+$4 = {
+  <java.lang.Class> = {
+    <java.lang.Object> = {
+      <_objhdr> = {
+        hub = 0x1dc860,
+        idHash = 1530752816
+      }, <No data fields>},
+    members of java.lang.Class:
+    name = 0x171af8,
+    . . .
+  }, <No data fields>}
+
+
+ +

Since the indirect types inherit from the corresponding raw type it is possible to use an expression that identifies an indirect type pointer in almost all cases where an expression identifying a raw type pointer would work. +The only case case where care might be needed is when casting a displayed numeric field value or displayed register value.

+ +

For example, if the indirect hub oop printed above is passed to hubname_raw, the cast to type Object internal to that command fails to force the required indirect oops translation. +The resulting memory access fails:

+ +
(gdb) hubname_raw 0x1dc860
+Cannot access memory at address 0x1dc860
+
+ +

In this case it is necessary to use a slightly different command that casts its argument to an indirect pointer type:

+
(gdb) define hubname_indirect
+ x/s (('_z_.java.lang.Object' *)($arg0))->hub->name->value->data
+end
+(gdb) hubname_indirect 0x1dc860
+0x7ffff78a52f0:	"java.lang.Class"
+
+ +

Debugging Helper Methods

+ +

On platforms where the debugging information is not fully supported, or when debugging complex issues, it can be helpful to print or query high-level information about the Native Image execution state. +For those scenarios, Native Image provides debug helper methods that can be embedded into a native executable by specifying the build-time option -H:+IncludeDebugHelperMethods. +While debugging, it is then possible to invoke those debug helper methods like any normal C method. +This functionality is compatible with pretty much any debugger.

+ +

While debugging with gdb, the following command can be used to list all debug helper methods that are embedded into the native image:

+
(gdb) info functions svm_dbg_
+
+ +

Before invoking a method, it is best to directly look at the source code of the Java class DebugHelper to determine which arguments each method expects. +For example, calling the method below prints high-level information about the Native Image execution state similar to what is printed for a fatal error:

+
(gdb) call svm_dbg_print_fatalErrorDiagnostics($r15, $rsp, $rip)
+
+ +

Special Considerations for using perf and valgrind

+ +

Debug info includes details of address ranges for top level and +inlined compiled method code as well as mappings from code addresses +to the corresponding source files and lines. +perf and valgrind are able to use this information for some of +their recording and reporting operations. +For example, perf report is able to associate code adresses sampled +during a perf record session with Java methods and print the +DWARF-derived method name for the method in its output histogram.

+ +
    . . .
+    68.18%     0.00%  dirtest          dirtest               [.] _start
+            |
+            ---_start
+               __libc_start_main_alias_2 (inlined)
+               |          
+               |--65.21%--__libc_start_call_main
+               |          com.oracle.svm.core.code.IsolateEnterStub::JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b (inlined)
+               |          com.oracle.svm.core.JavaMainWrapper::run (inlined)
+               |          |          
+               |          |--55.84%--com.oracle.svm.core.JavaMainWrapper::runCore (inlined)
+               |          |          com.oracle.svm.core.JavaMainWrapper::runCore0 (inlined)
+               |          |          |          
+               |          |          |--55.25%--DirTest::main (inlined)
+               |          |          |          |          
+               |          |          |           --54.91%--DirTest::listAll (inlined)
+               . . .
+
+ +

Unfortunately, other operations require Java methods to be identified +by an ELF (local) function symbol table entry locating the start of +the compiled method code. +In particular, assembly code dumps provided by both tools identify +branch and call targets using an offset from the nearest symbol. +Omitting Java method symbols means that offsets are generally +displayed relative to some unrelated global symbol, usually the entry +point for a method exported for invocation by C code.

+ +

As an illustration of the problem, the following excerpted output from +perf annotate displays the first few annotated instructions of the +compiled code for method java.lang.String::String().

+ +
    . . .
+         : 501    java.lang.String::String():
+         : 521    public String(byte[] bytes, int offset, int length, Charset charset) {
+    0.00 :   519d50: sub    $0x68,%rsp
+    0.00 :   519d54: mov    %rdi,0x38(%rsp)
+    0.00 :   519d59: mov    %rsi,0x30(%rsp)
+    0.00 :   519d5e: mov    %edx,0x64(%rsp)
+    0.00 :   519d62: mov    %ecx,0x60(%rsp)
+    0.00 :   519d66: mov    %r8,0x28(%rsp)
+    0.00 :   519d6b: cmp    0x8(%r15),%rsp
+    0.00 :   519d6f: jbe    51ae1a <graal_vm_locator_symbol+0xe26ba>
+    0.00 :   519d75: nop
+    0.00 :   519d76: nop
+         : 522    Objects.requireNonNull(charset);
+    0.00 :   519d77: nop
+         : 524    java.util.Objects::requireNonNull():
+         : 207    if (obj == null)
+    0.00 :   519d78: nop
+    0.00 :   519d79: nop
+         : 209    return obj;
+    . . .
+
+ +

The leftmost column shows percentages for the amount of time recorded +at each instruction in samples obtained during the perf record run. +Each instruction is prefaced with it’s address in the program’s code +section. +The disassembly interleaves the source lines from which the code is +derived, 521-524 for the top level code and 207-209 for the code +inlined from from Objects.requireNonNull(). +Also, the start of the method is labelled with the name defined in the +DWARF debug info, java.lang.String::String(). +However, the branch instruction jbe at address 0x519d6f uses a +very large offset from graal_vm_locator_symbol. +The printed offset does identify the correct address relative to the +location of the symbol. +However, this fails to make clear that the target address actually +lies within the compiled code range for method String::String() i.e. that thsi is a method-local branch.

+ +

Readability of the tool output is significantly improved if +option -H-DeleteLocalSymbols is passed to the native-image +command. +The equivalent perf annotate output with this option enabled is as +follows:

+ +
    . . .
+         : 5      000000000051aac0 <String_constructor_f60263d569497f1facccd5467ef60532e990f75d>:
+         : 6      java.lang.String::String():
+         : 521    *          {@code offset} is greater than {@code bytes.length - length}
+         : 522    *
+         : 523    * @since  1.6
+         : 524    */
+         : 525    @SuppressWarnings("removal")
+         : 526    public String(byte[] bytes, int offset, int length, Charset charset) {
+    0.00 :   51aac0: sub    $0x68,%rsp
+    0.00 :   51aac4: mov    %rdi,0x38(%rsp)
+    0.00 :   51aac9: mov    %rsi,0x30(%rsp)
+    0.00 :   51aace: mov    %edx,0x64(%rsp)
+    0.00 :   51aad2: mov    %ecx,0x60(%rsp)
+    0.00 :   51aad6: mov    %r8,0x28(%rsp)
+    0.00 :   51aadb: cmp    0x8(%r15),%rsp
+    0.00 :   51aadf: jbe    51bbc1 <String_constructor_f60263d569497f1facccd5467ef60532e990f75d+0x1101>
+    0.00 :   51aae5: nop
+    0.00 :   51aae6: nop
+         : 522    Objects.requireNonNull(charset);
+    0.00 :   51aae7: nop
+         : 524    java.util.Objects::requireNonNull():
+         : 207    * @param <T> the type of the reference
+         : 208    * @return {@code obj} if not {@code null}
+         : 209    * @throws NullPointerException if {@code obj} is {@code null}
+         : 210    */
+         : 211    public static <T> T requireNonNull(T obj) {
+         : 212    if (obj == null)
+    0.00 :   51aae8: nop
+    0.00 :   51aae9: nop
+         : 209    throw new NullPointerException();
+         : 210    return obj;
+    . . .
+
+ +

In this version the start address of the method is now labelled with +the mangled symbol name String_constructor_f60263d569497f1facccd5467ef60532e990f75d +as well as the DWARF name. +The branch target is now printed using an offset from that start +symbol.

+ +

Unfortunately, perf and valgrind do not correctly understand the +mangling algorithm employed by GraalVM, nor are they currently able to +replace the mangled name with the DWARF name in the disassembly even +though both symbol and DWARF function data are known to identify code +starting at the same address. +So, the branch instruction still prints its target using a symbol plus +offset but it is at least using the method symbol this time.

+ +

Also, because address 51aac0 is now recognized as a method start, +perf has preceded the first line of the method with 5 context lines, +which list the tail end of the method’s javadoc comment. +Unfortunately, perf has numbered these lines incorrectly, labelling +the first comment with 521 rather than 516.

+ +

Executing command perf annotate will provide a disassembly listing +for all methods and C functions in the image. +It is possible to annotate a specific method by passing it’s name as +an argument to the perf annotate command. +Note, however, that perf requries the mangled symbol name as +argument rather than the DWARF name. +So, in order to annotate method java.lang.String::String() it is +necessary to run command perf annotate +String_constructor_f60263d569497f1facccd5467ef60532e990f75d.

+ +

The valgrind tool callgrind also requires local symbols to be +retained in order to provide high quality output. +When callgrind is used in combination with a viewer like +kcachegrind it is possible to identify a great deal of valuable +information about native image execution aand relate it back to +specific source code lines.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/InspectTool/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/InspectTool/index.html new file mode 100644 index 0000000..71a18a1 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/InspectTool/index.html @@ -0,0 +1,153 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Inspection Tool

+ +

Native Image provides the inspection tool to list all methods included in a native executable or a native shared library. Run the command $JAVA_HOME/bin/native-image-inspect <path_to_binary> to list classes, methods, fields, and constructors in the JSON format that validates against the JSON schema defined in native-image-inspect-schema-v0.2.0.json.

+ +
+

Note: The inspection tool is not available in GraalVM Community Edition.

+
+ +

The native-image builder, by default, includes metadata in the native executable which then enables the inspection tool to list the included methods.

+ +

The amount of data included is fairly minimal compared to the overall image size, however you can set the -H:-IncludeMethodsData option to disable the metadata emission. +Images compiled with this option will not be able to be inspected by the tool.

+ +

Software Bill of Materials (SBOM)

+ +

Native Image can embed a Software Bill of Materials (SBOM) at build time to detect any libraries that may be susceptible to known security vulnerabilities. +Native Image provides the --enable-sbom option to embed an SBOM into a native executable.

+ +
+

Note: Embedding a Software Bill of Materials (SBOM) is not available in GraalVM Community Edition. The feature is currently experimental.

+
+ +

The tool is able to extract the compressed SBOM using an optional --sbom parameter accessible through $JAVA_HOME/bin/native-image-inspect --sbom <path_to_binary>.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/JFR/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/JFR/index.html new file mode 100644 index 0000000..56d5465 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/JFR/index.html @@ -0,0 +1,297 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

JDK Flight Recorder (JFR) with Native Image

+ +

JDK Flight Recorder (JFR) is an event recorder for capturing information about a JVM, and an application running on the JVM. +GraalVM Native Image supports building a native executable with JFR events, and users can use jdk.jfr.Event API with a similar experience to using JFR in the Java HotSpot VM.

+ +

To record JFR events when running a native executable, JFR support and JFR recording must be enabled.

+ +

Add JFR Support and Record Events at Run Time

+ +

To build a native executable with the JFR events support, you first need to include JFR at build time, then enable the system, start a recording, and configure logging at native executable run time.

+ +

To build a native executable with JFR, use the --enable-monitoring=jfr flag:

+
native-image --enable-monitoring=jfr JavaApplication
+
+

To enable the system, start a recording, and configure logging at run time, the following options are supported:

+ +
    +
  • -XX:+FlightRecorder: use to enable JFR
  • +
  • -XX:StartFlightRecording: use to start a recording on application’s startup
  • +
  • -XX:FlightRecorderLogging: use to configure the log output for the JFR system
  • +
+ +

To enable JFR and start a recording, simply use -XX:StartFlightRecording. +For example:

+
./javaapplication -XX:StartFlightRecording="filename=recording.jfr"
+
+ +

Run a Demo

+ +

Transform this very simple demo application into a native image and see how to use JFR events from it. +Save the following code to the Example.java file.

+ +
import jdk.jfr.Event;
+import jdk.jfr.Description;
+import jdk.jfr.Label;
+
+public class Example {
+
+  @Label("Hello World")
+  @Description("Helps programmer getting started")
+  static class HelloWorldEvent extends Event {
+      @Label("Message")
+      String message;
+  }
+
+  public static void main(String... args) {
+      HelloWorldEvent event = new HelloWorldEvent();
+      event.message = "hello, world!";
+      event.commit();
+  }
+}
+
+ +

You can further configure the JFR recording or enable logging.

+ +

Configure JFR Recording

+ +

You can configure the JFR recording by passing a comma-separated list of key-value pairs to the -XX:StartFlightRecording option. +For example:

+
-XX:StartFlightRecording="filename=recording.jfr,dumponexit=true,duration=10s"
+
+ +

The following key-value pairs are supported:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDefault ValueDescription
namenoneName that can be used to identify the recording, e.g., “name=MyRecording”
settingsnoneSettings file (profile.jfc, default.jfc, etc.), e.g., “settings=myprofile.jfc”
delaynoneDelay recording start with (s)econds, (m)inutes), (h)ours), or (d)ays, e.g., “delay=5h”
durationinfinite (0)Duration of recording in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g., “duration=300s”
filenamenoneResulting recording filename, e.g., “filename=recording1.jfr”
maxageno limit (0)Maximum time to keep the recorded data on disk in (s)econds, (m)inutes, (h)ours, or (d)ays, e.g., 60m, or 0 for no limit. For example, “maxage=1d”
maxsizeno limit (0)Maximum amount of bytes to keep on disk in (k)B, (M)B or (G)B, e.g., 500M, or 0 for no limit. For example, “maxsize=1G”
dumponexitfalseWhether to dump a running recording when the JVM shuts down, e.g., “dumponexit=true”
+ +

Configure JFR System Logging

+ +

You can configure the logging for the JFR system with a separate flag -XX:FlightRecorderLogging. +The usage is: -XX:FlightRecorderLogging=[tag1[+tag2...][*][=level][,...]]. +For example:

+
-XX:FlightRecorderLogging=jfr,system=debug
+-XX:FlightRecorderLogging=all=trace
+-XX:FlightRecorderLogging=jfr*=error
+
+ +
    +
  • When this option is not set, logging is enabled at a level of WARNING.
  • +
  • When this option is set to an empty string, logging is enabled at a level of INFO.
  • +
  • When this option is set to “disable”, logging is disabled entirely.
  • +
+ +

Available log levels are: trace, debug, info, warning, error, off.

+ +

Available log tags are: all, jfr, system, event, setting, bytecode, parser, metadata, dcmd.

+ +

Otherwise, this option expects a comma separated list of tag combinations, each with an optional wildcard (*) and level.

+ +
    +
  • A tag combination without a level is given a default level of INFO.
  • +
  • Messages with tags that match a given tag combination will be logged if they meet the tag combination’s level.
  • +
  • If a tag combination does not have a wildcard, then only messages with exactly the same tags are matched. Otherwise, messages whose tags are a subset of the tag combination are matched.
  • +
  • If more than one tag combination matches a message’s tags, the rightmost one will apply.
  • +
  • Messages with tags that do not have any matching tag combinations are set to log at a default level of WARNING.
  • +
  • This option is case insensitive.
  • +
+ + + + + +

Current Limitations

+ +

JFR support is currently incomplete: for example, few VM-internal events are present. +However, JFR currently includes the following features: custom and system events, disk-based recordings, and stack traces. +To see an exhaustive list of JFR events and features supported by Native Image, see this GitHub issue.

+ +
+

Note: the GraalVM distribution for Windows does not include JFR event recording.

+
+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/StaticAnalysisReports/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/StaticAnalysisReports/index.html new file mode 100644 index 0000000..b500b2f --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/StaticAnalysisReports/index.html @@ -0,0 +1,298 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Points-to Analysis Reports

+ +

The points-to analysis produces two kinds of reports: an analysis call tree and an object tree. +This information is produced by an intermediate step in the building process and represents the static analysis view of the call graph and heap object graph. +These graphs are further transformed in the building process before they are compiled ahead-of-time into the binary and written into the binary heap, respectively.

+ +

Call tree

+

The call tree is a a breadth-first tree reduction of the call graph as seen by the points-to analysis. +The points-to analysis eliminates calls to methods that it determines cannot be reachable at runtime, based on the analyzed receiver types. +It also completely eliminates invocations in unreachable code blocks, such as blocks guarded by a type check that always fails. +The call tree report is enabled using the -H:+PrintAnalysisCallTree command-line option and can be formatted either as a TXT file (default) or as a set of CSV files using the -H:PrintAnalysisCallTreeType=CSV option.

+ +

TXT Format

+ +

When using the TXT format a file with the following structure is generated:

+ +
VM Entry Points
+├── entry <entry-method> id=<entry-method-id>
+│   ├── directly calls <callee> id=<callee-id> @bci=<invoke-bci>
+│   │   └── <callee-sub-tree>
+│   ├── virtually calls <callee> @bci=<invoke-bci>
+│   │   ├── is overridden by <override-method-i> id=<override-method-i-id>
+│   │   │   └── <callee-sub-tree>
+│   │   └── is overridden by <override-method-j> id-ref=<override-method-j-id>
+│   └── interfacially calls <callee> @bci=<invoke-bci>
+│       ├── is implemented by <implementation-method-x> id=<implementation-method-x-id>
+│       │   └── <callee-sub-tree>
+│       └── is implemented by <implementation-method-y> id-ref=<implementation-method-y-id>
+├── entry <entry-method> id=<entry-method-id>
+│   └── <callee-sub-tree>
+└── ...
+
+ +

The tags between <and > are expanded with concrete values, the remainder is printed as illustrated. +The methods are formatted using <qualified-holder>.<method-name>(<qualified-parameters>):<qualified-return-type> and are expanded until no more callees can be reached.

+ +

Since this is a tree reduction of the call graph each concrete method is expanded exactly once. +The tree representation inherently omits calls to methods that have already been explored in a different branch or previously on the same branch. +This restriction implicitly fixes the recursion problem. +To convey the information that is lost through tree reduction each concrete method is given a unique id. +Thus when a method is reached for the first time it declares an identifier as id=<method-id>. +Subsequent discoveries of the same method use an identifier reference to point to the previously expansion location: id-ref=<method-id>. +Each id=<method-id> and id-ref=<method-id> are followed by a blank space to make it easy to search.

+ +

Each invoke is tagged with the invocation bci: @bci=<invoke-bci>. +For invokes of inline methods the <invoke-bci> is a list of bci values, separated with ->, enumerating the inline locations, backwards to the original invocation location.

+ +

CSV Format

+

When using the CSV format a set of files containing raw data for methods and their relationships is generated. +The aim of these files is to enable this raw data to be easily imported into a graph database. +A graph database can provide the following functionality:

+ +
    +
  • Sophisticated graphical visualization of the call tree graph that provides a different perspective compared to text-based formats.
  • +
  • Ability to execute complex queries that can (for example) show a subset of the tree that causes certain code path to be included in the call tree analysis. +This querying functionality is crucial in making big analysis call trees manageable.
  • +
+ +

The process to import the files into a graph database is specific to each database. +Please follow the instructions provided by the graph database provider.

+ +

Object tree

+

The object tree is an exhaustive expansion of the objects included in the native binary heap. +The tree is obtained by a depth first walk of the native binary heap object graph. +It is enabled using the -H:+PrintImageObjectTree option. +The roots are either static fields or method graphs that contain embedded constants. +The printed values are concrete constant objects added to the native binary heap. +Produces a file with the structure:

+ +
Heap roots
+├── root <root-field> value:
+│   └── <value-type> id=<value-id> toString=<value-as-string> fields:
+│       ├── <field-1> value=null
+│       ├── <field-2> toString=<field-2-value-as-string> (expansion suppressed)
+│       ├── <field-3> value:
+│       │   └── <field-3-value-type> id=<field-3-value-id> toString=<field-3-value-as-string> fields:
+│       │       └── <object-tree-rooted-at-field-3>
+│       ├── <array-field-4> value:
+│       │   └── <array-field-4-value-type> id=<array-field-4-value-id> toString=<array-field-4-value-as-string> elements (excluding null):
+│       │       ├── [<index-i>] <element-index-i-value-type> id=<element-index-i-value-id> toString=<element-index-i-value-as-string> fields:
+│       │       │   └── <object-tree-rooted-at-index-i>
+│       │       └── [<index-j>] <element-index-j-value-type> id=<element-index-j-value-id> toString=<element-index-j-value-as-string> elements (excluding null):
+│       │           └── <object-tree-rooted-at-index-j>
+│       ├── <field-5> value:
+│       │   └── <field-5-value-type> id-ref=<field-5-value-id> toString=<field-5-value-as-string>
+│       ├── <field-6> value:
+│       │   └── <field-6-value-type> id=<field-6-value-id> toString=<field-6-value-as-string> (no fields)
+│       └── <array-field-7> value:
+│           └── <array-field-7-value-type> id=<array-field-7-id> toString=<array-field-7-as-string> (no elements)
+├── root <root-field> id-ref=<value-id> toString=<value-as-string>
+├── root <root-method> value:
+│   └── <object-tree-rooted-at-constant-embeded-in-the-method-graph>
+└── ...
+
+ +

The tags between <and > are expanded with concrete values, the remainder is printed as illustrated. +The root fields are formatted using <qualified-holder>.<field-name>:<qualified-declared-type>. +The non-root fields are formatted using <field-name>:<qualified-declared-type>. +The value types are formatted using <qualified-type>. +The root methods are formatted using <qualified-holder>.<method-name>(<unqualified-parameters>):<qualified-return-type> +No-array objects are expanded for all fields (including null). +No-array objects with no fields are tagged with (no fields). +Array objects are expanded for all non-null indexes: [<element-index>] <object-tree-rooted-at-array-element> +Empty array objects or with all null elements are tagged with (no elements).

+ +

Each constant value is expanded exactly once to compress the format. +When a value is reached from multiple branches it is expanded only the first time and given an identifier: id=<value-id>. +Subsequent discoveries of the same value use an identifier reference to point to the previously expansion location: id-ref=<value-id>.

+ +

Suppressing Expansion of Values

+ +

Some values, such as String, BigInteger and primitive arrays, are not expanded by default and marked with (expansion suppressed). +All the other types are expanded by default. +To force the suppression of types expanded by default you can use -H:ImageObjectTreeSuppressTypes=<comma-separated-patterns>. +To force the expansion of types suppressed by default or through the option you can use -H:ImageObjectTreeExpandTypes=<comma-separated-patterns>. +When both -H:ImageObjectTreeSuppressTypes and -H:ImageObjectTreeExpandTypes are specified -H:ImageObjectTreeExpandTypes has precedence.

+ +

Similarly, some roots, such as java.lang.Character$UnicodeBlock.map" that prints a lot of strings, are not expanded at all and marked with (expansion suppressed) as well. +All the other roots are expanded by default. +To force the suppression of roots expanded by default you can use -H:ImageObjectTreeSuppressRoots=<comma-separated-patterns>. +To force the expansion of roots suppressed by default or through the option you can use -H:ImageObjectTreeExpandRoots=<comma-separated-patterns>. +When both -H:ImageObjectTreeSuppressRoots and -H:ImageObjectTreeExpandRoots are specified -H:ImageObjectTreeExpandRoots has precedence.

+ +

All the suppression/expansion options above accept a comma-separated list of patterns. +For types the pattern is based on the fully qualified name of the type and refers to the concrete type of the constants. +(For array types it is enough to specify the elemental type; it will match all the arrays of that type, of all dimensions.) +For roots the pattern is based on the string format of the root as described above. +The pattern accepts the * modifier:

+
    +
  • ends-with: *<str> - the pattern exactly matches all entries that end with <str>
  • +
  • starts-with: <str>* - the pattern exactly matches all entries that start with <str>
  • +
  • contains: *<str>* - the pattern exactly matches all entries that contain <str>
  • +
  • equals: <str> - the pattern exactly matches all entries that are equal to <str>
  • +
  • all: * - the pattern matches all entries
  • +
+ +

Examples

+

Types suppression/expansion:

+
    +
  • -H:ImageObjectTreeSuppressTypes=java.io.BufferedWriter - suppress the expansion of java.io.BufferedWriter objects
  • +
  • -H:ImageObjectTreeSuppressTypes=java.io.BufferedWriter,java.io.BufferedOutputStream - suppress the expansion of java.io.BufferedWriter and java.io.BufferedOutputStream objects
  • +
  • -H:ImageObjectTreeSuppressTypes=java.io.* - suppress the expansion of all java.io.* objects
  • +
  • -H:ImageObjectTreeExpandTypes=java.lang.String - force the expansion of java.lang.String objects
  • +
  • -H:ImageObjectTreeExpandTypes=java.lang.String,java.math.BigInteger - force the expansion of java.lang.String and java.math.BigInteger objects
  • +
  • -H:ImageObjectTreeExpandTypes=java.lang.* - force the expansion of all java.lang.* objects
  • +
  • -H:ImageObjectTreeSuppressTypes=java.io.* -H:ImageObjectTreeExpandTypes=java.io.PrintStream - suppress the expansion of all java.io.* but not java.io.PrintStream objects
  • +
  • -H:ImageObjectTreeExpandTypes=* - force the expansion of objects of all types, including those suppressed by default
  • +
+ +

Roots suppression/expansion:

+
    +
  • -H:ImageObjectTreeSuppressRoots="java.nio.charset.Charset.lookup(String)" - suppress the expansion of all constants embedded in the graph of com.oracle.svm.core.amd64.FrameAccess.wordSize()
  • +
  • -H:ImageObjectTreeSuppressRoots=java.util.* - suppress the expansion of all roots that start with java.util.
  • +
  • -H:ImageObjectTreeExpandRoots=java.lang.Character$UnicodeBlock.map - force the expansion of java.lang.Character$UnicodeBlock.map static field root
  • +
  • -H:ImageObjectTreeSuppressRoots=java.util.* -H:ImageObjectTreeExpandRoots=java.util.Locale - suppress the expansion of all roots that start with java.util. but not java.util.Locale
  • +
  • -H:ImageObjectTreeExpandRoots=* - force the expansion of all roots, including those suppressed by default
  • +
+ +

Report Files

+ +

The reports are generated in the reports subdirectory, relative to the build directory. +When executing the native-image executable the build directory defaults to the working directory and can be modified using the -H:Path=<dir> option.

+ +

The call tree report name has the structure call_tree_<binary_name>_<date_time>.txt when using the TXT format or, when using the CSV format, the call tree reports’ names have the structure call_tree_*_<binary_name>_<date_time>.csv. +When producing CSV formatted call tree reports, symbolic links following the structure call_tree_*.csv pointing to the latest call tree CSV reports are also generated. +The object tree report name has the structure: object_tree_<binary_name>_<date_time>.txt. +The binary name is the name of the generated binary, which can be set with the -H:Name=<name> option. +The <date_time> is in the yyyyMMdd_HHmmss format.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/index.html new file mode 100644 index 0000000..71b0526 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/debugging-and-diagnostics/index.html @@ -0,0 +1,134 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Debugging and Diagnostics

+ +

Native Image provides utilities for debugging and inspecting the produced binary:

+ + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/CertificateManagement/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/CertificateManagement/index.html new file mode 100644 index 0000000..63e57e3 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/CertificateManagement/index.html @@ -0,0 +1,171 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Certificate Management in Native Image

+ +

Native Image provides multiple ways to specify the certificate file used to define the default TrustStore. +In the following sections we describe the available build-time and run-time options. +Note: The default behavior for native-image is to capture and use the default TrustStore from the build-time host environment.

+ +

Build-time Options

+ +

During the image building process, the native-image builder captures the host environment’s default TrustStore and embeds it into the native executable. +This TrustStore is by default created from the root certificate file provided within the JDK, but can be changed to use a different certificate file by setting the build-time system property javax.net.ssl.trustStore (see Properties for how to do it).

+ +

Since the contents of the build-time certificate file is embedded into the native executable, the file itself does not need to be present in the target environment.

+ +

Runtime Options

+ +

The certificate file can also be changed dynamically at run time via setting the javax.net.ssl.trustStore\* system properties.

+ +

If any of the following system properties are set during the image execution, native-image also requires javax.net.ssl.trustStore to be set, and for it to point to an accessible certificate file:

+
    +
  • javax.net.ssl.trustStore
  • +
  • javax.net.ssl.trustStoreType
  • +
  • javax.net.ssl.trustStoreProvider
  • +
  • javax.net.ssl.trustStorePassword
  • +
+ +

If any of these properties are set and javax.net.ssl.trustStore does not point to an accessible file, then an UnsupportedFeatureError will be thrown.

+ +

Note that this behavior is different than OpenJDK. +When the javax.net.ssl.trustStore system property is unset or invalid, OpenJDK will fallback to using a certificate file shipped within the JDK. +However, such files will not be present alongside the image executable and hence cannot be used as a fallback.

+ +

During the execution, it also possible to dynamically change the javax.net.ssl.trustStore\* properties and for the default TrustStore to be updated accordingly.

+ +

Finally, whenever all of the javax.net.ssl.trustStore\* system properties listed above are unset, the default TrustStore will be the one captured during the build time, as described in the prior section.

+ +

Untrusted Certificates

+ +

During the image building process, a list of untrusted certificates is loaded from the file <java.home>/lib/security/blacklisted.certs. +This file is used when validating certificates at both build time and run time. +In other words, when a new certificate file is specified at run time via setting the javax.net.ssl.trustStore\* system properties, the new certificates will still be checked against the <java.home>/lib/security/blacklisted.certs loaded at +image build time.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/DynamicProxy/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/DynamicProxy/index.html new file mode 100644 index 0000000..842c4b6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/DynamicProxy/index.html @@ -0,0 +1,207 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Dynamic Proxy in Native Image

+ +

Java dynamic proxies, implemented by java.lang.reflect.Proxy, provide a mechanism which enables object level access control by routing all method invocations through java.lang.reflect.InvocationHandler. +Dynamic proxy classes are generated from a list of interfaces.

+ +

Native Image does not provide machinery for generating and interpreting bytecode at run time. +Therefore all dynamic proxy classes need to be generated at image build time.

+ +

Automatic Detection

+ +

Native Image employs a simple static analysis that detects calls to java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler) and java.lang.reflect.Proxy.getProxyClass(ClassLoader, Class<?>[]), then tries to determine the list of interfaces that define dynamic proxies automatically. +Given the list of interfaces, Native Image generates proxy classes at image build time and adds them to the native image heap. +In addition to generating the dynamic proxy class, the constructor of the generated class that takes a java.lang.reflect.InvocationHandler argument, i.e., the one reflectively invoked by java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[], InvocationHandler), is registered for reflection so that dynamic proxy instances can be allocated at run time.

+ +

The analysis is limited to situations where the list of interfaces comes from a constant array or an array that is allocated in the same method. +For example, in the code snippets bellow the dynamic proxy interfaces can be determined automatically.

+ +

Static Final Array:

+ +
class ProxyFactory {
+
+    private static final Class<?>[] interfaces = new Class<?>[]{java.util.Comparator.class};
+
+    static Comparator createProxyInstanceFromConstantArray() {
+        ClassLoader classLoader = ProxyFactory.class.getClassLoader();
+        InvocationHandler handler = new ProxyInvocationHandler();
+        return (Comparator) Proxy.newProxyInstance(classLoader, interfaces, handler);
+    }
+}
+
+ +

Note that the analysis operates on compiler graphs and not source code. +Therefore the following ways to declare and populate an array are equivalent from the point of view of the analysis:

+ +
private static final Class<?>[] interfacesArrayPreInitialized = new Class<?>[]{java.util.Comparator.class};
+
+ +
private static final Class<?>[] interfacesArrayLiteral = {java.util.Comparator.class};
+
+ +
private static final Class<?>[] interfacesArrayPostInitialized = new Class<?>[1];
+static {
+    interfacesArrayPostInitialized[0] = java.util.Comparator.class;
+}
+
+ +

However, there are no immutable arrays in Java. +Even if the array is declared as static final, its contents can change later on. +The simple analysis employed here does not track further changes to the array.

+ +

New Array:

+ +
class ProxyFactory {
+
+    static Comparator createProxyInstanceFromNewArray() {
+        ClassLoader classLoader = ProxyFactory.class.getClassLoader();
+        InvocationHandler handler = new ProxyInvocationHandler();
+        Class<?>[] interfaces = new Class<?>[]{java.util.Comparator.class};
+        return (Comparator) Proxy.newProxyInstance(classLoader, interfaces, handler);
+    }
+}
+
+ +

Note: Just like with constant arrays, the following ways to declare and populate an array are equivalent from the point of view of the analysis:

+
Class<?>[] interfaces = new Class<?>[]{java.util.Comparator.class};
+
+ +
Class<?>[] interfaces = new Class<?>[1];
+interfaces[0] = Question.class;
+
+ +
Class<?>[] interfaces = {java.util.Comparator.class};
+
+ +

The static analysis covers code patterns most frequently used to define dynamic proxy classes. +For the exceptional cases where the analysis cannot discover the interface array there is also a manual dynamic proxy configuration mechanism.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/JCASecurityServices/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/JCASecurityServices/index.html new file mode 100644 index 0000000..85a8b62 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/JCASecurityServices/index.html @@ -0,0 +1,187 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

JCA Security Services in Native Image

+ +

This page explains Native Image support of the Java Cryptography Architecture (JCA) framework.

+ +

The JCA framework uses a provider architecture to access security services such as digital signatures, message digests, certificates and certificate validation, encryption (symmetric/asymmetric block/stream ciphers), key generation and management, and secure random number generation, etc. +To achieve algorithm independence and extensibility it relies on reflection, therefore it requires a custom configuration in Native Image. +By default the native-image builder uses static analysis to discover which of these services are used (see next section for details). +The automatic registration of security services can be disabled with -H:-EnableSecurityServicesFeature. +Then a custom reflection configuration file or feature can be used to register the security services required by a specific application. +Note that when automatic registration of security providers is disabled, all providers are, by default, filtered from special JDK caches that are necessary for security functionality. +In this case, you must manually mark used providers with -H:AdditionalSecurityProviders.

+ +

Security Services Automatic Registration

+ +

The mechanism, implemented in the com.oracle.svm.hosted.SecurityServicesFeature class, uses reachability of specific API methods in the JCA framework to determine which security services are used.

+ +

Each JCA provider registers concrete implementation classes for the algorithms it supports. +Each of the service classes (Signature, Cipher, Mac, KeyPair, KeyGenerator, KeyFactory, KeyStore, etc.) declares a series of getInstance(<algorithm>, <provider> factory methods which provide a concrete service implementation. +When a specific algorithm is requested, the framework searches the registered providers for the corresponding implementation classes and dynamically allocates objects for concrete service implementations. +The native-image builder uses static analysis to discover which of these services are used. +It does so by registering reachability handlers for each of the getInstance() factory methods. +When it determines that a getInstance() method is reachable at run time, it automatically performs the reflection registration for all the concrete implementations of the corresponding service type.

+ +

Tracing of the security services automatic registation can be enabled with -H:+TraceSecurityServices. +The report will detail all registered service classes, the API methods that triggered registration, and the parsing context for each reachable API method.

+ +
+

Note: The --enable-all-security-services option is now deprecated and it will be removed in a future release.

+
+ +

Provider Registration

+ +

The native-image builder captures the list of providers and their preference order from the underlying JVM. +The provider order is specified in the java.security file under <java-home>/lib/security/java.security. +New security providers cannot be registered at run time; all providers must be statically configured at executable build time.

+ +

Providers Reordering at Run Time

+ +

It is possible to reorder security providers at run time, however only existing provider instances can be used. +For example, if the BouncyCastle provider is registered at build time and you want to insert it at position 1 at run time:

+
Provider bcProvider = Security.getProvider("BC");
+Security.removeProvider("BC");
+Security.insertProviderAt(bcProvider, 1);
+
+ +

SecureRandom

+ +

The SecureRandom implementations open the /dev/random and /dev/urandom files which are used as sources. +These files are usually opened in class initializers. +To avoid capturing state from the machine that runs the native-image builder, these classes need to be initialized at run time.

+ +

Custom Service Types

+ +

By default, only services specified in the JCA framework are automatically registered. To automatically register custom service types, you can use the -H:AdditionalSecurityServiceTypes option. +Note that for automatic registration to work, the service interface must have a getInstance method and have the same name as the service type. +If relying on the third-party code that does not comply to the above requirements, a manual configuration will be required. In that case, providers for such services must explicitly be registered using the -H:AdditionalSecurityProviders option. Note that these options are only required in very specific cases and should not normally be needed.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/JNI/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/JNI/index.html new file mode 100644 index 0000000..2f26406 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/JNI/index.html @@ -0,0 +1,310 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Java Native Interface (JNI) in Native Image

+ +

Java Native Interface (JNI) is a native API that enables Java code to interact with native code and vice versa. +This page gives an overview of the JNI implementation in Native Image.

+ +

JNI support is enabled by default and built into Native Image. Individual classes, methods, and fields that should be accessible via JNI must be specified at image build time in a configuration file (read below).

+ +

Java code can load native code from a shared object with System.loadLibrary(). +Alternatively, native code can load the JVM’s native library and attach to its Java environment using JNI’s Invocation API. +The Native Image JNI implementation supports both approaches.

+ +

Table of Contents

+ + + +

Loading Native Libraries

+ +

When loading native libraries using System.loadLibrary() (and related APIs), the native image will search the +directory containing the native image before searching the Java library path. So as long as the native libraries +to be loaded are in the same directory as the native image, no other settings should be necessary.

+ +

Reflection Metadata

+ +

JNI supports looking up classes by their names, and looking up methods and fields by their names and signatures. +This requires keeping the necessary metadata for these lookups around. +The native-image builder must know beforehand which items will be looked up in case they might not be reachable otherwise and therefore would not be included in a native image. +Moreover, native-image must generate call wrapper code ahead-of-time for any method that can be called via JNI. +Therefore, specifying a concise list of items that need to be accessible via JNI guarantees their availability and allows for a smaller footprint. +Such a list can be specified with the following image build argument:

+
-H:JNIConfigurationFiles=/path/to/jniconfig
+
+

Here, jniconfig is a JSON configuration file. +Check the JSON schema for specifing JNI metadata here.

+ +

The native-image builder generates JNI reflection metadata for all classes, methods, and fields referenced in the configuration file. +More than one JNI configuration can be used by specifying multiple paths for JNIConfigurationFiles and separating them with ,. +Also, -H:JNIConfigurationResources can be specified to load one or several configuration files from the image build’s class path, such as from a JAR file.

+ +

Alternatively, a custom Feature implementation can register program elements before and during the analysis phase of the image build using the JNIRuntimeAccess class. For example:

+
class JNIRegistrationFeature implements Feature {
+  public void beforeAnalysis(BeforeAnalysisAccess access) {
+    try {
+      JNIRuntimeAccess.register(String.class);
+      JNIRuntimeAccess.register(String.class.getDeclaredField("value"));
+      JNIRuntimeAccess.register(String.class.getDeclaredField("hash"));
+      JNIRuntimeAccess.register(String.class.getDeclaredConstructor(char[].class));
+      JNIRuntimeAccess.register(String.class.getDeclaredMethod("charAt", int.class));
+      JNIRuntimeAccess.register(String.class.getDeclaredMethod("format", String.class, Object[].class));
+      JNIRuntimeAccess.register(String.CaseInsensitiveComparator.class);
+      JNIRuntimeAccess.register(String.CaseInsensitiveComparator.class.getDeclaredMethod("compare", String.class, String.class));
+    } catch (NoSuchMethodException | NoSuchFieldException e) { ... }
+  }
+}
+
+

To activate the custom feature --features=<fully qualified name of JNIRegistrationFeature class> needs to be passed to native-image. +Native Image Build Configuration explains how this can be automated with a native-image.properties file in META-INF/native-image.

+ +

java.lang.reflect Support

+

The JNI functions FromReflectedMethod and ToReflectedMethod can be used to obtain the corresponding jmethodID to a java.lang.reflect.Method, or to a java.lang.reflect.Constructor object, and vice versa. +The functions FromReflectedField and ToReflectedField convert between jfieldID and java.lang.reflect.Field. +In order to use these functions, reflection support must be enabled and the methods and fields in question must be included in the reflection configuration, which is specified with -H:ReflectionConfigurationFiles=.

+ +

Object Handles

+ +

JNI does not permit direct access to Java objects. +Instead, JNI provides word-sized object handles that can be passed to JNI functions to access objects indirectly. +Local handles are only valid for the duration of a native call and only in the caller’s thread, while global handles are valid across threads and remain valid until they are destroyed explicitly. +The handle 0 represents NULL.

+ +

Native Image implements local handles with a thread-local, growing array of referenced objects, where the index in the array is the handle value. +A “finger” points to where the next handle will be allocated. +Native calls can be nested, so before a native method is invoked, the call stub pushes the current finger on a stack, and after it returns, it restores the old finger from the stack and nullifies all object references from the call in the array.

+ +

Global handles are implemented using a variable number of object arrays in which referenced objects are inserted and nullified using atomic operations. +A global handle’s value is a negative integer that is determined from the index of the containing array and the index within the array. +Therefore, the JNI code can distinguish local and global handles by only looking at their sign. +The analysis is not obstructed by object handles because it can observe the entire flow of object references and the handles that are passed to native code are only numeric values.

+ +

Java-to-Native Method Calls

+ +

Methods declared with the native keyword have a JNI-compliant implementation in native code, but can be called like any other Java method. For example:

+ +
// Java declaration
+native int[] sort0(int[] array);
+// native declaration with JNI name mangling
+jintArray JNICALL Java_org_example_sorter_IntSorter_sort0(JNIEnv *env, jobject this, jintArray array)
+
+ +

When the image build encounters a method that is declared native, it generates a graph with a wrapper that performs the transition to native code and back, adds the JNIEnv* and this arguments, boxes any object arguments in handles, and in case of an object return type, unboxes the returned handle.

+ +

The actual native call target address can only be determined at run time. +Therefore, the native-image builder also creates an extra linkage object in the reflection metadata of native-declared methods. +When a native method is called, the call wrapper looks up the matching symbol in all loaded libraries and stores the resolved address in the linkage object for future calls. +Alternatively, instead of requiring symbols that conform to JNI name mangling scheme, Native Image also supports the RegisterNatives JNI function to explicitly provide code addresses for native methods.

+ +

Native-to-Java Method Calls

+ +

Native code can invoke Java methods by first obtaining a jmethodID for the target method, and then using one of the Call<Type>Method, CallStatic<Type>Method or CallNonvirtual<Type>Method functions for the invocation. +Each of these Call... functions is also available in a Call...MethodA and a Call...MethodV variant, which take arguments as an array or as a va_list instead of variadic arguments. For example:

+ +
jmethodID intcomparator_compare_method = (*env)->GetMethodID(env, intcomparator_class, "compare", "(II)I");
+jint result = (*env)->CallIntMethod(env, this, intcomparator_compare_method, a, b);
+
+ +

The native-image builder generates call wrappers for each method that can be called via JNI according to the provided JNI configuration. +The call wrappers conform to the signature of the JNI Call... functions that are appropriate for the method. +The wrappers perform the transition to Java code and back, adapt the argument list to the target Java method’s signature, unbox any passed object handles, and if necessary, box the return type in an object handle.

+ +

Each method that can be called via JNI has a reflection metadata object. +The address of this object is used as the method’s jmethodID. +The metadata object contains the addresses of all of the method’s generated call wrappers. +Because each call wrapper conforms precisely to the corresponding Call... function signature, the Call... functions themselves are nothing more than an unconditional jump to the appropriate call wrapper based on the passed jmethodID. +As another optimization, the call wrappers are able to efficiently restore the current thread’s Java context from the JNIEnv* argument.

+ +

JNI Functions

+ +

JNI provides a set of functions that native code can use to interact with Java code. +Native Image implements these functions using @CEntryPoint, for example:

+
@CEntryPoint(...) private static void DeleteGlobalRef(JNIEnvironment env, JNIObjectHandle globalRef) { /* setup; */ JNIGlobalHandles.singleton().delete(globalRef); }
+
+

JNI specifies that these functions are provided via function pointers in a C struct that is accessible via the JNIEnv* argument. +The automatic initialization of this struct is prepared during the image build.

+ +

Object Creation

+ +

JNI provides two ways of creating Java objects, either by calling AllocObject to allocate memory and then using CallVoidMethod to invoke the constructor, or by using NewObject to create and initialize the object in a single step (or variants NewObjectA or NewObjectV). For example:

+
jclass calendarClass = (*env)->FindClass(env, "java/util/GregorianCalendar");
+jmethodID ctor = (*env)->GetMethodID(env, calendarClass, "<init>", "(IIIIII)V");
+jobject firstObject = (*env)->AllocObject(env, calendarClass);
+(*env)->CallVoidMethod(env, obj, ctor, year, month, dayOfMonth, hourOfDay, minute, second);
+jobject secondObject = (*env)->NewObject(env, calendarClass, ctor, year, month, dayOfMonth, hourOfDay, minute, second);
+
+

Native Image supports both approaches. +The constructor must be included in the JNI configuration with a method name of <init>. +Instead of generating additional call wrappers for NewObject, the regular CallVoidMethod wrapper is reused and detects when it is called via NewObject because it is passed the Class object of the target class. +In that case, the call wrapper allocates a new instance before invoking the actual constructor.

+ +

Field Accesses

+ +

Native code can access a Java field by obtaining its jfieldID and then using one of the Get<Type>Field, Set<Type>Field, GetStatic<Type>Field or SetStatic<Type>Field functions. For example:

+
jfieldID intsorter_comparator_field = (*env)->GetFieldID(env, intsorter_class, "comparator", "Lorg/example/sorter/IntComparator;");
+jobject value = (*env)->GetObjectField(env, self, intsorter_comparator_field);
+
+ +

For a field that is accessible via JNI, its offset within an object (or within the static field area) is stored in the reflection metadata and is used as its jfieldID. +The native-image builder generates accessor methods for fields of all primitive types and for object fields. +These accessor methods perform the transition to Java code and back, and use unsafe loads or stores to directly manipulate the field value. +Because the analysis cannot observe assignments of object fields via JNI, it assumes that any subtype of the field’s declared type can occur in a field that is accessible via JNI.

+ +

JNI also permits writing fields that are declared final, which must be enabled for individual fields with an allowWrite property in the configuration file. +However, code accessing final fields might not observe changes of final field values in the same way as for non-final fields because of optimizations.

+ +

Exceptions

+ +

JNI specifies that exceptions in Java code that are the result of a call from native code must be caught and retained. +In Native Image, this is done in the native-to-Java call wrappers and in the implementation of JNI functions. +Native code can then query and clear a pending exception with the ExceptionCheck, ExceptionOccurred, ExceptionDescribe, and ExceptionClear functions. +Native code can also throw exceptions with Throw, ThrowNew, or FatalError. +When an exception remains unhandled in native code or the native code itself throws an exception, the Java-to-native call wrapper rethrows that exception upon reentering Java code.

+ +

Monitors

+ +

JNI declares the functions MonitorEnter and MonitorExit to acquire and release the intrinsic lock of an object. +Native Image provides implementations of these functions. +However, the native-image builder directly assigns intrinsic locks only to objects of classes which the analysis can observe as being used in Java synchronized statements and with wait(), notify() and notifyAll(). +For other objects, synchronization falls back on a slower mechanism which uses a map to store lock associations, which itself needs synchronization. +For that reason, it can be beneficial to wrap synchronization in Java code.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/Reflection/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/Reflection/index.html new file mode 100644 index 0000000..859a26f --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/Reflection/index.html @@ -0,0 +1,316 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Reflection in Native Image

+ +

Java reflection support (the java.lang.reflect.* API) enables Java code to examine its own classes, methods, fields and their properties at run time.

+ +

Native Image has partial support for reflection and needs to know ahead-of-time the reflectively accessed program elements. +Examining and accessing program elements through java.lang.reflect.* or loading classes with Class.forName(String) at run time requires preparing additional metadata for those program elements. +(Note: loading classes with Class.forName(String) are included here since it is closely related to reflection.)

+ +

Native Image tries to resolve the target elements through a static analysis that detects calls to the Reflection API. +Where the analysis fails, the program elements reflectively accessed at run time must be specified using a manual configuration. See Reachability Metadata and Collect Metadata with the Tracing Agent for more information.

+ +

Table of Contents

+ + + +

Automatic Detection

+ +

The analysis intercepts calls to Class.forName(String), Class.forName(String, ClassLoader), Class.getDeclaredField(String), Class.getField(String), Class.getDeclaredMethod(String, Class[]), Class.getMethod(String, Class[]), Class.getDeclaredConstructor(Class[]), and Class.getConstructor(Class[]).

+ +

If the arguments to these calls can be reduced to a constant, Native Image tries to resolve the target elements. +If the target elements can be resolved, the calls are removed and instead the target elements are embedded in the code. +If the target elements cannot be resolved, e.g., a class is not on the classpath or it does not declare a field/method/constructor, then the calls are replaced with a snippet that throws the appropriate exception at run time. +The benefits are twofold. +First, at run time there are no calls to the Reflection API. +Second, GraalVM can employ constant folding and optimize the code further.

+ +

The calls are intercepted and processed only when it can be unequivocally determined that the parameters can be reduced to a constant. +For example, the call Class.forName(String) will be replaced with a Class literal only if the String argument can be constant folded, assuming that the class is actually on the classpath. +Additionally, a call to Class.getMethod(String, Class[]) will be processed only if the contents of the Class[] argument can be determined with certainty. +This last restriction is due to the fact that Java does not have immutable arrays. +Therefore, all the changes to the array between the time it is allocated and the time it is passed as an argument need to be tracked. +The analysis follows a simple rule: if all the writes to the array happen in linear sections of code, i.e., no control flow splits, then the array is effectively constant for the purpose of analyzing the call.

+ +

That is why the analysis does not accept Class[] arguments coming from static fields, since the contents of those can change at any time, even if the fields are final. +Although this may seem too restrictive, it covers the most commonly used patterns of the Reflection API calls.

+ +

The only exception to the constant arguments rule is that the ClassLoader argument of Class.forName(String, ClassLoader) does not need to be a constant; it is ignored and instead a class loader that can load all the classes on the class path is used. +The analysis runs to a fix point which means that a chain of calls like Class.forName(String).getMethod(String, Class[]) will first replace the class constant and then the method will effectively reduce this to java.lang.reflect.Method.

+ +

Following are examples of calls that can be intercepted and replaced with the corresponding element:

+ +
Class.forName("java.lang.Integer")
+Class.forName("java.lang.Integer", true, ClassLoader.getSystemClassLoader())
+Class.forName("java.lang.Integer").getMethod("equals", Object.class)
+Integer.class.getDeclaredMethod("bitCount", int.class)
+Integer.class.getConstructor(String.class)
+Integer.class.getDeclaredConstructor(int.class)
+Integer.class.getField("MAX_VALUE")
+Integer.class.getDeclaredField("value")
+
+ +

The following ways to declare and populate an array are equivalent from the point of view of the analysis:

+ +
Class<?>[] params0 = new Class<?>[]{String.class, int.class};
+Integer.class.getMethod("parseInt", params0);
+
+ +
Class<?>[] params1 = new Class<?>[2];
+params1[0] = Class.forName("java.lang.String");
+params1[1] = int.class;
+Integer.class.getMethod("parseInt", params1);
+
+ +
Class<?>[] params2 = {String.class, int.class};
+Integer.class.getMethod("parseInt", params2);
+
+ +

If a call cannot be processed it is simply skipped. +For these situations a manual configuration as described below can be provided.

+ +

Manual Configuration

+ +

A configuration that specifies the program elements that will be accessed reflectively can be provided as follows:

+
-H:ReflectionConfigurationFiles=/path/to/reflectconfig
+
+

Here, reflectconfig is a JSON file in the following format (use --expert-options for more details):

+
[
+  {
+    "name" : "java.lang.Class",
+    "queryAllDeclaredConstructors" : true,
+    "queryAllPublicConstructors" : true,
+    "queryAllDeclaredMethods" : true,
+    "queryAllPublicMethods" : true,
+    "allDeclaredClasses" : true,
+    "allPublicClasses" : true
+  },
+  {
+    "name" : "java.lang.String",
+    "fields" : [
+      { "name" : "value" },
+      { "name" : "hash" }
+    ],
+    "methods" : [
+      { "name" : "<init>", "parameterTypes" : [] },
+      { "name" : "<init>", "parameterTypes" : ["char[]"] },
+      { "name" : "charAt" },
+      { "name" : "format", "parameterTypes" : ["java.lang.String", "java.lang.Object[]"] }
+    ]
+  },
+  {
+    "name" : "java.lang.String$CaseInsensitiveComparator",
+    "queriedMethods" : [
+      { "name" : "compare" }
+    ]
+  }
+]
+
+ +

The configuration distinguishes between methods and constructors that can be invoked during execution via Method.invoke(Object, Object...) or Constructor.newInstance(Object...) and those that can not. +Including a function in the configuration without invocation capabilities helps the static analysis correctly assess its reachability status and results in smaller binary sizes. +The function metadata is then accessible at runtime like it would for any other registered reflection method or constructor, but trying to call the function will result in a runtime error. +The configuration fields prefixed by query or queried only include the metadata, while the other ones (e.g., methods) enable runtime invocation.

+ +

The native image builder generates reflection metadata for all classes, methods, and fields referenced in that file. +The queryAllPublicConstructors, queryAllDeclaredConstructors, queryAllPublicMethods, queryAllDeclaredMethods, allPublicConstructors, allDeclaredConstructors, allPublicMethods, allDeclaredMethods, allPublicFields, allDeclaredFields, allPublicClasses, and allDeclaredClasses attributes can be used to automatically include an entire set of members of a class.

+ +

However, allPublicClasses and allDeclaredClasses do not automatically register the inner classes for reflective access. +They just make them available via Class.getClasses() and Class.getDeclaredClasses() when called on the declaring class. +Code may also write non-static final fields like String.value in this example, but other code might not observe changes of final field values in the same way as for non-final fields because of optimizations. Static final fields may never be written.

+ +

More than one configuration can be used by specifying multiple paths for ReflectionConfigurationFiles and separating them with ,. +Also, -H:ReflectionConfigurationResources can be specified to load one or several configuration files from the build’s class path, such as from a JAR file.

+ +

Conditional Configuration

+ +

With conditional configuration, a class configuration entry is applied only if a provided condition is satisfied. +The only currently supported condition is typeReachable, which enables the configuration entry if the specified type is reachable through other code. +For example, to support reflective access to sun.misc.Unsafe.theUnsafe only when io.netty.util.internal.PlatformDependent0 is reachable, the configuration should look like:

+ +
{
+  "condition" : { "typeReachable" : "io.netty.util.internal.PlatformDependent0" },
+  "name" : "sun.misc.Unsafe",
+  "fields" : [
+    { "name" : "theUnsafe" }
+  ]
+}
+
+ +

Conditional configuration is the preferred way to specify reflection configuration: if code doing a reflective access is not reachable, it is unnecessary to include its corresponding reflection entry. +The consistent usage of condition results in smaller binaries and better build times as the image builder can selectively include reflectively accessed code.

+ +

If a condition is omitted, the element is always included. +When the same condition is used for two distinct elements in two configuration entries, both elements will be included when the condition is satisfied. +When a configuration entry should be enabled if one of several types are reachable, it is necessary to add two configuration entries: one entry for each condition.

+ +

When used with assisted configuration, conditional entries of existing configuration will not be fused with agent-collected entries.

+ +

Configuration with Features

+ +

Alternatively, a custom Feature implementation can register program elements before and during the analysis phase of the build using the RuntimeReflection class. For example:

+
class RuntimeReflectionRegistrationFeature implements Feature {
+  public void beforeAnalysis(BeforeAnalysisAccess access) {
+    try {
+      RuntimeReflection.register(String.class);
+      RuntimeReflection.register(String.class.getDeclaredField("value"));
+      RuntimeReflection.register(String.class.getDeclaredField("hash"));
+      RuntimeReflection.register(String.class.getDeclaredConstructor(char[].class));
+      RuntimeReflection.register(String.class.getDeclaredMethod("charAt", int.class));
+      RuntimeReflection.register(String.class.getDeclaredMethod("format", String.class, Object[].class));
+      RuntimeReflection.register(String.CaseInsensitiveComparator.class);
+      RuntimeReflection.register(String.CaseInsensitiveComparator.class.getDeclaredMethod("compare", String.class, String.class));
+    } catch (NoSuchMethodException | NoSuchFieldException e) { ... }
+  }
+}
+
+

To activate the custom feature --features=<fully qualified name of RuntimeReflectionRegistrationFeature class> needs to be passed to native-image. +Native Image Build Configuration explains how this can be automated with a native-image.properties file in META-INF/native-image.

+ +

Use of Reflection at Build Time

+ +

Reflection can be used without restrictions during the native binary generation, for example, in static initializers. +At this point, code can collect information about methods and fields and store them in their own data structures, which are then reflection-free at run time.

+ +

Unsafe Accesses

+ +

The Unsafe class, although its use is discouraged, provides direct access to the memory of Java objects. +The Unsafe.objectFieldOffset() method provides the offset of a field within a Java object. +Note that the offsets that are queried at native binary build time can be different from the offsets at run time.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/Resources/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/Resources/index.html new file mode 100644 index 0000000..2790e29 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/Resources/index.html @@ -0,0 +1,249 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Accessing Resources in Native Image

+ +

By default, the native-image builder will not integrate any of the resources that are on the classpath into the native executable. +To make calls such as Class.getResource() or Class.getResourceAsStream() (or their corresponding ClassLoader methods) return specific resources (instead of null), you must specify the resources that should be accessible at runtime. +This can be achieved using a configuration file with the following content:

+ +
{
+  "resources": {
+    "includes": [
+      {"pattern": "<Java regexp that matches resource(s) to be included in the executable>"},
+      {"pattern": "<another regexp>"},
+      ...
+    ],
+    "excludes": [
+      {"pattern": "<Java regexp that matches resource(s) to be excluded from the executable>"},
+      {"pattern": "<another regexp>"},
+      ...
+    ]
+  }
+}
+
+ +

Provide the configuration file’s path to the native-image tool using the option -H:ResourceConfigurationFiles=/path/to/resource-config.json. +Alternatively, you can specify individual resource paths directly to the native-image tool as follows:

+ +
native-image -H:IncludeResources="<Java regexp that matches resources to be included in the executable>" -H:ExcludeResources="<Java regexp that matches resources to be excluded from the executable>" ...
+
+

You can pass the -H:IncludeResources and -H:ExcludeResources options several times to define more than one regexp to include or exclude resources, respectively.

+ +

To see which resources are included in the native executable, use the option -H:Log=registerResource:<log level>. The <log level> argument must be in the range 1 to 5 (from least detailed to most detailed). A log level of 3 provides brief details of the included resources.

+ +

Example Usage

+ +

Given this project structure:

+
my-app-root
+└── src
+    ├── main
+    │   └── com.my.app
+    │       ├── Resource0.txt
+    │       └── Resource1.txt
+    └── resources
+        ├── Resource2.txt
+        └── Resource3.txt
+
+

Then:

+ +
    +
  • All resources can be loaded with ".*/Resource.*txt$", specified as {"pattern":".*/Resource.*txt$"} in a configuration file, or -H:IncludeResources=".*/Resource.*txt$" on the command line.
  • +
  • Resource0.txt can be loaded with .*/Resource0.txt$.
  • +
  • Resource0.txt and Resource1.txt can be loaded with .*/Resource0.txt$ and .*/Resource1.txt$ + (or alternatively with a single .*/(Resource0|Resource1).txt$).
  • +
  • Also, if we want to include everything except the Resource2.txt file, we can simply exclude it using -H:IncludeResources=".*/Resource.*txt$" followed by -H:ExcludeResources=".*/Resource2.txt$".
  • +
+ +

Check this guide which illustrates how to include a resource into a native executable.

+ +

Locales

+ +

It is also possible to specify which locales should be included in the executable and which should be the default. +For example, to switch the default locale to Swiss German and also include French and English, use the following options:

+
native-image -Duser.country=CH -Duser.language=de -H:IncludeLocales=fr,en
+
+

The locales are specified using language tags. You can include all +locales via -H:+IncludeAllLocales, but note that it increases the size of the resulting +executable.

+ +

Resource Bundles

+ +

Java localization support (java.util.ResourceBundle) enables Java code to load L10N resources and show the user messages suitable for runtime settings such as time, locale, and format.

+ +

Native Image needs knowledge ahead-of-time of the resource bundles your application needs so that it can load and store the appropriate bundles for usage in the generated executable. +The bundles can be specified in the resource configuration file (see above), in the bundles section:

+ +
{
+  "bundles": [
+    {"name":"your.pkg.Bundle"},
+    {"name":"another.pkg.Resource"},
+    {"name":"etc.Bundle"}
+  ],
+  "resources": <see above>
+}
+
+ +

Alternatively, bundles can be specified directly as options to the native-image tool as follows:

+
native-image -H:IncludeResourceBundles=your.pgk.Bundle,another.pkg.Resource,etc.Bundle ...
+
+

By default, requested bundles are included for all requested locales. +To optimize this, it is possible to use IncludeResourceBundles with a locale-specific substring, for example -H:+IncludeResourceBundles=com.company.bundles.MyBundle_fr-FR will only include the bundle in French.

+ +

Resources in Java Modules

+ +

Wherever resources are specified with <Java regexp that matches resources to be included in the image> or resource bundles are specified via bundle name, it is possible to specify the exact modules from which these resources or bundles should be taken. To do so, specify the module name before the resource-regex or bundle name with : as the separator. For example:

+ +
{
+   "resources": {
+      "includes": [
+         {
+            "pattern": "library-module:^resource-file.txt$"
+         }
+      ]
+   },
+   "bundles": [
+      {"name":"main-module:your.pkg.Bundle"}
+   ]
+}
+
+ +

This will cause the native-image tool to only include resource-file.txt from the Java module library-module. If other modules or the classpath contains resources that match the pattern ^resource-file.txt$ only the one in module library-module is registered for inclusion in the executable. Similarly, if other bundles are accessible with the same bundle name your.pkg.Bundle only the one from main-module is included. Native image will also ensure that the modules are guaranteed to be accessible at runtime. That is, the following code pattern:

+
InputStream resource = ModuleLayer.boot().findModule(moduleName).getResourceAsStream(resourcePath);
+
+

will always work as expected for resources registered as described above (even if the module does not contain any code that is considered reachable by static analysis).

+ +

Java VM Mode of Localization

+ +

Resource Bundle lookup is a complex and dynamic mechanism which utilizes a lot of Java VM infrastructure. +As a result, it causes the size of the executable to increase for smaller applications such as HelloWorld. +Therefore, an optimized mode is set by default in which this lookup is simplified utilizing the fact that all bundles are known ahead of build time. +For the original Java VM lookup, use the -H:-LocalizationOptimizedMode option.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/URLProtocols/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/URLProtocols/index.html new file mode 100644 index 0000000..395614a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/URLProtocols/index.html @@ -0,0 +1,159 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

URL Protocols in Native Image

+ +

URL Protocols in Native Image can be divided into three classes:

+ +
    +
  • supported and enabled by default
  • +
  • supported and disabled by default
  • +
  • HTTPS support
  • +
+ +

URL Protocols that are supported and enabled by default will be included into every generated native binary. +Currently, file and resource are the only supported URL protocols enabled by default.

+ +

There are URL Protocols that are supported but not enabled by default when building a native binary. +They must be enabled during build time by using the --enable-url-protocols=<protocols> option on the command line. +The option accepts a list of comma-separated protocols.

+ +

The rationale behind enabling protocols on-demand is that you can start with a minimal binary and add features as you need them. +This way your binary will only include the features you use, which helps keep the overall size small. +Currently http and https are the only URL protocols that are supported and can be enabled on demand. +They can be enabled using the --enable-http and --enable-https command-line options.

+ +

HTTPS Support

+

Support for the https URL protocol relies on the Java Cryptography Architecture (JCA) framework. +Thus enabling https will add the code required by the JCA to the generated binary, including statically linked native libraries that the JCA may depend on. +See the documentation on security services for more details.

+ +

No other URL protocols are currently tested. +They can still be enabled using --enable-url-protocols=<protocols>, however they might not work as expected.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/index.html new file mode 100644 index 0000000..5bbc98c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/dynamic-features/index.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Dynamic Features of Java

+ +

When you build a native image, it only includes the reachable elements starting from your application entry point, its dependent libraries, and the JDK classes discovered through a static analysis. +However, the reachability of some elements may not be discoverable due to Java’s dynamic features including reflection, resource access, etc. +If an element is not reachable, it will not be included in the generated binary and this can lead to run time failures.

+ +

Thus, some dynamic Java features may require special “treatment” such as a command line option or provisioning metadata to be compatible with ahead-of-time compilation using Native Image.

+ +

The reference information here explains how Native Image handles some dynamic features of Java:

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/features/JCASecurityServices/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/features/JCASecurityServices/index.html new file mode 100644 index 0000000..c25c963 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/features/JCASecurityServices/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/featuresJCASecurityServices/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/featuresJCASecurityServices/index.html new file mode 100644 index 0000000..c25c963 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/featuresJCASecurityServices/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/access-environment-variables/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/access-environment-variables/index.html new file mode 100644 index 0000000..0f4daec --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/access-environment-variables/index.html @@ -0,0 +1,58 @@ +

Access Environment Variables in a Native Executable at Run Time

+ +

A native executable accesses your environment variables in the same way as a regular Java application. +For example, assume you have the following source code:

+ +
import java.util.Map;
+
+public class EnvMap {
+    public static void main (String[] args) {
+        var filter = args.length > 0 ? args[0] : "";
+        Map<String, String> env = System.getenv();
+        for (String envName : env.keySet()) {
+            if(envName.contains(filter)) {
+                System.out.format("%s=%s%n",
+                                envName,
+                                env.get(envName));
+            }
+        }
+    }
+}
+
+ +

This code iterates over your environment variables and prints out the ones that contain the String of characters passed as the command-line argument.

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. Compile the file and build a native executable, as follows: +
     javac EnvMap.java
    +
    +
     native-image EnvMap
    +
    +
  4. +
  5. Run the resulting native executable and pass a command-line argument, such as “HELLO”. There should be no output, because there is no environment variable with a matching name. +
     ./envmap HELLO
    + <no output>
    +
    +
  6. +
  7. Create a new environment variable named “HELLOWORLD” and give it the value “Hello World!”. (If you are using a bash shell, follow the example below.) Now, run the native executable again–it will correctly print out the name and value of the matching environment variable(s). +
     export HELLOWORLD='Hello World!'
    +
    +
     ./envmap HELLO
    +
    +

    You should receive the expected output:

    +
     HELLOWORLD=Hello World!
    +
    +
  8. +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/add-logging-to-native-executable/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/add-logging-to-native-executable/index.html new file mode 100644 index 0000000..dfd2a9c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/add-logging-to-native-executable/index.html @@ -0,0 +1,119 @@ +

Add Logging to a Native Executable

+ +

By default, a native executable produced by Native Image supports logging via the java.util.logging.* API.

+ +

Default Logging Configuration

+ +

The default logging configuration in a native executable is based on the logging.properties file found in the JDK. +This file configures a java.util.logging.ConsoleHandler which will only show messages at the INFO level and above. +Custom logging configuration can be loaded either at executable build time or at runtime as described below.

+ +

If you require additional logging handlers, you must register the corresponding classes for reflection. +For example, if you use java.util.logging.FileHandler then provide the following reflection configuration:

+
{
+    "name" : "java.util.logging.FileHandler",
+    "methods" : [
+      { "name" : "<init>", "parameterTypes" : [] },
+    ]
+  }
+
+

For more details, see Reflection Support.

+ +

Initializing a Logger at Build Time

+ +

The logger can be initialized at executable build time with a custom logging.properties configuration file, as illustrated in following example.

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. Save the following Java code into a file named LoggerBuildTimeInit.java, then compile it using javac: +
     import java.io.IOException;
    + import java.util.logging.Level;
    + import java.util.logging.LogManager;
    + import java.util.logging.Logger;
    +
    + public class LoggerBuildTimeInit {
    +     private static final Logger LOGGER;
    +     static {
    +         try {
    +             LogManager.getLogManager().readConfiguration(LoggerBuildTimeInit.class.getResourceAsStream("/logging.properties"));
    +         } catch (IOException | SecurityException | ExceptionInInitializerError ex) {
    +             Logger.getLogger(LoggerBuildTimeInit.class.getName()).log(Level.SEVERE, "Failed to read logging.properties file", ex);
    +         }
    +         LOGGER = Logger.getLogger(LoggerBuildTimeInit.class.getName());
    +     }
    +
    +     public static void main(String[] args) throws IOException {
    +         LOGGER.log(Level.WARNING, "Danger, Will Robinson!");
    +     }
    + } 
    +
    +
  4. +
  5. +

    Download the logging.properties resource file and save it in the same directory as LoggerBuildTimeInit.java.

    +
  6. +
  7. Build and run the native executable: +
     native-image LoggerBuildTimeInit --initialize-at-build-time=LoggerBuildTimeInit
    +
    +
     ./loggerbuildtimeinit
    +
    +

    It should produce output that looks similar to:

    +
     WARNING: Danger, Will Robinson! [Wed May 18 17:20:39 BST 2022]
    +
    + +

    This demonstrates that the logging.properties file is processed at when the executable is built. + The file does not need to be included in the native executable and reduces the size of the resulting executable file.

    + +

    LoggerHolder.LOGGER is also initialized at build time and is readily available at runtime, therefore improving the startup time. +Unless your application needs to process a custom logging.properties configuration file at runtime, this approach is recommended.

    +
  8. +
+ +

Initializing a Logger at Run Time

+ +

The logger can also be initialized at run time, as shown in the following example.

+ +
    +
  1. +

    Save the following Java code into a file named LoggerRunTimeInit.java, then compile it using javac:

    + +
     import java.io.IOException;
    + import java.util.logging.Level;
    + import java.util.logging.LogManager;
    + import java.util.logging.Logger;
    +    
    + public class LoggerRunTimeInit {
    +     public static void main(String[] args) throws IOException {
    +         LogManager.getLogManager().readConfiguration(LoggerRunTimeInit.class.getResourceAsStream("/logging.properties"));
    +         Logger logger = Logger.getLogger(LoggerRunTimeInit.class.getName());
    +         logger.log(Level.WARNING, "Danger, Will Robinson!");
    +     }
    + }
    +
    +
  2. +
  3. +

    Download the logging.properties resource file and save it in the same directory as LoggerRunTimeInit.java.

    +
  4. +
  5. +

    Build and run the native executable

    +
     native-image LoggerRunTimeInit -H:IncludeResources="logging.properties"
    +
    +
     ./loggerruntimeinit
    +
    +

    It should produce output that looks similar to:

    +
     WARNING: Danger, Will Robinson! [Wed May 18 17:22:40 BST 2022]
    +
    + +

    In this case, the logging.properties file needs to be available for runtime processing and it must be included in the executable via the -H:IncludeResources=logging.properties option. For more details on this option, see Use of Resources in a Native Executable.

    +
  6. +
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-and-run-native-executable-with-jfr/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-and-run-native-executable-with-jfr/index.html new file mode 100644 index 0000000..e2e6019 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-and-run-native-executable-with-jfr/index.html @@ -0,0 +1,103 @@ +

Build and Run Native Executables with JFR

+ +

JDK Flight Recorder (JFR) is a tool for collecting diagnostic and profiling data about a running Java application, built into the JVM. +GraalVM Native Image supports JFR events and users can use the jdk.jfr.Event API with a similar experience to using JFR in the Java HotSpot VM.

+ +

To record JFR events when running a native executable, enable JFR support and JFR recording as described in this guide.

+ +
+

Note: JFR event recording is not yet supported on GraalVM JDK for Windows.

+
+ +

Enable JFR Support and Record Events at Run Time

+ +

To build a native executable with the JFR events support, you first need to add the --enable-monitoring=jfr option when invoking the native-image tool. Then enable the system, start a recording, and configure logging at native executable run time:

+
    +
  • -XX:+FlightRecorder: use to enable JFR at run time
  • +
  • -XX:StartFlightRecording: use to start a recording on application’s startup
  • +
  • -XX:FlightRecorderLogging: use to configure the log output for the JFR system
  • +
+ +

Follow the steps below to practice building a native executable with JFR support and recording events at run time.

+ +
+

Note: Make sure you have installed a GraalVM JDK. The easiest way to get started is with SDKMAN!. For other installation options, visit the Downloads section.

+
+ +
    +
  1. Install VisualVM by running: +
     gu install visualvm
    +
    +
  2. +
  3. +

    Save the following code to the file named JFRDemo.java.

    + +
     import jdk.jfr.Event;
    + import jdk.jfr.Description;
    + import jdk.jfr.Label;
    +
    + public class JFRDemo {
    +
    +   @Label("Hello World")
    +   @Description("Build and run a native executable with JFR.")
    +   static class HelloWorldEvent extends Event {
    +       @Label("Message")
    +       String message;
    +   }
    +
    +   public static void main(String... args) {
    +       HelloWorldEvent event = new HelloWorldEvent();
    +       event.message = "Hello, World!";
    +       event.commit();
    +   }
    + }
    +
    + +

    This demo application consists of a simple class and JDK library classes. + It creates an event, labelled with the @Label annotation from the jdk.jfr.* package. + If you run this application, it will not print anything and just run that event.

    +
  4. +
  5. Compile the Java file using the GraalVM JDK: +
     $JAVA_HOME/bin/javac JFRDemo.java
    +
    +

    It creates two class files: JFRDemo$HelloWorldEvent.class and JFRDemo.class.

    +
  6. +
  7. Build a native executable with the VM inspection enabled: +
     $JAVA_HOME/bin/native-image --enable-monitoring=jfr JFRDemo
    +
    +

    The --enable-monitoring=jfr option enables features such as JFR that can be used to inspect the VM.

    +
  8. +
  9. Run the executable and start recording: +
     ./jfrdemo -XX:StartFlightRecording="filename=recording.jfr"
    +
    +

    This command runs the application as a native executable. The -XX:StartFlightRecording option enables the built-in Flight Recorder and starts recording to a specified binary file, recording.jfr.

    +
  10. +
  11. +

    Start VisualVM to view the contents of the recording file in a user-friendly way. GraalVM provides VisualVM in the core installation. To start the tool, run:

    + +
     $JAVA_HOME/bin/jvisualvm
    +
    +
  12. +
  13. +

    Go to File, then Add JFR Snapshot, browse recording.jfr, and open the selected file. Confirm the display name and click OK. Once opened, there is a bunch of options you can check: Monitoring, Threads, Exceptions, etc., but you should be mostly interested in the events browsing. It will look something like this:

    + +

    JDK Flight Recorder

    + +

    Alternatively, you can view the contents of the recording file in the console window by running this command:

    + +
     $JAVA_HOME/bin/jfr print recording.jfr
    +
    +

    It prints all the events recorded by Flight Recorder.

    +
  14. +
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-and-run-native-executable-with-remote-jmx/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-and-run-native-executable-with-remote-jmx/index.html new file mode 100644 index 0000000..f8d8149 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-and-run-native-executable-with-remote-jmx/index.html @@ -0,0 +1,162 @@ +

Build and Run Native Executables with Remote JMX

+ +

Remote management using Java Management Extensions (JMX) is possible in executables built with GraalVM Native Image.

+ +
+

Note: The feature is experimental.

+
+ +

This guide covers the steps required to build, run, and interact with such a native executable using JMX. +It also shows you how to register a custom managed bean (MBean) with the JMX server and the additional steps required for it to work with Native Image.

+ +

Currently Supported Features and Limitations

+ +

A JMX connection from a client to a remote MBean server is supported. +The client, the server, or both may be a native executable. +Only MXBeans, and standard user-defined MBeans, are supported. +Dynamic and model MBeans are not supported because their management interfaces are defined at run time. +Although remote management of MXBeans is supported, not all platform MXBean functionality is implemented or is applicable in Native Image. Additionally, to define and use standard MBeans, you must specify metadata configuration. +This is further explained in this guide.

+ +

Step 1: Create a Demo Application

+ +

Create a demo application in a directory named demo. +Change your working directory to there and run the commands from that directory.

+ +

Save the following code to a file named SimpleJmx.java. +The main() method registers a custom MBean, then loop endlessly, so you have time to inspect the process using VisualVM.

+ +
import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import java.lang.management.ManagementFactory;
+
+public class SimpleJmx {
+    public static void main(String args[]) throws Exception {
+        ObjectName objectName = new ObjectName("com.jmx.test.basic:name=simple");
+        Simple simple = new Simple();
+        simple.setName("someName");
+        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+        server.registerMBean(simple, objectName);
+        while (true) {
+            Thread.sleep(1000);
+            System.out.println("JMX server running...");
+        }
+    }
+
+    public static interface SimpleMBean {
+        String getName();
+
+        void setName(String name);
+
+        String print();
+    }
+
+    static class Simple implements SimpleMBean {
+        private String name;
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String print() {
+            return "Print output " + name;
+        }
+    }
+}
+
+ +

Step 2: Compile to Java Bytecode

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. +

    Compile the Java file using the GraalVM JDK:

    +
     $JAVA_HOME/bin/javac SimpleJmx.java
    +
    +

    This creates SimpleJmx.class, SimpleJmx$Simple.class, and SimpleJmx$SimpleMBean.class files.

    +
  4. +
+ +

Step 3: Make a Dynamic Proxy Configuration

+ +

JMX uses dynamic proxies, a dynamic feature of Java, to access MBeans. +To be able to interact with the custom SimpleMBean at run time, you need to provide Native Image with additional dynamic proxy configuration for the MBean interface. +For this, create a JSON file named proxy-config.json with the following contents:

+ +
[
+  { "interfaces": [ "SimpleJmx$SimpleMBean"] }
+]
+
+ +

In the next step, we will pass this JSON file to the native-image builder.

+ +

Step 4: Build a Native Executable with JMX Support

+ +

Build a native executable with VM monitoring enabled:

+ +
$JAVA_HOME/bin/native-image --enable-monitoring=jmxserver,jvmstat  -H:DynamicProxyConfigurationFiles=proxy-config.json SimpleJmx
+
+ +

The --enable-monitoring=jmxserver option enables the JMX Server feature which allows accepting incoming connections. +The --enable-monitoring=jmxclient option enables the JMX Client feature which allows making outgoing connections. +Both features can be used together, comma-separated, for example, --enable-monitoring=jmxserver,jmxclient. +The jvmstat option should also be included if you want to enable discovery by VisualVM and other JVMs: --enable-monitoring=jmxserver,jmxclient,jvmstat.

+ +

Step 5: Run the Executable with JMX Properties

+ +

Now run your native executable with JMX properties:

+ +
./simplejmx -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=9996 -Dcom.sun.management.jmxremote.ssl=false
+
+

This starts the application as a simple JMX server, without password authentication or SSL using port 9996. +You can configure JMX to apply all the usual properties as shown in this guide, but this example uses a basic configuration for simplicity.

+ +

Step 6: Inspect Using VisualVM

+ +
    +
  1. +

    Start VisualVM to view the managed beans in a user-friendly way. +Note that VisualVM is shipped separately and should be first added to GraalVM using gu, and then started:

    + +
     $JAVA_HOME/bin/gu install visualvm
    + $JAVA_HOME/bin/visualvm
    +
    +
  2. +
  3. +

    Make sure you have the “VisualVM-MBeans” plugin installed (Tools > Plugins > Available Plugins > select “VisualVM-MBeans” and click Install).

    +
  4. +
  5. +

    Go to the Applications tab and select the SimpleJmx process. +From there you can select the MBeans tab.

    + +

    Remote JMX

    +
  6. +
  7. +

    In the MBeans tab, you can inspect the custom MBean you created earlier and perform operations on it.

    + +

    Custom MBean Attributes

    + +

    Custom MBean Operations

    +
  8. +
+ +

To conclude, Native Image now provides support for remote management using JMX. +Users can enable the JMX agent in a native executable to monitor a client application running on a remote system.

+ + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-java-modules-into-native-executable/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-java-modules-into-native-executable/index.html new file mode 100644 index 0000000..3a60753 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-java-modules-into-native-executable/index.html @@ -0,0 +1,70 @@ +

Build Java Modules into a Native Executable

+ +

GraalVM Native Image supports the Java Platform Module System, introduced in Java 9, which means you can convert a modularized Java application into a native executable.

+ +

The native-image tool accepts the module-related arguments like --module (-m), --module-path (-p), --add-opens, --add-exports (same as for the java launcher). +When such a module-related argument is used, the native-image tool itself is used as a module too.

+ +

In addition to supporting --add-reads and --add-modules, all module related options are considered prior to scanning the module path. +This helps prevent class loading errors and allow for better module introspection at run time.

+ +

The command to build a native executable from a Java module is:

+
native-image [options] --module <module>[/<mainclass>] [options]
+
+ +

Run a Demo

+ +

Follow the steps below to practice building a modular Java application into a native executable. +For the demo, you will use a simple HelloWorld Java module gathered with Maven:

+ +
├── hello
+│   └── Main.java
+│       > package hello;
+│       > 
+│       > public class Main {
+│       >     public static void main(String[] args) {
+│       >         System.out.println("Hello from Java Module: "
+│       >             + Main.class.getModule().getName());
+│       >     }
+│       > }
+│
+└── module-info.java
+    > module HelloModule {
+    >     exports hello;
+    > }
+
+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. Download or clone the demos repository and navigate to the directory native-hello-module: +
     git clone https://github.com/graalvm/graalvm-demos
    + cd graalvm-demos/native-hello-module
    +
    +
  4. +
  5. Compile and package the project with Maven: +
     ./mvnw package
    +
    +
  6. +
  7. Test running it on GraalVM’s JDK: +
     $JAVA_HOME/bin/java --module-path target/HelloModule-1.0-SNAPSHOT.jar --module HelloModule
    +
    +
  8. +
  9. Now build this module into a native executable: +
     $JAVA_HOME/bin/native-image --module-path target/HelloModule-1.0-SNAPSHOT.jar --module HelloModule
    +
    + +

    It builds the modular Java application into a native executable called hellomodule in the project root directory that you can execute:

    +
     ./hellomodule
    +
    +
  10. +
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-native-executable-from-jar/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-native-executable-from-jar/index.html new file mode 100644 index 0000000..36eb64a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-native-executable-from-jar/index.html @@ -0,0 +1,84 @@ +

Build a Native Executable from a JAR File

+ +

You can build a native executable from a class file, from a JAR file, or from a module. This guide demonstrates how to build a native executable from a JAR file.

+ +

To build a native executable from a JAR file in the current working directory, use the following command:

+
native-image [options] -jar jarfile [executable name]
+
+
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. +

    Prepare the application.

    + +
      +
    • +

      Create a new Java project named “App”, for example in your favorite IDE or from your terminal, with the following structure:

      + +
        | src
      +  |   --com/
      +  |      -- example
      +  |          -- App.java
      +
      +
    • +
    • +

      Add the following Java code to the src/com/example/App.java file:

      + +
        package com.example;
      +
      +  public class App {
      +
      +      public static void main(String[] args) {
      +          String str = "Native Image is awesome";
      +          String reversed = reverseString(str);
      +          System.out.println("The reversed string is: " + reversed);
      +      }
      +
      +      public static String reverseString(String str) {
      +          if (str.isEmpty())
      +              return str;
      +          return reverseString(str.substring(1)) + str.charAt(0);
      +      }
      +  }
      +
      + +

      This is a small Java application that reverses a String using recursion.

      +
    • +
    +
  4. +
  5. Compile the application: +
     javac -d build src/com/example/App.java
    +
    + +

    This produces the file App.class in the build/com/example directory.

    +
  6. +
  7. Create a runnable JAR file: +
     jar --create --file App.jar --main-class com.example.App -C build .
    +
    + +

    It will generate a runnable JAR file, named App.jar, in the project root directory: + To view its contents, run the command jar tf App.jar.

    +
  8. +
  9. Create a native executable: +
     native-image -jar App.jar
    +
    + +

    It will produce a native executable in the project root directory.

    +
  10. +
  11. Run the native executable: +
     ./App
    +
    +
  12. +
+ +

The default behavior of native-image is aligned with the java command which means you can pass the -jar, -cp, -m options to build with Native Image as you would normally do with java. For example, java -jar App.jar someArgument becomes native-image -jar App.jar and ./App someArgument.

+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-native-shared-library/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-native-shared-library/index.html new file mode 100644 index 0000000..700e73b --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-native-shared-library/index.html @@ -0,0 +1,181 @@ +

Build a Native Shared Library

+ +

To build a native shared library, pass the command-line argument --shared to the native-image tool, as follows

+ +
native-image <class name> --shared
+
+ +

To build a native shared library from a JAR file, use the following syntax:

+
native-image -jar <jarfile> --shared
+
+ +

The resulting native shared library will have the main() method of the given Java class as its entrypoint method.

+ +

If your library doesn’t include a main() method, use the -H:Name= command-line option to specify the library name, as follows:

+ +
native-image --shared -H:Name=<libraryname> <class name>
+native-image --shared -jar <jarfile> -H:Name=<libraryname>
+
+ +

GraalVM makes it easy to use C to call into a native shared library. +There are two primary mechanisms for calling a method (function) embedded in a native shared library: the Native Image C API and the JNI Invocation API.

+ +

This guide describes how to use the Native Image C API. It consists of the following steps:

+
    +
  1. Create and compile a Java class library containing at least one entrypoint method.
  2. +
  3. Use the native-image tool to create a shared library from the Java class library.
  4. +
  5. Create and compile a C application that calls an entrypoint method in the shared library.
  6. +
+ +

Tips and Tricks

+ +

The shared library must have at least one entrypoint method. +By default, only a method named main(), originating from a public static void main() method, is identified as an entrypoint and callable from a C application.

+ +

To export any other Java method:

+ +
    +
  • Declare the method as static.
  • +
  • Annotate the method with @CEntryPoint (org.graalvm.nativeimage.c.function.CEntryPoint).
  • +
  • Make one of the method’s parameters of type IsolateThread or Isolate, for example, the first parameter (org.graalvm.nativeimage.IsolateThread) in the method below. This parameter provides the current thread’s execution context for the call.
  • +
  • Restrict your parameter and return types to non-object types. These are Java primitive types including pointers, from the org.graalvm.nativeimage.c.type package.
  • +
  • Provide a unique name for the method. If you give two exposed methods the same name, the native-image builder will fail with the duplicate symbol message. If you do not specify the name in the annotation, you must provide the -H:Name=libraryName flag at build time.
  • +
+ +

Below is an example of an entrypoint method:

+ +
@CEntryPoint(name = "function_name")
+static int add(IsolateThread thread, int a, int b) {
+    return a + b;
+}
+
+ +

When the native-image tool builds a native shared library, it also generates a C header file. +The header file contains declarations for the Native Image C API (which enables you to create isolates and attach threads from C code) as well as declarations for each entrypoint in the shared library. +The native-image tool generates a C header file containing the following C declaration for the example above:

+
int add(graal_isolatethread_t* thread, int a, int b);
+
+ +

A native shared library can have an unlimited number of entrypoints, for example to implement callbacks or APIs.

+ +

Run a Demo

+ +

In the following example, you’ll create a small Java class library (containing one class), use native-image to create a shared library from the class library, and then create a small C application that uses the shared library. +The C application takes a string as its argument, passes it to the shared library, and prints environment variables that contain the argument.

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. Then install the LLVM toolchain: +
     gu install llvm-toolchain
    +
    +
    +

    Note: The llvm-toolchain GraalVM component is not available on Microsoft Windows.

    +
    +
  4. +
  5. +

    Save the following Java code to a file named LibEnvMap.java:

    + +
     import java.util.Map;
    + import org.graalvm.nativeimage.IsolateThread;
    + import org.graalvm.nativeimage.c.function.CEntryPoint;
    + import org.graalvm.nativeimage.c.type.CCharPointer;
    + import org.graalvm.nativeimage.c.type.CTypeConversion;
    +
    + public class LibEnvMap {
    +     //NOTE: this class has no main() method
    +
    +     @CEntryPoint(name = "filter_env")
    +     private static int filterEnv(IsolateThread thread, CCharPointer cFilter) {
    +         String filter = CTypeConversion.toJavaString(cFilter);
    +         Map<String, String> env = System.getenv();
    +         int count = 0;
    +         for (String envName : env.keySet()) {
    +             if(!envName.contains(filter)) continue;
    +             System.out.format("%s=%s%n",
    +                             envName,
    +                             env.get(envName));
    +             count++;
    +         }
    +         return count;
    +     }
    + }
    +
    +

    Notice how the method filterEnv() is identified as an entrypoint using the @CEntryPoint annotation and the method is given a name as a argument to the annotation.

    +
  6. +
  7. Compile the Java code and build a native shared library, as follows: +
     $JAVA_HOME/bin/javac LibEnvMap.java
    +
    +
     $JAVA_HOME/bin/native-image -H:Name=libenvmap --shared 
    +
    + +

    It will produce the following artifacts:

    +
     --------------------------------------------------
    + Produced artifacts:
    + /demo/libenvmap.dylib (shared_lib)
    + /demo/libenvmap.h (header)
    + /demo/graal_isolate.h (header)
    + /demo/libenvmap_dynamic.h (header)
    + /demo/graal_isolate_dynamic.h (header)
    + /demo/libenvmap.build_artifacts.txt
    + ==================================================
    +
    + +

    If you work with C or C++, use these header files directly. For other languages, such as Java, use the function declarations in the headers to set up your foreign call bindings.

    +
  8. +
  9. +

    Create a C application, main.c, in the same directory containing the following code:

    + +
     #include <stdio.h>
    + #include <stdlib.h>
    +
    + #include "libenvmap.h"
    +
    + int main(int argc, char **argv) {
    + if (argc != 2) {
    +     fprintf(stderr, "Usage: %s <filter>\n", argv[0]);
    +     exit(1);
    + }
    +
    + graal_isolate_t *isolate = NULL;
    + graal_isolatethread_t *thread = NULL;
    +
    + if (graal_create_isolate(NULL, &isolate, &thread) != 0) {
    +     fprintf(stderr, "initialization error\n");
    +     return 1;
    + }
    +
    + printf("Number of entries: %d\n", filter_env(thread, argv[1]));
    +
    + graal_tear_down_isolate(thread);
    + }
    +
    + +

    The statement #include "libenvmap.h" loads the native shared library.

    +
  10. +
  11. Compile the C application using clang. +
     $JAVA_HOME/languages/llvm/native/bin/clang -I ./ -L ./ -l envmap -Wl,-rpath ./ -o main main.c 
    +
    +
  12. +
  13. Run the C application by passing a string as an argument. For example: +
     ./main USER
    +
    +

    It will correctly print out the name and value of the matching environment variable(s).

    +
  14. +
+ +

The advantage of using the Native Image C API is that you can determine what your API will look like. +The restriction is that your parameter and return types must be non-object types. +If you want to manage Java objects from C, you should consider JNI Invocation API.

+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-polyglot-native-executable/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-polyglot-native-executable/index.html new file mode 100644 index 0000000..e5341e0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-polyglot-native-executable/index.html @@ -0,0 +1,103 @@ +

Build a Polyglot Native Executable (Java and JavaScript)

+ +

With GraalVM Polyglot API you can embed and run code from a guest language in a Java-based host application. +GraalVM makes it possible to ahead-of-time compile a Java application with embedded JavaScript too and create a polyglot native executable. +Embedding Reference on how to interact with a guest language like JavaScript from a Java host application for more information.

+ +

This guide will show how to build a polyglot native executable with Java host language and JavaScript as a guest language.

+ +

For a demo, you will use this JSON Pretty Printer Java application that prints the output in the JSON format:

+ +
import java.io.*;
+import java.util.stream.*;
+import org.graalvm.polyglot.*;
+
+public class PrettyPrintJSON {
+  public static void main(String[] args) throws java.io.IOException {
+    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+    String input = reader.lines()
+    .collect(Collectors.joining(System.lineSeparator()));
+    try (Context context = Context.create("js")) {
+      Value parse = context.eval("js", "JSON.parse");
+      Value stringify = context.eval("js", "JSON.stringify");
+      Value result = stringify.execute(parse.execute(input), null, 2);
+      System.out.println(result.asString());
+    }
+  }
+} 
+
+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. +

    Install the JavaScript runtime:

    + +
     gu install js
    +
    +
  4. +
  5. +

    Save the above application in a file named PrettyPrintJSON.java and compile it:

    + +
     javac PrettyPrintJSON.java
    +
    +
  6. +
  7. +

    Build a native executable by enabling the JavaScript interoperability:

    + +
     native-image --language:js PrettyPrintJSON
    +
    +

    The --language:js argument ensures that JavaScript is available in the generated image. + It will take several minutes as it does not just build the executable, but also pulls in the JavaScript engine.

    + +
    +

    Note: Building a polyglot native executable requires more physical memory because the Truffle framework is included.

    +
    +
  8. +
  9. +

    Run the resulting executable and perform some pretty-printing:

    + +
     ./prettyprintjson <<EOF
    + {"GraalVM":{"description":"Language Abstraction Platform","supports":["combining languages","embedding languages","creating native images"],"languages": ["Java","JavaScript","Node.js", "Python", "Ruby","R","LLVM"]}}
    + EOF
    +
    +

    The expected output is:

    + +
     {
    + "GraalVM": {
    +     "description": "Language Abstraction Platform",
    +     "supports": [
    +     "combining languages",
    +     "embedding languages",
    +     "creating native images"
    +     ],
    +     "languages": [
    +     "Java",
    +     "JavaScript",
    +     "Node.js",
    +     "Python",
    +     "Ruby",
    +     "R",
    +     "LLVM"
    +     ]
    + }
    + }
    +
    +
  10. +
+ +

The native executable version runs faster than running the same application on the JVM.

+ +
+

Note: JavaScript support by GraalVM Native Image is considered general availability. The remaining languages support is experimental.

+
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-static-executables/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-static-executables/index.html new file mode 100644 index 0000000..bef4ebc --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-static-executables/index.html @@ -0,0 +1,141 @@ +

Build a Statically Linked or Mostly-Statically Linked Native Executable

+ +

GraalVM Native Image by default builds dynamically linked binaries: at build time it loads your application classes and interfaces and hooks them together in a process of dynamic linking.

+ +

However, you can create a statically linked or mostly-static linked native executable, depending on your needs.

+ +

A static native executable is a statically linked binary that can be used without any additional library dependencies. +A static native executable is easy to distribute and deploy on a slim or distroless container (a scratch container). +You can create a static native executable by statically linking it against musl-libc, a lightweight, fast and simple libc implementation.

+ +

A mostly-static native executable is a binary that links everything (zlib, JDK shared libraries) except the standard C library, libc. This is an alternative option to staticly linking everything. Also, depending on the user’s code, it may link libstdc+ and libgcc. +This approach is ideal for deployment on a distroless container image.

+ +
+

Note: This currently only works when linked against libc.

+
+ +

This guide shows how you can take advantage of Native Image linking options including fully dynamic, fully static, and mostly static (except libc) to generate an executable ideal for your deployment scenario.

+ +

Table of Contents

+ + + +

Prerequisites and Preparation

+ +

The following prerequisites should be met:

+ +
    +
  • Linux AMD64 operating system
  • +
  • GraalVM distribution for Java 17 of higher
  • +
  • A 64-bit musl toolchain, make, and configure
  • +
  • The latest zlib library
  • +
+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. +

    Next, you should install the musl toolchain, compile and install zlib into the toolchain. +Download the musl toolchain from musl.cc. +(We recommend this one). +Extract the toolchain to a directory of your choice. This directory will be referred as $TOOLCHAIN_DIR.

    +
  4. +
  5. +

    Download the latest zlib library sources from zlib.net and extract them. (This documentation uses zlib-1.2.11.)

    +
  6. +
  7. Create a new environment variable, named CC: +
     CC=$TOOLCHAIN_DIR/bin/gcc
    +
    +
  8. +
  9. Change into the zlib directory, and then run the following commands to compile and install zlib into the toolchain: +
     ./configure --prefix=$TOOLCHAIN_DIR --static
    + make
    + make install
    +
    +
  10. +
+ +

Build a Static Native Executable

+ +

Assume you have the following source code saved in the EnvMap.java file:

+ +
import java.util.Map;
+
+public class EnvMap {
+    public static void main (String[] args) {
+        var filter = args.length > 0 ? args[0] : "";
+        Map<String, String> env = System.getenv();
+        for (String envName : env.keySet()) {
+            if(envName.contains(filter)) {
+                System.out.format("%s=%s%n",
+                                envName,
+                                env.get(envName));
+            }
+        }
+    }
+}
+
+ +

This application iterates over your environment variables and prints out the ones that contain the String of characters passed as a command line argument.

+ +
    +
  1. First, ensure the directory named $TOOLCHAIN_DIR/bin is present on your PATH. + To verify this, run the following command: +
     x86_64-linux-musl-gcc
    +
    +

    You should see output similar to the following:

    +
     x86_64-linux-musl-gcc: fatal error: no input files
    + compilation terminated.
    +
    +
  2. +
  3. Compile the file: +
     javac EnvMap.java
    +
    +
  4. +
  5. Build a static native executable by running this command: +
     native-image --static --libc=musl EnvMap
    +
    +

    This produces a native executable with statically linked system libraries. + You can pass other arguments before a class or JAR file.

    +
  6. +
+ +

Build a Mostly-Static Native Executable

+ +

With GraalVM Native Image you can build a mostly-static native executable that statically links everything except libc. Statically linking all your libraries except libc ensures your application has all the libraries it needs to run on any Linux libc-based distribution.

+ +

To build a mostly-static native executable, use this command:

+ +
native-image -H:+StaticExecutableWithDynamicLibC [other arguments] <Class>
+
+ +

To build a a mostly-static native executable for the above EnvMap demo, run:

+ +
native-image -H:+StaticExecutableWithDynamicLibC EnvMap
+
+ +

This produces a native executable that statically links all involved libraries (including JDK shared libraries) except for libc. This includes zlib. Also, depending on the user’s code, it may link libstdc+ and libgcc. +One way to check what dynamic libraries your application depends on is to run ldd with the native executable, for example, ldd helloworld.

+ +

Frequently Asked Questions

+ + + +

A fully static native executable gives you the most flexibility to choose a base container image - it can run on anything including a FROM scratch image. +A mostly-static native executable requires a container image that provides libc, but has no additional requirements. +In both cases, choosing the base container image generally depends on your native executable’s specific requirements.

+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-with-reflection/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-with-reflection/index.html new file mode 100644 index 0000000..2b1517e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/build-with-reflection/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/configure-dynamic-proxies/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/configure-dynamic-proxies/index.html new file mode 100644 index 0000000..89cfbba --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/configure-dynamic-proxies/index.html @@ -0,0 +1,29 @@ +

Configure Dynamic Proxies Manually

+ +

You can generate dynamic proxy classes at native executable build time by specifying the list of interfaces that they implement. +Native Image provides two options:

+
    +
  • -H:DynamicProxyConfigurationFiles=<comma-separated-config-files>
  • +
  • -H:DynamicProxyConfigurationResources=<comma-separated-config-resources>
  • +
+ +

These options accept JSON files whose structure is an array of arrays of fully qualified interface names. For example:

+ +
[
+ { "interfaces": [ "java.lang.AutoCloseable", "java.util.Comparator" ] },
+ { "interfaces": [ "java.util.Comparator" ] },
+ { "interfaces": [ "java.util.List" ] }
+]
+
+
+

Note: the order of the specified proxy interfaces is significant: two requests for a Proxy class with the same combination of interfaces but in a different order will result in two distinct behaviors (for more detailed information, refer to Class Proxy).

+
+ +

The java.lang.reflect.Proxy API also enables you to create a dynamic proxy that does not implement any user provided interfaces. +In this case the generated dynamic proxy class implements java.lang.reflect.Proxy only.

+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/configure-with-tracing-agent/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/configure-with-tracing-agent/index.html new file mode 100644 index 0000000..a475797 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/configure-with-tracing-agent/index.html @@ -0,0 +1,162 @@ +

Configure Native Image with the Tracing Agent

+ +

To build a native executable for a Java application that uses Java reflection, dynamic proxy objects, JNI, or class path resources, you should either provide the native-image tool with JSON-formatted configuration files or pre-compute metadata in the code.

+ +

You can create configuration file(s) by hand, but a more convenient approach is to generate the configuration using the Tracing agent (from now on, the agent). +This guide demonstrates how to configure native-image with the agent. The agent generates the configuration for you automatically when you run an application on a JVM.

+ +

To learn how to build a native executable with the metadata pre-computed in the code, follow this guide.

+ +

The example application in this guide uses Java reflection. The native-image tool only partially detects application elements that are accessed using the Java Reflection API. So, you need to provide it with details about reflectively accessed classes, methods, and fields.

+

Example with No Configuration

+ +

The following application demonstrates the use of Java reflection.

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. Save the following source code in a file named ReflectionExample.java: +
     import java.lang.reflect.Method;
    +    
    + class StringReverser {
    +     static String reverse(String input) {
    +         return new StringBuilder(input).reverse().toString();
    +     }
    + }
    +    
    + class StringCapitalizer {
    +     static String capitalize(String input) {
    +         return input.toUpperCase();
    +     }
    + }
    +    
    + public class ReflectionExample {
    +     public static void main(String[] args) throws ReflectiveOperationException {
    +         if (args.length == 0) {
    +             System.err.println("You must provide the name of a class, the name of its method and input for the method");
    +             return;
    +         }
    +         String className = args[0];
    +         String methodName = args[1];
    +         String input = args[2];
    +    
    +         Class<?> clazz = Class.forName(className);
    +         Method method = clazz.getDeclaredMethod(methodName, String.class);
    +         Object result = method.invoke(null, input);
    +         System.out.println(result);
    +     }
    + }
    +
    +

    This Java application uses command-line arguments to determine the operation to be performed.

    +
  4. +
  5. Compile the example and then run each command below. +
     $JAVA_HOME/bin/javac ReflectionExample.java
    +
    +
     $JAVA_HOME/bin/java ReflectionExample StringReverser reverse "hello"
    +
    +
     $JAVA_HOME/bin/java ReflectionExample StringCapitalizer capitalize "hello"
    +
    +

    The output of each command should be "olleh" and "HELLO", respectively. (An exception is thrown if you provide any other string to identify the class or method.)

    +
  6. +
  7. Use the native-image utility to create a native executable, as follows: +
     $JAVA_HOME/bin/native-image --no-fallback ReflectionExample
    +
    +
    +

    NOTE: The --no-fallback option to native-image causes the utility to fail if it can not create an executable file.

    +
    +
  8. +
  9. Run the resulting native executable, using the following command: +
     ./reflectionexample StringReverser reverse "hello"
    +
    +

    You’ll see an exception, similar to:

    +
     Exception in thread "main" java.lang.ClassNotFoundException: StringReverser
    +     at java.lang.Class.forName(DynamicHub.java:1338)
    +     at java.lang.Class.forName(DynamicHub.java:1313)
    +     at ReflectionExample.main(ReflectionExample.java:25)
    +
    +

    This shows that, from its static analysis, the native-image tool was unable to determine that class StringReverser + is used by the application and therefore did not include it in the native executable.

    +
  10. +
+ +

Example with Configuration

+ +

The following steps demonstrate how to use the agent, and its output, to create a native executable that relies on reflection and requires configuration.

+ +
    +
  1. Create a directory named META-INF/native-image in the working directory: +
     mkdir -p META-INF/native-image
    +
    +
  2. +
  3. Run the application with the agent enabled, as follows: +
     $JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=META-INF/native-image ReflectionExample StringReverser reverse "hello"
    +
    +

    This command creates a file named reflect-config.json containing the name of the class StringReverser and its reverse() method.

    +
     [
    +     {
    +     "name":"StringReverser",
    +     "methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }]
    +     }
    + ]
    +
    +
  4. +
  5. Build a native executable: +
     $JAVA_HOME/bin/native-image ReflectionExample
    +
    +

    The native-image tool automatically uses configuration files in the META-INF/native-image directory. + However, we recommend that the META-INF/native-image directory is on the class path, either via a JAR file or using the -cp flag. (This avoids confusion for IDE users where a directory structure is defined by the IDE itself.)

    +
  6. +
  7. Test your executable. +
     ./reflectionexample StringReverser reverse "hello"
    + olleh
    +
    +
     ./reflectionexample StringCapitalizer capitalize "hello"
    +
    + +

    You’ll see again an exception, similar to:

    +
     Exception in thread "main" java.lang.ClassNotFoundException: StringCapitalizer
    +     at java.lang.Class.forName(DynamicHub.java:1338)
    +     at java.lang.Class.forName(DynamicHub.java:1313)
    +     at ReflectionExample.main(ReflectionExample.java:25)
    +
    +

    Neither the tracing agent nor the native-image tool can ensure that the configuration file is complete. + The agent observes and records which program elements are accessed using reflection when you run the program. In this case, the native-image tool has not been configured to include references to class StringCapitalizer.

    +
  8. +
  9. Update the configuration to include class StringCapitalizer. + You can manually edit the reflect-config.json file or re-run the tracing agent to update the existing configuration file using the config-merge-dir option, as follows: +
     $JAVA_HOME/bin/java -agentlib:native-image-agent=config-merge-dir=META-INF/native-image ReflectionExample StringCapitalizer capitalize "hello"
    +
    + +

    This command updates the reflect-config.json file to include the name of the class StringCapitalizer and its capitalize() method.

    +
     [
    +     {
    +     "name":"StringCapitalizer",
    +     "methods":[{"name":"capitalize","parameterTypes":["java.lang.String"] }]
    +     },
    +     {
    +     "name":"StringReverser",
    +     "methods":[{"name":"reverse","parameterTypes":["java.lang.String"] }]
    +     }
    + ]
    +
    +
  10. +
  11. Rebuild the native executable and run it. +
     $JAVA_HOME/bin/native-image ReflectionExample
    +
    +
     ./reflectionexample StringCapitalizer capitalize "hello"
    +
    + +

    The application should now work as intended.

    +
  12. +
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/containerise-native-executable-and-run-in-docker-container/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/containerise-native-executable-and-run-in-docker-container/index.html new file mode 100644 index 0000000..bd95b3c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/containerise-native-executable-and-run-in-docker-container/index.html @@ -0,0 +1,146 @@ +

Containerise a Native Executable and Run in a Docker Container

+ +

Docker containers provide the flexibility of development environments to match a production environment, to help isolate your application, and to minimize overhead. +For self-contained executables, generated with GraalVM Native Image, containers are an obvious deployment choice.

+ +

To support container-based development, there are several GraalVM container images available, depending on the platform, the architecture, the Java version, and the edition:

+ + + +

This guide shows how to containerise a native executable for your Java application. +You will use a GraalVM container image with Native Image to compile a Java application ahead-of-time into a native executable.

+ +

Sample Application

+ +

This guide uses the Spring Boot 3 Native Image Microservice example. +The example is a minimal REST-based API application, built on top of Spring Boot 3. +If you call the HTTP endpoint /jibber, it will return some nonsense verse generated in the style of the Jabberwocky poem, by Lewis Carroll.

+ +

Prerequisites

+ +
    +
  1. +

    Download and install the latest Oracle GraalVM from Downloads. +The easiest option is to use SDKMAN!. Run the following command to install Oracle GraalVM for JDK 17:

    + +
     sdk install java 17.0.8-graal 
    +
    +
  2. +
  3. +

    Install and run a Docker-API compatible container runtime such as Rancher Desktop, Docker, or Podman.

    +
  4. +
  5. +

    Clone the GraalVM Demos repository:

    + +
     git clone https://github.com/graalvm/graalvm-demos
    +
    +
  6. +
  7. +

    Change directory to the demo directory:

    + +
     cd spring-native-image
    +
    +
  8. +
+ +

Build and Run as a Native Executable

+ +

With the built-in support for GraalVM Native Image in Spring Boot 3, it has become much easier to compile a Spring Boot 3 application into a native executable.

+ +
    +
  1. +

    Build a native executable:

    + +
     ././mvnww native:compile -Pnative
    +
    + +

    The -Pnative profile is used to generate a native executable for your platform. + This will generate a native executable called benchmark-jibber in the target directory.

    +
  2. +
  3. +

    Run the native executable and put it into the background by appending &:

    + +
     ./target/benchmark-jibber &
    +
    +
  4. +
  5. +

    Call the endpoint using curl:

    + +
     curl http://localhost:8080/jibber
    +
    + +

    You should get a random nonsense verse.

    +
  6. +
  7. +

    Bring the application to the foreground using fg, and then enter <CTRL-c> to terminate the application.

    +
  8. +
+ +

Containerise the Native Executable

+ +

The generated native executable is platform-dependent.

+ +
    +
  1. +

    Containerise the native executable using the following command:

    + +
      +
    • +

      On Linux, containerise the native executable generated in the previous step using the following command:

      + +
        docker build -f Dockerfiles/Dockerfile.native --build-arg APP_FILE=benchmark-jibber -t jibber-benchmark:native.0.0.1-SNAPSHOT .
      +
      +
    • +
    • +

      On MacOS, Windows, or Linux, use multistage Docker builds to build a native executable inside a container, and package the native executable in a lightweight container image:

      + +
        docker build -f Dockerfiles/Dockerfile -t jibber-benchmark:native.0.0.1-SNAPSHOT .
      +
      +
    • +
    +
  2. +
  3. +

    Run the application:

    + +
     docker run --rm --name native -p 8080:8080 jibber-benchmark:native.0.0.1-SNAPSHOT
    +
    +
  4. +
  5. +

    From a new terminal window, call the endpoint using curl:

    + +
     curl http://localhost:8080/jibber
    +
    + +

    It should generate a random nonsense verse.

    +
  6. +
  7. +

    To stop the application, first get the container id using docker ps, and then run:

    + +
     docker rm -f <container_id>
    +
    +
  8. +
  9. +

    To delete the container images, first get the image id using docker images, and then run:

    + +
     docker rmi -f <image_1_id> <image_n_id>
    +
    +
  10. +
+ +

Summary

+ +

In this guide, you saw how to use GraalVM container images to containerize a native executable for your Java application.

+ +

With GraalVM Native Image you can build a statically linked native executable by packaging the native executable directly into tiny containers such as scratch or distroless images. +Continue to Build a Static or Mostly-Static Native Executable guide to learn more.

+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/create-heap-dump/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/create-heap-dump/index.html new file mode 100644 index 0000000..4a2dfd6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/create-heap-dump/index.html @@ -0,0 +1,285 @@ +

Create a Heap Dump from a Native Executable

+ +

You can create a heap dump of a running executable to monitor its execution. Just like any other Java heap dump, it can be opened with the VisualVM tool.

+ +

To enable heap dump support, native executables must be built with the --enable-monitoring=heapdump option. Heap dumps can then be created in three different ways:

+ +
    +
  1. Create heap dumps with VisualVM.
  2. +
  3. Dump the initial heap of a native executable using the -XX:+DumpHeapAndExit command-line option.
  4. +
  5. Create heap dumps sending a SIGUSR1 signal at run time.
  6. +
  7. Create heap dumps programmatically using the org.graalvm.nativeimage.VMRuntime#dumpHeap API.
  8. +
+ +

All approaches are described below.

+ +
+

Note: By default, heap dumps are created in the current working directory. The -XX:HeapDumpPath option can be used to specify an alternative filename or directory. For example:
+./helloworld -XX:HeapDumpPath=$HOME/helloworld.hprof

+
+ +
+

Also note: Creating heap dumps is not available on the Microsoft Windows platform.

+
+ +

Create Heap Dumps with VisualVM

+ +

A convenient way to create heap dumps is to use VisualVM. +For this, you need to add jvmstat to the --enable-monitoring option (for example, --enable-monitoring=heapdump,jvmstat). +This will allow VisualVM to pick up and list running Native Image processes. +You can then request heap dumps in the same way you can request them when your application runs on the JVM (for example, right-click on the process, then select “Heap Dump”).

+ +

Dump the Initial Heap of a Native Executable

+ +

Use the -XX:+DumpHeapAndExit command-line option to dump the initial heap of a native executable. +This can be useful to identify which objects the Native Image build process allocated to the executable’s heap. +For a HelloWorld example, use the option as follows:

+ +
$JAVA_HOME/bin/native-image HelloWorld --enable-monitoring=heapdump
+./helloworld -XX:+DumpHeapAndExit
+Heap dump created at '/path/to/helloworld.hprof'.
+
+ +

Create Heap Dumps with SIGUSR1 (Linux/macOS only)

+ +
+

Note: This requires the Signal API, which is enabled by default except when building shared libraries.

+
+ +

The following example is a simple multi-threaded Java application that runs for 60 seconds. +This provides you with enough time to send it a SIGUSR1 signal. The application will handle the signal and create a heap dump in the application’s working directory. The heap dump will contain the Collection of Persons referenced by the static variable CROWD.

+ +

Follow these steps to build a native executable that will produce a heap dump when it receives a SIGUSR1 signal.

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. Save the following code in a file named SVMHeapDump.java: +
    import java.nio.charset.Charset;
    +import java.text.DateFormat;
    +import java.util.ArrayList;
    +import java.util.Collection;
    +import java.util.Date;
    +import java.util.Random;
    +import org.graalvm.nativeimage.ProcessProperties;
    +
    +public class SVMHeapDump extends Thread {
    +    static Collection<Person> CROWD = new ArrayList<>();
    +    static DateFormat DATE_FORMATTER = DateFormat.getDateTimeInstance();
    +    static int i = 0;
    +    static int runs = 60;
    +    static int sleepTime = 1000;
    +    @Override
    +    public void run() {
    +        System.out.println(DATE_FORMATTER.format(new Date()) + ": Thread started, it will run for " + runs + " seconds");
    +        while (i < runs) {
    +            // Add a new person to the collection
    +            CROWD.add(new Person());
    +            System.out.println("Sleeping for " + (runs - i) + " seconds.");
    +            try {
    +                Thread.sleep(sleepTime);
    +            } catch (InterruptedException ie) {
    +                System.out.println("Sleep interrupted.");
    +            }
    +            i++;
    +        }
    +    }
    +
    +    /**
    +    * @param args the command line arguments
    +    */
    +    public static void main(String[] args) throws InterruptedException {
    +        // Add objects to the heap
    +        for (int i = 0; i < 1000; i++) {
    +            CROWD.add(new Person());
    +        }
    +        long pid = ProcessProperties.getProcessID();
    +        StringBuffer sb1 = new StringBuffer(100);
    +        sb1.append(DATE_FORMATTER.format(new Date()));
    +        sb1.append(": Hello GraalVM native image developer! \n");
    +        sb1.append("The PID of this process is: " + pid + "\n");
    +        sb1.append("Send it a signal: ");
    +        sb1.append("'kill -SIGUSR1 " + pid + "' \n");
    +        sb1.append("to dump the heap into the working directory.\n");
    +        sb1.append("Starting thread!");
    +        System.out.println(sb1);
    +        SVMHeapDump t = new SVMHeapDump();
    +        t.start();
    +        while (t.isAlive()) {
    +            t.join(0);
    +        }
    +        sb1 = new StringBuffer(100);
    +        sb1.append(DATE_FORMATTER.format(new Date()));
    +        sb1.append(": Thread finished after: ");
    +        sb1.append(i);
    +        sb1.append(" iterations.");
    +        System.out.println(sb1);
    +    }
    +}
    +
    +class Person {
    +        private static Random R = new Random();
    +        private String name;
    +        private int age;
    +            
    +        public Person() {
    +            byte[] array = new byte[7];
    +            R.nextBytes(array);
    +            name = new String(array, Charset.forName("UTF-8"));
    +            age = R.nextInt(100);
    +        }
    +    }
    +
    +
  4. +
  5. +

    Build a native executable:

    + +

    Compile SVMHeapDump.java as follows:

    +
     $JAVA_HOME/bin/javac SVMHeapDump.java
    +
    +

    Build a native executable using the --enable-monitoring=heapdump command-line option. + (This causes the resulting native executable to produce a heap dump when it receives a SIGUSR1 signal.)

    + +
     $JAVA_HOME/bin/native-image SVMHeapDump --enable-monitoring=heapdump
    +
    + +

    (The native-image builder creates a native executable from the SVMHeapDump.class. + When the command completes, the native executable svmheapdump is created in the current directory.)

    +
  6. +
  7. +

    Run the application, send it a signal, and check the heap dump:

    + +

    Run the application:

    +
     ./svmheapdump
    + 17 May 2022, 16:38:13: Hello GraalVM native image developer! 
    + The PID of this process is: 57509
    + Send it a signal: 'kill -SIGUSR1 57509' 
    + to dump the heap into the working directory.
    + Starting thread!
    + 17 May 2022, 16:38:13: Thread started, it will run for 60 seconds
    +
    + +

    Make a note of the PID and open a second terminal. Use the PID to send a signal to the application. For example, if the PID is 57509:

    +
     kill -SIGUSR1 57509
    +
    + +

    The heap dump will be created in the working directory while the application continues to run. The heap dump can be opened with the VisualVM tool, as illustrated below.

    + +

    Native Image Heap Dump View in VisualVM

    +
  8. +
+ +

Create a Heap Dump from within a Native Executable

+ +

The following example shows how to create a heap dump from a running native executable using VMRuntime.dumpHeap() if some condition is met. +The condition to create a heap dump is provided as an option on the command line.

+ +
    +
  1. +

    Save the code below in a file named SVMHeapDumpAPI.java.

    + +
     import java.io.File;
    + import java.io.FileOutputStream;
    + import java.io.IOException;
    + import java.nio.charset.Charset;
    + import java.text.DateFormat;
    + import java.util.ArrayList;
    + import java.util.Collection;
    + import java.util.Date;
    + import java.util.Random;
    + import org.graalvm.nativeimage.VMRuntime;
    +
    + public class SVMHeapDumpAPI {
    +     static Collection<Person> CROWD = new ArrayList<>();
    +
    +     /**
    +     * @param args the command line arguments
    +     */
    +     public static void main(String[] args) {   	
    +         // Populate the crowd
    +         for (int i = 0; i < 1000; i++) {
    +             CROWD.add(new Person());
    +         }
    +         StringBuffer sb1 = new StringBuffer(100);
    +         sb1.append(DateFormat.getDateTimeInstance().format(new Date()));
    +         sb1.append(": Hello GraalVM native image developer. \nYour command line options are: ");
    +         if (args.length > 0) {
    +             sb1.append(args[0]);
    +             System.out.println(sb1);
    +             if (args[0].equalsIgnoreCase("--heapdump")) {
    +                 createHeapDump();
    +             }
    +         } else {
    +             sb1.append("None");
    +             System.out.println(sb1);
    +         }
    +     }
    +
    +     /**
    +     * Create a heap dump and save it into temp file
    +     */
    +     private static void createHeapDump() {
    +         try {
    +             File file = File.createTempFile("SVMHeapDumpAPI-", ".hprof");
    +             VMRuntime.dumpHeap(file.getAbsolutePath(), false);
    +             System.out.println("  Heap dump created " + file.getAbsolutePath() + ", size: " + file.length());
    +         } catch (UnsupportedOperationException unsupported) {
    +             System.err.println("Heap dump creation failed: " + unsupported.getMessage());
    +         } catch (IOException ioe) {
    +             System.err.println("IO went wrong: " + ioe.getMessage());
    +         }
    +     }
    +
    + }
    +
    + class Person {
    +         private static Random R = new Random();
    +         private String name;
    +         private int age;
    +            
    +         public Person() {
    +             byte[] array = new byte[7];
    +             R.nextBytes(array);
    +             name = new String(array, Charset.forName("UTF-8"));
    +             age = R.nextInt(100);
    +         }
    +     }
    +
    + +

    As in the earlier example, the application creates a Collection of Persons referenced by the static variable CROWD. It then checks the command line to see if heap dump has to be created, and then in method createHeapDump() creates the heap dump.

    +
  2. +
  3. +

    Build a native executable

    + +

    Compile SVMHeapDumpAPI.java and build a native executable:

    +
     $JAVA_HOME/bin/javac SVMHeapDumpAPI.java
    +
    +
     $JAVA_HOME/bin/native-image SVMHeapDumpAPI
    +
    +

    When the command completes, the svmheapdumpapi native executable is created in the current directory.

    +
  4. +
  5. +

    Run the application and check the heap dump

    + +

    Now you can run your native executable and create a heap dump from it with output similar to the following:

    +
     ./svmheapdumpapi --heapdump
    +
    +
     Sep 15, 2020, 4:06:36 PM: Hello GraalVM native image developer.
    + Your command line options are: --heapdump
    +   Heap dump created /var/folders/hw/s9d78jts67gdc8cfyq5fjcdm0000gp/T/SVMHeapDump-6437252222863577987.hprof, size: 8051959
    +
    +

    The resulting heap dump can be then opened with the VisualVM tool like any other Java heap dump, as illustrated below.

    + +

    Native Image Heap Dump View in VisualVM

    +
  6. +
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/debug-native-image-process/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/debug-native-image-process/index.html new file mode 100644 index 0000000..dd6eb6f --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/debug-native-image-process/index.html @@ -0,0 +1,157 @@ +

Debug Native Executables with GDB

+ +

A generated native executable is heavily optimized code with minimal symbol information which makes debugging harder. +This can be solved by embedding debug information into the resulting binary at build time. +This information tells the debugger precisely how to interpret the machine code and point it back to the original Java method.

+ +

In this guide you will learn how to debug a native executable using the standard Linux GNU Debugger (GDB).

+ +
+

Note: Native Image debugging with GDB currently works on Linux with initial support for macOS. The feature is experimental.

+
+ +

Run a Demo

+ +

To build a native executable with debug information, provide the -g command-line option for javac when compiling the application, and then to the native-image builder. +This enables source-level debugging, and the debugger (GDB) then correlates machine instructions with specific source lines in Java files.

+ +

Prerequisites

+ +
    +
  • Linux AMD64
  • +
  • GDB 10.1 or higher
  • +
+ +

Follow the steps to test debugging a native executable with GDB. The below workflow is known to work on Linux with GDB 10.1.

+ +
    +
  1. Download and install the latest GraalVM JDK with Native Image using the GraalVM JDK Downloader: +
     bash <(curl -sL https://get.graalvm.org/jdk)
    +
    +
  2. +
  3. +

    Save the following code to the file named GDBDemo.java.

    + +
     public class GDBDemo {
    +     static long fieldUsed = 1000;
    +
    +     public static void main(String[] args) {
    +         if (args.length > 0) {
    +             int n = -1;
    +             try {
    +                 n = Integer.parseInt(args[0]);
    +             } catch (NumberFormatException ex) {
    +                 System.out.println(args[0] + " is not a number!");
    +             }
    +             if (n < 0) {
    +                 System.out.println(args[0] + " is negative.");
    +             }
    +             double f = factorial(n);
    +             System.out.println(n + "! = " + f);
    +         } 
    +
    +         if (false)
    +             neverCalledMethod();
    +
    +         StringBuilder text = new StringBuilder();
    +         text.append("Hello World from GraalVM Native Image and GDB in Java.\n");
    +         System.out.println(text.toString());
    +     }
    +
    +     static void neverCalledMethod() {
    +         System.out.println("This method is unreachable and will not be included in the native executable.");
    +     }
    +
    +     static double factorial(int n) {
    +         if (n == 0) {
    +             return 1;
    +         }
    +         if (n >= fieldUsed) {
    +             return Double.POSITIVE_INFINITY;
    +         }
    +         double f = 1;
    +         while (n > 1) {
    +             f *= n--;
    +         }
    +         return f;
    +     }
    + }
    +
    +
  4. +
  5. +

    Compile it and generate a native executable with debug information:

    + +
     $JAVA_HOME/bin/javac -g GDBDemo.java
    +
    +
     native-image -g -O0 GDBDemo
    +
    +

    The -g option instructs native-image to generate debug information. The resulting native executable will contain debug records in a format GDB understands.

    + +

    Notice that you can also pass -O0 which specifies that no compiler optimizations should be performed. Disabling all optimizations is not required, but in general it makes the debugging experience better.

    +
  6. +
  7. +

    Launch the debugger and run your native executable:

    + +
     gdb ./gdbdemo
    +
    +

    The gdb prompt will open.

    +
  8. +
  9. +

    Set a breakpoint: type breakpoint <java method> to set a breakpoint and run <arg> to run the native executable. You can put breakpoints configured by file and line, or by method name. See below the example of a debugging session.

    + +
     $ gdb ./gdbdemo
    + GNU gdb (GDB) 10.2
    + Copyright (C) 2021 Free Software Foundation, Inc.
    + ...
    + Reading symbols from ./gdbdemo...
    + Reading symbols from /dev/gdbdemo.debug...
    + (gdb) info func ::main
    + All functions matching regular expression "::main":
    +
    + File GDBDemo.java:
    + 5:	void GDBDemo::main(java.lang.String[]*);
    + (gdb) b ::factorial
    + Breakpoint 1 at 0x2d000: file GDBDemo.java, line 32.
    + (gdb) run 42
    + Starting program: /dev/gdbdemo 42
    + Thread 1 "gdbdemo" hit Breakpoint 1, GDBDemo::factorial (n=42) at GDBDemo.java:32
    + 32	        if (n == 0) {
    + (gdb) info args
    + n = 42
    + (gdb) step
    + 35	        if (n >= fieldUsed) {
    + (gdb) next
    + 38	        double f = 1;
    + (gdb) next
    + 39	        while (n > 1) {
    + (gdb) info locals
    + f = 1
    + (gdb) ...
    +
    +
  10. +
+ +

In case your native executable segfaults, you can print the backtrace of the entire stack (bt).

+ +

The debugger points machine instructions back from the binary to specific source lines in Java files. Note that single stepping within a compiled method includes file and line number information for inlined code. GDB may switch files even though you are still in the same compiled method.

+ +

Most of the regular debugging actions are supported by GDB, namely:

+ +
    +
  • single stepping including both into and over function calls
  • +
  • stack backtraces (not including frames detailing inlined code)
  • +
  • printing of primitive values
  • +
  • structured, field by field, printing of Java objects
  • +
  • casting and printing objects at different levels of generality
  • +
  • access through object networks via path expressions
  • +
  • reference by name to methods and static field data
  • +
+ +

The generation of debug information is implemented by modeling the Java program as an equivalent C++ program. Since GDB was primarily designed for debugging C (and C++), there are certain considerations to be taken into account when debugging Java applications. +Read more about Native Image debugging support in the reference documentation.

+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/include-resources/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/include-resources/index.html new file mode 100644 index 0000000..4fd6c42 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/include-resources/index.html @@ -0,0 +1,72 @@ +

Include Resources in a Native Executable

+ +

The following steps illustrate how to include a resource in a native executable. The application fortune simulates the traditional fortune Unix program (for more information, see fortune).

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. +

    Save the following Java source code as a file named Fortune.java:

    + +
     import java.io.BufferedReader;
    + import java.io.InputStreamReader;
    + import java.util.ArrayList;
    + import java.util.Random;
    + import java.util.Scanner;
    +
    + public class Fortune {
    +
    +     private static final String SEPARATOR = "%";
    +     private static final Random RANDOM = new Random();
    +     private ArrayList<String> fortunes = new ArrayList<>();
    +
    +     public Fortune(String path) {
    +         // Scan the file into the array of fortunes
    +         Scanner s = new Scanner(new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream(path))));
    +         s.useDelimiter(SEPARATOR);
    +         while (s.hasNext()) {
    +             fortunes.add(s.next());
    +         }
    +     }
    +        
    +     private void printRandomFortune() throws InterruptedException {
    +         int r = RANDOM.nextInt(fortunes.size()); //Pick a random number
    +         String f = fortunes.get(r);  //Use the random number to pick a random fortune
    +         for (char c: f.toCharArray()) {  // Print out the fortune
    +           System.out.print(c);
    +             Thread.sleep(100); 
    +         }
    +     }
    +      
    +     public static void main(String[] args) throws InterruptedException {
    +         Fortune fortune = new Fortune("/fortunes.u8");
    +         fortune.printRandomFortune();
    +     }
    + }
    +
    +
  4. +
  5. +

    Download the fortunes.u8 resource file and save it in the same directory as Fortune.java.

    +
  6. +
  7. Compile the Java source code: +
     $JAVA_HOME/bin/javac Fortune.java
    +
    +
  8. +
  9. Build a native executable by specifying the resource path: +
     $JAVA_HOME/bin/native-image Fortune -H:IncludeResources=".*u8$"
    +
    +
  10. +
  11. Run the executable image: +
     ./fortune
    +
    +
  12. +
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/index.html new file mode 100644 index 0000000..3bdcea9 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/optimize-native-executable-with-pgo/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/optimize-native-executable-with-pgo/index.html new file mode 100644 index 0000000..f4fb034 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/optimize-native-executable-with-pgo/index.html @@ -0,0 +1,191 @@ +

Optimize a Native Executable with Profile-Guided Optimizations

+ +

GraalVM Native Image offers quick startup and less memory consumption for a Java application, running as a native executable, by default. +You can optimize this native executable even more for additional performance gain and higher throughput by applying Profile-Guided Optimizations (PGO).

+ +

With PGO you can collect the profiling data in advance and then feed it to the native-image tool, which will use this information to optimize the performance of the resulting binary.

+ +
+

Note: PGO is not available in GraalVM Community Edition.

+
+ +

This guide shows how to apply PGO and transform your Java application into an optimized native executable.

+ +

Run a Demo

+ +

For the demo part, you will run a Java application performing queries implemented with the Java Streams API. A user is expected to provide two integer arguments: the number of iterations and the length of the data array. The application creates the data set with a deterministic random seed and iterates 10 times. The time taken for each iteration and its checksum is printed to the console.

+ +

Below is the stream expression to optimize:

+ +
Arrays.stream(persons)
+   .filter(p -> p.getEmployment() == Employment.EMPLOYED)
+   .filter(p -> p.getSalary() > 100_000)
+   .mapToInt(Person::getAge)
+   .filter(age -> age > 40)
+   .average()
+   .getAsDouble();
+
+ +

Follow these steps to build an optimized native executable using PGO.

+ +
+

Note: Make sure you have installed a GraalVM JDK. The easiest way to get started is with SDKMAN!. For other installation options, visit the Downloads section.

+
+ +
    +
  1. +

    Save the following code to the file named Streams.java:

    + +
    import java.util.Arrays;
    +import java.util.Random;
    +
    +public class Streams {
    +
    +  static final double EMPLOYMENT_RATIO = 0.5;
    +  static final int MAX_AGE = 100;
    +  static final int MAX_SALARY = 200_000;
    +
    +  public static void main(String[] args) {
    +
    +    int iterations;
    +    int dataLength;
    +    try {
    +      iterations = Integer.valueOf(args[0]);
    +      dataLength = Integer.valueOf(args[1]);
    +    } catch (Throwable ex) {
    +      System.out.println("Expected 2 integer arguments: number of iterations, length of data array");
    +      return;
    +    }
    +
    +    Random random = new Random(42);
    +    Person[] persons = new Person[dataLength];
    +    for (int i = 0; i < dataLength; i++) {
    +      persons[i] = new Person(
    +          random.nextDouble() >= EMPLOYMENT_RATIO ? Employment.EMPLOYED : Employment.UNEMPLOYED,
    +          random.nextInt(MAX_SALARY),
    +          random.nextInt(MAX_AGE));
    +    }
    +
    +    long totalTime = 0;
    +    for (int i = 1; i <= 20; i++) {
    +      long startTime = System.currentTimeMillis();
    +
    +      long checksum = benchmark(iterations, persons);
    +
    +      long iterationTime = System.currentTimeMillis() - startTime;
    +      totalTime += iterationTime;
    +      System.out.println("Iteration " + i + " finished in " + iterationTime + " milliseconds with checksum " + Long.toHexString(checksum));
    +    }
    +    System.out.println("TOTAL time: " + totalTime);
    +  }
    +
    +  static long benchmark(int iterations, Person[] persons) {
    +    long checksum = 1;
    +    for (int i = 0; i < iterations; ++i) {
    +      double result = getValue(persons);
    +
    +      checksum = checksum * 31 + (long) result;
    +    }
    +    return checksum;
    +  }
    +
    +  public static double getValue(Person[] persons) {
    +    return Arrays.stream(persons)
    +        .filter(p -> p.getEmployment() == Employment.EMPLOYED)
    +        .filter(p -> p.getSalary() > 100_000)
    +        .mapToInt(Person::getAge)
    +        .filter(age -> age >= 40).average()
    +        .getAsDouble();
    +  }
    +}
    +
    +enum Employment {
    +  EMPLOYED, UNEMPLOYED
    +}
    +
    +class Person {
    +  private final Employment employment;
    +  private final int age;
    +  private final int salary;
    +
    +  public Person(Employment employment, int height, int age) {
    +    this.employment = employment;
    +    this.salary = height;
    +    this.age = age;
    +  }
    +
    +  public int getSalary() {
    +    return salary;
    +  }
    +
    +  public int getAge() {
    +    return age;
    +  }
    +
    +  public Employment getEmployment() {
    +    return employment;
    +  }
    +}
    +
    +
  2. +
  3. Compile the application: +
    $JAVA_HOME/bin/javac Streams.java
    +
    +

    (Optional) Run the demo application, providing some arguments to observe performance.

    +
    $JAVA_HOME/bin/java Streams 100000 200
    +
    +
  4. +
  5. Build a native executable from the class file, and run it to compare the performance: +
     $JAVA_HOME/bin/native-image Streams
    +
    +

    An executable file, streams, is created in the current working directory. + Now run it with the same arguments to see the performance:

    + +
     ./streams 100000 200
    +
    +

    This version of the program is expected to run slower than on GraalVM’s or any regular JDK.

    +
  6. +
  7. +

    Build an instrumented native executable by passing the --pgo-instrument option to native-image:

    + +
     $JAVA_HOME/bin/native-image --pgo-instrument Streams
    +
    +
  8. +
  9. +

    Run it to collect the code-execution-frequency profiles:

    + +
     ./streams 100000 20
    +
    + +

    Notice that you can profile with a much smaller data size. + Profiles collected from this run are stored by default in the default.iprof file.

    + +
    +

    Note: You can specify where to collect the profiles when running an instrumented native executable by passing the -XX:ProfilesDumpFile=YourFileName option at run time.

    +
    +
  10. +
  11. +

    Finally, build an optimized native executable by specifying the path to the collected profiles:

    + +
     $JAVA_HOME/bin/native-image --pgo=default.iprof Streams
    +
    +
    +

    Note: You can also collect multiple profile files, by specifying different filenames, and pass them to the native-image tool at build time.

    +
    + +

    Run this optimized native executable timing the execution to see the system resources and CPU usage:

    +
     time ./streams 100000 200
    +
    +

    You should get the performance comparable to, or faster, than the Java version of the program. For example, on a machine with 16 GB of memory and 8 cores, the TOTAL time for 10 iterations reduced from ~2200 to ~270 milliseconds.

    +
  12. +
+ +

This guide showed how you can optimize native executables for additional performance gain and higher throughput. +Oracle GraalVM offers extra benefits for building native executables, such as Profile-Guided Optimizations (PGO). +With PGO you “train” your application for specific workloads and significantly improve the performance.

+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/specify-class-initialization/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/specify-class-initialization/index.html new file mode 100644 index 0000000..7ed75ec --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/specify-class-initialization/index.html @@ -0,0 +1,23 @@ +

Specify Class Initialization Explicitly

+ +

Two command line flags explicitly specify when a class should be initialized: --initialize-at-build-time and --initialize-at-run-time. +You can use the flags to specify whole packages or individual classes. +For example, if you have the classes p.C1, p.C2, … ,p.Cn, you can specify that all the classes in the package p are initialized at build time by passing the following argument to native-image on the command line:

+
--initialize-at-build-time=p
+
+ +

If you want only one of the classes in package p to be initialized at runtime, use:

+
--initialize-at-run-time=p.C1
+
+ +

The whole class hierarchy can be initialized at build time by passing --initialize-at-build-time on the command line.

+ +

Class initialization can also be specified programmatically using RuntimeClassInitialization from the Native Image feature.

+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/use-graalvm-dashboard/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/use-graalvm-dashboard/index.html new file mode 100644 index 0000000..9e1742d --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/use-graalvm-dashboard/index.html @@ -0,0 +1,157 @@ +

Use GraalVM Dashboard to Optimize the Size of a Native Executable

+ +

The GraalVM Dashboard is a web-based tool that visualizes the composition of a native executable. It displays the breakdown of packages, classes, and methods included in a native executable, and provides a visual summary of objects that contributed most to its heap size. +The GraalVM Dashboard uses report files created by the native image builder. (For more information, see GraalVM Getting Started.)

+ +

This guide demonstrates how to use the dashboard to optimize the size of a native executable. It introduces two implementations of a “fortune” sample application that simulate the traditional fortune Unix program (for more information, see fortune).

+ +
+

Note: This guide assumes you have installed Maven.

+
+ +

Fortune

+

Package the contents of the first implementation (fortune) as a native executable and review its composition.

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. Download or clone the repository and navigate into the fortune-demo/fortune directory: +
     git clone https://github.com/graalvm/graalvm-demos
    +
    +
     cd fortune-demo/fortune
    +
    +
  4. +
  5. Build the project: +
     ./mvnw clean package
    +
    +
  6. +
  7. When the build succeeds, run the application on the JVM with the Tracing agent. Since you have installed GraalVM, it will run on GraalVM JDK. +
     ./mvnw -Pnative -Dagent exec:exec@java-agent
    +
    +

    The application will return a random saying. + The agent generates the configuration files in the target/native/agent-output subdirectory.

    +
  8. +
  9. Build a native executable of this application with GraalVM Native Image and Maven: +
     ./mvnw -Pnative -Dagent package
    +
    +

    When the command completes, a native executable, fortune, is generated in the /target directory of the project and ready for use.

    +
  10. +
  11. Run the application by launching a native executable directly: +
     ./target/fortune
    +
    +

    The application should slowly print a random phrase.

    + +

    The application’s pom.xml file employs the Native Image Maven plugin to build a native executable, configured to produce diagnostic data using these two options:

    + +
     -H:DashboardDump=fortune -H:+DashboardAll
    +
    + +

    These options result in a file named fortune.bgv. (For more information about different options, see Dumping the Data for GraalVM Dashboard.)

    + +

    Compare the sizes of the JAR file and the native executable (for example, using du):

    + +
     du -sh target/*
    + 0B	    target/archive-tmp
    + 136K	target/classes
    + 17M     target/fortune
    + 2.0M	target/fortune-1.0-jar-with-dependencies.jar
    + 32K	    target/fortune-1.0.jar
    + 44M	    target/fortune.bgv
    + 4.0K	target/fortune.build_artifacts.txt
    + 0B	    target/generated-sources
    + 4.0K	target/maven-archiver
    + 8.0K	target/maven-status
    + 0B	    target/test-classes
    +
    + +

    The size of the JAR file is 2MB, compared to the 17MB size of the native executable. The increase in size is because the native executable contains all necessary runtime code as well as pre-initialized data in its heap.

    +
  12. +
  13. +

    Open the GraalVM Dashboard and load the fortune.bgv file. (Click +, click Select File, select the fortune.bgv file from the target directory, and then click OK.)

    + +

    The GraalVM dashboard provides two visualizations of a native executable: code size breakdown and heap size breakdown. (For more information, see Code Size Breakdown and Heap Size Breakdown, respectively.)

    + +

    Code Size Breakdown View

    + +

    The screenshot above visualizes the code breakdown of the fortune native executable, a great part of which consists of the Jackson JSON parser library implemented in the package com.fasterxml. One approach to reduce the size of a native executable is to minimize the amount of space taken by code. The code size breakdown gives you an insight into the amount of space taken up by the packages that are included in your native executable.

    + +

    Furthermore, the screenshot below shows that the heap of the native executable contains 4MB of Bytes and almost 800KB of Strings. Another approach to reduce the size of a native executable is to minimize the size of its heap.

    + +

    Heap Size Breakdown View

    + +

    In the next section, we’ll consider an alternative implementation for the fortune application that reduces the amount of code and reduces the size of the heap.

    +
  14. +
+ +

Static Fortune

+ +

The first implementation of the fortune application uses the Jackson JSON parser (package com.fasterxml) at runtime to parse the contents of a resource file that the native image builder has included in the native executable. An alternative implementation (named “staticfortune”) parses the contents of the resource file at build time. This means that the resource file is no longer included in the executable, and the code required to parse the file is omitted from the native executable because it is only required at build time.

+ +
    +
  1. Change to the project directory and build the project: +
     cd ../staticfortune
    +
    +
     ./mvnw clean package
    +
    +
  2. +
  3. Run the application on the JVM (GraalVM JDK) with the Tracing agent: +
     ./mvnw -Pnative -Dagent exec:exec@java-agent
    +
    +

    The application will print a random saying. The agent generates the configuration files in the target/native/agent-output subdirectory.

    +
  4. +
  5. Build a static native executable: +
     ./mvnw -Pnative -Dagent package
    +
    +

    When the command completes, a native executable, staticfortune, is generated in the /target directory of the project and ready for use.

    +
  6. +
  7. Run the demo by launching a native executable directly or with the Maven profile: +
     ./target/staticfortune
    +
    +

    The application should behave in exactly the same way as the first implementation.

    + +

    The application’s pom.xml file again uses the Native Image Maven plugin to build a native executable. However, for this implementation it adds an extra option to initialize class demo.StaticFortune at build time:

    +
     -H:DashboardDump=staticfortune -H:+DashboardAll --initialize-at-build-time=demo.StaticFortune
    +
    + +

    Compare the sizes of the JAR file and the native executable:

    +
     du -sh target/*
    + 0B	    target/archive-tmp
    + 76K	    target/classes
    + 0B	    target/generated-sources
    + 4.0K	target/maven-archiver
    + 4.0K	target/maven-status
    + 4.3M	target/staticfortune
    + 2.0M	target/staticfortune-1.0-jar-with-dependencies.jar
    + 32K	    target/staticfortune-1.0.jar
    + 9.0M	target/staticfortune.bgv
    + 4.0K	target/staticfortune.build_artifacts.txt
    + 0B	    target/test-classes
    +
    + +

    The size of the native executable has reduced in size from 17MB to 4.3MB.

    + +

    The reduction in size is due to the use of the --initialize-at-build-time= argument used with the Native Image Maven plugin.

    +
  8. +
  9. +

    The build created a file named staticfortune.bgv. Load it into the GraalVM Dashboard to view the composition of the staticfortune native executable.

    + +

    Code Size Breakdown View

    + +

    The screenshot above demonstrates that the code in the com.fasterxml package is no longer present. There are also reductions in the amount of code included from the java.util, java.math, and java.text packages.

    + +

    The screenshot below illustrates that there has also been a significant reduction in the amount of heap given to Strings (767KB versus 184KB), and a reduction in Bytes from 4MB to 862KB.

    + +

    Heap Size Breakdown View

    +
  10. +
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/use-reachability-metadata-repository-gradle/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/use-reachability-metadata-repository-gradle/index.html new file mode 100644 index 0000000..c03802c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/use-reachability-metadata-repository-gradle/index.html @@ -0,0 +1,296 @@ +

Configure Native Image Using Shared Reachability Metadata

+ +

With the Gradle plugin for GraalVM Native Image you can easily build a native executable from a Java application. The plugin is provided as part of the Native Build Tools project and uses the Gradle build tool. +If the application does not dynamically load any classes at run time, then your workflow is just one command: ./gradlew nativeRun.

+ +

In the real-world, your application will, most likely, call either Java Reflection, Dynamic Proxy objects, or call some native code, or access resources on the class path - dynamic features that the native-image tool must be aware of at build time, and provided in the form of metadata. +Native Image loads classes dynamically at build time, and not at run time.

+ +

Depending on your application dependencies, there are three ways to provide the metadata with the Native Image Gradle Plugin:

+ +
    +
  1. Using the Tracing Agent
  2. +
  3. Using the shared GraalVM Reachability Metadata Repository
  4. +
  5. Autodetecting (if the required resources are directly available on the classpath, in the src/main/resources directory)
  6. +
+ +

For the Java application used in this guide the first two approaches are applicable. +This guide demonstrates how to build a native executable with the Tracing agent and using the GraalVM Reachability Metadata Repository. +The goal is to show users the difference, and prove how using shared metadata can simplify the work.

+ +

We recommend that you follow the instructions and create the application step-by-step. Alternatively, you can go right to the completed example.

+ +

Prepare a Demo Application

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. +

    Create a new Java project with Gradle in your favorite IDE, called “H2Example”, in the org.graalvm.example package.

    +
  4. +
  5. +

    Rename the default app directory to H2Example, then rename the default filename App.java to H2Example.java and replace its contents with the following:

    + +
     package org.graalvm.example;
    +
    + import java.sql.Connection;
    + import java.sql.DriverManager;
    + import java.sql.PreparedStatement;
    + import java.sql.ResultSet;
    + import java.sql.SQLException;
    + import java.util.ArrayList;
    + import java.util.Comparator;
    + import java.util.HashSet;
    + import java.util.List;
    + import java.util.Set;
    +
    + public class H2Example {
    +
    +     public static final String JDBC_CONNECTION_URL = "jdbc:h2:./data/test";
    +
    +     public static void main(String[] args) throws Exception {
    +         // Cleanup
    +         withConnection(JDBC_CONNECTION_URL, connection -> {
    +             connection.prepareStatement("DROP TABLE IF EXISTS customers").execute();
    +             connection.commit();
    +         });
    +
    +         Set<String> customers = Set.of("Lord Archimonde", "Arthur", "Gilbert", "Grug");
    +
    +         System.out.println("=== Inserting the following customers in the database: ");
    +         printCustomers(customers);
    +
    +         // Insert data
    +         withConnection(JDBC_CONNECTION_URL, connection -> {
    +             connection.prepareStatement("CREATE TABLE customers(id INTEGER AUTO_INCREMENT, name VARCHAR)").execute();
    +             PreparedStatement statement = connection.prepareStatement("INSERT INTO customers(name) VALUES (?)");
    +             for (String customer : customers) {
    +                 statement.setString(1, customer);
    +                 statement.executeUpdate();
    +             }
    +             connection.commit();
    +         });
    +
    +         System.out.println("");
    +         System.out.println("=== Reading customers from the database.");
    +         System.out.println("");
    +
    +         Set<String> savedCustomers = new HashSet<>();
    +         // Read data
    +         withConnection(JDBC_CONNECTION_URL, connection -> {
    +             try (ResultSet resultSet = connection.prepareStatement("SELECT * FROM customers").executeQuery()) {
    +                 while (resultSet.next()) {
    +                     savedCustomers.add(resultSet.getObject(2, String.class));
    +                 }
    +             }
    +         });
    +
    +         System.out.println("=== Customers in the database: ");
    +         printCustomers(savedCustomers);
    +     }
    +
    +     private static void printCustomers(Set<String> customers) {
    +         List<String> customerList = new ArrayList<>(customers);
    +         customerList.sort(Comparator.naturalOrder());
    +         int i = 0;
    +         for (String customer : customerList) {
    +             System.out.println((i + 1) + ". " + customer);
    +             i++;
    +         }
    +     }
    +
    +     private static void withConnection(String url, ConnectionCallback callback) throws SQLException {
    +         try (Connection connection = DriverManager.getConnection(url)) {
    +             connection.setAutoCommit(false);
    +             callback.run(connection);
    +         }
    +     }
    +
    +     private interface ConnectionCallback {
    +         void run(Connection connection) throws SQLException;
    +     }
    + }
    +
    +
  6. +
  7. +

    Delete the H2Example/src/test/java directory.

    +
  8. +
  9. Open the Gradle configuration file build.gradle, and update the main class in the application section: +
     application {
    +     mainClass.set('org.graalvm.example.H2Example')
    + }
    +
    +
  10. +
  11. Add explicit dependency on H2 Database, an open source SQL database for Java. The application interacts with this database through the JDBC driver. Insert the following line in the dependencies section of build.gradle: +
     dependencies {
    +     implementation("com.h2database:h2:2.1.210")
    + }
    +
    +

    Also, in the dependencies section, remove the dependency on guava that will not be used.

    + +

    The next steps will be focused what you should do to enable the Native Image Gradle plugin.

    +
  12. +
  13. Register the Native Image Gradle plugin. Add the following to plugins section of your project’s build.gradle file: +
     plugins {
    + // ...
    + id 'org.graalvm.buildtools.native' version '0.9.13'
    + }
    +
    +

    The plugin discovers which JAR files it needs to pass to the native-image builder and what the executable main class should be.

    +
  14. +
  15. +

    The plugin is not yet available on the Gradle Plugin Portal, so declare an additional plugin repository. Open the settings.gradle file and replace the default content with this:

    + +
     pluginManagement {
    +     repositories {
    +         mavenCentral()
    +         gradlePluginPortal()
    +     }
    + }
    +
    + rootProject.name = 'H2Example'
    + include('H2Example')
    +
    +

    Note that the pluginManagement {} block must appear before any other statements in the file.

    +
  16. +
+ +

Build a Native Executable with the Agent

+ +

The Native Image Gradle plugin simplifies generation of the required metadata by injecting the Tracing agent (later the agent) automatically for you at compile time. +To enable the agent, just pass the -Pagent option to any Gradle tasks that extends JavaForkOptions (for example, test or run).

+ +

The agent can run in multiple modes:

+
    +
  • Standard: Collects metadata without conditions. This is recommended if you are building an executable.
  • +
  • Conditional: Collects metadata with conditions. This is recommended if you are creating conditional metadata for a library intended for further use.
  • +
  • Direct: For advanced users only. This mode allows directly controlling the command line passed to the agent.
  • +
+ +

You can configure the agent either passing the options on the command line, or in the build.gradle file. See below how to configure the Native Image Gradle plugin, collect metadata with the tracing agent, and build a native executable applying the provided configuration.

+ +
    +
  1. +

    (Optional) Instruct the agent to run in the standard mode. Insert this configuration block at the bottom of the build.gradle file:

    + +
     graalvmNative {
    +     agent {
    +         defaultMode = "standard"
    +     }
    +     binaries {
    +         main {
    +             imageName.set('h2demo') 
    +         }
    +     }
    +     toolchainDetection = false
    + }
    +
    +

    If you prefer the command-lime option, that will be -Pagent=standard. + The second part of the configuration shows how to specify a custom name for a final native executable.

    + +

    Another thing to note here, the plugin may not be able to properly detect the GraalVM installation, because of limitations in Gradle. The workaround is to disable toolchain detection with this command: toolchainDetection = false. Learn more about selecting the GraalVM toolchain here.

    +
  2. +
  3. +

    Now run your application with the agent enabled, on the JVM:

    + +
     ./gradlew -Pagent run
    +
    +

    The agent captures and writes down calls to the H2 Database and all the dynamic features encountered during a test run into multiple *-config.json files.

    +
  4. +
  5. +

    Once the metadata is collected, copy it into the project’s /META-INF/native-image directory using the metadataCopy task:

    + +
     ./gradlew metadataCopy --task run --dir src/main/resources/META-INF/native-image
    +
    + +

    The JSON files are stored in the META-INF/native-image/<group.id>/<artifact.id> project directory. It is not required but recommended that the output directory is /resources/META-INF/native-image/. The native-image tool will pick up metadata from that location automatically. For more information about how to collect metadata for your application automatically, see Collecting Metadata Automatically. + Here is the expected files tree after this step:

    + +

    Configuration Files Generated by the Agent

    +
  6. +
  7. +

    Build a native executable using metadata acquired by the agent:

    + +
     ./gradlew nativeCompile
    +
    +

    The native executable, named h2demo, is created in the build/native/nativeCompile directory.

    +
  8. +
  9. +

    Run the application from the native executable:

    + +
     ./H2Example/build/native/nativeCompile/h2demo
    +
    +
  10. +
+ +

Learn more about using the agent with the Native Image Gradle plugin here.

+ +
+

Important: To proceed to the next section, clean up the project: ./gradlew clean. Make sure to delete META-INF and its contents.

+
+ +

Build a Native Executable Using the GraalVM Reachability Metadata Repository

+ +

Since release 0.9.11, the Native Image Gradle plugin adds experimental support for the GraalVM Reachability Metadata repository. +This repository provides GraalVM configuration for libraries which do not support GraalVM Native Image by default. The support needs to be enabled explicitly.

+ +
    +
  1. +

    Open the build.gradle file, and enable the GraalVM Reachability Metadata Repository in the graalvmNative plugin configuration:

    + +
     metadataRepository {
    +     enabled = true
    + }
    +
    +

    The whole configuration block should look like:

    +
     graalvmNative {
    +     agent {
    +         defaultMode = "standard"
    +     }
    +     binaries {
    +         main {
    +             imageName.set('h2demo') 
    +         }
    +     }
    +     metadataRepository {
    +         enabled = true
    +     }
    +     toolchainDetection = false
    + }
    +
    +

    The plugin will automatically download the metadata from the repository.

    +
  2. +
  3. Now build a native executable re-using metadata from the shared repository: +
     ./gradlew nativeRun
    +
    +
  4. +
  5. +

    Run the application from the native executable:

    + +
     ./H2Example/build/native/nativeCompile/h2demo
    +
    +
  6. +
+ +

You are reaching the same results in less steps. Using the shared GraalVM Reachability Metadata Repository enhances the usability of Native Image for Java applications depending on 3rd party libraries.

+ +

Summary

+ +

The GraalVM Reachability Metadata Repository enables Native Image users to share and reuse metadata for libraries and frameworks in the Java ecosystem, and, thus share the burden of maintaining third-party dependencies.

+ +

Note that if your application does not call any dynamic features at run time, running the agent or enabling the GraalVM Reachability Metadata Repository is needless. +Your workflow in that case would just be:

+
./gradlew nativeRun
+
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/use-system-properties/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/use-system-properties/index.html new file mode 100644 index 0000000..e031fa8 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/guides/use-system-properties/index.html @@ -0,0 +1,104 @@ +

Use System Properties in a Native Executable

+ +

Assume you have compiled the following Java application using javac:

+
public class App {
+    public static void main(String[] args) {
+        System.getProperties().list(System.out);
+    }
+}
+
+

If you build a native executable using native-image -Dfoo=bar App, the system property foo will be available at executable build time. This means it is available to the code in your application that is run at build time (usually static field initializations and static initializers). +Thus, if you run the resulting executable, it will not contain foo in the printed list of properties.

+ +

If, on the other hand, you run the executable with app -Dfoo=bar, it will display foo in the list of properties because you specified property at executable runtime.

+ +

In other words:

+
    +
  • Pass -D<key>=<value> as an argument to native-image to control the properties seen at executable build time.
  • +
  • Pass -D<key>=<value> as an argument to a native executable to control the properties seen at executable runtime.
  • +
+ +

Reading System Properties at Build Time

+

You can read system properties at build time and incorporate them into the resulting executable file, as shown in the following example.

+ +
    +
  1. +

    Make sure you have installed a GraalVM JDK. +The easiest way to get started is with SDKMAN!. +For other installation options, visit the Downloads section.

    +
  2. +
  3. +

    Save the following Java code into a file named ReadProperties.java, then compile it using javac:

    + +
     public class ReadProperties {
    +     private static final String STATIC_PROPERTY_KEY = "static_key";
    +     private static final String INSTANCE_PROPERTY_KEY = "instance_key";
    +     private static final String STATIC_PROPERTY;
    +     private final String instanceProperty;
    +     static {
    +         System.out.println("Getting value of static property with key: " + STATIC_PROPERTY_KEY);
    +         STATIC_PROPERTY = System.getProperty(STATIC_PROPERTY_KEY);
    +     }
    +    
    +     public ReadProperties() {
    +         System.out.println("Getting value of instance property with key: " + INSTANCE_PROPERTY_KEY);
    +         instanceProperty = System.getProperty(INSTANCE_PROPERTY_KEY);
    +     }
    +        
    +     public void print() {
    +         System.out.println("Value of instance property: " + instanceProperty);
    +     } 
    +        
    +     public static void main(String[] args) {
    +         System.out.println("Value of static property: " + STATIC_PROPERTY);
    +         ReadProperties rp = new ReadProperties();
    +         rp.print();
    +     } 
    + }
    +
    +
  4. +
  5. Build the native executable, passing a system property as a command-line argument. Then run the native executable, passing a different system property on the command line. +
     native-image -Dstatic_key=STATIC_VALUE ReadProperties
    +
    +
     ./readproperties -Dinstance_key=INSTANCE_VALUE
    +
    + +

    You should see the following output:

    +
     Getting value of static property with key: static_key
    + Value of static property: null
    + Getting value of instance property with key: instance_key
    + Value of instance property: INSTANCE_VALUE
    +
    + +

    This indicates that the class static initializer was not run at build time, but at runtime.

    +
  6. +
  7. +

    To force the class static initializer to run at build time, use the --initialize-at-build-time flag, as follows:

    + +
     native-image --initialize-at-build-time=ReadProperties -Dstatic_key=STATIC_VALUE ReadProperties
    +
    +

    In the output from the native-image tool you should see output similar to the following:

    +
     ...
    + [1/7] Initializing...                                            (7.7s @ 0.07GB)
    + Getting value of static property with key: static_key
    + ...
    +
    +

    Run the executable again, as follows:

    +
     ./readproperties -Dinstance_key=INSTANCE_VALUE
    +
    + +

    This time you should see the following output, confirming that the static initializer was run at build time, not at runtime.

    + +
     Value of static property: STATIC_VALUE
    + Getting value for instance property key: instance_key
    + Value of instance property: INSTANCE_VALUE
    +
    +
  8. +
+ + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/index.html new file mode 100644 index 0000000..0854541 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/index.html @@ -0,0 +1,536 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image

+ +

Native Image is a technology to compile Java code ahead-of-time to a binary – a native executable. +A native executable includes only the code required at run time, that is the application classes, standard-library classes, the language runtime, and statically-linked native code from the JDK.

+ +

An executable file produced by Native Image has several important advantages, in that it

+ +
    +
  • Uses a fraction of the resources required by the Java Virtual Machine, so is cheaper to run
  • +
  • Starts in milliseconds
  • +
  • Delivers peak performance immediately, with no warmup
  • +
  • Can be packaged into a lightweight container image for fast and efficient deployment
  • +
  • Presents a reduced attack surface
  • +
+ +

A native executable is created by the Native Image builder or native-image that processes your application classes and other metadata to create a binary for a specific operating system and architecture. +First, the native-image tool performs static analysis of your code to determine the classes and methods that are reachable when your application runs. +Second, it compiles classes, methods, and resources into a binary. +This entire process is called build time to clearly distinguish it from the compilation of Java source code to bytecode.

+ +

The native-image tool can be used to build a native executable, which is the default, or a native shared library. This quick start guide focuses on building a native executable; to learn more about native shared libraries, go here.

+ +

To get used to Native Image terminology and get better understanding of the technology, we recommend you to read the Basics of Native Image.

+ +

Table of Contents

+ + + +

Prerequisites

+ +

The native-image tool, available in the bin directory of your GraalVM installation, depends on the local toolchain (header files for the C library, glibc-devel, zlib, gcc, and/or libstdc++-static). +These dependencies can be installed (if not yet installed) using a package manager on your machine. +Choose your operating system to find instructions to meet the prerequisites.

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + Unknown snippet type! markdown + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + Unknown snippet type! markdown + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + Unknown snippet type! markdown + + + + + +
+ +
+ + + +
+ + + +
+ + + +

Build a Native Executable

+ +

The native-image tool takes Java bytecode as its input. You can build a native executable from a class file, from a JAR file, or from a module (with Java 9 and higher).

+ +

From a Class

+

To build a native executable from a Java class file in the current working directory, use the following command:

+
native-image [options] class [imagename] [options]
+
+ +

For example, build a native executable for a HelloWorld application.

+ +
    +
  1. Save this code into file named HelloWorld.java: +
     public class HelloWorld {
    +     public static void main(String[] args) {
    +         System.out.println("Hello, Native World!");
    +     }
    + }
    +
    +
  2. +
  3. Compile it and build a native executable from the Java class: +
     javac HelloWorld.java
    + native-image HelloWorld
    +
    +

    It will create a native executable, helloWorld, in the current working directory.

    +
  4. +
  5. +

    Run the application:

    + +
     ./helloWorld
    +
    +

    You can time it to see the resources used:

    + +
     time -f 'Elapsed Time: %e s Max RSS: %M KB' ./helloworld
    + # Hello, Native World!
    + # Elapsed Time: 0.00 s Max RSS: 7620 KB
    +
    +
  6. +
+ +

From a JAR file

+ +

To build a native executable from a JAR file in the current working directory, use the following command:

+
native-image [options] -jar jarfile [imagename]
+
+ +

The default behavior of native-image is aligned with the java command which means you can pass the -jar, -cp, -m options to build with Native Image as you would normally do with java. For example, java -jar App.jar someArgument becomes native-image -jar App.jar and ./App someArgument.

+ +

Follow this guide to build a native executable from a JAR file.

+ +

From a Module

+ +

You can also convert a modularized Java application into a native executable.

+ +

The command to build a native executable from a Java module is:

+
native-image [options] --module <module>[/<mainclass>] [options]
+
+ +

For more information about how to produce a native executable from a modular Java application, see Building a HelloWorld Java Module into a Native Executable.

+ +

Getting Notified When the Build Process Is Done

+ +

Depending on the size of your application and the available resources of your build machine, it can take a few minutes to AOT-compile your Java application to a native executable. +If you are building your project in the background, consider using a command that notifies you when the build process is completed. +Below, example commands are listed per operating system:

+ +

Linux

+
# Ring the terminal bell
+native-image -jar App.jar ... ; printf '\a'
+
+# Use libnotify to create a desktop notification
+native-image -jar App.jar ... ; notify-send "GraalVM Native Image build completed with exit code $?"
+
+# Use Zenity to open an info dialog box with text
+native-image -jar App.jar ... ; zenity --info --text="GraalVM Native Image build completed with exit code $?"
+
+ +

macOS

+
# Ring the terminal bell
+native-image -jar App.jar ... ; printf '\a'
+
+# Use Speech Synthesis
+native-image -jar App.jar ... ; say "GraalVM Native Image build completed"
+
+ +

Windows

+
REM Ring the terminal bell (press Ctrl+G to enter ^G)
+native-image.exe -jar App.jar & echo ^G
+
+REM Open an info dialog box with text
+native-image.exe -jar App.jar & msg "%username%" GraalVM Native Image build completed
+
+ +

Build Configuration

+ +

There many options you can pass to the native-image builder to configure the build process. Run native-image --help to see the full list. +The options passed to native-image are evaluated left-to-right.

+ +

For different build tweaks and to learn more about build time configuration, see Native Image Build Configuration.

+ +

Native Image will output the progress and various statistics during the build. To learn more about the output and the different build phases, see Build Output.

+ +

Native Image and Third-Party Libraries

+ +

For more complex applications that use external libraries, you must provide the native-image builder with metadata.

+ +

Building a standalone binary with the native-image tool takes place under a “closed world assumption”. +The native-image tool performs an analysis to see which classes, methods, and fields within your application are reachable and must be included in the native image. +The analysis is static: it does not run your application. +This means that all the bytecode in your application that can be called at run time must be known (observed and analyzed) at build time.

+ +

The analysis can determine some cases of dynamic class loading, but it cannot always exhaustively predict all usages of the Java Native Interface (JNI), Java Reflection, Dynamic Proxy objects, or class path resources. +To deal with these dynamic features of Java, you inform the analysis with details of the classes that use Reflection, Proxy, and so on, or what classes to be dynamically loaded. +To achieve this, you either provide the native-image tool with JSON-formatted configuration files or pre-compute metadata in the code.

+ +

To learn more about metadata, ways to provide it, and supported metadata types, see Reachability Metadata. +To automatically collect metadata for your application, see Automatic Collection of Metadata.

+ +

There are also Maven and Gradle plugins for Native Image to automate building, testing and configuring native executables. Learn more here.

+ +

Some applications may need additional configuration to be compiled with GraalVM Native Image. +For more details, see Native Image Compatibility Guide.

+ +

Native Image can also interop with native languages through a custom API. +Using this API, you can specify custom native entry points into your Java application and build it into a nativw shared library. +To learn more, see Interoperability with Native Code.

+ +

Further Reading

+ +

This getting started guide is intended for new users or those with little experience of using GraalVM Native Image. +We strongly recommend these users to check the Basics of Native Image page to better understand some key aspects before going deeper.

+ +

Check user guides to become more experienced with GraalVM Native Image, find demo examples, and learn about potential usage scenarios.

+ +

For a gradual learning process, check the Native Image Build Overview and Build Configuration documentation.

+ +

Consider running interactive workshops to get some practical experience: go to Luna Labs and search for “Native Image”.

+ +

If you have stumbled across a potential bug, please submit an issue in GitHub.

+ +

If you would like to contribute to Native Image, follow our standard contributing workflow.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/inspect/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/inspect/index.html new file mode 100644 index 0000000..88c8f0b --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/inspect/index.html @@ -0,0 +1,11 @@ + + + + Redirecting… + + + + +

Redirecting…

+ Click here if you are not redirected. + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/AutomaticMetadataCollection/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/AutomaticMetadataCollection/index.html new file mode 100644 index 0000000..1fce16a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/AutomaticMetadataCollection/index.html @@ -0,0 +1,317 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Collect Metadata with the Tracing Agent

+ +

The Native Image tool relies on the static analysis of an application’s reachable code at runtime. +However, the analysis cannot always completely predict all usages of the Java Native Interface (JNI), Java Reflection, Dynamic Proxy objects, or class path resources. +Undetected usages of these dynamic features must be provided to the native-image tool in the form of metadata (precomputed in code or as JSON configuration files).

+ +

Here you will find information how to automatically collect metadata for an application and write JSON configuration files. +To learn how to compute dynamic feature calls in code, see Reachability Metadata.

+ +

Table of Contents

+ + + +

Tracing Agent

+ +

GraalVM provides a Tracing Agent to easily gather metadata and prepare configuration files. +The agent tracks all usages of dynamic features during application execution on a regular Java VM.

+ +

Enable the agent on the command line with the java command from the GraalVM JDK:

+
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/ ...
+
+ +
+

Note: -agentlib must be specified before a -jar option or a class name or any application parameters as part of the java command.

+
+ +

When run, the agent looks up classes, methods, fields, resources for which the native-image tool needs additional information. +When the application completes and the JVM exits, the agent writes metadata to JSON files in the specified output directory (/path/to/config-dir/).

+ +

It may be necessary to run the application more than once (with different execution paths) for improved coverage of dynamic features. +The config-merge-dir option adds to an existing set of configuration files, as follows:

+
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-merge-dir=/path/to/config-dir/ ...                                                              ^^^^^
+
+ +

The agent also provides the following options to write metadata on a periodic basis:

+
    +
  • config-write-period-secs=n: writes metadata files every n seconds; n must be greater than 0.
  • +
  • config-write-initial-delay-secs=n: waits n seconds before first writing metadata; defaults to 1.
  • +
+ +

For example:

+
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/,config-write-period-secs=300,config-write-initial-delay-secs=5 ...
+
+ +

The above command will write metadata files to /path/to/config-dir/ every 300 seconds after an initial delay of 5 seconds.

+ +

It is advisable to manually review the generated configuration files. +Because the agent observes only executed code, the application input should cover as many code paths as possible.

+ +

The generated configuration files can be supplied to the native-image tool by placing them in a META-INF/native-image/ directory on the class path. +This directory (or any of its subdirectories) is searched for files with the names jni-config.json, reflect-config.json, proxy-config.json, resource-config.json, predefined-classes-config.json, serialization-config.json which are then automatically included in the build process. +Not all of those files must be present. +When multiple files with the same name are found, all of them are considered.

+ +

To test the agent collecting metadata on an example application, go to the Build a Native Executable with Reflection guide.

+ +

Conditional Metadata Collection

+ +

The agent can deduce metadata conditions based on their usage in executed code. +Conditional metadata is mainly aimed towards library maintainers with the goal of reducing overall footprint.

+ +

To collect conditional metadata with the agent, see Conditional Metadata Collection.

+ +

Agent Advanced Usage

+ +

Caller-based Filters

+ +

By default, the agent filters dynamic accesses which Native Image supports without configuration. +The filter mechanism works by identifying the Java method performing the access, also referred to as caller method, and matching its declaring class against a sequence of filter rules. +The built-in filter rules exclude dynamic accesses which originate in the JVM, or in parts of a Java class library directly supported by Native Image (such as java.nio) from the generated configuration files. +Which item (class, method, field, resource, etc.) is being accessed is not relevant for filtering.

+ +

In addition to the built-in filter, custom filter files with additional rules can be specified using the caller-filter-file option. +For example: -agentlib:caller-filter-file=/path/to/filter-file,config-output-dir=...

+ +

Filter files have the following structure:

+
{ "rules": [
+    {"excludeClasses": "com.oracle.svm.**"},
+    {"includeClasses": "com.oracle.svm.tutorial.*"},
+    {"excludeClasses": "com.oracle.svm.tutorial.HostedHelper"}
+  ],
+  "regexRules": [
+    {"includeClasses": ".*"},
+    {"excludeClasses": ".*\\$\\$Generated[0-9]+"}
+  ]
+}
+
+ +

The rules section contains a sequence of rules. +Each rule specifies either includeClasses, which means that lookups originating in matching classes will be included in the resulting configuration, or excludeClasses, which excludes lookups originating in matching classes from the configuration. +Each rule defines a pattern to match classes. The pattern can end in .* or .**, interpreted as follows: + - .* matches all classes in the package and only that package; + - .** matches all classes in the package as well as in all subpackages at any depth. +Without .* or .**, the rule applies only to a single class with the qualified name that matches the pattern. +All rules are processed in the sequence in which they are specified, so later rules can partially or entirely override earlier ones. +When multiple filter files are provided (by specifying multiple caller-filter-file options), their rules are chained together in the order in which the files are specified. +The rules of the built-in caller filter are always processed first, so they can be overridden in custom filter files.

+ +

In the example above, the first rule excludes lookups originating in all classes from package com.oracle.svm and from all of its subpackages (and their subpackages, etc.) from the generated metadata. +In the next rule however, lookups from those classes that are directly in package com.oracle.svm.tutorial are included again. +Finally, lookups from the HostedHelper class is excluded again. Each of these rules partially overrides the previous ones. +For example, if the rules were in the reverse order, the exclusion of com.oracle.svm.** would be the last rule and would override all other rules.

+ +

The regexRules section also contains a sequence of rules. +Its structure is the same as that of the rules section, but rules are specified as regular expression patterns which are matched against the entire fully qualified class identifier. +The regexRules section is optional. +If a regexRules section is specified, a class will be considered included if (and only if) both rules and regexRules include the class and neither of them exclude it. +With no regexRules section, only the rules section determines whether a class is included or excluded.

+ +

For testing purposes, the built-in filter for Java class library lookups can be disabled by adding the no-builtin-caller-filter option, but the resulting metadata files are generally unsuitable for the build. +Similarly, the built-in filter for Java VM-internal accesses based on heuristics can be disabled with no-builtin-heuristic-filter and will also generally lead to less usable metadata files. +For example: -agentlib:native-image-agent=no-builtin-caller-filter,no-builtin-heuristic-filter,config-output-dir=...

+ +

Access Filters

+ +

Unlike the caller-based filters described above, which filter dynamic accesses based on where they originate, access filters apply to the target of the access. +Therefore, access filters enable directly excluding packages and classes (and their members) from the generated configuration.

+ +

By default, all accessed classes (which also pass the caller-based filters and the built-in filters) are included in the generated configuration. +Using the access-filter-file option, a custom filter file that follows the file structure described above can be added. +The option can be specified more than once to add multiple filter files and can be combined with the other filter options, for example, -agentlib:access-filter-file=/path/to/access-filter-file,caller-filter-file=/path/to/caller-filter-file,config-output-dir=....

+ +

Specify Configuration Files as Arguments

+ +

A directory containing configuration files that is not part of the class path can be specified to native-image via -H:ConfigurationFileDirectories=/path/to/config-dir/. +This directory must directly contain all files: jni-config.json, reflect-config.json, proxy-config.json and resource-config.json. +A directory with the same metadata files that is on the class path, but not in META-INF/native-image/, can be provided via -H:ConfigurationResourceRoots=path/to/resources/. +Both -H:ConfigurationFileDirectories and -H:ConfigurationResourceRoots can also take a comma-separated list of directories.

+ +

Injecting the Agent via the Process Environment

+ +

Altering the java command line to inject the agent can prove to be difficult if the Java process is launched by an application or script file, or if Java is even embedded in an existing process. +In that case, it is also possible to inject the agent via the JAVA_TOOL_OPTIONS environment variable. +This environment variable can be picked up by multiple Java processes which run at the same time, in which case each agent must write to a separate output directory with config-output-dir. +(The next section describes how to merge sets of configuration files.) +In order to use separate paths with a single global JAVA_TOOL_OPTIONS variable, the agent’s output path options support placeholders:

+
export JAVA_TOOL_OPTIONS="-agentlib:native-image-agent=config-output-dir=/path/to/config-output-dir-{pid}-{datetime}/"
+
+ +

The {pid} placeholder is replaced with the process identifier, while {datetime} is replaced with the system date and time in UTC, formatted according to ISO 8601. +For the above example, the resulting path could be: /path/to/config-output-dir-31415-20181231T235950Z/.

+ +

Trace Files

+ +

In the examples above, native-image-agent has been used to both keep track of the dynamic accesses on a JVM and then to generate a set of configuration files from them. +However, for a better understanding of the execution, the agent can also write a trace file in JSON format that contains each individual access:

+
$JAVA_HOME/bin/java -agentlib:native-image-agent=trace-output=/path/to/trace-file.json ...
+
+ +

The native-image-configure tool can transform trace files to configuration files. +The following command reads and processes trace-file.json and generates a set of configuration files in the directory /path/to/config-dir/:

+
native-image-configure generate --trace-input=/path/to/trace-file.json --output-dir=/path/to/config-dir/
+
+ +

Interoperability

+ +

The agent uses the JVM Tool Interface (JVMTI) and can potentially be used with other JVMs that support JVMTI. +In this case, it is necessary to provide the absolute path of the agent:

+
/path/to/some/java -agentpath:/path/to/graalvm/jre/lib/amd64/libnative-image-agent.so=<options> ...
+
+ +

Experimental Options

+ +

The agent has options which are currently experimental and might be enabled in future releases, but can also be changed or removed entirely. +See the ExperimentalAgentOptions.md guide.

+ +

Native Image Configure Tool

+ +

When using the agent in multiple processes at the same time as described in the previous section, config-output-dir is a safe option, but it results in multiple sets of configuration files. +The native-image-configure tool can be used to merge these configuration files. +This tool must first be built with:

+
native-image --macro:native-image-configure-launcher
+
+ +

Then, the tool can be used to merge sets of configuration files as follows:

+
native-image-configure generate --input-dir=/path/to/config-dir-0/ --input-dir=/path/to/config-dir-1/ --output-dir=/path/to/merged-config-dir/
+
+ +

This command reads one set of configuration files from /path/to/config-dir-0/ and another from /path/to/config-dir-1/ and then writes a set of configuration files that contains both of their information to /path/to/merged-config-dir/. +An arbitrary number of --input-dir arguments with sets of configuration files can be specified. See native-image-configure help for all options.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/Compatibility/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/Compatibility/index.html new file mode 100644 index 0000000..7b22063 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/Compatibility/index.html @@ -0,0 +1,238 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Compatibility Guide

+ +

Native Image uses a different way of compiling a Java application than the traditional Java virtual machine (VM). +It distinguishes between build time and run time. +At the image build time, the native-image builder performs static analysis to find all the methods that are reachable from the entry point of an application. +The builder then compiles these (and only these) methods into an executable binary. +Because of this different compilation model, a Java application can behave somewhat differently when compiled into a native image.

+ +

Native Image provides an optimization to reduce the memory footprint and startup time of an application. +This approach relies on a “closed-world assumption” in which all code is known at build time. That is, no new code is loaded at run time. +As with most optimizations, not all applications are amenable to this approach. +If the native-image builder is unable to optimize an application at build time, it generates a so-called “fallback file” that requires a Java VM to run. +We recommend to check Native Image Basics for a detailed description what happens with your Java application at build and run times.

+ +

Features Requiring Metadata

+ +

To be suitable for closed-world assumption, the following Java features generally require metadata to pass to native-image at build time. +This metadata ensures that a native image uses the minimum amount of space necessary.

+ +

The compatibility of Native Image with the most popular Java libraries was recently enhanced by publishing shared reachability metadata on GitHub. The users can share the burden of maintaining metadata for third-party dependencies and reuse it. +See Reachability Metadata to learn more.

+ +

Features Incompatible with Closed-World Assumption

+ +

Some Java features are not yet supported within the closed-world assumption, and if used, result in a fallback file.

+ +

invokedynamic Bytecode and Method Handles

+ +

Under the closed-world assumption, all methods that are called and their call sites must be known. +The invokedynamicmethod and method handles can introduce calls at run time or change the method that is invoked.

+ +

Note that invokedynamic use cases generated by javac for, for example, Java lambda expressions and String concatenation that are supported because they do not change called methods at run time.

+ +

Security Manager

+ +

Native Image will not allow a Java Security Manager to be enabled because this functionality has deprecated since Java 17.

+ +

Features That May Operate Differently in a Native Image

+ +

Native Image implements some Java features differently to the Java VM.

+ +

Signal Handlers

+ +

Registering a signal handler requires a new thread to start that handles the signal and invokes shutdown hooks. +By default, no signal handlers are registered when building a native image, unless they are registered explicitly by the user. +For example, it is not recommended to register the default signal handlers when building a shared library, but it is desirable to include signal handlers when building a native executable for containerized environments, such as Docker containers.

+ +

To register the default signal handlers, pass the --install-exit-handlers option to the native-image builder. +This option gives you the same signal handlers as a Java VM.

+ +

Class Initializers

+ +

By default, classes are initialized at run time. +This ensures compatibility, but limits some optimizations. +For faster startup and better peak performance, it is better to initialize classes at build time. +Class initialization behavior can be specified using the options --initialize-at-build-time or --initialize-at-run-time for specific classes and packages or for all classes. +Classes that are members of the JDK class libraries are initialized by default.

+ +

Note: Class initialization at build time may break specific assumptions in existing code. +For example, files loaded in a class initializer may not be in the same place at build time as at run time. +Also, certain objects such as a file descriptors or running threads must not be stored in a native executable. +If such objects are reachable at build time, the native image builder fails with an error.

+ +

For more information, see Class Initialization in Native Image.

+ +

Finalizers

+ +

The Java base class java.lang.Object defines the method finalize(). +It is called by the garbage collector on an object when garbage collection determines that there are no more references to the object. +A subclass can override the finalize() method to dispose of system resources or to perform other cleanup operations.

+ +

Finalizers have been deprecated since Java SE 9. +They are complicated to implement, and have badly designed semantics. +For example, a finalizer can cause an object to be reachable again by storing a reference to it in a static field. +Therefore, finalizers are not invoked. +We recommend you replace finalizers with weak references and reference queues.

+ +

Threads

+ +

Native Image does not implement long-deprecated methods in java.lang.Thread such as Thread.stop().

+ +

Unsafe Memory Access

+ +

Fields that are accessed using sun.misc.Unsafe need to be marked as such for the static analysis if classes are initialized at build time. +In most cases, that happens automatically: field offsets stored in static final fields are automatically rewritten from the hosted value (the field offset for the Java VM on which the native image builder is running) to the native executable value, and as part of that rewrite the field is marked as Unsafe-accessed. +For non-standard patterns, field offsets can be recomputed manually using the annotation RecomputeFieldValue.

+ +

Debugging and Monitoring

+ +

Java has some optional specifications that a Java implementation can use for debugging and monitoring Java programs, including JVMTI. +They help you monitor the Java VM at runtime for events such as compilation, for example, which do not occur in most native images. +These interfaces are built on the assumption that Java bytecodes are available at run time, which is not the case for native images built with the closed-world optimization. +Because the native-image builder generates a native executable, users must use native debuggers and monitoring tools (such as GDB or VTune) rather than tools targeted for Java. +JVMTI and other bytecode-based tools are not supported with Native Image.

+ +

Limitations on Linux AArch64 Architecture

+ +

Mostly all Native Image features are supported on Linux AArch64 architecture, except for the limitations described below.

+ +
    +
  • -R:[+|-]WriteableCodeCache: must be disabled.
  • +
  • --libc=<value>: musl is not supported.
  • +
  • --gc=<value>: The G1 garbage collector (G1) is not supported.
  • +
+ +

Find a complete list of options to the native-image builder here.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/ExperimentalAgentOptions/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/ExperimentalAgentOptions/index.html new file mode 100644 index 0000000..c2a1fac --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/ExperimentalAgentOptions/index.html @@ -0,0 +1,222 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Experimental Agent Options

+ +

The native-image-agent tool has options which are currently experimental and might be enabled in future releases, but can also be changed or removed entirely. +These options are described here.

+ +

Support For Predefined Classes

+ +

Native-image needs all classes to be known at image build time (a “closed-world assumption”). +However, Java has support for loading new classes at runtime. +To emulate class loading, the agent can be told to trace dynamically loaded classes and save their bytecode for later use by the image builder. +This functionality can be enabled by adding experimental-class-define-support to the agent option string, e.g.: -agentlib:native-image-agent=config-output-dir=config,experimental-class-define-support +Apart from the standard configuration files, the agent will create an agent-extracted-predefined-classes directory in the configuration output directory and write bytecode of newly loaded classes on the go. +The configuration directory can then be used by image builder without additional tweaks,. +The classes will be loaded during the image build, but will not be initialized or made available to the application. +At runtime, if there is an attempt to load a class with the same name and bytecodes as one of the classes encountered during tracing, the predefined class will be supplied to the application.

+ +

Known Limitations

+ +
    +
  • Native images support “loading” a predefined class only once per execution, by just a single class loader.
  • +
  • Predefined classes are initialized when they are “loaded” at runtime and cannot be initialized at build time.
  • +
  • The agent collects all classes which are not loaded by one of the Java VM’s built-in class loaders (with some exceptions), that is, from the class path or module path. This includes classes loaded by any custom class loaders.
  • +
  • Classes that are generated with varying data in their name or bytecodes, such as sequential or random numbers or timestamps, can generally not be matched to predefined classes at runtime. In these cases, the way such classes are generated needs to be adjusted.
  • +
+ +

Printing Configuration With Origins

+ +

For debugging, it may be useful to know the origin of certain configuration entries. +By supplying experimental-configuration-with-origins to the agent option string, the agent will output configuration files with configuration entries broken down to the calling context (stack trace) they originate from in tree form. +This option should be used in conjunction with config-output-dir=<path> to tell the agent where to output the configuration files. +An example agent option string: -agentlib:native-image-agent=config-output-dir=config-with-origins/,experimental-configuration-with-origins

+ +

Omitting Configuration From The Agent’s Output

+ +

The agent can omit traced configuration entries present in existing configuration files. +There are two ways to specify these existing configuration files:

+
    +
  • By using configuration files from the class path or module path. When experimental-omit-config-from-classpath is added to the agent option string, the class path and module path of the running application are scanned for META-INF/native-image/**/*.json configuration files.
  • +
  • By explicitly pointing the agent to an existing configuration file directory using config-to-omit=<path>.
  • +
+ +

Generating Conditional Configuration Using the Agent

+ +

The agent can, using a heuristic, generate configuration with reachability conditions on user specified classes. +The agent will track configuration origins and try to deduce the conditions automatically. +User classes are specified via an agent filter file (for more information on the format, see more about the agent). +Additionally, the resulting configuration can further be filtered using another filter file.

+ +

Currently, this feature supports two modes:

+
    +
  1. Generating conditional configuration in a single run with the agent.
  2. +
  3. Generating conditional configuration from multiple runs with the agent and finally merging the collected data.
  4. +
+ +

Generating Conditional Configuration During an Agent Run

+ +

To enable this mode, add experimental-conditional-config-filter-file=<path> to the agent’s command line, where <path> points to an agent filter file. +Classes that are considered included by this filter will be designated as user code classes. +To further filter the generated configuration, you can use conditional-config-class-filter-file=<path>, where <path> is a path to an agent filter file.

+ +

Generating Conditional Configuration From Multiple Agent Runs

+ +

Conditional configuration can be generated from multiple agent runs that reach different code paths in the application. +Each agent run produces configuration with metadata. native-image-configure is then used to merge the collected data and produce a conditional configuration. +To run the agent in this mode, add experimental-conditional-config-part to the agent’s command line. +Once all the agent runs have finished, you can generate a conditional configuration by invoking:

+
native-image-configure generate-conditional --user-code-filter=<path-to-filter-file> --class-name-filter=<path-to-filter-file> --input-dir=<path-to-agent-run-output-1> --input-dir=<path-to-agent-run-ouput-2> ... --output-dir=<path-to-resulting-conditional-config>
+
+

where:

+
    +
  • --user-code-filter=<path-to-filter-file>: path to an agent filter file that specifies user classes
  • +
  • (optional) --class-name-filter=<path-to-filter-file>: path to an agent filter file that further filters the generated config
  • +
+ +

The Underlying Heuristics

+ +

Conditions are generated using the call tree of the application. The heuristics work as follows:

+
    +
  1. For each unique method, create a list of all nodes in the call tree that correspond to the method
  2. +
  3. For each unique method, if the method has more than one call node in the tree: +
      +
    • Find common configuration across all call nodes of that method
    • +
    • For each call node of the method, propagate configuration that isn’t common across these calls to the caller node
    • +
    +
  4. +
  5. Repeat 2. until an iteration produced no changes in the call tree.
  6. +
  7. For each node that contains configuration, generate a conditional configuration entry with the method’s class as the condition.
  8. +
+ +

The primary goal of this heuristic is to attempt to find where a method creates different configuration entries depending on the caller (for example, a method that wraps Class.forName calls.) +This implies that the heuristic will not work well for code that generates configuration through a different dependency (for example, same method returns calls Class.forName with different class parameters depending on a system property).

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/index.html new file mode 100644 index 0000000..b0899b7 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/metadata/index.html @@ -0,0 +1,565 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Reachability Metadata

+ +

The dynamic language features of the JVM (including reflection and resource handling) compute the dynamically-accessed program elements such as invoked methods or resource URLs at runtime. +The native-image tool performs static analysis while building a native binary to determine those dynamic features, but it cannot always exhaustively predict all uses. +To ensure inclusion of these elements into the native binary, you should provide reachability metadata (in further text referred as metadata) to the native-image builder. +Providing the builder with reachability metadata also ensures seamless compatibility with third-party libraries at runtime.

+ +

Metadata can be provided to the native-image builder in following ways:

+ + +

Table of Contents

+ + + +

Computing Metadata in Code

+ +

Computing metadata in code can be achieved in two ways:

+ +
    +
  1. +

    By providing constant arguments to functions that dynamically access elements of the JVM. A good example of such a function is the Class.forName method. In the following code:

    + +
     class ReflectiveAccess {
    +     public Class<Foo> fetchFoo() throws ClassNotFoundException {
    +         return Class.forName("Foo");
    +     }
    + }
    +
    +

    the Class.forName("Foo") will be computed into a constant when native binary is built and stored in its initial heap. + If the class Foo does not exist, the call will be transformed into throw ClassNotFoundException("Foo").

    +
  2. +
  3. +

    By initializing classes at build time and storing dynamically accessed elements into the initial heap of the native executable. For example:

    + +
     class InitializedAtBuildTime {
    +     private static Class<?> aClass;
    +     static {
    +         try {
    +             aClass = Class.forName(readFile("class.txt"));
    +         } catch (ClassNotFoundException e) {
    +             throw RuntimeException(e);
    +         }
    +     }
    +
    +     public Class<?> fetchClass() {
    +         return aClass;
    +     }
    + }
    +
    +
  4. +
+ +

When metadata is computed in code, the dynamically accessed elements will be included into the native executable’s heap only if that part of the heap is reachable through an enclosing method (for example, ReflectiveAccess#fetchFoo) or a static field (for example, InitializedAtBuildTime.aClass).

+ +

Specifying Metadata with JSON

+ +

Each dynamic Java feature that requires metadata has a corresponding JSON file named <feature>-config.json. +The JSON file consists of entries that tell Native Image the elements to include. +For example, Java reflection metadata is specified in reflect-config.json, and a sample entry looks like:

+
{
+  "name": "Foo"
+}
+
+ +

Each entry in json-based metadata should be conditional to avoid unnecessary growth in the size of the native binary. +A condition is specified in the following way:

+
{
+  "condition": {
+    "typeReachable": "<fully-qualified-class-name>"
+  },
+  <metadata-entry>
+}
+
+

An entry with a typeReachable condition is considered only when the fully-qualified class is reachable. +Currently, we support only typeReachable as a condition.

+ +

Metadata Types

+ +

Native Image accepts the following types of reachability metadata:

+
    +
  • Java reflection (the java.lang.reflect.* API) enables Java code to examine its own classes, methods, fields, and their properties at run time.
  • +
  • JNI allows native code to access classes, methods, fields and their properties at run time.
  • +
  • Resources and Resource Bundles allow arbitrary files present in the application to be loaded.
  • +
  • Dynamic JDK Proxies create classes on demand that implement a given list of interfaces.
  • +
  • Serialization enables writing and reading Java objects to and from streams.
  • +
  • Predefined Classes provide support for dynamically generated classes.
  • +
+ +

Reflection

+

Computing Reflection Metadata in Code

+ +

Some reflection methods are treated specially and are evaluated at build time when given constant arguments. +These methods, in each of the listed classes, are:

+
    +
  • java.lang.Class: getField, getMethod, getConstructor, getDeclaredField, getDeclaredMethod, getDeclaredConstructor, forName, getClassLoader
  • +
  • java.lang.invoke.MethodHandles: publicLookup, privateLookupIn, arrayConstructor, arrayLength, arrayElementGetter, arrayElementSetter, arrayElementVarHandle, byteArrayViewVarHandle, byteBufferViewVarHandle, lookup
  • +
  • java.lang.invoke.MethodHandles.Lookup: in , findStatic , findVirtual , findConstructor , findClass , accessClass , findSpecial , findGetter , findSetter , findVarHandle , findStaticGetter , findStaticSetter , findStaticVarHandle , unreflect , unreflectSpecial , unreflectConstructor , unreflectGetter , unreflectSetter , unreflectVarHandle
  • +
  • java.lang.invoke.MethodType: methodType, genericMethodType, changeParameterType, insertParameterTypes, appendParameterTypes, replaceParameterTypes, dropParameterTypes, changeReturnType, erase, generic, wrap, unwrap, parameterType, parameterCount, returnType, lastParameterType
  • +
+ +

Below are examples of calls that are replaced with the corresponding metadata element:

+ +
Class.forName("java.lang.Integer")
+Class.forName("java.lang.Integer", true, ClassLoader.getSystemClassLoader())
+Class.forName("java.lang.Integer").getMethod("equals", Object.class)
+Integer.class.getDeclaredMethod("bitCount", int.class)
+Integer.class.getConstructor(String.class)
+Integer.class.getDeclaredConstructor(int.class)
+Integer.class.getField("MAX_VALUE")
+Integer.class.getDeclaredField("value")
+
+ +

When passing constant arrays, the following approaches to declare and populate an array are equivalent from the point of view of the native-image builder:

+ +
Class<?>[] params0 = new Class<?>[]{String.class, int.class};
+Integer.class.getMethod("parseInt", params0);
+
+ +
Class<?>[] params1 = new Class<?>[2];
+params1[0] = Class.forName("java.lang.String");
+params1[1] = int.class;
+Integer.class.getMethod("parseInt", params1);
+
+ +
Class<?>[] params2 = {String.class, int.class};
+Integer.class.getMethod("parseInt", params2);
+
+ +

Specifying Reflection Metadata in JSON

+ +

Reflection metadata can be specified in the reflect-config.json file. +The JSON file is an array of reflection entries:

+
[
+    {
+        "condition": {
+            "typeReachable": "<condition-class>"
+        },
+        "name": "<class>",
+        "methods": [
+            {"name": "<methodName>", "parameterTypes": ["<param-one-type>"]}
+        ],
+        "queriedMethods": [
+            {"name": "<methodName>", "parameterTypes": ["<param-one-type>"]}
+        ],
+        "fields": [
+            {"name": "<fieldName>"}
+        ],
+        "allDeclaredMethods": true,
+        "allDeclaredFields": true,
+        "allDeclaredConstructors": true,
+        "allPublicMethods": true,
+        "allPublicFields": true,
+        "allPublicConstructors": true,
+        "queryAllDeclaredMethods": true,
+        "queryAllDeclaredConstructors": true,
+        "queryAllPublicMethods": true,
+        "queryAllPublicConstructors": true,
+        "unsafeAllocated": true
+    }
+]
+
+ +

The fields in a reflection entry have the following meaning:

+
    +
  • condition: See Conditional Metadata Entries
  • +
  • name: Name of the class that will be reflectively looked up. This property is mandatory.
  • +
  • methods: List class methods that can be looked up and executed reflectively. + Each method is described by its name and a list of parameter types. + The parameter types are fully qualified Java class names.
  • +
  • queriedMethods: List of class methods that can only be looked up. + The description of each method is identical to the methods list.
  • +
  • fields: List of class fields that can be looked up, read, or modified.
  • +
  • all<access>(Methods/Fields/Constructors): Registers all methods/fields/constructors for lookup. Methods and constructors can also be invoked. + <access> refers to different ways of querying these members in Java and can be either Declared or Public. + For more information, see java.lang.Class.getDeclaredMethods() and java.lang.Class.getPublicMethods().
  • +
  • queryAll<access>(Methods/Constructors): Registers all methods/constructors for lookup only.
  • +
  • unsafeAllocated: Allows objects of this class to be allocated using Unsafe.allocateInstance.
  • +
+ +

Java Native Interface

+ +

Java Native Interface (JNI) allows native code to access arbitrary Java types and type members. +Native Image cannot predict what such native code will lookup, write to or invoke. +To build a native binary for a Java application that uses JNI, JNI metadata is most likely required. +For example, the given C code:

+
jclass clazz = FindClass(env, "java/lang/String");
+
+

looks up the java.lang.String class, which can then be used, for example, to invoke different String methods. +The generated metadata entry for the above call would look like:

+
{
+  "name": "java.lang.String"
+}
+
+ +

JNI Metadata In Code

+

It is not possible to specify JNI metadata in code.

+ +

JNI Metadata in JSON

+

Metadata for JNI is provided in jni-config.json files. +The JSON schema of JNI metadata is identical to the Reflection metadata schema.

+ +

Resources and Resource Bundles

+

Java is capable of accessing any resource on the application class path, or the module path for which the requesting code has permission to access. +Resource metadata instructs the native-image builder to include specified resources and resource bundles in the produced binary. +A consequence of this approach is that some parts of the application that use resources for configuration (such as logging) are effectively configured at build time.

+ +

Resource Metadata In Code

+

Native Image will detect calls to java.lang.Class#getResource and java.lang.Class#getResourceAsStream in which:

+
    +
  • The class on which these methods are called is constant
  • +
  • The first parameter, name, is a constant +and automatically register such resources.
  • +
+ +

The code below will work out of the box, because:

+
    +
  • We are using a class literal (Example.class)
  • +
  • We are using a string literal as the name parameter +
    class Example {
    + public void conquerTheWorld() {
    +     ...
    +     InputStream plan = Example.class.getResourceAsStream("plans/v2/conquer_the_world.txt");
    +     ...
    + }
    +}
    +
    +
  • +
+ +

Resource Metadata in JSON

+

Metadata for resources is provided in resource-config.json files.

+
{
+  "resources": {
+    "includes": [
+      {
+        "condition": {
+          "typeReachable": "<condition-class>" 
+        },
+        "pattern": ".*\\.txt"
+      }
+    ],
+    "excludes": [
+      {
+        "condition": {
+          "typeReachable": "<condition-class>"
+        },
+        "pattern": ".*\\.txt"
+      }
+    ]
+  },
+  "bundles": [
+    {
+      "condition": {
+        "typeReachable": "<condition-class>"
+      },
+      "name": "fully.qualified.bundle.name",
+      "locales": ["en", "de", "sk"]
+    }
+  ]
+}
+
+ +

Native Image will iterate over all resources and match their relative paths against the Java regex specified in includes. +If the path matches the regex, the resource is included. +The excludes statement instructs native-image to omit certain included resources that match the given pattern.

+ +

Dynamic Proxy

+ +

The JDK supports generating proxy classes for a given interface list. +Native Image does not support generating new classes at runtime and requires metadata to properly run code that uses these proxies.

+ +
+

Note: The order of interfaces in the interface list used to create a proxy matters. Creating a proxy with two identical interface lists in which the interfaces are not in the same order, creates two distinct proxy classes.

+
+ +

Code Example

+

The following code creates two distinct proxies:

+ +
import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+
+interface IA {
+}
+
+interface IB {
+}
+
+class Example {
+    public void doWork() {
+        InvocationHandler handler;
+        ...
+        Object proxyOne = Proxy.newProxyInstance(Example.class.getClassLoader(), new Class[]{IA.class, IB.class}, handler);
+        Object proxyTwo = Proxy.newProxyInstance(Example.class.getClassLoader(), new Class[]{IB.class, IA.class}, handler);
+        ...
+    }
+}
+
+ +

Dynamic Proxy Metadata In Code

+ +

The following methods are evaluated at build time when called with constant arguments:

+
    +
  • java.lang.reflect.Proxy.getProxyClass
  • +
  • java.lang.reflect.Proxy.newProxyInstance
  • +
+ +

Dynamic Proxy Metadata in JSON

+

Metadata for dynamic proxies is provided in proxy-config.json files.

+
[
+  {
+    "condition": {
+      "typeReachable": "<condition-class>"
+    },
+    "interfaces": [
+      "IA",
+      "IB"
+    ]
+  }
+]
+
+ +

Serialization

+

Java can serialize any class that implements the Serializable interface. +Native Image supports serialization with proper serializaiton metadata registration. This is necessary as serialization usually +requires reflectively accessing the class of the object that is being serialized.

+ +

Serialization Metadata Registration In Code

+ +

Native Image detects calls to ObjectInputFilter.Config#createFilter(String pattern) and if the pattern argument is constant, the exact classes mentioned in the pattern will be registered for serialization. +For example, the following pattern will register the class pkg.SerializableClass for serialization:

+
  var filter = ObjectInputFilter.Config.createFilter("pkg.SerializableClass;!*;")
+  objectInputStream.setObjectInputFilter(proof);
+
+

Using this pattern has a positive side effect of improving security on the JVM as only pkg.SerializableClass can be received by the +objectInputStream.

+ +

Wildcard patterns do the serialization registration only for lambda-proxy classes of an enclosing class. For example, to register lambda serialization in an enclosing class pkg.LambdaHolder use:

+
  ObjectInputFilter.Config.createFilter("pkg.LambdaHolder$$Lambda$*;")
+
+ +

Patterns like "pkg.**" and "pkg.Prefix*" will not perform serialization registration as they are too general and would increase image size significantly.

+ +

For calls to the sun.reflect.ReflectionFactory#newConstructorForSerialization(java.lang.Class) and sun.reflect.ReflectionFactory#newConstructorForSerialization(java.lang.Class, ) native image detects calls to these functions when all arguments and the receiver are constant. For example, the following call will register SerializlableClass for serialization:

+
  ReflectionFactory.getReflectionFactory().newConstructorForSerialization(SerializableClass.class);
+
+

To create a custom constructor for serialization use:

+
  var constructor = SuperSuperClass.class.getDeclaredConstructor();
+  var newConstructor = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(BaseClass.class, constructor);
+
+

Proxy classes can only be registered for serialization via the JSON files.

+ +

Serialization Metadata in JSON

+

Metadata for serialization is provided in serialization-config.json files.

+
{
+  "types": [
+    {
+      "condition": {
+        "typeReachable": "<condition-class>"
+      },
+      "name": "<fully-qualified-class-name>",
+      "customTargetConstructorClass": "<custom-target-constructor-class>"
+    }
+  ],
+  "lambdaCapturingTypes": [
+    {
+      "condition": {
+        "typeReachable": "<condition-class>"
+      },
+      "name": "<fully-qualified-class-name>"
+    }
+  ],
+ "proxies": [
+   {
+     "condition": {
+       "typeReachable": "<condition-class>"
+     },
+     "interfaces": ["<fully-qualified-interface-name-1>", "<fully-qualified-interface-name-n>"]
+   }
+ ]
+}
+
+ +

Each entry in types enables serializing and deserializing objects of the class given by name.

+ +

Each entry in lambdaCapturingTypes enables lambda serialization: all lambdas declared in the methods of the class given by name can be serialized and deserialized.

+ +

Each entry in proxies enables the Proxy serialization by providing an interface list that a proxy implements.

+ +

Predefined Classes

+ +

Native Image requires all classes to be known at build time (a “closed-world assumption”).

+ +

However, Java has support for loading new classes at runtime. +To emulate class loading, the agent can trace dynamically loaded classes and save their bytecode for later use by the native-image builder. +At runtime, if there is an attempt to load a class with the same name and bytecodes as one of the classes encountered during tracing, the predefined class will be supplied to the application.

+ +
+

Note: Predefined classes metadata is not meant to be manually written.

+
+ +

Predefined Classes Metadata In Code

+ +

It is not possible to specify predefined classes in code.

+ +

Predefined Classes Metadata in JSON

+

Metadata for predefined classes is provided in predefined-classes-config.json files.

+
[
+  {
+    "type": "agent-extracted",
+    "classes": [
+      {
+        "hash": "<class-bytecodes-hash>",
+        "nameInfo": "<class-name"
+      }
+    ]
+  }
+]
+
+ +

The JSON schema is accompanied by the agent-extracted-predefined-classes directory that contains the bytecode of the listed classes.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/native-code-interoperability/C-API/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/native-code-interoperability/C-API/index.html new file mode 100644 index 0000000..c1ef6b0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/native-code-interoperability/C-API/index.html @@ -0,0 +1,208 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image C API

+ +

Native Image provides a GraalVM-specific API to manage Java objects from the C/C++ languages, initialize isolates and attach threads. +The C API is available when Native Image is built as a shared library and its declarations are included in the header file that is generated during the native image build.

+ +
/*
+ * Structure representing an isolate. A pointer to such a structure can be
+ * passed to an entry point as the execution context.
+ */
+struct __graal_isolate_t;
+typedef struct _graal_isolate_t graal_isolate_t;
+
+/*
+ * Structure representing a thread that is attached to an isolate. A pointer to
+ * such a structure can be passed to an entry point as the execution context,
+ * requiring that the calling thread has been attached to that isolate.
+ */
+struct __graal_isolatethread_t;
+typedef struct __graal_isolatethread_t graal_isolatethread_t;
+
+/* Parameters for the creation of a new isolate. */
+struct __graal_create_isolate_params_t {
+    /* for future use */
+};
+typedef struct __graal_create_isolate_params_t graal_create_isolate_params_t;
+
+/*
+ * Create a new isolate, considering the passed parameters (which may be NULL).
+ * Returns 0 on success, or a non-zero value on failure.
+ * On success, the current thread is attached to the created isolate, and the
+ * address of the isolate and the isolate thread structures is written to the
+ * passed pointers if they are not NULL.
+ */
+int graal_create_isolate(graal_create_isolate_params_t* params, graal_isolate_t** isolate, graal_isolatethread_t** thread);
+
+/*
+ * Attaches the current thread to the passed isolate.
+ * On failure, returns a non-zero value. On success, writes the address of the
+ * created isolate thread structure to the passed pointer and returns 0.
+ * If the thread has already been attached, the call succeeds and also provides
+ * the thread's isolate thread structure.
+ */
+int graal_attach_thread(graal_isolate_t* isolate, graal_isolatethread_t** thread);
+
+/*
+ * Given an isolate to which the current thread is attached, returns the address of
+ * the thread's associated isolate thread structure.  If the current thread is not
+ * attached to the passed isolate or if another error occurs, returns NULL.
+ */
+graal_isolatethread_t* graal_get_current_thread(graal_isolate_t* isolate);
+
+/*
+ * Given an isolate thread structure, determines to which isolate it belongs and
+ * returns the address of its isolate structure. If an error occurs, returns NULL
+ * instead.
+ */
+graal_isolate_t* graal_get_isolate(graal_isolatethread_t* thread);
+
+/*
+ * Detaches the passed isolate thread from its isolate and discards any state or
+ * context that is associated with it. At the time of the call, no code may still
+ * be executing in the isolate thread's context.
+ * Returns 0 on success, or a non-zero value on failure.
+ */
+int graal_detach_thread(graal_isolatethread_t* thread);
+
+/*
+ * Tears down the isolate of the passed (and still attached) isolate thread
+ * waiting for any attached threads to detach from it, then discards its objects,
+ * threads, and any other state or context that is associated with it.
+ * Returns 0 on success, or a non-zero value on failure.
+ */
+int graal_tear_down_isolate(graal_isolatethread_t* thread);
+
+ +

In addition to the C level API, you can use the JNI Invocation API to create an isolate from Java, expose and call Java methods embedded in a native shared library.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/native-code-interoperability/JNIInvocationAPI/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/native-code-interoperability/JNIInvocationAPI/index.html new file mode 100644 index 0000000..daeae20 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/native-code-interoperability/JNIInvocationAPI/index.html @@ -0,0 +1,385 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

JNI Invocation API

+ +

Native Image can be used to implement low-level system operations in Java and make them available via JNI Invocation API to Java code executing on a standard JVM. +As a result one can use the same language to write the application logic as well as the system calls.

+ +

Note that this document describes the opposite of what is commonly done via JNI: usually low-level system operations are implemented in C and invoked from Java using JNI. +If you are interested in how Native Image supports the common use case, see Java Native Interface (JNI) in Native Image.

+ +

Create a Shared Library

+ +

First of all one has to use the native-image builder to generate a shared library with some JNI-compatible entry points. +Start with the Java code:

+
package org.pkg.implnative;
+
+import org.graalvm.nativeimage.c.function.CEntryPoint;
+import org.graalvm.word.Pointer;
+
+public final class NativeImpl {
+    @CEntryPoint(name = "Java_org_pkg_apinative_Native_add")
+    public static int add(Pointer jniEnv, Pointer clazz, @CEntryPoint.IsolateThreadContext long isolateId, int a, int b) {
+        return a + b;
+    }
+}
+
+

After being processed by the native-image builder, the code exposes a C function Java_org_pkg_apinative_Native_add (the name follows conventions of JNI that will be handy later) and a Native Image signature typical for JNI methods. +The first parameter is a reference to the JNIEnv* value. +The second parameter is a reference to the jclass value for the class declaring the method. +The third parameter is a portable (e.g., long) identifier of the Native Image isolatethread. +The rest of the parameters are the actual parameters of the Java Native.add method described in the next section. Compile the code with the --shared option:

+
$JAVA_HOME/bin/native-image --shared -H:Name=libnativeimpl -cp nativeimpl
+
+

The libnativeimpl.so is generated. We are ready to use it from standard Java code.

+ +

Bind a Java Native Method

+ +

Now we need another Java class to use the native library generated in the previous step:

+
package org.pkg.apinative;
+
+public final class Native {
+    private static native int add(long isolateThreadId, int a, int b);
+}
+
+

The package name of the class, as well as the name of the method, has to correspond (after the JNI mangling) to the name of the @CEntryPoint introduced previously. +The first argument is a portable (e.g., long) identifier of the Native Image isolate thread. +The rest of the arguments match the parameters of the entry point.

+ +

Load the Native Library

+ +

The next step is to bind the JDK with the generated .so library. +For example, make sure the implementation of the native Native.add method is loaded. +Simple load or loadLibrary calls will do:

+
public static void main(String[] args) {
+    System.loadLibrary("nativeimpl");
+    // ...
+}
+
+

This is assuming your LD_LIBRARY_PATH environment variable is specified, or the java.library.path Java property is properly set.

+ +

Initialize a Native Image Isolate

+ +

Before making calls to the Native.add method, we need to create a Native Image isolate. +Native Image provides a special built-in to allow that: CEntryPoint.Builtin.CREATE_ISOLATE. +Define another method along your other existing @CEntryPoint methods. +Let it return IsolateThread and take no parameters:

+
public final class NativeImpl {
+    @CEntryPoint(name = "Java_org_pkg_apinative_Native_createIsolate", builtin=CEntryPoint.Builtin.CREATE_ISOLATE)
+    public static native IsolateThread createIsolate();
+}
+
+

Native Image then generates default native implementation of the method into the final .so library. +The method initializes the Native Image runtime and returns a portable identification, e.g., long, to hold an instance of a Native Image isolatethread. +The isolate thread can then be used for multiple invocations of the native part of your code:

+
package org.pkg.apinative;
+
+public final class Native {
+    public static void main(String[] args) {
+        System.loadLibrary("nativeimpl");
+
+        long isolateThread = createIsolate();
+
+        System.out.println("2 + 40 = " + add(isolateThread, 2, 40));
+        System.out.println("12 + 30 = " + add(isolateThread, 12, 30));
+        System.out.println("20 + 22 = " + add(isolateThread, 20, 22));
+    }
+
+    private static native int add(long isolateThread, int a, int b);
+    private static native long createIsolate();
+}
+
+

The standard JVM is started. It initializes a Native Image isolate, attaches the current thread to the isolate, and the universal answer 42 is then computed three times inside of the isolate.

+ +

Call JVM from Native Java

+ +

There is a detailed tutorial on the C interface of Native Image. +The following example shows how to make a callback to JVM.

+ +

In the classical setup, when C needs to call into JVM, it uses a jni.h header file. +The file defines essential JVM structures (like JNIEnv) as well as functions one can invoke to inspect classes, access fields, and call methods in the JVM. +In order to call these functions from the NativeImpl class in the above example, you need to define appropriate Java API wrappers of the jni.h concepts:

+ +
@CContext(JNIHeaderDirectives.class)
+@CStruct(value = "JNIEnv_", addStructKeyword = true)
+interface JNIEnvironment extends PointerBase {
+    @CField("functions")
+    JNINativeInterface getFunctions();
+}
+
+@CPointerTo(JNIEnvironment.class)
+interface JNIEnvironmentPointer extends PointerBase {
+    JNIEnvironment read();
+    void write(JNIEnvironment value);
+}
+
+@CContext(JNIHeaderDirectives.class)
+@CStruct(value = "JNINativeInterface_", addStructKeyword = true)
+interface JNINativeInterface extends PointerBase {
+    @CField
+    GetMethodId getGetStaticMethodID();
+
+    @CField
+    CallStaticVoidMethod getCallStaticVoidMethodA();
+}
+
+interface GetMethodId extends CFunctionPointer {
+    @InvokeCFunctionPointer
+    JMethodID find(JNIEnvironment env, JClass clazz, CCharPointer name, CCharPointer sig);
+}
+
+interface JObject extends PointerBase {
+}
+
+interface CallStaticVoidMethod extends CFunctionPointer {
+    @InvokeCFunctionPointer
+    void call(JNIEnvironment env, JClass cls, JMethodID methodid, JValue args);
+}
+
+interface JClass extends PointerBase {
+}
+interface JMethodID extends PointerBase {
+}
+
+ +

Leaving aside the meaning of JNIHeaderDirectives for now, the rest of the interfaces are type-safe representations of the C pointers found in the jni.h file. JClass, JMethodID, and JObject are all pointers. +Thanks to the above definitions, you now have Java interfaces to represent instances of these objects in your native Java code in a type-safe way.

+ +

The core part of any JNI API is the set of functions one can call when talking to the JVM. +There are dozens of them, but in the JNINativeInterface definition, you just define wrappers for those few that are needed in the example. +Again, give them proper types, so in your native Java code you can use GetMethodId.find(...), CallStaticVoidMethod.call(...), etc. +In addition, there is another important part missing in the puzzle - the jvalue union type wrapping all the possible Java primitive and object types. +Here are definitions of its getters and setters:

+ +
@CContext(JNIHeaderDirectives.class)
+@CStruct("jvalue")
+interface JValue extends PointerBase {
+    @CField boolean z();
+    @CField byte b();
+    @CField char c();
+    @CField short s();
+    @CField int i();
+    @CField long j();
+    @CField float f();
+    @CField double d();
+    @CField JObject l();
+
+
+    @CField void z(boolean b);
+    @CField void b(byte b);
+    @CField void c(char ch);
+    @CField void s(short s);
+    @CField void i(int i);
+    @CField void j(long l);
+    @CField void f(float f);
+    @CField void d(double d);
+    @CField void l(JObject obj);
+
+    JValue addressOf(int index);
+}
+
+

The addressOf method is a special Native Image construct used to perform C pointer arithmetics. +Given a pointer, one can treat it as the initial element of an array, then, for example, use addressOf(1) to access the subsequent element. +With this you have all the API needed to make a callback - redefine the previously introduced NativeImpl.add method to accept properly typed pointers, and then use these pointers to invoke a JVM method before computing the sum of a + b:

+ +
@CEntryPoint(name = "Java_org_pkg_apinative_Native_add")
+static int add(JNIEnvironment env, JClass clazz, @CEntryPoint.IsolateThreadContext long isolateThreadId, int a, int b) {
+    JNINativeInterface fn = env.getFunctions();
+
+    try (
+        CTypeConversion.CCharPointerHolder name = CTypeConversion.toCString("hello");
+        CTypeConversion.CCharPointerHolder sig = CTypeConversion.toCString("(ZCBSIJFD)V");
+    ) {
+        JMethodID helloId = fn.getGetStaticMethodID().find(env, clazz, name.get(), sig.get());
+
+        JValue args = StackValue.get(8, JValue.class);
+        args.addressOf(0).z(false);
+        args.addressOf(1).c('A');
+        args.addressOf(2).b((byte)22);
+        args.addressOf(3).s((short)33);
+        args.addressOf(4).i(39);
+        args.addressOf(5).j(Long.MAX_VALUE / 2l);
+        args.addressOf(6).f((float) Math.PI);
+        args.addressOf(7).d(Math.PI);
+        fn.getCallStaticVoidMethodA().call(env, clazz, helloId, args);
+    }
+
+    return a + b;
+}
+
+ +

The above example seeks a static method hello and invokes it with eight JValue parameters in an array reserved by StackValue.get on the stack. +Individual parameters are accessed by use of the addressOf operator and filled with appropriate primitive values before the call happens. +The method hello is defined in the class Native and prints values of all parameters to verify they are properly propagated from the NativeImpl.add caller:

+ +
public class Native {
+    public static void hello(boolean z, char c, byte b, short s, int i, long j, float f, double d) {
+        System.err.println("Hi, I have just been called back!");
+        System.err.print("With: " + z + " " + c + " " + b + " " + s);
+        System.err.println(" and: " + i + " " + j + " " + f + " " + d);
+    }
+
+ +

There is just one final piece to explain: the JNIHeaderDirectives. +The Native Image C interface needs to understand the layout of the C structures. +It needs to know at which offset of JNINativeInterface structure it can find the pointer to the GetMethodId function. +To do so, it needs jni.h and additional files during compilation. One can specify them by @CContext annotation and implementation of its Directives:

+ +
final class JNIHeaderDirectives implements CContext.Directives {
+    @Override
+    public List<String> getOptions() {
+        File[] jnis = findJNIHeaders();
+        return Arrays.asList("-I" + jnis[0].getParent(), "-I" + jnis[1].getParent());
+    }
+
+    @Override
+    public List<String> getHeaderFiles() {
+        File[] jnis = findJNIHeaders();
+        return Arrays.asList("<" + jnis[0] + ">", "<" + jnis[1] + ">");
+    }
+
+    private static File[] findJNIHeaders() throws IllegalStateException {
+        final File jreHome = new File(System.getProperty("java.home"));
+        final File include = new File(jreHome.getParentFile(), "include");
+        final File[] jnis = {
+            new File(include, "jni.h"),
+            new File(new File(include, "linux"), "jni_md.h"),
+        };
+        return jnis;
+    }
+}
+
+ +

The good thing is that jni.h is inside of every JDK, so one can use the java.home property to locate the necessary header files. +The actual logic can, of course, be made more robust and OS-independent.

+ +

Implementing any JVM native method in Java and/or making callbacks to the JVM with Native Image should now be as easy as expanding upon the given example and invoking native-image.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/native-code-interoperability/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/native-code-interoperability/index.html new file mode 100644 index 0000000..51cb623 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/native-code-interoperability/index.html @@ -0,0 +1,143 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Interoperability with Native Code

+ +

You can use Native Image to convert Java code into a native shared library and call it from a native (C/C++) application just like any C function. +There are two mechanisms for calling natively compiled Java methods:

+ +
    +
  • JNI Invocation API, an API to load the JVM into an arbitrary native application. The advantage of using JNI Invocation API is support for multiple, isolated execution environments within the same process.
  • +
  • Native Image C API, an API specific to GraalVM Native Image. The advantage of using Native Image C API is that you can determine what your API will look like, but parameter and return types must be non-object types.
  • +
+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/optimizations-and-performance/ClassInitialization/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/optimizations-and-performance/ClassInitialization/index.html new file mode 100644 index 0000000..946de18 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/optimizations-and-performance/ClassInitialization/index.html @@ -0,0 +1,197 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Class Initialization in Native Image

+ +

The semantics of Java requires that a class is initialized the first time it is accessed at runtime. +Class initialization has negative consequences for compiling Java applications ahead-of-time for the following two reasons:

+ +
    +
  • It significantly degrades the performance of a native executable: every access to a class (via a field or method) requires a check to ensure the class is already initialized. Without optimization, this can reduce performance by more than twofold.
  • +
  • It increases the amount of computation–and time–to startup an application. For example, the simple “Hello, World!” application requires more than 300 classes to be initialized.
  • +
+ +

To reduce the negative impact of class initialization, Native Image supports class initialization at build time: it can initialize classes when it builds an executable, making runtime initialization and checks unnecessary. +All the static state from initialized classes is stored in the executable. +Access to a class’s static fields that were initialized at build time is transparent to the application and works as if the class was initialized at runtime.

+ +

However, Java class initialization semantics impose several constraints that complicate class initialization policies, such as:

+ +
    +
  • +

    When a class is initialized, all its superclasses and superinterfaces with default methods must also be initialized. +Interfaces without default methods, however, are not initialized. +To accommodate this requirement, a short-term “relevant supertype” is used, as well as a “relevant subtype” for subtypes of classes and interfaces with default methods.

    +
  • +
  • Relevant supertypes of types initialized at build time must also be initialized at build time.
  • +
  • Relevant subtypes of types initialized at runtime must also be initialized at runtime.
  • +
  • No instances of classes that are initialized at runtime must be present in the executable.
  • +
+ +

To enjoy the complete out-of-the-box experience of Native Image and still get the benefits of build-time initialization, Native Image does two things:

+ + + +

To track which classes were initialized and why, pass the command-line option -H:+PrintClassInitialization to the native-image tool. +This option helps you configure the native image builder to work as required. +The goal is to have as many classes as possible initialized at build time, yet keep the correct semantics of the application.

+ +

Build-Time Initialization

+ +

Native Image initializes most JDK classes at build time, including the garbage collector, important JDK classes, and the deoptimizer. +For all of the classes that are initialized at build time, Native Image gives proper support so that the semantics remain consistent despite class initialization occurring at build time. +If you discover an issue with a JDK class behaving incorrectly because of class initialization at build time, please report an issue.

+ +

Automatic Initialization of Safe Classes

+ +

For application classes, Native Image tries to find classes that can be safely initialized at build time. +A class is considered safe if all of its relevant supertypes are safe and if the class initializer does not call any unsafe methods or initialize other unsafe classes.

+ +

A method is considered unsafe if:

+ +
    +
  • It transitively calls into native code (such as System.out.println): native code is not analyzed so Native Image cannot know if illegal actions are performed.
  • +
  • It calls a method that cannot be reduced to a single target (a virtual method). +This restriction avoids the explosion of search space for the safety analysis of static initializers.
  • +
  • It is substituted by Native Image. Running initializers of substituted methods would yield different results in the hosting Java virtual machine (VM) than in the produced executable. +As a result, the safety analysis would consider some methods safe but calling them would lead to illegal states.
  • +
+ +

A test that shows examples of classes that are proven safe can be found here. +The list of all classes that are proven safe is output to a file via the -H:+PrintClassInitialization command-line option to the native-image tool.

+ +
+

Note: You can also Specify Class Initialization Explicitly.

+
+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/optimizations-and-performance/MemoryManagement/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/optimizations-and-performance/MemoryManagement/index.html new file mode 100644 index 0000000..7a2e306 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/optimizations-and-performance/MemoryManagement/index.html @@ -0,0 +1,371 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Memory Management

+ +

A native image, when being executed, does not run on the Java HotSpot VM but on the runtime system provided with GraalVM. +That runtime includes all necessary components, and one of them is the memory management.

+ +

Java objects that a native image allocates at run time reside in the area called “the Java heap”. +The Java heap is created when the native image starts up, and may increase or decrease in size while the native image runs. +When the heap becomes full, a garbage collection is triggered to reclaim memory of objects that are no longer used.

+ +

For managing the Java heap, Native Image provides different garbage collector (GC) implementations:

+
    +
  • The Serial GC is the default GC in GraalVM. +It is optimized for low memory footprint and small Java heap sizes.
  • +
  • The G1 GC (not available in GraalVM Community Edition) is a multi-threaded GC that is optimized to reduce stop-the-world pauses and therefore improve latency, while achieving high throughput. +To enable G1, specify the option --gc=G1 at image build time. +Currently, G1 can only be used in native images that are built on Linux for AMD64.
  • +
  • The Epsilon GC (available with GraalVM 21.2 or later) is a no-op garbage collector that does not do any garbage collection and therefore never frees any allocated memory. +The primary use case for this GC are very short running applications that only allocate a small amount of memory. +To enable the Epsilon GC, specify the option --gc=epsilon at image build time.
  • +
+ +

Performance Considerations

+ +

The primary metrics for garbage collection are throughput, latency, and footprint:

+
    +
  • Throughput is the percentage of total time not spent in garbage collection considered over long periods of time.
  • +
  • Latency is the responsiveness of an application. +Garbage collection pauses negatively affect the responsiveness.
  • +
  • Footprint is the working set of a process, measured in pages and cache lines.
  • +
+ +

Choosing settings for the Java heap is always a trade-off between these metrics. +For example, a very large young generation may maximize throughput, but does so at the expense of footprint and latency. +Young generation pauses can be minimized by using a small young generation at the expense of throughput.

+ +

By default, Native Image automatically determines values for the Java heap settings that are listed below. +The exact values may depend on the system configuration and the used GC.

+ +
    +
  • The maximum Java heap size defines the upper limit for the size of the whole Java heap. +If the Java heap is full and the GC is unable reclaim sufficient memory for a Java object allocation, the allocation will fail with the OutOfMemoryError. +Note: The maximum heap size is only the upper limit for the Java heap and not necessarily the upper limit for the total amount of consumed memory, as Native Image places some data such as thread stacks, just-in-time compiled code, and internal data structures in memory that is separate from the Java heap.
  • +
  • The minimum Java heap size defines how much memory the GC may always assume as reserved for the Java heap, no matter how little of that memory is actually used.
  • +
  • The young generation size determines the amount of Java memory that can be allocated without triggering a garbage collection.
  • +
+ +

Serial Garbage Collector

+ +

The Serial GC is optimized for low footprint and small Java heap sizes. +If no other GC is specified, the Serial GC will be used implicitly as the default on GraalVM. +It is also possible to explicitly enable the Serial GC by passing the option --gc=serial to the native image builder.

+ +
# Build a native image that uses the serial GC with default settings
+native-image --gc=serial HelloWorld
+
+ +

Overview

+ +

In its core, the Serial GC is a simple (non-parallel, non-concurrent) stop and copy GC. +It divides the Java heap into a young and an old generation. +Each generation consists of a set of equally sized chunks, each a contiguous range of virtual memory. +Those chunks are the GC-internal unit for memory allocation and memory reclamation.

+ +

The young generation contains recently created objects and is divided into the eden and survivor regions. +New objects are allocated in the eden region, and when this region is full, a young collection is triggered. +Objects that are alive in the eden region will be moved to the survivor region, and alive objects in the survivor region stay in that region until they reach a certain age (have survived a certain number of collections), at which time they are moved to the old generation. +When the old generation becomes full, a full collection is triggered that reclaims the space of unused objects in both the young and old generations. +Typically, a young collection is much faster than a full collection, however doing full collections is important for keeping the memory footprint low. +By default, the Serial GC tries to find a size for the generations that provides good throughput, but to not increase sizes further when doing so gives diminishing returns. +It also tries to maintain a ratio between the time spent in young collections and in full collections to keep the footprint small.

+ +

If no maximum Java heap size is specified, a native image that uses the Serial GC will set its maximum Java heap size to 80% of the physical memory size. +For example, on a machine with 4GB of RAM, the maximum Java heap size will be set to 3.2GB. +If the same image is executed on a machine that has 32GB of RAM, the maximum Java heap size will be set to 25.6GB. +Note that this is just the maximum value. +Depending on the application, the amount of actually used Java heap memory can be much lower. +To override this default behavior, either specify a value for -XX:MaximumHeapSizePercent or explicitly set the maximum Java heap size.

+ +

Note that GraalVM releases up to (and including) 21.3 use a different default configuration for the Serial GC with no survivor regions, a young generation that is limited to 256 MB, and a default collection policy that balances the time that is spent in young collections and old collections. +This configuration can be enabled with: -H:InitialCollectionPolicy=BySpaceAndTime

+ +

Be mindful that the GC needs some extra memory when performing a garbage collection (2x of the maximum heap size is the worst case, usually, it is significantly less). +Therefore, the resident set size, RSS, can increase temporarily during a garbage collection which can be an issue in any environment with memory constraints (such as a container).

+ +

Performance Tuning

+ +

For tuning the GC performance and the memory footprint, the following options can be used:

+
    +
  • -XX:MaximumHeapSizePercent - the percentage of the physical memory size that is used as the maximum Java heap size if the maximum Java heap size is not specified otherwise.
  • +
  • -XX:MaximumYoungGenerationSizePercent - the maximum size of the young generation as a percentage of the maximum Java heap size.
  • +
  • -XX:±CollectYoungGenerationSeparately (since GraalVM 21.0) - determines if a full GC collects the young generation separately or together with the old generation. +If enabled, this may reduce the memory footprint during full GCs. +However, full GCs may take more time.
  • +
  • -XX:MaxHeapFree (since GraalVM 21.3) - maximum total size (in bytes) of free memory chunks that remain reserved for allocations after a collection and are therefore not returned to the operating system.
  • +
  • -H:AlignedHeapChunkSize (can only be specified at image build time) - the size of a heap chunk in bytes.
  • +
  • -H:MaxSurvivorSpaces (since GraalVM 21.1, can only be specified at image build time) - the number of survivor spaces that are used for the young generation, that is, the maximum age at which an object will be promoted to the old generation. +With a value of 0, objects that survive a young collection are directly promoted to the old generation.
  • +
  • -H:LargeArrayThreshold (can only be specified at image build time) - the size at or above which an array will be allocated in its own heap chunk. +Arrays that are considered as large are more expensive to allocate but they are never copied by the GC, which can reduce the GC overhead.
  • +
+ +
# Build and execute a native image that uses a maximum heap size of 25% of the physical memory
+native-image --gc=serial -R:MaximumHeapSizePercent=25 HelloWorld
+./helloworld
+
+# Execute the native image from above but increase the maximum heap size to 75% of the physical memory
+./helloworld -XX:MaximumHeapSizePercent=75
+
+ +

The following options are available with -H:InitialCollectionPolicy=BySpaceAndTime only:

+ +
    +
  • -XX:PercentTimeInIncrementalCollection - determines how much time the GC should spend doing young collections. +With the default value of 50, the GC tries to balance the time spent on young and full collections. +Increasing this value will reduce the number of full GCs, which can improve performance but may worsen the memory footprint. +Decreasing this value will increase the number of full GCs, which can improve the memory footprint but may decrease performance.
  • +
+ +

G1 Garbage Collector

+ +

Oracle GraalVM also provides the Garbage-First (G1) garbage collector, which is based on the G1 GC from the Java HotSpot VM. +Currently, G1 can only be used in native images that are built on Linux for AMD64. +To enable it, pass the option --gc=G1 to the native image builder.

+ +
# Build a native image that uses the G1 GC with default settings
+native-image --gc=G1 HelloWorld
+
+ +

Note: In GraalVM 20.0, 20.1, and 20.2, the G1 GC was called low-latency GC and could be enabled via the experimental option -H:+UseLowLatencyGC.

+ +

Overview

+ +

G1 is a generational, incremental, parallel, mostly concurrent, stop-the-world, and evacuating GC. +It aims to provide the best balance between latency and throughput.

+ +

Some operations are always performed in stop-the-world pauses to improve throughput. +Other operations that would take more time with the application stopped, such as whole-heap operations like global marking, are performed in parallel and concurrently with the application. +The G1 GC tries to meet set pause-time targets with high probability over a longer time. +However, there is no absolute certainty for a given pause.

+ +

G1 partitions the heap into a set of equally sized heap regions, each a contiguous range of virtual memory. +A region is the GC-internal unit for memory allocation and memory reclamation. +At any given time, each of these regions can be empty, or assigned to a particular generation.

+ +

If no maximum Java heap size is specified, a native image that uses the G1 GC will set its maximum Java heap size to 25% of the physical memory size. +For example, on a machine with 4GB of RAM, the maximum Java heap size will be set to 1GB. +If the same image is executed on a machine that has 32GB of RAM, the maximum Java heap size will be set to 8GB. +To override this default behavior, either specify a value for -XX:MaxRAMPercentage or explicitly set the maximum Java heap size.

+ +

Performance Tuning

+ +

The G1 GC is an adaptive garbage collector with defaults that enable it to work efficiently without modification. +However, it can be tuned to the performance needs of a particular application. +Here is a small subset of the options that can be specified when doing performance tuning:

+ +
    +
  • -H:G1HeapRegionSize (can only be specified at image build time) - the size of a G1 region.
  • +
  • -XX:MaxRAMPercentage - the percentage of the physical memory size that is used as the maximum heap size if the maximum heap size is not specified otherwise.
  • +
  • -XX:MaxGCPauseMillis - the goal for the maximum pause time.
  • +
  • -XX:ParallelGCThreads - the maximum number of threads used for parallel work during garbage collection pauses.
  • +
  • -XX:ConcGCThreads - the maximum number of threads used for concurrent work.
  • +
  • -XX:InitiatingHeapOccupancyPercent - the Java heap occupancy threshold that triggers a marking cycle.
  • +
  • -XX:G1HeapWastePercent - the allowed unreclaimed space in the collection set candidates. G1 stops the space-reclamation phase if the free space in the collection set candidates is lower than that.
  • +
+ +
# Build and execute a native image that uses the G1 GC with a region size of 2MB and a maximum pause time goal of 100ms
+native-image --gc=G1 -H:G1RegionSize=2m -R:MaxGCPauseMillis=100 HelloWorld
+./helloworld
+
+# Execute the native image from above and override the maximum pause time goal
+./helloworld -XX:MaxGCPauseMillis=50
+
+ +

Memory Management Options

+ +

This section describes the most important memory management command-line options that are independent of the used GC. +For all numeric values the suffix k, m, or g can be used for scaling. +Further options to the native image builder can be listed using native-image --expert-options-all.

+ +

Java Heap Size

+ +

When executing a native image, suitable Java heap settings will be determined automatically based on the system configuration and the used GC. +To override this automatic mechanism and to explicitly set the heap size at run time, the following command-line options can be used:

+
    +
  • -Xmx - maximum heap size in bytes
  • +
  • -Xms - minimum heap size in bytes
  • +
  • -Xmn - the size of the young generation in bytes
  • +
+ +

It is also possible to preconfigure default heap settings at image build time. +The specified values will then be used as the default values at run time:

+
    +
  • -R:MaxHeapSize (since GraalVM 20.0) - maximum heap size in bytes
  • +
  • -R:MinHeapSize (since GraalVM 20.0) - minimum heap size in bytes
  • +
  • -R:MaxNewSize (since GraalVM 20.0) - size of the young generation in bytes
  • +
+ +
# Build a native image with the default heap settings and override the heap settings at run time
+native-image HelloWorld
+./helloworld -Xms2m -Xmx10m -Xmn1m
+
+# Build a native image and "bake" heap settings into the image. The specified values will be used at run time
+native-image -R:MinHeapSize=2m -R:MaxHeapSize=10m -R:MaxNewSize=1m HelloWorld
+./helloworld
+
+ +

Compressed References

+ +

Oracle GraalVM supports compressed references to Java objects that use 32-bit instead of 64-bit. +Compressed references are enabled by default and can have a large impact on the memory footprint. +However, they limit the maximum Java heap size to 32 GB of memory. +If more than 32 GB are needed, compressed references need to be disabled.

+ +
    +
  • -H:±UseCompressedReferences (can only be specified at image build time) - determines if 32-bit instead of 64-bit references to Java objects are used.
  • +
+ +

Native Memory

+ +

Native Image may also allocate memory that is separate from the Java heap. +One common use-case is a java.nio.DirectByteBuffer that directly references native memory.

+ +
    +
  • -XX:MaxDirectMemorySize - the maximum size of direct buffer allocations.
  • +
+ +

Printing Garbage Collections

+ +

When executing a native image, the following options can be be used to print some information on garbage collection. +Which data is printed in detail depends on the used GC.

+
    +
  • -XX:+PrintGC - print basic information for every garbage collection
  • +
  • -XX:+VerboseGC - can be added to print further garbage collection details
  • +
+ +
# Execute a native image and print basic garbage collection information
+./helloworld -XX:+PrintGC
+
+# Execute a native image and print detailed garbage collection information
+./helloworld -XX:+PrintGC -XX:+VerboseGC
+
+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/optimizations-and-performance/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/optimizations-and-performance/index.html new file mode 100644 index 0000000..4ddd68d --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/optimizations-and-performance/index.html @@ -0,0 +1,133 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Optimizations and Performance

+ +

Native Image provides advanced mechanisms to further optimize the generated binary:

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/Build-Overview/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/Build-Overview/index.html new file mode 100644 index 0000000..489eda6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/Build-Overview/index.html @@ -0,0 +1,154 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Build Overview

+ +

The syntax of the native-image command is:

+ +
    +
  • native-image [options] <mainclass> [imagename] [options] to build a native binary from <mainclass> class in the current working directory. The classpath may optionally be provided with the -cp <classpath> option where <classpath> is a colon-separated (on Windows, semicolon-separated) list of paths to directories and jars.
  • +
  • native-image [options] -jar jarfile [imagename] [options] to build native binary from a JAR file.
  • +
  • native-image [options] -m <module>/<mainClass> [imagename] [options] to build a native binary from a Java module.
  • +
+ +

The options passed to native-image are evaluated from left to right.

+ +

The options fall into three categories:

+ + +

Find a complete list of options for the native-image tool here.

+ +

There are some expert level options that a Native Image developer may find useful or needed, for example, the option to dump graphs of the native-image builder or enable assertions at image run time. This information can be found in Native Image Hosted and Runtime Options.

+ +

Further Reading

+ +

If you are new to GraalVM Native Image or have little experience using it, see the Native Image Basics to better understand some key aspects before going further.

+ +

For more tweaks and how to properly configure the native-image tool, see Build Configuration.

+ +

Native Image will output the progress and various statistics when building the native binary. To learn more about the output, and the different build phases, see Build Output.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/BuildConfiguration/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/BuildConfiguration/index.html new file mode 100644 index 0000000..a5fab1e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/BuildConfiguration/index.html @@ -0,0 +1,293 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Build Configuration

+ +

Native Image supports a wide range of options to configure the native-image builder.

+ +

Table of Contents

+ + + +

Embed a Configuration File

+ +

We recommend that you provide the configuration for the native-image builder by embedding a native-image.properties file into a project JAR file. +The native-image builder will also automatically pick up all configuration options provided in the META-INF/native-image/ directory (or any of its subdirectories) and use it to construct native-image command-line options.

+ +

To avoid a situation when constituent parts of a project are built with overlapping configurations, we recommended you use subdirectories within META-INF/native-image: a JAR file built from multiple maven projects cannot suffer from overlapping native-image configurations. +For example:

+
    +
  • foo.jar has its configurations in META-INF/native-image/foo_groupID/foo_artifactID
  • +
  • bar.jar has its configurations in META-INF/native-image/bar_groupID/bar_artifactID
  • +
+ +

The JAR file that contains foo and bar will then contain both configurations without conflict. +Therefore the recommended layout to store configuration data in JAR files is as follows:

+
META-INF/
+└── native-image
+    └── groupID
+        └── artifactID
+            └── native-image.properties
+
+ +

Note that the use of ${.} in a native-image.properties file expands to the resource location that contains that exact configuration file. +This can be useful if the native-image.properties file refers to resources within its subdirectory, for example, -H:ResourceConfigurationResources=${.}/custom_resources.json. +Always make sure you use the option variants that take resources, that is, use -H:ResourceConfigurationResources instead of -H:ResourceConfigurationFiles. +Other options that work in this context are:

+
    +
  • -H:DynamicProxyConfigurationResources
  • +
  • -H:JNIConfigurationResources
  • +
  • -H:ReflectionConfigurationResources
  • +
  • -H:ResourceConfigurationResources
  • +
  • -H:SerializationConfigurationResources
  • +
+ +

By having such a composable native-image.properties file, building a native executable does not require any additional option on the command line. +It is sufficient to run the following command:

+
$JAVA_HOME/bin/native-image -jar target/<name>.jar
+
+ +

To identify which configuration is applied when building a native executable, use native-image --verbose. +This shows from where native-image picks up the configurations to construct the final composite configuration command-line options for the native image builder.

+
native-image --verbose -jar build/basic-app-0.1-all.jar
+Apply jar:file://~/build/basic-app-0.1-all.jar!/META-INF/native-image/io.netty/common/native-image.properties
+Apply jar:file://~/build/basic-app-0.1-all.jar!/META-INF/native-image/io.netty/buffer/native-image.properties
+Apply jar:file://~/build/basic-app-0.1-all.jar!/META-INF/native-image/io.netty/transport/native-image.properties
+Apply jar:file://~/build/basic-app-0.1-all.jar!/META-INF/native-image/io.netty/handler/native-image.properties
+Apply jar:file://~/build/basic-app-0.1-all.jar!/META-INF/native-image/io.netty/codec-http/native-image.properties
+...
+Executing [
+    <composite configuration command line options for the image builder>
+]
+
+ +

Typical examples of configurations that use a configuration from META-INF/native-image can be found in Native Image configuration examples.

+ +

Configuration File Format

+ +

A native-image.properties file is a Java properties file that specifies configurations for native-image. +The following properties are supported.

+ +

Args

+ +

Use this property if your project requires custom native-image command-line options to build correctly. +For example, the native-image-configure-examples/configure-at-runtime-example contains Args = --initialize-at-build-time=com.fasterxml.jackson.annotation.JsonProperty$Access in its native-image.properties file to ensure the class com.fasterxml.jackson.annotation.JsonProperty$Access is initialized at executable build time.

+ +

JavaArgs

+ +

Sometimes it can be necessary to provide custom options to the JVM that runs the native-image builder. +Use the JavaArgs property in this case.

+ +

ImageName

+ +

This property specifies a user-defined name for the executable. +If ImageName is not used, a name is automatically chosen: + * native-image -jar <name.jar> has a default executable name <name> + * native-image -cp ... fully.qualified.MainClass has a default executable name fully.qualified.mainclass

+ +

Note that using ImageName does not prevent you from overriding the name via the command line. +For example, if foo.bar contains ImageName=foo_app: + * native-image -jar foo.bar generates the executable foo_app but + * native-image -jar foo.bar application generates the executable application

+ +

Changing the Default Configuration Directory

+ +

Native Image by default stores configuration information in the user’s home directory: $HOME/.native-image/. +To change this default, set the environment variable NATIVE_IMAGE_USER_HOME to a different location. For example:

+
export NATIVE_IMAGE_USER_HOME= $HOME/.local/share/native-image
+
+ +

Order of Arguments Evaluation

+

The options passed to native-image are evaluated from left to right. +This also extends to options that are passed indirectly via configuration files in the META-INF/native-image directory. +Consider the example where there is a JAR file that includes native-image.properties containing Args = -H:Optimize=0. +You can override the setting that is contained in the JAR file by using the -H:Optimize=2 option after -cp <jar-file>.

+ +

Memory Configuration for Native Image Build

+ +

The native-image builder runs on a JVM and uses the memory management of the underlying platform. +The usual Java command-line options for garbage collection apply to the native-image builder.

+ +

During the creation of a native executable, the representation of the whole application is created to determine which classes and methods will be used at runtime. +It is a computationally intensive process that uses the following default values for memory usage:

+
-Xss10M \
+-XX:MaxRAMPercentage=<percentage based on available memory> \
+-XX:GCTimeRatio=19 \
+-XX:+ExitOnOutOfMemoryError \
+
+

These defaults can be changed by passing -J + <jvm option for memory> to the native-image tool.

+ +

The -XX:MaxRAMPercentage value determines the maximum heap size of the builder and is computed based on available memory of the system. +It maxes out at 32GB by default and can be overwritten with, for example, -J-XX:MaxRAMPercentage=90.0 for 90% of physical memory or -Xmx4g for 4GB. +-XX:GCTimeRatio=19 increases the goal of the total time for garbage collection to 5%, which is more throughput-oriented and reduces peak RSS. +The build process also exits on the first OutOfMemoryError (-XX:+ExitOnOutOfMemoryError) to provide faster feedback in environments under a lot of memory pressure.

+ +

By default, the native-image tool uses up to 32 threads (but not more than the number of processors available). For custom values, use the option -H:NumberOfThreads=....

+ +

For other related options available to the native-image tool, see the output from the command native-image --expert-options-all.

+ +

Specify Types Required to Be Defined at Build Time

+ +

A well-structured library or application should handle linking of Java types (ensuring all reachable Java types are fully defined at build time) when building a native binary by itself. +The default behavior is to throw linking errors, if they occur, at runtime. +However, you can prevent unwanted linking errors by specifying which classes are required to be fully linked at build time. +For that, use the --link-at-build-time option. +If the option is used in the right context (see below), you can specify required classes to link at build time without explicitly listing classes and packages. +It is designed in a way that libraries can only configure their own classes, to avoid any side effects on other libraries. +You can pass the option to the native-image tool on the command line, embed it in a native-image.properties file on the module-path or the classpath.

+ +

Depending on how and where the option is used it behaves differently:

+ +
    +
  • If you use --link-at-build-time without arguments, all classes in the scope are required to be fully defined. If used without arguments on command line, all classes will be treated as “link-at-build-time” classes. If used without arguments embedded in a native-image.properties file on the module-path, all classes of the module will be treated as “link-at-build-time” classes. If you use --link-at-build-time embedded in a native-image.properties file on the classpath, the following error will be thrown: +
      Error: Using '--link-at-build-time' without args only allowed on module-path. 'META-INF/native-image/org.mylibrary/native-image.properties' in 'file:///home/test/myapp/MyLibrary.jar' not part of module-path.
    +
    +
  • +
  • If you use the --link-at-build-time option with arguments, for example, --link-at-build-time=foo.bar.Foobar,demo.myLibrary.Name,..., the arguments should be fully qualified class names or package names. When used on the module-path or classpath (embedded in native-image.properties files), only classes and packages defined in the same JAR file can be specified. Packages for libraries used on the classpath need to be listed explicitly. To make this process easy, use the @<prop-values-file> syntax to generate a package list (or a class list) in a separate file automatically.
  • +
+ +

Another handy option is --link-at-build-time-paths which allows to specify which classes are required to be fully defined at build time by other means. +This variant requires arguments that are of the same type as the arguments passed via -p (--module-path) or -cp (--class-path):

+ +
--link-at-build-time-paths <class search path of directories and zip/jar files>
+
+ +

The given entries are searched and all classes inside are registered as --link-at-build-time classes. +This option is only allowed to be used on command line.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/BuildOptions/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/BuildOptions/index.html new file mode 100644 index 0000000..2940eae --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/BuildOptions/index.html @@ -0,0 +1,228 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Build Options

+ +

Depending on the GraalVM version, the options to the native-image builder may differ.

+ +
    +
  • -cp, -classpath, --class-path <class search path of directories and zip/jar files>: a : (; on Windows) separated list of directories, JAR archives, and ZIP archives to search for class files
  • +
  • -p <module path>, --module-path <module path>: a : (; on Windows) separated list of directories. Each directory is a directory of modules.
  • +
  • --add-modules <module name>[,<module name>...]: add root modules to resolve in addition to the initial module. <module name> can also be ALL-DEFAULT, ALL-SYSTEM, ALL-MODULE-PATH.
  • +
  • -D<name>=<value>: set a system property
  • +
  • -J<flag>: pass an option directly to the JVM running the native-image builder
  • +
  • --diagnostics-mode: enable diagnostics output which includes class initialization, substitutions, etc.
  • +
  • --enable-preview: allow classes to depend on preview features of this release
  • +
  • --verbose: enable verbose output
  • +
  • --version: print the product version and exit
  • +
  • --help: print this help message
  • +
  • --help-extra: print help on non-standard options
  • +
  • --auto-fallback: build standalone native executable if possible
  • +
  • --configure-reflection-metadata: enable runtime instantiation of reflection objects for non-invoked methods
  • +
  • --enable-all-security-services: add all security service classes to a generated native executable
  • +
  • --enable-http: enable HTTP support in a native executable
  • +
  • --enable-https: enable HTTPS support in a native executable
  • +
  • --enable-monitoring: enable monitoring features that allow the VM to be inspected at run time. A comma-separated list can contain heapdump, jfr, jvmstat, jmxserver (experimental), jmxclient (experimental), or all (deprecated behavior: defaults to all if no argument is provided). For example: --enable-monitoring=heapdump,jfr.
  • +
  • --enable-sbom: embed a Software Bill of Materials (SBOM) in a native executable or shared library for passive inspection. A comma-separated list can contain cyclonedx, strict (defaults to cyclonedx if no argument is provided), or export to save the SBOM to the native executable’s output directory. The optional strict flag aborts the build if any class cannot be matched to a library in the SBOM. For example: --enable-sbom=cyclonedx,strict. (Not available in GraalVM Community Edition.)
  • +
  • --enable-url-protocols: list comma-separated URL protocols to enable
  • +
  • --features: a comma-separated list of fully qualified Feature implementation classes
  • +
  • --force-fallback: force building of a fallback native executable
  • +
  • --gc=<value>: select Native Image garbage collector implementation. Allowed options for <value> are: G1 for G1 garbage collector (not available in GraalVM Community Edition); epsilon for Epsilon garbage collector; serial for Serial garbage collector (default).
  • +
  • --initialize-at-build-time: a comma-separated list of packages and classes (and implicitly all of their superclasses) that are initialized during generation of a native executable. An empty string designates all packages.
  • +
  • --initialize-at-run-time: a comma-separated list of packages and classes (and implicitly all of their subclasses) that must be initialized at run time and not during generation. An empty string is currently not supported.
  • +
  • --install-exit-handlers: provide java.lang.Terminator exit handlers
  • +
  • --libc: select the libc implementation to use. Available implementations are glibc, musl, bionic.
  • +
  • --link-at-build-time: require types to be fully defined at executable’s build time. If used without arguments, all classes in scope of the option are required to be fully defined.
  • +
  • --link-at-build-time-paths: require all types in given class or module-path entries to be fully defined at at executable’s build time
  • +
  • --list-cpu-features: show CPU features specific to the target platform and exit
  • +
  • --list-modules: list observable modules and exit
  • +
  • --native-compiler-options: provide a custom C compiler option used for query code compilation
  • +
  • --native-compiler-path: provide a custom path to the C compiler used to query code compilation and linking
  • +
  • --native-image-info: show the native toolchain information and executable’s build settings
  • +
  • --no-fallback: build a standalone native executable or report a failure
  • +
  • --pgo: a comma-separated list of files from which to read the data collected for profile-guided optimizations of AOT-compiled code (reads from default.iprof if nothing is specified). (Not available in GraalVM Community Edition.)
  • +
  • --pgo-instrument: instrument AOT-compiled code to collect data for profile-guided optimizations into the default.iprof file. (Not available in GraalVM Community Edition.)
  • +
  • --report-unsupported-elements-at-runtime: report the usage of unsupported methods and fields at run time when they are accessed the first time, instead of an error during executable’s building
  • +
  • --shared: build a shared library
  • +
  • --silent: silence build output
  • +
  • --static: build a statically-linked executable (requires libc and zlib static libraries)
  • +
  • --target: select the compilation target for native-image (in - format). It defaults to host's OS-architecture pair.
  • +
  • --trace-class-initialization: provide a comma-separated list of fully-qualified class names that a class initialization is traced for
  • +
  • --trace-object-instantiation: provide a comma-separated list of fully-qualified class names that an object instantiation is traced for
  • +
  • -O<level>: control code optimizations where available variants are b for quick build mode for development, 0 - no optimizations, 1 - basic optimizations, 2 - aggressive optimizations (default)
  • +
  • -da, -da[:[packagename]|:[classname], disableassertions[:[packagename]|:[classname]: disable assertions with specified granularity at run time
  • +
  • -dsa, -disablesystemassertions: disable assertions in all system classes at run time
  • +
  • -ea, -ea[:[packagename]|:[classname], enableassertions[:[packagename]|:[classname]: enable assertions with specified granularity at run time
  • +
  • -esa, -enablesystemassertions: enable assertions in all system classes at run time
  • +
  • -g: generate debugging information
  • +
  • -march: generate instructions for a specific machine type. Defaults to x86-64-v3 on AMD64 and armv8-a on AArch64. Use -march=compatibility for best compatibility, or -march=native for best performance if a native executable is deployed on the same machine or on a machine with the same CPU features. To list all available machine types, use -march=list.
  • +
  • -o: name of the output file to be generated
  • +
+ +

Macro Options

+ +
    +
  • --language:nfi: make the Truffle Native Function Interface language available
  • +
  • --tool:coverage: add source code coverage support to a GraalVM-supported language
  • +
  • --tool:insight: add support for detailed access to program’s runtime behavior, allowing users to inspect values and types at invocation or allocation sites
  • +
  • --tool:dap: allow image to open a debugger port serving the Debug Adapter Protocol in IDEs like Visual Studio Code
  • +
  • --tool:chromeinspector: add debugging support to a GraalVM-supported language
  • +
  • --tool:insightheap: snapshot a region of image heap during the execution
  • +
  • --tool:lsp: add the Language Server Protocol support to later attach compatible debuggers to GraalVM in IDEs like Visual Studio Code
  • +
  • --tool:sandbox: enables the Truffle sandbox resource limits. For more information, check the dedicated documentation
  • +
  • --tool:profiler: add profiling support to a GraalVM-supported language
  • +
+ +

The --language:js --language:nodejs, --language:python, --language:ruby, --language:R, --language:wasm, --language:llvm, --language:regex (enables the Truffle Regular Expression engine) polyglot macro options become available once the corresponding languages are added to the base GraalVM JDK.

+ +

Non-standard Options

+ +

Run native-image --help-extra for non-standard options help.

+ +
    +
  • --exclude-config: exclude configuration for a comma-separated pair of classpath/modulepath pattern and resource pattern. For example: ‘–exclude-config foo.jar,META-INF\/native-image\/.*.properties’ ignores all .properties files in ‘META-INF/native-image’ in all JARs named ‘foo.jar’.
  • +
  • --expert-options: list image build options for experts
  • +
  • --expert-options-all : list all image build options for experts (use at your own risk). Options marked with Extra help available contain help that can be shown with --expert-options-detail
  • +
  • --expert-options-detail: display all available help for a comma-separated list of option names. Pass * to show extra help for all options that contain it.
  • +
  • --configurations-path <search path of option-configuration directories>: a separated list of directories to be treated as option-configuration directories.
  • +
  • --debug-attach[=< port (* can be used as host meaning bind to all interfaces)>]: attach to debugger during image building (default port is 8000)
  • +
  • --diagnostics-mode: Enables logging of image-build information to a diagnostics folder.
  • +
  • --dry-run: output the command line that would be used for building
  • +
  • --bundle-create[=new-bundle.nib]: in addition to image building, create a native image bundle file (*.nibfile) that allows rebuilding of that image again at a later point. If a bundle-file gets passed the bundle will be created with the given name. Otherwise, the bundle-file name is derived from the image name. Note both bundle options can be combined with --dry-run to only perform the bundle operations without any actual image building.
  • +
  • --bundle-apply=some-bundle.nib: an image will be built from the given bundle file with the exact same arguments and files that have been passed to Native Image originally to create the bundle. Note that if an extra --bundle-create gets passed after --bundle-apply, a new bundle will be written based on the given bundle args plus any additional arguments that haven been passed afterwards. For example: native-image --bundle-apply=app.nib --bundle-create=app_dbg.nib -g creates a new bundle based on the given _app.nib_ bundle. Both bundles are the same except the new one also uses the -g option.
  • +
  • -E<env-var-key>[=<env-var-value>]: allow Native Image to access the given environment variable during image build. If the optional is not given, the value of the environment variable will be taken from the environment Native Image was invoked from.
  • +
  • -V<key>=<value>: provide values for placeholders in native-image.properties files
  • +
  • --add-exports: value <module>/<package>=<target-module>(,<target-module>) updates <module> to export <package> to <target-module>, regardless of module declaration. <target-module> can be ALL-UNNAMED to export to all unnamed modules
  • +
  • --add-opens: value <module>/<package>=<target-module>(,<target-module>) updates <module> to open <package> to <target-module>, regardless of module declaration.
  • +
  • --add-reads: value <module>=<target-module>(,<target-module>) updates <module> to read <target-module>, regardless of module declaration. <target-module> can be ALL-UNNAMED to read all unnamed modules.
  • +
+ +

Native Image options are also distinguished as hosted and runtime options.

+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/BuildOutput/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/BuildOutput/index.html new file mode 100644 index 0000000..059e7eb --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/BuildOutput/index.html @@ -0,0 +1,428 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Build Output

+ + + +

Here you will find information about the build output of GraalVM Native Image. +Below is the example output when building a native executable of the HelloWorld class:

+ +
================================================================================
+GraalVM Native Image: Generating 'helloworld' (executable)...
+================================================================================
+[1/8] Initializing...                                            (2.8s @ 0.15GB)
+ Java version: 20+34, vendor version: GraalVM CE 20-dev+34.1
+ Graal compiler: optimization level: 2, target machine: x86-64-v3
+ C compiler: gcc (linux, x86_64, 12.2.0)
+ Garbage collector: Serial GC (max heap size: 80% of RAM)
+[2/8] Performing analysis...  [****]                             (4.5s @ 0.54GB)
+   3,175 (72.62%) of  4,372 types reachable
+   3,842 (50.58%) of  7,596 fields reachable
+  15,260 (45.61%) of 33,458 methods reachable
+     962 types,    83 fields, and   492 methods registered for reflection
+      57 types,    55 fields, and    52 methods registered for JNI access
+       4 native libraries: dl, pthread, rt, z
+[3/8] Building universe...                                       (0.8s @ 0.99GB)
+[4/8] Parsing methods...      [*]                                (0.6s @ 0.75GB)
+[5/8] Inlining methods...     [***]                              (0.3s @ 0.32GB)
+[6/8] Compiling methods...    [**]                               (3.7s @ 0.60GB)
+[7/8] Layouting methods...    [*]                                (0.8s @ 0.83GB)
+[8/8] Creating image...       [**]                               (3.1s @ 0.58GB)
+   5.32MB (24.22%) for code area:     8,702 compilation units
+   7.03MB (32.02%) for image heap:   93,301 objects and 5 resources
+   8.96MB (40.83%) for debug info generated in 1.0s
+ 659.13kB ( 2.93%) for other data
+  21.96MB in total
+--------------------------------------------------------------------------------
+Top 10 origins of code area:            Top 10 object types in image heap:
+   4.03MB java.base                        1.14MB byte[] for code metadata
+ 927.05kB svm.jar (Native Image)         927.31kB java.lang.String
+ 111.71kB java.logging                   839.68kB byte[] for general heap data
+  63.38kB org.graalvm.nativeimage.base   736.91kB java.lang.Class
+  47.59kB jdk.proxy1                     713.13kB byte[] for java.lang.String
+  35.85kB jdk.proxy3                     272.85kB c.o.s.c.h.DynamicHubCompanion
+  27.06kB jdk.internal.vm.ci             250.83kB java.util.HashMap$Node
+  23.44kB org.graalvm.sdk                196.52kB java.lang.Object[]
+  11.42kB jdk.proxy2                     182.77kB java.lang.String[]
+   8.07kB jdk.internal.vm.compiler       154.26kB byte[] for embedded resources
+   1.39kB for 2 more packages              1.38MB for 884 more object types
+--------------------------------------------------------------------------------
+Recommendations:
+ HEAP: Set max heap for improved and more predictable memory usage.
+ CPU:  Enable more CPU features with '-march=native' for improved performance.
+--------------------------------------------------------------------------------
+    0.8s (4.6% of total time) in 35 GCs | Peak RSS: 1.93GB | CPU load: 9.61
+--------------------------------------------------------------------------------
+Produced artifacts:
+ /home/janedoe/helloworld/helloworld (executable)
+ /home/janedoe/helloworld/helloworld.debug (debug_info)
+ /home/janedoe/helloworld/sources (debug_info)
+================================================================================
+Finished generating 'helloworld' in 17.0s.
+
+ +

Build Stages

+ +

Initializing

+

In this stage, the Native Image build process is set up and Features are initialized.

+ +

Native Image Kind

+

By default, Native Image generates executables but it can also generate native shared libraries and static executables.

+ +

Java Version Info

+

The Java and vendor version of the Native Image process. +Both are also used for the java.vm.version and java.vendor.version properties within the generated native binary. +Please report version and vendor when you file issues.

+ +

Graal Compiler

+

The selected optimization level and targeted machine type used by the Graal compiler. +The optimization level can be controlled with the -O option and defaults to 2, which enables aggressive optimizations. +Use -Ob to enable quick build mode, which speeds up the compilation stage. +This is useful during development, or when peak throughput is less important and you would like to optimize for size. +The targeted machine type can be selected with the -march option and defaults to x86-64-v3 on AMD64 and armv8-a on AArch64. +See here for recommendations on how to use this option.

+ +

On Oracle GraalVM, the line also shows information about Profile-Guided Optimizations (PGO).

+
    +
  • off: PGO is not used
  • +
  • instrument: The generated executable or shared library is instrumented to collect data for PGO (--pgo-instrument)
  • +
  • user-provided: PGO is enabled and uses a user-provided profile (for example --pgo default.iprof)
  • +
  • ML-inferred: A machine learning (ML) model is used to infer profiles for control split branches statically.
  • +
+ +

C Compiler

+

The C compiler executable, vendor, target architecture, and version info used by the Native Image build process.

+ +

Garbage Collector

+

The garbage collector used within the generated executable:

+
    +
  • The Serial GC is the default GC and optimized for low memory footprint and small Java heap sizes.
  • +
  • The G1 GC (not available in GraalVM Community Edition) is a multi-threaded GC that is optimized to reduce stop-the-world pauses and therefore improve latency while achieving high throughput.
  • +
  • The Epsilon GC does not perform any garbage collection and is designed for very short-running applications that only allocate a small amount of memory.
  • +
+ +

For more information see the docs on Memory Management.

+ +

Maximum Heap Size

+

By default, the heap size is limited to a certain percentage of your system memory, allowing the garbage collector to freely allocate memory according to its policy. +Use the -Xmx option when invoking your native executable (for example ./myapp -Xmx64m for 64MB) to limit the maximum heap size for a lower and more predictable memory footprint. +This can also improve latency in some cases. +Use the -R:MaxHeapSize option when building with Native Image to pre-configure the maximum heap size.

+ +

User-Specific Features

+

All Features that are either provided or specifically enabled by the user, or implicitly registered for the user, for example, by a framework. +GraalVM Native Image deploys a number of internal features, which are excluded from this list.

+ +

Performing Analysis

+

In this stage, a points-to analysis is performed. +The progress indicator visualizes the number of analysis iterations. +A large number of iterations can indicate problems in the analysis likely caused by misconfiguration or a misbehaving feature.

+ +

Reachable Types, Fields, and Methods

+

The number of types (primitives, classes, interfaces, and arrays), fields, and methods that are reachable versus the total number of types, fields, and methods loaded as part of the build process. +A significantly larger number of loaded elements that are not reachable can indicate a configuration problem. +To reduce overhead, please ensure that your class path and module path only contain entries that are needed for building the application.

+ +

Reflection Registrations

+

The number of types, fields, and methods that are registered for reflection. +Large numbers can cause significant reflection overheads, slow down the build process, and increase the size of the native binary (see reflection metadata).

+ +

JNI Access Registrations

+

The number of types, fields, and methods that are registered for JNI access.

+ +

Runtime Compiled Methods

+

The number of methods marked for runtime compilation. +This number is only shown if runtime compilation is built into the executable, for example, when building a Truffle language. +Runtime-compiled methods account for graph encodings in the heap.

+ +

Building Universe

+

In this stage, a universe with all types, fields, and methods is built, which is then used to create the native binary.

+ +

Parsing Methods

+

In this stage, the Graal compiler parses all reachable methods. +The progress indicator is printed periodically at an increasing interval.

+ +

Inlining Methods

+

In this stage, trivial method inlining is performed. +The progress indicator visualizes the number of inlining iterations.

+ +

Compiling Methods

+

In this stage, the Graal compiler compiles all reachable methods to machine code. +The progress indicator is printed periodically at an increasing interval.

+ +

Layouting Methods

+

In this stage, compiled methods are layouted. +The progress indicator is printed periodically at an increasing interval.

+ +

Creating Image

+

In this stage, the native binary is created and written to disk. +Debug info is also generated as part of this stage (if requested).

+ +

Code Area

+

The code area contains machine code produced by the Graal compiler for all reachable methods. +Therefore, reducing the number of reachable methods also reduces the size of the code area.

+ +
Origins of Code Area
+

To help users understand where the machine code of the code area comes from, the build output shows a breakdown of the top origins. +An origin is a group of Java sources and can be a JAR file, a package name, or a class name, depending on the information available. +The java.base module, for example, contains base classes from the JDK. +The svm.jar file, the org.graalvm.nativeimage.base module, and similar origins contain internal sources for the Native Image runtime. +To reduce the size of the code area and with that, the total size of the native executable, re-evaluate the dependencies of your application based on the code area breakdown. +Some libraries and frameworks are better prepared for Native Image than others, and newer versions of a library or framework may improve (or worsen) their code footprint.

+ +

Image Heap

+

The heap contains reachable objects such as static application data, metadata, and byte[] for different purposes (see below).

+ +
General Heap Data Stored in byte[]
+

The total size of all byte[] objects that are neither used for java.lang.String, nor code metadata, nor reflection metadata, nor graph encodings. +Therefore, this can also include byte[] objects from application code.

+ +
Embedded Resources Stored in byte[]
+

The total size of all byte[] objects used for storing resources (for example, files accessed via Class.getResource()) within the native binary. The number of resources is shown in the Heap section.

+ +
Code Metadata Stored in byte[]
+

The total size of all byte[] objects used for metadata for the code area. +Therefore, reducing the number of reachable methods also reduces the size of this metadata.

+ +
Reflection Metadata Stored in byte[]
+

The total size of all byte[] objects used for reflection metadata, including types, field, method, and constructor data. +To reduce the amount of reflection metadata, reduce the number of elements registered for reflection.

+ +
Graph Encodings Stored in byte[]
+

The total size of all byte[] objects used for graph encodings. +These encodings are a result of runtime compiled methods. +Therefore, reducing the number of such methods also reduces the size of corresponding graph encodings.

+ +

Debug Info

+

The total size of generated debug information (if enabled).

+ +

Other Data

+

The amount of data in the binary that is neither in the code area, nor in the heap, nor debug info. +This data typically contains internal information for Native Image and should not be dominating.

+ +

Recommendations

+ +

The build output may contain one or more of the following recommendations that help you get the best out of Native Image.

+ +

AWT: Missing Reachability Metadata for Abstract Window Toolkit

+ +

The Native Image analysis has included classes from the java.awt package but could not find any reachability metadata for it. +Use the tracing agent to collect such metadata for your application. +Otherwise, your application is unlikely to work properly. +If your application is not a desktop application (for example using Swing or AWT directly), you may want to re-evaluate whether the dependency on AWT is actually needed.

+ +

CPU: Enable More CPU Features for Improved Performance

+ +

The Native Image build process has determined that your CPU supports more features, such as AES or LSE, than currently enabled. +If you deploy your application on the same machine or a similar machine with support for the same CPU features, consider using -march=native at build time. +This option allows the Graal compiler to use all CPU features available, which in turn can significantly improve the performance of your application. +Use -march=list to list all available machine types that can be targeted explicitly.

+ +

G1GC: Use G1 Garbage Collector for Improved Latency and Throughput

+ +

The G1 garbage collector is available for your platform. +Consider enabling it using --gc=G1 at build time to improve the latency and throughput of your application. +For more information see the docs on Memory Management. +For best peak performance, also consider using Profile-Guided Optimizations.

+ +

HEAP: Specify a Maximum Heap Size

+ +

Please refer to Maximum Heap Size.

+ +

PGO: Use Profile-Guided Optimizations for Improved Throughput

+ +

Consider using Profile-Guided Optimizations to optimize your application for improved throughput. +These optimizations allow the Graal compiler to leverage profiling information, similar to when it is running as a JIT compiler, when AOT-compiling your application. +For this, perform the following steps:

+ +
    +
  1. Build your application with --pgo-instrument.
  2. +
  3. Run your instrumented application with a representative workload to generate profiling information in the form of an .iprof file.
  4. +
  5. Re-build your application and pass in the profiling information with --pgo=<your>.iprof to generate an optimized version of your application.
  6. +
+ +

Relevant guide: Optimize a Native Executable with Profile-Guided Optimizations.

+ +

For best peak performance, also consider using the G1 garbage collector.

+ +

QBM: Use Quick Build Mode for Faster Builds

+ +

Consider using the quick build mode (-Ob) to speed up your builds during development. +More precisely, this mode reduces the number of optimizations performed by the Graal compiler and thus reduces the overall time of the compilation stage. +The quick build mode is not only useful for development, it can also cause the generated executable file to be smaller in size. +Note, however, that the overall peak throughput of the executable may be lower due to the reduced number of optimizations.

+ +

Resource Usage Statistics

+ +

Garbage Collections

+

The total time spent in all garbage collectors, total GC time divided by the total process time as a percentage, and the total number of garbage collections. +A large number of collections or time spent in collectors usually indicates that the system is under memory pressure. +Increase the amount of available memory to reduce the time to build the native binary.

+ +

Peak RSS

+

Peak resident set size as reported by the operating system. +This value indicates the maximum amount of memory consumed by the build process. +By default, the process will only use available memory, so memory that the operating system can make available without having to swap out memory used by other processes. +Therefore, consider freeing up memory if builds are slow, for example, by closing applications that you do not need. +Note that, by default, the build process will also not use more than 32GB if available. +If the GC statistics do not show any problems, the amount of total memory of the system can be reduced to a value closer to the peak RSS to lower operational costs.

+ +

CPU load

+

The CPU time used by the process divided by the total process time. +Increase the number of CPU cores to reduce the time to build the native binary.

+ +

Machine-Readable Build Output

+ +

The build output produced by the native-image builder is designed for humans, can evolve with new releases, and should thus not be parsed in any way by tools. +Instead, use the -H:BuildOutputJSONFile=<file.json> option to instruct the builder to produce machine-readable build output in JSON format that can be used, for example, for building monitoring tools. +The JSON files validate against the JSON schema defined in build-output-schema-v0.9.1.json. +Note that a JSON file is produced if and only if a build succeeds.

+ +

The following example illustrates how this could be used in a CI/CD build pipeline to check that the number of reachable methods does not exceed a certain threshold:

+ +
native-image -H:BuildOutputJSONFile=build.json HelloWorld
+# ...
+cat build.json | python3 -c "import json,sys;c = json.load(sys.stdin)['analysis_results']['methods']['reachable']; assert c < 12000, f'Too many reachable methods: {c}'"
+Traceback (most recent call last):
+  File "<string>", line 1, in <module>
+AssertionError: Too many reachable methods: 12128
+
+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/Bundles/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/Bundles/index.html new file mode 100644 index 0000000..ca4679a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/Bundles/index.html @@ -0,0 +1,475 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Bundles

+ +

Native Image provides a feature that enables users to build native executables from a self-contained bundle. +In contrast to regular native-image building, this mode of operation takes only a single *.nib file as input. +The file contains everything required to build a native executable (or a native shared library). +This can be useful when large applications consisting of many input files (JAR files, configuration files, auto-generated files, downloaded files) need to be rebuilt at a later point in time without worrying whether all files are still available. +Often complex builds involve downloading many libraries that are not guaranteed to remain accessible later in time. +Using Native Image bundles is a safe solution to encapsulate all this input required for building into a single file.

+ +
+

Note: The feature is experimental.

+
+ +

Table of Contents

+ + + +

Creating Bundles

+ +

To create a bundle, pass the --bundle-create option along with the other arguments for a specific native-image command line invocation. +This will cause native-image to create a *.nib file in addition to the actual image.

+ +

Here is the option description:

+
--bundle-create[=new-bundle.nib]
+                      in addition to image building, create a Native Image bundle file (*.nib
+                      file) that allows rebuilding of that image again at a later point. If a
+                      bundle-file gets passed, the bundle will be created with the given
+                      name. Otherwise, the bundle-file name is derived from the image name.
+                      Note both bundle options can be combined with --dry-run to only perform
+                      the bundle operations without any actual image building.
+
+ +

For example, assuming a Micronaut application is built with Maven, make sure the --bundle-create option is used. +For that, the following needs to be added to the plugins section of pom.xml:

+
<plugin>
+  <groupId>org.graalvm.buildtools</groupId>
+  <artifactId>native-maven-plugin</artifactId>
+  <configuration>
+      <buildArgs combine.children="append">
+          <buildArg>--bundle-create</buildArg>
+      </buildArgs>
+  </configuration>
+</plugin>
+
+ +

Then, when you run the Maven package command ././mvnww package -Dpackaging=native-image, you will get the following build artifacts:

+
Finished generating 'micronautguide' in 2m 0s.
+
+Native Image Bundles: Bundle build output written to /home/testuser/micronaut-data-jdbc-repository-maven-java/target/micronautguide.output
+Native Image Bundles: Bundle written to /home/testuser/micronaut-data-jdbc-repository-maven-java/target/micronautguide.nib
+
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESS
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time:  02:08 min
+[INFO] Finished at: 2023-03-27T15:09:36+02:00
+[INFO] ------------------------------------------------------------------------
+
+ +

This output indicates that you have created a native executable, micronautguide, and a bundle, micronautguide.nib. +The bundle file is created in the target/ directory. +It should be copied to some safe place where it can be found if the native executable needs to be rebuilt later.

+ +

Obviously, a bundle file can be large because it contains all input files as well as the executable itself (the executable is compressed within the bundle). +Having the image inside the bundle allows comparing a native executable rebuilt from the bundle against the original one. +In the case of the micronaut-data-jdbc-repository example, the bundle is 60.7 MB (the executable is 103.4 MB). +To see what is inside a bundle, run jar tf *.nib:

+
$ jar tf micronautguide.nib
+META-INF/MANIFEST.MF
+META-INF/nibundle.properties
+output/default/micronautguide
+input/classes/cp/micronaut-core-3.8.7.jar
+input/classes/cp/netty-buffer-4.1.87.Final.jar
+input/classes/cp/jackson-databind-2.14.1.jar
+input/classes/cp/micronaut-context-3.8.7.jar
+input/classes/cp/reactive-streams-1.0.4.jar
+...
+input/classes/cp/netty-handler-4.1.87.Final.jar
+input/classes/cp/micronaut-jdbc-4.7.2.jar
+input/classes/cp/jackson-core-2.14.0.jar
+input/classes/cp/micronaut-runtime-3.8.7.jar
+input/classes/cp/micronautguide-0.1.jar
+input/stage/build.json
+input/stage/environment.json
+input/stage/path_substitutions.json
+input/stage/path_canonicalizations.json
+
+ +

As you can see, a bundle is just a JAR file with a specific layout. +This is explained in detail below.

+ +

Next to the bundle, you can also find the output directory: target/micronautguide.output. +It contains the native executable and all other files that were created as part of the build. +Since you did not specify any options that would produce extra output (for example, -g to generate debugging information or --diagnostics-mode), only the executable can be found there:

+
$ tree target/micronautguide.output
+target/micronautguide.output
+├── default
+│   └── micronautguide
+└── other
+
+ +

Combining –bundle-create with –dry-run

+ +

As mentioned in the --bundle-create option description, it is also possible to let native-image build a bundle but not actually perform the image building. +This might be useful if a user wants to move the bundle to a more powerful machine and build the image there. +Modify the above native-maven-plugin configuration to also contain the argument <buildArg>--dry-run</buildArg>. +Then running ././mvnww package -Dpackaging=native-image takes only seconds and the created bundle is much smaller:

+
Native Image Bundles: Bundle written to /home/testuser/micronaut-data-jdbc-repository-maven-java/target/micronautguide.nib
+
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESS
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time:  2.267 s
+[INFO] Finished at: 2023-03-27T16:33:21+02:00
+[INFO] ------------------------------------------------------------------------
+
+ +

Now micronautguide.nib is only 20 MB in file size and the executable is not included:

+
$ jar tf micronautguide.nib
+META-INF/MANIFEST.MF
+META-INF/nibundle.properties
+input/classes/cp/micronaut-core-3.8.7.jar
+...
+
+ +

Note that this time you do not see the following message in the Maven output:

+
Native Image Bundles: Bundle build output written to /home/testuser/micronaut-data-jdbc-repository-maven-java/target/micronautguide.output
+
+

Since no executable is created, no bundle build output is available.

+ +

Building with Bundles

+ +

Assuming that the native executable is used in production and once in a while an unexpected exception is thrown at run time. +Since you still have the bundle that was used to create the executable, it is trivial to build a variant of that executable with debugging support. +Use --bundle-apply=micronautguide.nib like this:

+
$ native-image --bundle-apply=micronautguide.nib -g
+
+Native Image Bundles: Loaded Bundle from /home/testuser/micronautguide.nib
+Native Image Bundles: Bundle created at 'Tuesday, March 28, 2023, 11:12:04 AM Central European Summer Time'
+Native Image Bundles: Using version: '20.0.1+8' (vendor 'Oracle Corporation') on platform: 'linux-amd64'
+Warning: Native Image Bundles are an experimental feature.
+========================================================================================================================
+GraalVM Native Image: Generating 'micronautguide' (executable)...
+========================================================================================================================
+...
+Finished generating 'micronautguide' in 2m 16s.
+
+Native Image Bundles: Bundle build output written to /home/testuser/micronautguide.output
+
+ +

After running this command, the executable is rebuilt with an extra option -g passed after --bundle-apply. +The output of this build is in the directory micronautguide.output:

+
micronautguide.output
+micronautguide.output/other
+micronautguide.output/default
+micronautguide.output/default/micronautguide.debug
+micronautguide.output/default/micronautguide
+micronautguide.output/default/sources
+micronautguide.output/default/sources/javax
+micronautguide.output/default/sources/javax/smartcardio
+micronautguide.output/default/sources/javax/smartcardio/TerminalFactory.java
+...
+micronautguide.output/default/sources/java/lang/Object.java
+
+ +

You successfully rebuilt the application from the bundle with debug info enabled.

+ +

The full option help of --bundle-apply shows a more advanced use case that will be discussed later in detail:

+
--bundle-apply=some-bundle.nib
+                      an image will be built from the given bundle file with the exact same
+                      arguments and files that have been passed to native-image originally
+                      to create the bundle. Note that if an extra --bundle-create gets passed
+                      after --bundle-apply, a new bundle will be written based on the given
+                      bundle args plus any additional arguments that haven been passed
+                      afterwards. For example:
+                      > native-image --bundle-apply=app.nib --bundle-create=app_dbg.nib -g
+                      creates a new bundle app_dbg.nib based on the given app.nib bundle.
+                      Both bundles are the same except the new one also uses the -g option.
+
+ +

Capturing Environment Variables

+ +

Before bundle support was added, all environment variables were visible to the native-image builder. +This approach does not work well with bundles and is problematic for image building without bundles. +Consider having an environment variable that holds sensitive information from your build machine. +Due to Native Image’s ability to run code at build time that can create data to be available at run time, it is very easy to build an image were you accidentally leak the contents of such variables.

+ +

Passing environment variables to native-image now requires explicit arguments.

+ +

Suppose a user wants to use an environment variable (for example, KEY_STORAGE_PATH) from the environment in which the native-image tool is invoked, in the class initializer that is set to be initialized at build time. +To allow accessing the variable in the class initializer (with java.lang.System.getenv), pass the option -EKEY_STORAGE_PATH to the builder.

+ +

To make an environment variable accessible to build time, use:

+
-E<env-var-key>[=<env-var-value>]
+                      allow native-image to access the given environment variable during
+                      image build. If the optional <env-var-value> is not given, the value
+                      of the environment variable will be taken from the environment
+                      native-image was invoked from.
+
+ +

Using -E works as expected with bundles. +Any environment variable specified with -E will be captured in the bundle. +For variables where the optional <env-var-value> is not given, the bundle would capture the value the variable had at the time the bundle was created. +The prefix -E was chosen to make the option look similar to the related -D<java-system-property-key>=<java-system-property-value> option (which makes Java system properties available at build time).

+ +

Combining –bundle-create and –bundle-apply

+ +

As already mentioned in Building with Bundles, it is possible to create a new bundle based on an existing one. +The --bundle-apply help message has a simple example. +A more interesting example arises if an existing bundle is used to create a new bundle that builds a PGO-optimized version of the original application.

+ +

Assuming you have already built the micronaut-data-jdbc-repository example into a bundle named micronautguide.nib. +To produce a PGO-optimized variant of that bundle, first build a variant of the native executable that generates PGO profiling information at run time (you will use it later):

+
$ native-image --bundle-apply=micronautguide.nib --pgo-instrument
+
+ +

Now run the generated executable so that profile information is collected:

+
$ /home/testuser/micronautguide.output/default/micronautguide
+
+ +

Based on this walkthrough, you use the running native executable to add new database entries and query the information in the database afterwards so that you get real-world profiling information. +Once completed, stop the Micronaut application using Ctrl+C (SIGTERM). +Looking into the current working directory, you can find a new file:

+
$ ls -lh  *.iprof
+-rw------- 1 testuser testuser 19M Mar 28 14:52 default.iprof
+
+ +

The file default.iprof contains the profiling information that was created because you ran the Micronaut application from the executable built with --pgo-instrument. +Now you can create a new optimized bundle out of the existing one:

+
native-image --bundle-apply=micronautguide.nib --bundle-create=micronautguide-pgo-optimized.nib --dry-run --pgo
+
+ +

Now take a look how micronautguide-pgo-optimized.nib is different from micronautguide.nib:

+
$ ls -lh *.nib
+-rw-r--r-- 1 testuser testuser  20M Mar 28 11:12 micronautguide.nib
+-rw-r--r-- 1 testuser testuser  23M Mar 28 15:02 micronautguide-pgo-optimized.nib
+
+ +

You can see that the new bundle is 3 MB larger than the original. +The reason, as can be guessed, is that now the bundle contains the default.iprof file. +Using a tool to compare directories, you can inspect the differences in detail:

+ +

visual-bundle-compare

+ +

As you can see, micronautguide-pgo-optimized.nib contains default.iprof in the directory input/auxiliary, and there +are also changes in other files. The contents of META-INF/nibundle.properties, input/stage/path_substitutions.json +and input/stage/path_canonicalizations.json will be explained later. +For now, look at the diff in build.json:

+
@@ -4,5 +4,6 @@
+   "--no-fallback",
+   "-H:Name=micronautguide",
+   "-H:Class=example.micronaut.Application",
+-  "--no-fallback"
++  "--no-fallback",
++  "--pgo"
+ ]
+
+ +

As expected, the new bundle contains the --pgo option that you passed to native-image to build an optimized bundle. +Building a native executable from this new bundle generates a PGO-optimized executable out of the box (see PGO: on in build output):

+
$ native-image --bundle-apply=micronautguide-pgo-optimized.nib
+...
+[1/8] Initializing...                                                                                    (3.9s @ 0.27GB)
+ Java version: 20.0.1+8, vendor version: GraalVM EE 20.0.1+8.1
+ Graal compiler: optimization level: '2', target machine: 'x86-64-v3', PGO: on
+ C compiler: gcc (redhat, x86_64, 13.0.1)
+ Garbage collector: Serial GC (max heap size: 80% of RAM)
+ 6 user-specific feature(s)
+...
+
+ +

Bundle File Format

+ +

A bundle file is a JAR file with a well-defined internal layout. +Inside a bundle you can find the following inner structure:

+ +
[bundle-file.nib]
+├── META-INF
+│   ├── MANIFEST.MF
+│   └── nibundle.properties <- Contains build bundle version info:
+│                              * Bundle format version (BundleFileVersion{Major,Minor})
+│                              * Platform and architecture the bundle was created on 
+│                              * GraalVM / Native-image version used for bundle creation
+├── input <- All information required to rebuild the image
+│   ├── auxiliary <- Contains auxiliary files passed to native-image via arguments
+│   │                (e.g. external `config-*.json` files or PGO `*.iprof`-files)
+│   ├── classes   <- Contains all class-path and module-path entries passed to the builder
+│   │   ├── cp
+│   │   └── p
+│   └── stage
+│       ├── build.json          <- Full native-image command line (minus --bundle options)
+│       ├── environment.json              <- Environment variables used in the image build
+│       ├── path_canonicalizations.json  <- Record of path-canonicalizations that happened
+│       │                                       during bundle creation for the input files  
+│       └── path_substitutions.json          <- Record of path-substitutions that happened
+│                                               during bundle creation for the input files
+└── output
+    ├── default
+    │   ├── myimage         <- Created image and other output created by the image builder 
+    │   ├── myimage.debug
+    |   └── sources
+    └── other      <- Other output created by the builder (not relative to image location)
+
+

META-INF

+ +

The layout of a bundle file itself is versioned. +There are two properties in META-INF/nibundle.properties that declare which version of the layout a given bundle file is based on. +Bundles currently use the following layout version:

+
BundleFileVersionMajor=0
+BundleFileVersionMinor=9
+
+ +

Future versions of GraalVM might alter or extend the internal structure of bundle files. +The versioning enables us to evolve the bundle format with backwards compatibility in mind.

+ +

input

+ +

This directory contains all input data that gets passed to the native-image builder. +The file input/stage/build.json holds the original command line that was passed to native-image when the bundle was created.

+ +

Parameters that make no sense to get reapplied in a bundle-build are already filtered out. +These include:

+
    +
  • --bundle-{create,apply}
  • +
  • --verbose
  • +
  • --dry-run
  • +
+ +

The state of environment variables that are relevant for the build are captured in input/stage/environment.json. +For every -E argument that were seen when the bundle was created, a snapshot of its key-value pair is recorded in the file. +The remaining files path_canonicalizations.json and path_substitutions.json contain a record of the file-path transformations that were performed by the native-image tool based on the input file paths as specified by the original command line arguments.

+ +

output

+ +

If a native executable is built as part of building the bundle (for example, the --dry-run option was not used), you also have an output directory in the bundle. +It contains the executable that was built along with any other files that were generated as part of building. +Most output files are located in the directory output/default (the executable, its debug info, and debug sources). +Builder output files, that would have been written to arbitrary absolute paths if the executable had not been built in the bundle mode, can be found in output/other.

+ + + + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/HostedvsRuntimeOptions/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/HostedvsRuntimeOptions/index.html new file mode 100644 index 0000000..fe8e484 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/overview/HostedvsRuntimeOptions/index.html @@ -0,0 +1,200 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Native Image Hosted and Runtime Options

+ +

Along with all the options listed in the Native Image Build Options, we also distinguish between hosted and runtime options.

+ +
    +
  • +

    Hosted options: configure a native image build, for example, influence what is put into the image and how the image is built. These are set using the prefix -H: on the command line.

    +
  • +
  • +

    Runtime options: get their initial value during the image build, using the prefix -R: on the command line for the native-image builder. At run time, the default prefix is -XX: (but this is application-specific and not mandated by Native Image).

    +
  • +
+ +

For developer documentation on how to define and use options, read the documentation of the com.oracle.svm.core.option package.

+ +

List of Useful Options

+ +

Graph Dumping

+

Native Image re-used the GraalVM options for graph dumping, logging, counters, and everything else in the GraalVM debug environment. +These GraalVM options can be used both as hosted options (if you want to dump graphs of the native image builder), and as runtime options (if you want to dump graphs during dynamic compilation at runtime).

+ +

The Graal compiler options that work as expected include Dump, DumpOnError, Log, MethodFilter, and the options to specify file names and ports for the dump handlers.

+ +

For example:

+
    +
  • +

    To dump the compiler graphs of the native-image builder: -H:Dump= -H:MethodFilter=ClassName.MethodName.

    +
  • +
  • +

    To dump the compile graphs at runtime, specify the dump flags at runtime: -XX:Dump= -XX:MethodFilter=ClassName.MethodName.

    +
  • +
+ +

Software Bill of Materials (SBOM)

+ +

Oracle GraalVM Native Image can embed a Software Bill of Materials (SBOM) into a native executable by using an experimental option. +The option takes the desired SBOM format as input, and the embedded SBOM can be passively obtained by using the Native Image Inspection Tool or by saving the SBOM alongside the native executable using the optional print flag. +Currently, the CycloneDX format is supported. Users may embed a CycloneDX SBOM into a native executable by using the --enable-sbom option during compilation. +The SBOM is stored in a compressed format (gzip) with the exported sbom symbol referencing its start address and the sbom_length symbol its size.

+ +

Debug Options

+ +

These options enable additional checks in the generated binary to help with debugging:

+ +
    +
  • -H:[+|-]HostedAssertions +enables or disables Java assert statements in the native-image builder. +This flag is translated to either -ea -esa or -da -dsa for the HotSpot VM.
  • +
  • -H:[+|-]RuntimeAssertions +enables or disables Java assert statements at run time.
  • +
  • -H:TempDirectory=FileSystemPath +generates a directory for temporary files during a native image generation. +If this option is specified, the temporary files are not deleted so that you can inspect them after the native image generation.
  • +
+ +

Control the Main Entry Points

+ +
    +
  • -H:Kind=[EXECUTABLE | SHARED_LIBRARY]: +generates an executable with a main entry point, or a shared library with all entry points that are marked via @CEntryPoint.
  • +
  • -H:Class=ClassName: +the class containing the default entry point method. +Ignored if Kind == SHARED_LIBRARY.
  • +
  • -H:Projects=Project1,Project2: +the project that contains the application (and transitively all projects that it depends on).
  • +
  • -H:Name=FileName: +the name of the executable file that is generated.
  • +
  • -H:Path=FileSystemPath: +the directory where the generated executable is placed.
  • +
+ +

Further Reading

+ + + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/workshops/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/workshops/index.html new file mode 100644 index 0000000..17444b6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/native-image/workshops/index.html @@ -0,0 +1,151 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Interactive Labs and Workshops

+ +

Interactive Labs

+ +

Consider running interactive workshops in Oracle Cloud to get some practical experience. +The interactive cloud platform provides you with a temporary account for the time you run a lab with required cloud resources (a virtual machine, Oracle Linux, storage, and so on). +This means you can try Native Image without downloading any additional software to your computer.

+ +

Go to Luna Labs and search for “Native Image”. +The following labs are available:

+ +
    +
  • GraalVM Native Image Quick Start: This lab is for developers new to Native Image who are keen to start building cloud native Java applications.
  • +
  • GraalVM, Reflection and Native Image: This lab shows how to pre-configure the native-image builder when reflection is used, and make your Java application compatible with ahead-of-time compilation using Native Image.
  • +
  • GraalVM Native Image, Spring and Containerisation: This lab shows you how to package a Spring Boot application into a native executable; containerise it; shrink the footprint even more by creating small a Distroless container Image.
  • +
  • Optimize Cloud Native Java Apps with Oracle GraalVM PGO: This lab shows how to run a Java Microbenchmark Harness (JMH) benchmark as a native executable, built with GraalVM Native Image, and then optimize it for higher throughput using Profile-Guided Optimizations (PGO).
  • +
  • OCI MySQL, Vault, Micronaut, GraalVM Native Image: In this lab, you will learn how to build a portable cloud-native Java application with Micronaut, GraalVM Native Image and MySQL Database service, and Secrets in Vault on Oracle Cloud Infrastructure.
  • +
  • GraalVM Native Build Tools, for Maven: This lab shows you how to use the GraalVM Native Build Tools to easily build a native executable of your application using Maven. It is aimed at developers with the knowledge of Java.
  • +
  • Build Cloud Native Java Applications with Micronaut and GraalVM Native Image: The workshop features several labs that walk you through the steps to get going with Micronaut, GraalVM, and Oracle Cloud services.
  • +
  • GraalVM Native Image Bootcamp: This workshop covers how GraalVM Native Image works, what it can do, when to use it, and how to tune and debug applications. You will also look at containerisation options and deployment to cloud platforms including Oracle Cloud Infrastructure.
  • +
+ +

Workshops

+ +

Have a look at GraalVM Workshops and Tutorials collection on GitHub. There you can find the workshops intended to work on your own device (laptop, server). Each workshop begins with prerequisites that you need to have installed.

+ +

This repository is continually updated with new workshops. Check-in regularly to keep up to date.

+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/polyglot-programming/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/polyglot-programming/index.html new file mode 100644 index 0000000..cf43251 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/polyglot-programming/index.html @@ -0,0 +1,2616 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+ Table of Contents + +

Polyglot Programming

+ + + +

GraalVM allows users to write polyglot applications that seamlessly pass values from one language to another by means of the Truffle language implementation framework (henceforth “Truffle”).

+ +

Truffle is a Java library for building programming languages implementations as interpreters for self-modifying Abstract Syntax Trees. +When writing a language interpreter with Truffle, it will automatically use the Graal compiler as a just-in-time compiler for the language. +By having access to this framework, a Ruby application, for example, can run on the same JVM as a Java application. +Also, a host JVM-based language and a guest language can directly interoperate with each other and pass data back and forth in the same memory space.

+ +

In order to provide foreign polyglot values in the languages implemented with Truffle, the so-called polyglot interoperability protocol has been developed. +This interoperability protocol consists of a set of standardized messages that every language implements and uses for foreign polyglot values. +The protocol allows GraalVM to support interoperability between any combination of languages without requiring them to know of each other. +For more details, proceed to the High-Performance Cross-Language Interoperability in a Multi-Language Runtime paper.

+ +

Throughout this section you learn how to combine multiple languages using GraalVM Polyglot APIs.

+ +

Running Polyglot Applications

+ +

The following examples are designed to get you started with a basic polyglot application. +Select a section for your Start Language and then select a tab for the Target Language.

+ +

Ensure you set up GraalVM before you begin.

+ +

The below examples work:

+
    +
  • on a JVM, by passing --polyglot --jvm.
  • +
  • on native launchers with --polyglot (e.g., js --polyglot). +It might be required to rebuild images to access languages installed with gu.
  • +
  • with native executables (e.g., native-image --language:js).
  • +
+ +

For native launchers and native executables using Java as a Target Language and accessing classes other than Java arrays, it is required to recompile the image and provide a reflection configuration file.

+ +

Note: To start an application with LLVM as a Target Language, make sure to precompile the polyglot.c file provided below.

+ +

Start from JavaScript / Node.js

+ +

Create the file polyglot.js:

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+var array = Polyglot.eval("R", "c(1,2,42,4)")
+console.log(array[2]);
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+var array = Polyglot.eval("ruby", "[1,2,42,4]")
+console.log(array[2]);
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+var array = Polyglot.eval("python", "[1,2,42,4]")
+console.log(array[2]);
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+var array = new (Java.type("int[]"))(4);
+array[2] = 42;
+console.log(array[2])
+// END-SNIPPET
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+var cpart = Polyglot.evalFile("llvm", "polyglot");
+cpart.main()
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ +
+ + + +
+ +

Run:

+ +
js --polyglot --jvm polyglot.js
+42
+node --polyglot --jvm polyglot.js
+42
+
+ +

Start Language R

+ +

Create the file polyglot.R:

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
 
+  # BEGIN-SNIPPET
+array <- eval.polyglot("js", "[1,2,42,4]")
+print(array[3L])
+# END-SNIPPET
+
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
 
+  # BEGIN-SNIPPET
+array <- eval.polyglot("ruby", "[1,2,42,4]")
+print(array[3L])
+# END-SNIPPET
+
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
 
+  # BEGIN-SNIPPET
+array <- eval.polyglot("python", "[1,2,42,4]")
+print(array[3L])
+# END-SNIPPET
+
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
 
+  # BEGIN-SNIPPET
+array <- new("int[]", 4)
+array[3L] <- 42
+print(array[3L])
+# END-SNIPPET
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
 
+  # BEGIN-SNIPPET
+cpart <- eval.polyglot("llvm", path="polyglot")
+cpart$main()
+# END-SNIPPET
+
+  
+ + + + + + +
+ +
+ +
+ + + +
+ +

Run:

+ +
Rscript --polyglot --jvm polyglot.R
+[1] 42
+
+ +

Start Language Ruby

+ +

Create the file polyglot.rb:

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+array = Polyglot.eval('js', '[1,2,42,4]')
+puts array[2]
+# END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+array = Polyglot.eval('R', 'c(1L,2L,42L,4L)')
+puts array[2]
+# END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+array = Polyglot.eval('python', '[1,2,42,4]')
+puts array[2]
+# END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+array = Java.type('int[]').new(4)
+array[2] = 42
+print(array[2])
+# END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+cpart = Polyglot.eval_file('llvm', 'polyglot')
+cpart.main()
+# END-SNIPPET
+
+  
+ + + + + + +
+ +
+ +
+ + + +
+ +

Run:

+ +
ruby --polyglot --jvm polyglot.rb
+42
+
+ +

Start Language Python

+ +

Create the file polyglot.py:

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+import polyglot
+array = polyglot.eval(language="js", string="[1,2,42,4]")
+print(array[2])
+# END-SNIPPET
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+import polyglot
+array = polyglot.eval(language="R", string="c(1L,2L,42L,4L)")
+print(array[2])
+# END-SNIPPET
+
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+import polyglot
+array = polyglot.eval(language="ruby", string="[1,2,42,4]")
+print(array[2])
+# END-SNIPPET
+
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+import java
+array = java.type("int[]")(4)
+array[2] = 42
+print(array[2])
+# END-SNIPPET
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  # BEGIN-SNIPPET
+import polyglot
+cpart = polyglot.eval(language="llvm", path="polyglot")
+cpart.main()
+# END-SNIPPET
+
+  
+ + + + + + +
+ +
+ +
+ + + +
+ +

Run:

+ +
graalpy --polyglot --jvm polyglot.py
+42
+
+ +

Start Language Java

+ +

Create the file Polyglot.java:

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+import org.graalvm.polyglot.*;
+
+class Polyglot {
+    public static void main(String[] args) {
+        Context polyglot = Context.create();
+        Value array = polyglot.eval("js", "[1,2,42,4]");
+        int result = array.getArrayElement(2).asInt();
+        System.out.println(result);
+    }
+}
+// END-SNIPPET
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+import org.graalvm.polyglot.*;
+
+class Polyglot {
+    public static void main(String[] args) {
+        Context polyglot = Context.newBuilder().
+    	    		               allowAllAccess(true).build();
+        Value array = polyglot.eval("R", "c(1,2,42,4)");
+        int result = array.getArrayElement(2).asInt();
+        System.out.println(result);
+    }
+}
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+import org.graalvm.polyglot.*;
+
+class Polyglot {
+    public static void main(String[] args) {
+        Context polyglot = Context.newBuilder().
+        		               allowAllAccess(true).build();
+        Value array = polyglot.eval("ruby", "[1,2,42,4]");
+        int result = array.getArrayElement(2).asInt();
+        System.out.println(result);
+    }
+}
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+import org.graalvm.polyglot.*;
+
+class Polyglot {
+    public static void main(String[] args) {
+        Context context = Context.newBuilder().allowIO(true).build();
+        Value array = context.eval("python", "[1,2,42,4]");
+        int result = array.getArrayElement(2).asInt();
+        System.out.println(result);
+    }
+}
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
  // BEGIN-SNIPPET
+import java.io.*;
+import org.graalvm.polyglot.*;
+
+class Polyglot {
+    public static void main(String[] args) throws IOException {
+        Context polyglot = Context.newBuilder().
+        		               allowAllAccess(true).build();
+        File file = new File("polyglot");
+        Source source = Source.newBuilder("llvm", file).build();
+        Value cpart = polyglot.eval(source);
+        cpart.execute();
+    }
+}
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ +
+ + + +
+ +

Run:

+ +
javac Polyglot.java
+java Polyglot
+42
+
+ +

Start Language C

+ +

Create the file polyglot.c:

+ +
+ +
+ +
+ +
+
+ + + +
+ + + + + + +
 
+  // BEGIN-SNIPPET
+#include <stdio.h>
+#include <graalvm/llvm/polyglot.h>
+
+int main() {
+    void *array = polyglot_eval("js", "[1,2,42,4]");
+    int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
+    printf("%d\n", element);
+    return element;
+}
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
 
+  // BEGIN-SNIPPET
+#include <stdio.h>
+#include <graalvm/llvm/polyglot.h>
+
+int main() {
+    void *array = polyglot_eval("R", "c(1,2,42,4)");
+    int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
+    printf("%d\n", element);
+    return element;
+}
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
 
+  // BEGIN-SNIPPET
+#include <stdio.h>
+#include <graalvm/llvm/polyglot.h>
+
+int main() {
+    void *array = polyglot_eval("ruby", "[1,2,42,4]");
+    int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
+    printf("%d\n", element);
+    return element;
+}
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
 
+  // BEGIN-SNIPPET
+#include <stdio.h>
+#include <graalvm/llvm/polyglot.h>
+
+int main() {
+    void *array = polyglot_eval("python", "[1,2,42,4]");
+    int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
+    printf("%d\n", element);
+    return element;
+}
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ + +
+ +
+
+ + + +
+ + + + + + +
 
+  // BEGIN-SNIPPET
+#include <stdio.h>
+#include <graalvm/llvm/polyglot.h>
+
+int main() {
+    void *arrayType = polyglot_java_type("int[]");
+    void *array = polyglot_new_instance(arrayType, 4);
+    polyglot_set_array_element(array, 2, 42);
+    int element = polyglot_as_i32(polyglot_get_array_element(array, 2));
+    printf("%d\n", element);
+    return element;
+}
+// END-SNIPPET
+
+  
+ + + + + + +
+ +
+ +
+ + + +
+ +

The example C code has to be compiled to LLVM bitcode using the LLVM frontend such as clang. +A user can use clang shipped with GraalVM by installing a pre-built LLVM toolchain support:

+ +
gu install llvm-toolchain
+export LLVM_TOOLCHAIN=$(lli --print-toolchain-path)
+
+

Run:

+ +
$LLVM_TOOLCHAIN/clang polyglot.c -lgraalvm-llvm -o polyglot
+lli --polyglot polyglot
+42
+
+ +

Polyglot Launcher

+ +

With polyglot applications it is often impossible to decide what the primary language of an application is. +Therefore, an experimental new launcher, called polyglot, has been added to GraalVM. +For the moment, this launcher runs code for JavaScript, Ruby, and R without requiring the selection of a primary language. +The polyglot launcher does not require the --polyglot option; it is enabled by default.

+ +

This is how you can run a polyglot application by using the examples from above:

+ +
polyglot --jvm polyglot.js polyglot.R polyglot.rb
+
+ +

We have also included a basic experimental shell for multiple languages called the Polyglot Shell. +It is useful to quickly test the interactivity of languages implemented with the Truffle framework. +This is how you can start it:

+ +
polyglot --jvm --shell
+
+ +

If you have installed all optional languages packs to the core GraalVM installation, then the Polyglot Shell will look like:

+
GraalVM MultiLanguage Shell 22.2.0
+Copyright (c) 2013-2021, Oracle and/or its affiliates
+  Java version 22.2.0
+  JavaScript version 22.2.0
+  Python version 3.8.5
+  R version 4.0.3
+  Ruby version 3.0.3
+Usage:
+  Use Ctrl+n to switch language and Ctrl+d to exit.
+  Enter -usage to get a list of available commands.
+js>
+
+ +
+

Note: The polyglot launcher and the Polyglot Shell are experimental features in GraalVM.

+
+ +

Polyglot Options

+ +

You can configure a language engine for better throughput or startup.

+ +
    +
  • --engine.Mode=default configures the execution mode of the engine. The execution mode automatically tunes the polyglot engine towards latency or throughput. +
      +
    • throughput collects the maximum amount of profiling information and compiles using the + maximum number of optimizations. This mode results in slower application startup + but better throughput. This mode uses the compiler configuration community or + enterprise if not specified otherwise.
    • +
    • default uses a balanced engine configuration. This mode uses the compiler configuration community or enterprise if not specified otherwise.
    • +
    • latency collects only minimal profiling information and compiles as fast as possible + with less optimal-generated code. This mode results in faster application + startup but less optimal throughput. This mode uses the compiler configuration + economy if not specified otherwise.
    • +
    +
  • +
+ +

Passing Options for Language Launchers

+ +

Every language launcher has been extended with a set of so called polyglot options. +Polyglot options allow users of any language launcher to access the options of other languages supported by GraalVM (implemented with the Truffle language implementation framework). +The format is: --<languageID>.<property>=<value>. +For example, the R launcher also supports the --js.atomics=true JavaScript option.

+ +

Allowed values for the languageID are:

+
    +
  • js: options for JavaScript
  • +
  • python: options for Python
  • +
  • r: options for R
  • +
  • ruby: options for Ruby
  • +
  • llvm: options for LLVM
  • +
+ +

Use --help:languages to find out which options are available.

+ +

Options for polyglot tools work in the same way with the following format: --<toolID>.<property>=<value>.

+ +

Allowed values for <toolID> are:

+
    +
  • inspect: allows debugging with Chrome DevTools
  • +
  • cpusampler: collects data about CPU usage
  • +
  • cputracer: captures trace information about CPU usage
  • +
  • memtracer: captures trace information about memory usage
  • +
+ +

Use --help:tools to find out which options are available.

+ +

Passing Options Programmatically

+ +

Options can also be passed programmatically using the Java polyglot API.

+ +

Create a file called OptionsTest.java:

+
import org.graalvm.polyglot.*;
+
+class OptionsTest {
+
+    public static void main(String[] args) {
+        Context polyglot = Context.newBuilder()
+            .allowExperimentalOptions(true)
+            .option("js.shared-array-buffer", "true")
+            .build();
+        // the use of shared array buffer requires the 'js.shared-array-buffer' option to be 'true'
+        polyglot.eval("js", "new SharedArrayBuffer(1024)");
+    }
+}
+
+ +

Run:

+
javac OptionsTest.java
+java OptionsTest
+
+ +
+

Note: Tools options can be passed in the same way. Options cannot be modified after the context was created.

+
+ +

Passing Options Using JVM Arguments

+ +

Every polyglot option can also be passed as a Java system property. +Each available option translates to a system property with the polyglot. prefix. +For example, -Dpolyglot.js.strict=true sets the default value for a strict interpretation for all JavaScript code that runs in the JVM. +Options that were set programmatically take precedence over Java system properties. +For languages the following format can be used: -Dpolyglot.<languageID>.<property>=<value> and for tools it is: -Dpolyglot.<toolID>.<property>=<value>.

+ +

Create a file called SystemPropertiesTest.java:

+
import org.graalvm.polyglot.*;
+
+class SystemPropertiesTest {
+
+    public static void main(String[] args) {
+        Context polyglot = Context.newBuilder()
+        .allowExperimentalOptions(true)
+        .build();
+        // the use of shared array buffer requires the 'js.shared-array-buffer' option to be 'true'
+        polyglot.eval("js", "new SharedArrayBuffer(1024)");
+    }
+}
+
+ +

Run:

+
javac SystemPropertiesTest.java
+java -Dpolyglot.js.strict=true SystemPropertiesTest
+
+ +
+

Note: System properties are read once when the polyglot context is created. Subsequent changes have no effect.

+
+ +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/FAQ/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/FAQ/index.html new file mode 100644 index 0000000..50cb0dd --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/FAQ/index.html @@ -0,0 +1,182 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Frequently Asked Questions

+ +

Does module/package XYZ work on GraalPy?

+ +

It depends. +The first goal with GraalPy was to show that NumPy and related packages can run using the managed GraalVM LLVM runtime. +The GraalVM team continues to improve the number of passing CPython unit tests, and to track the compatibility with popular PyPI packages. +Of the top 500 PyPI packages, about 50% currently pass the majority of their tests on GraalPy.

+ +

Can GraalPy replace my Jython use case?

+ +

It can, but there are some caveats, such as Python code subclassing Java classes or use through the javax.script.ScriptEngine not being supported. +See the Jython Migration guide for details.

+ +

Do I need to compile and run native modules with LLVM to use GraalPy?

+ +

No. +Python C extension modules must be built from source to run on GraalPy, but the process is largely automatic when using pip and uses the system’s standard compilers. +To extend the tooling and sandboxing features of GraalVM to Python C extension modules, they can be run using the GraalVM LLVM runtime.

+ +

Can I use the GraalVM sandboxing features with GraalPy?

+ +

Yes, you can. +GraalPy provides two special launchers, graalpy-lt and graalpy-managed. +The former allows C extension libraries to call out to native system libraries, whereas the latter requires all libraries to be available as bitcode. +A venv environment created with these launchers will transparently produce such LLVM bitcode during the build process of native extensions when installed through pip. +Extensions installed in this manner work with the GraalVM tools for debugging, CPU and memory sampling, as well as sandboxing. +As an embedder, you can selectively disable system accesses, virtualize the filesystem even for the C extensions, or limit the amount of memory that is allocated. +The price to pay is in increased warm-up and footprint and sometimes lower peak performance, since all code, including the code for native libraries, is subject to JIT compilation.

+ +

Do all the GraalVM polyglot features work with GraalPy?

+ +

The team is continuously working to ensure all polyglot features of GraalVM work as a Python user would expect. +There are still many cases where expectations are unclear or where multiple behaviors are imaginable. +The team is actively looking at use cases and continuously evolving GraalPy to provide the most convenient and least surprising behavior.

+ +

What performance can I expect from GraalPy?

+ +

For pure Python code, performance after warm-up can be expected to be around 3-4 times faster than CPython 3.10 (or 4-5x faster than Jython). +Native extensions running in the default mode–with full native access–run at about the same speed as their CPython counterparts. +For native extensions running as LLVM bitcode to take advantage of our sandboxing features, GraalPy is usually slower–you should expect to reach at most half of CPython’s performance.

+ +

I heard languages with JIT compilers have slow startup. Is that true for GraalPy?

+ +

It depends. +When you use Native Image with Python, or the graalpy launcher, startup is competitive with CPython. +Both with a native executable created by Native Image or when running on the JVM, you first need to warm up to reach peak performance. +For small workloads, GraalPy often surpasses CPython performance a few seconds after reaching the code loop. +That being said, the actual startup behavior depends very much on the actual workload.

+ +

Can I share warmed-up code between multiple Python contexts?

+ +

Yes, this works, and you will find that starting up multiple contexts in the same engine, and running the same or similar code in them will get increasingly faster, because the compiled code is shared across contexts. +However, the peak performance in this setup is currently lower than in the single context case.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Interoperability/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Interoperability/index.html new file mode 100644 index 0000000..5f8201d --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Interoperability/index.html @@ -0,0 +1,479 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Interoperability

+ +

The Polyglot API

+ +

Since GraalVM supports several other programming languages including JavaScript, R, +Ruby, and those that compile to LLVM bitcode, it also provides a Python API to interact with them. +In fact, GraalVM uses this API internally when executing Python native extensions using the GraalVM LLVM runtime.

+ +

You can import the polyglot module to interact with other languages:

+
>>> import polyglot
+
+ +

You can evaluate some inlined code from another language:

+
>>> polyglot.eval(string="1 + 1", language="ruby")
+2
+
+ +

You can evaluate some code from a file, by passing the path to it:

+
>>> with open("./my_ruby_file.rb", "w") as f:
+...    f.write("Polyglot.export('RubyPolyglot', Polyglot)")
+41
+>>> polyglot.eval(path="./my_ruby_file.rb", language="ruby")
+<foreign object at ...>
+
+ +

You can import a global value from the entire polyglot scope:

+
>>> ruby_polyglot = polyglot.import_value("RubyPolyglot")
+
+ +

This global value should then work as expected:

+ +
    +
  • Accessing attributes assumes it reads from the members namespace. +
      >>> ruby_polyglot.to_s
    +  <foreign object at ...>
    +
    +
  • +
  • Calling methods on the result tries to do a straight invoke and falls back to reading the member and trying to execute it. +
      >>> ruby_polyglot.to_s()
    +  Polyglot
    +
    +
  • +
  • Accessing items is supported both with strings and numbers. +
      >>> ruby_polyglot.methods()[10] is not None
    +  True
    +
    +
  • +
+ +

You can export some object from Python to other supported languages so they can import it:

+
>>> foo = object()
+>>> polyglot.export_value(value=foo, name="python_foo")
+<object object at ...>
+>>> jsfoo = polyglot.eval(language="js", string="Polyglot.import('python_foo')")
+>>> jsfoo is foo
+True
+
+ +

The export function can be used as a decorator. +In this case the function name is used as the globally exported name:

+
>>> @polyglot.export_value
+... def python_method():
+...     return "Hello from Python!"
+
+ +

Here is an example of how to use the JavaScript regular expression engine to +match Python strings:

+
>>> js_re = polyglot.eval(string="RegExp()", language="js")
+
+>>> pattern = js_re.compile(".*(?:we have (?:a )?matching strings?(?:[!\\?] )?)(.*)")
+
+>>> if pattern.exec("This string does not match"):
+...    raise SystemError("that shouldn't happen")
+
+>>> md = pattern.exec("Look, we have matching strings! This string was matched by Graal.js")
+
+>>> "Here is what we found: '%s'" % md[1]
+"Here is what we found: 'This string was matched by Graal.js'"
+
+ +

This program matches Python strings using the JavaScript regular expression object. +Python reads the captured group from the JavaScript result and prints it.

+ +

As a more complex example, see how you can read a file using R, process the data in Python, and use R again to display the resulting data image, using both the R and Python libraries in conjunction. +To run this example, first install the required R library:

+
R -e 'install.packages("https://www.rforge.net/src/contrib/jpeg_0.1-8.tar.gz", repos=NULL)'
+
+ +

This example also uses image_magix.py and works +on a JPEG image input (you can try with this image). These files have to be in the same directory that the script below is located in and run from.

+
import polyglot
+import sys
+import time
+sys.path.insert(0, ".")
+from image_magix import Image
+
+load_jpeg = polyglot.eval(string="""function(file.name) {
+    library(jpeg)
+    jimg <- readJPEG(file.name)
+    jimg <- jimg*255
+    jimg
+}""", language="R")
+
+raw_data = load_jpeg("python_demo_picture.jpg")
+
+# the dimensions are R attributes; define function to access them
+getDim = polyglot.eval(string="function(v, pos) dim(v)[[pos]]", language="R")
+
+# Create object of Python class 'Image' with loaded JPEG data
+image = Image(getDim(raw_data, 2), getDim(raw_data, 1), raw_data)
+
+# Run Sobel filter
+result = image.sobel()
+
+draw = polyglot.eval(string="""function(processedImgObj) {
+    require(grDevices)
+    require(grid)
+    mx <- matrix(processedImgObj$`@data`/255, nrow=processedImgObj$`@height`, ncol=processedImgObj$`@width`)
+    grDevices:::awt()
+    grid.raster(mx, height=unit(nrow(mx),"points"))
+}""", language="R")
+
+draw(result)
+time.sleep(10)
+
+ +

The Java Host Interop API

+ +

Finally, to interoperate with Java (only when running on the JVM), you can use the java module:

+
>>> import java
+>>> BigInteger = java.type("java.math.BigInteger")
+>>> myBigInt = BigInteger.valueOf(42)
+>>> # public Java methods can just be called
+>>> myBigInt.shiftLeft(128)
+<JavaObject[java.math.BigInteger] at ...>
+>>> # Java method names that are keywords in Python can be accessed using `getattr`
+>>> getattr(myBigInt, "not")()
+<JavaObject[java.math.BigInteger] at ...>
+>>> byteArray = myBigInt.toByteArray()
+>>> # Java arrays can act like Python lists
+>>> list(byteArray)
+[42]
+
+ +

For packages under the java package, you can also use the normal Python import +syntax:

+
>>> import java.util.ArrayList
+>>> from java.util import ArrayList
+>>>
+>>> java.util.ArrayList == ArrayList
+True
+>>> al = ArrayList()
+>>> al.add(1)
+True
+>>> al.add(12)
+True
+>>> al
+[1, 12]
+
+ +

In addition to the type built-in method, the java module exposes the following +methods as well:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Built-inSpecification
instanceof(obj, class)returns True if obj is an instance of class (class must be a foreign object class)
is_function(obj)returns True if obj is a Java host language function wrapped using Truffle interop
is_object(obj)returns True if obj if the argument is Java host language object wrapped using Truffle interop
is_symbol(obj)returns True if obj if the argument is a Java host symbol, representing the constructor and static members of a Java class, as obtained by java.type
+ +
>>> ArrayList = java.type('java.util.ArrayList')
+>>> my_list = ArrayList()
+>>> java.is_symbol(ArrayList)
+True
+>>> java.is_symbol(my_list)
+False
+>>> java.is_object(ArrayList)
+True
+>>> java.is_function(my_list.add)
+True
+>>> java.instanceof(my_list, ArrayList)
+True
+
+ +

See Polyglot Programming and Embed Languages for more information about interoperability with other programming languages.

+ +

The Behavior of Types

+ +

The interop protocol defines different “types” which can overlap in all kinds of +ways and have restrictions on how they can interact with Python.

+ +

Interop Types to Python

+ +

Most importantly and upfront: all foreign objects passing into Python have the Python type foreign. +There is no emulation of i.e., objects that are interop booleans to have the Python type bool. +This is because interop types can overlap in ways that the Python built-in types cannot, and it would not be clear what should take precedence. +Instead, the foreign type defines all of the Python special methods for type conversion that are used throughout the interpreter (methods such as __add__, __int__, __str__, __getitem__, etc.) and these try to do the right thing based on the interop type (or raise an exception.)

+ +

Types not listed in the below table have no special interpretation in Python right now.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Interop typePython interpretation
NullIt is like None. Important to know: interop null values are equal, but not identical! This was done because JavaScript defines two “null-like” values; undefined and null, which are not identical
BooleanBehaves like Python booleans, including the fact that in Python, all booleans are also integers (1 and 0 for true and false, respectively)
NumberBehaves like Python numbers. Python only has one integral and one floating point type, but it cares about the ranges in some places such as typed arrays.
StringBehaves like Python strings.
BufferBuffers are also a concept in Python’s native API (albeit a bit different). Interop buffers are treated like Python buffers in some places (such as memoryview) to avoid copies of data.
ArrayArrays can be used with subscript access like Python lists, with integers and slices as indices.
HashHashes can be used with subscript access like Python dicts, with any hashable kind of object as key. “Hashable” follows Python semantics, generally all interop types with identity are deemed “hashable”. Note that if an interop object is both Array and Hash, the behavior of the subscript access is undefined.
MembersMembers can be read using normal Python ~.~ notation or the getattr etc. functions.
IterableIterables are treated like Python objects with an __iter__ method, that is, they can be used in loops and other places that accept Python iterables.
IteratorIterators are treated like Python objects with a __next__ method.
ExceptionInterop exceptions can be caught in generic except clauses.
MetaObjectInterop meta objects can be used in subtype and isinstance checks
ExecutableExecutable objects can be executed as functions, but never with keyword arguments.
InstantiableInstantiable objects behave like executable objects (similar to how Python treats this)
+ +

Python to Interop Types

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Interop typePython interpretation
NullOnly None.
BooleanOnly subtypes of Python bool. Note that in contrast to Python semantics, Python bool is never also an interop number.
NumberOnly subtypes of int and float.
StringOnly subtypes of str.
ArrayAny object with a __getitem__ and a __len__, but not if it also has keys, values, and items (like dict does.)
HashOnly subtypes of dict.
MembersAny Python object. Note that the rules for readable/writable are a bit ad-hoc, since checking that is not part of the Python MOP.
IterableAnything that has an __iter__ method or a __getitem__ method.
IteratorAnything with a __next__ method.
ExceptionAny Python BaseException subtype.
MetaObjectAny Python type.
ExecutableAnything with a __call__ method.
InstantiableAny Python type.
+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Jython/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Jython/index.html new file mode 100644 index 0000000..20a6a31 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Jython/index.html @@ -0,0 +1,448 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Jython Migration Guide

+ +

Most Jython code that uses Java integration will be based on a +stable Jython release, and these only come in Python 2.x versions. +GraalPy, in contrast, is only targeting Python 3.x. +GraalPy does not provide full compatibility with these earlier 2.x versions of Jython. +Thus, a significant migration step will have to be taken to migrate all your code to Python 3.

+ +

For Jython-specific features, follow this document to learn about migration to GraalPy.

+ +

Note that some features of Jython have a negative impact on runtime performance, and are disabled by default. +To make migration easier, you can enable some features using a command line option: --python.EmulateJython.

+ +

Importing Java Packages

+ +

There are certain features of Jython’s Java integration that are enabled by default on GraalPy. +Here is an example:

+ +
>>> import java.awt as awt
+>>> win = awt.Frame()
+>>> win.setSize(200, 200)
+>>> win.setTitle("Hello from Python!")
+>>> win.getSize().toString()
+'java.awt.Dimension[width=200,height=200]'
+>>> win.show()
+
+ +

This example works exactly the same on both Jython and GraalPy. +However, on GraalPy only packages in the java namespace can be directly imported. +Importing classes from packages outside the java namespace also requires the --python.EmulateJython option to be active.

+ +

Additionally, importing Java packages as Python modules is only supported under very specific circumstances. +For example, this will work:

+ +
import java.lang as lang
+
+ +

This will not work:

+ +
import javax.swing as swing
+from javax.swing import *
+
+ +

Instead, you will have to import one of the classes directly:

+ +
import javax.swing.Window as Window
+
+ +

Basic Object Usage

+ +

Constructing and working with Java objects and classes is achieved with natural +Python syntax. The methods of Java objects can also be retrieved and passed +around as first class objects (bound to their instance), the same as Python +methods:

+ +
>>> from java.util import Random
+>>> rg = Random(99)
+>>> rg.nextInt()
+1491444859
+>>> boundNextInt = rg.nextInt
+>>> boundNextInt()
+1672896916
+
+ +

Java-to-Python Types: Automatic Conversion

+ +

Method overloads are resolved by matching the Python arguments in a best-effort manner to the available parameter types. +This also happens during data conversion. +The goal here is to make using Java from Python as smooth as possible. +The matching allowed here is similar to Jython, but GraalPy uses a more dynamic approach to matching—Python types emulating int or float are also converted to the appropriate Java types. +This enables you, for example, to use Pandas frames as double[][] or NumPy array elements as int[] when the elements fit into those Java primitive types.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Java typePython type
nullNone
booleanbool
byte, short, int, longint, any object that has an __int__ method
floatfloat, any object that has a __float__ method
charstr of length 1
java.lang.Stringstr
byte[]bytes, bytearray, wrapped Java array, Python list with only the appropriate types
Java arraysWrapped Java array or Python list with only the appropriate types
Java objectsWrapped Java object of the appropriate type
java.lang.ObjectAny object
+ +

Special Jython Modules

+ +

The jarray module which is used to create primitive Java arrays is supported for compatibility.

+ +
>>> import jarray
+>>> jarray.array([1,2,3], 'i')
+
+ +

Note that its usage is equivalent of constructing the array types using the java.type function and filling the array.

+ +
>>> import java
+>>> java.type("int[]")(10)
+
+ +

The code that only needs to pass a Java array can also use Python types. +However, implicitly, this may entail a copy of the array data, which can be deceptive when using Java arrays as output parameters:

+ +
>>> i = java.io.ByteArrayInputStream(b"foobar")
+>>> buf = [0, 0, 0]
+>>> i.read(buf) # buf is automatically converted to a byte[] array
+3
+>>> buf
+[0, 0, 0] # the converted byte[] array got lost
+>>> jbuf = java.type("byte[]")(3)
+>>> i.read(jbuf)
+3
+>>> jbuf
+[98, 97, 122]
+
+ +

Modules other than jarray are not supported.

+ +

Exceptions from Java

+ +

Catching all kinds of Java exceptions comes with a performance penalty and is only enabled with the --python.EmulateJython option. For example:

+ +
>>> import java
+>>> v = java.util.Vector()
+>>> try:
+...    x = v.elementAt(7)
+... except java.lang.ArrayIndexOutOfBoundsException as e:
+...    print(e.getMessage())
+...
+7 >= 0
+
+ +

Java Collections

+

Java arrays and collections implementing java.util.Collection can be accessed using the [] syntax. Empty collections +are considered false in boolean conversions. Their length is exposed by len built-in function. For example:

+ +
>>> from java.util import ArrayList
+>>> l = ArrayList()
+>>> l.add("foo")
+True
+>>> l.add("baz")
+True
+>>> l[0]
+'foo'
+>>> l[1] = "bar"
+>>> del l[1]
+>>> len(l)
+1
+>>> bool(l)
+True
+>>> del l[0]
+>>> bool(l)
+False
+
+ +

Java iterables implementing java.lang.Iterable can be iterated over using a for loop or iter built-in function +and are accepted by all built-ins that expect iterables. For example:

+ +
>>> [x for x in l]
+['foo', 'bar']
+>>> i = iter(l)
+>>> next(i)
+'foo'
+>>> next(i)
+'bar'
+>>> next(i)
+Traceback (most recent call last):
+File "<stdin>", line 1, in <module>
+StopIteration
+>>> set(l)
+{'foo', 'bar'}
+
+ +

Iterators can be iterated as well. For example:

+ +
>>> from java.util import ArrayList
+>>> l = ArrayList()
+>>> l.add("foo")
+True
+>>> i = l.iterator()  # Calls the Java iterator methods
+>>> next(i)
+'foo'
+
+ +

Map collections implementing java.util.Map can be accessed using [] notation. +Empty maps are considered false in boolean conversions. Iteration of maps yields the keys, consistent with dict.

+ +
>>> from java.util import HashMap
+>>> m = HashMap()
+>>> m['foo'] = 5
+>>> m['foo']
+5
+>>> m['bar']
+Traceback (most recent call last):
+File "<stdin>", line 1, in <module>
+KeyError: bar
+>>> [k for k in m]
+['foo']
+>>> bool(m)
+True
+>>> del m['foo']
+>>> bool(m)
+False
+
+ +

Inheritance from Java

+ +

Inheriting from a Java class or implementing an interface is supported with some syntactical differences from Jython. A +class inheriting from a Java class can be created using an ordinary class statement where declared methods will +override/implement the superclass methods when they match in name. Super calls are performed using a special +attribute self.__super__. The created object won’t behave like a Python object but like a foreign Java object. Its +Python-level members can be accessed using its this attribute. For example:

+ +
import atexit
+from java.util.logging import Logger, Handler
+
+
+class MyHandler(Handler):
+    def __init__(self):
+        self.logged = []
+
+    def publish(self, record):
+        self.logged.append(record)
+
+
+logger = Logger.getLogger("mylog")
+logger.setUseParentHandlers(False)
+handler = MyHandler()
+logger.addHandler(handler)
+# Make sure the handler is not used after the Python context has been closed
+atexit.register(lambda: logger.removeHandler(handler))
+
+logger.info("Hi")
+logger.warning("Bye")
+
+# The python attributes/methods of the object have to be accessed through 'this' attribute
+for record in handler.this.logged:
+    print(f'Python captured message "{record.getMessage()}" at level {record.getLevel().getName()}')
+
+ +

Embedding Python into Java

+ +

The other way to use Jython is to embed it into Java applications.

+ +

There are two options for embedding Jython in a Java application. +One it to use the PythonInterpreter object that Jython provides. +Existing code using Jython in this manner depends directly on the Jython package (for example, in the Maven configuration), because the Java code has references to Jython internal classes. +These classes do not exist in GraalVM, and no equivalent classes are exposed. +To migrate from this usage, switch to the GraalVM SDK. +Using this SDK, no APIs particular to Python are exposed, everything is done through the GraalVM API, with maximum configurability of the Python runtime.

+ +

The other option to embed Jython in Java is via JSR 223 by using the classes of the the javax.script package, and, in particular, via the ScriptEngine class. +We do not recommend this approach, since the ScriptEngine APIs are not a clean fit for the options and capabilities of GraalPy. +However, to migrate existing code, the NetBeans project provides packages on Maven Central to help here. +Remove Jython and add the following dependencies instead (using a Maven pom.xml file as an example):

+ +
<dependency>
+  <groupId>org.netbeans.api</groupId>
+  <artifactId>org-netbeans-libs-graalsdk</artifactId>
+  <version>RELEASE150</version> <!-- or any later release -->
+</dependency>
+<dependency>
+  <groupId>org.netbeans.api</groupId>
+  <artifactId>org-netbeans-api-scripting</artifactId>
+  <version>RELEASE150</version> <!-- or any later release -->
+</dependency>
+
+ +

Afterwards, basic usage of GraalPy can be achieved by replacing

+ +
ScriptEngine python = new ScriptEngineManager().getEngineByName("python");
+
+ +

with

+ +
import org.netbeans.api.scripting.Scripting;
+// ...
+ScriptEngineManager manager = Scripting.newBuilder().allowAllAccess(true).build();
+ScriptEngine python = manager.getEngineByName("GraalVM:python");
+
+ +

It is important to note that either of those options will only work if your application is executed on GraalVM with the Python language installed. +For more details, refer to the Embed Languages guide.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/OSInterface/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/OSInterface/index.html new file mode 100644 index 0000000..a6654fe --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/OSInterface/index.html @@ -0,0 +1,201 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Operating System Interfaces

+ +

Truffle-based GraalVM languages usually implement the system related functions using the Truffle abstraction layer, which is OS independent and provides extension points for the users when embedding GraalPy or other Truffle based languages into Java applications. +See, for example, Truffle FileSystem service-provider.

+ +

The Python standard library also provides an OS abstraction, but exposes lower level interfaces, for instance, the os module directly exposes some POSIX functions. +On non-POSIX platforms, this interface is emulated to a degree.

+ +

GraalPy provides two alternative implementations of the system-related functionality offered by the built-in Python modules such as os. +Which implementation is used can be controlled by the option PosixModuleBackend: valid values are native and java.

+ +

Native Backend

+ +

The native backend directly calls the POSIX API in mostly the same way as CPython (the reference Python implementation).

+ +

This approach is the most compatible with CPython and provides bare access to the underlying OS interface without an intermediate emulation layer.

+ +

By default, this implementation bypasses the Truffle abstraction layer, therefore it is not sandboxed and does not support custom implementations of Truffle FileSystem service-provider, and other Polyglot API providers related to system interfaces.

+ +

The native backend is chosen by default when GraalPy is started via the graalpy or any other Python related launcher. +The exceptions are Python related launchers with -managed suffix available only in Oracle GraalVM (for example, graalpy-managed), which by default use the java POSIX backend.

+ +

Limitations of the Native Backend

+ +

Known limitations are:

+ +
    +
  • os.fork is not supported
  • +
  • _posixsubprocess.fork_exec does not support the preexec_fn parameter
  • +
+ +

Java Backend

+ +

The java backend uses the Truffle abstraction layer and therefore supports custom Polyglot API providers related to system interfaces and sandboxing. +Since this abstraction is POSIX agnostic, it does not expose all the necessary functionality. Some functionality is emulated, and some functionality is not supported at all.

+ +

The Java backend is the default when GraalPy is run via the Context API, i.e., embedded in Java applications, or when it is launched using Python related launchers with the -managed suffix (available only in Oracle GraalVM).

+ +

Limitations of the Emulated Backend

+ +

GraalPy can log information about known incompatibility of functions executed at runtime, which includes the OS interface-related functions. +To turn on this logging, use --log.python.compatibility.level=FINE or other desired logging level.

+ +

Known limitations of the emulated layer are:

+ +
    +
  • Its state is disconnected from the actual OS state, which applies especially to: +
      +
    • file descriptors: Python-level file descriptors are not usable in native code
    • +
    • current working directory: is initialized to the current working +directory at the startup, but then it is maintained separately. So, for example, chdir in Python +does not change the actual current working directory of the process.
    • +
    • umask: similarly to current working directory, but it is always initialized +to 0022 regardless of the actual system value at startup.
    • +
    +
  • +
  • Resolution of file access/modification times depends on the JDK. +The best the emulated backend can guarantee is seconds resolution.
  • +
  • os.access and any other functionality based on faccessat POSIX function does not support: +
      +
    • effective IDs
    • +
    • follow_symlinks=False unless the mode is only F_OK
    • +
    +
  • +
+ +

Relation to Python Native Extensions

+ +

Apart from operating system interfaces exposed as built-in Python level modules, Python native extensions run via the GraalVM LLVM runtime may also access OS interfaces at the C level. +How such accesses are handled depends on the GraalVM LLVM runtime configuration.

+ +

At this point, the only combination where OS handles, such as file descriptors, can be shared between Python and the C code of Python extensions is with native PosixModuleBackend and native mode of GraalVM LLVM runtime. +This combination is the default for the graalpy launcher.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Packages/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Packages/index.html new file mode 100644 index 0000000..8871cf6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Packages/index.html @@ -0,0 +1,181 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Installing Packages

+ +

Pip

+ +

The pip package installer is available and working when using a GraalPy virtual environment.

+ +

The GraalPy pip module ships some patches for packages that the project test internally, these will be applied automatically where necessary. +Support for as many extension modules as possible is a high priority for the project. +The project is actively adding support for the Python C API to make extensions such as NumPy, SciPy, Scikit-learn, Pandas, and TensorFlow work fully. +This means that some might already work, but the project is still working on compatibility, especially with native extensions.

+ +

Including packages in a Java application

+ +

When using Python from Java via the GraalVM embedder APIs, some preparation is required to make packages available to the runtime. +After you have created a venv virtual environment and installed your required packages, the virtual environment is made available to the Python embedded in Java by setting a context option. +A good idea is to include the entire virtual environment directory as a resource, and use Java’s resource API:

+ +
String venvExePath = getClass().
+        getClassLoader().
+        getResource(Paths.get("venv", "bin", "graalpy").toString()).
+        getPath();
+
+Context ctx = Context.newBuilder("python").
+        allowIO(true).
+        option("python.Executable", venvExePath).
+        build();
+
+ctx.eval("python", "import site");
+
+ +

The initial import site instruction loads the Python standard library module site, which sets up the library paths. +To do so, it uses the path of the Python executable that is currently running. +For a language such as Python, which is built around the filesystem, this makes sense, but in a Java embedding context, there is no Python executable running. +This is what the python.Executable option is for: it reports which executable would be running if we were running Python directly inside your virtual environment. +That is enough to make the machinery work and any packages inside the virtual environment available to the Python embedded in Java.

+ +

A simple virtual environment is already quite heavy, because it comes with the machinery to install more packages. +For a Java distribution, you can strip down the virtual environment. +Just run these inside the top-level virtual environment directory:

+ +
find . -type d -name "__pycache__" -exec rm -rf "{}" ";"
+rmdir include
+rm bin/*
+rmdir bin
+rm lib/python3.*/site-packages/easy_install.py
+rm -rf lib/python3.*/site-packages/pip*
+
+ +

Some packages may require the following, but most do not, so you can also remove these, but be aware that it may break a few packages:

+ +
rm -rf lib/python3.*/site-packages/setuptools*
+rm -rf lib/python3.*/site-packages/pkg_resources*
+
+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/ParserDetails/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/ParserDetails/index.html new file mode 100644 index 0000000..2488d3e --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/ParserDetails/index.html @@ -0,0 +1,220 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Python Code Parsing and pyc Files

+ +

This guide describes how Python files are parsed by GraalPy.

+ +

Creating and Managing pyc Files

+ +
+

GraalPy automatically creates a .pyc file when there is an invalid or absent .pyc file that matches the corresponding .py file.

+
+ +

When a Python source file (module) is imported during an execution for the first time, the appropriate .pyc file is created automatically. +If the same module is imported again, then the existing .pyc file is used. +That means that there are no .pyc files for source files that were not executed (imported) yet. +The creation of .pyc files is achieved entirely through the FileSystem API, so that embedders can manage file system access.

+ +

GraalPy never deletes a .pyc file.

+ +

Every subsequent execution of a script will reuse existing .pyc files, or will generate new ones. +A .pyc file is regenerated if the timestamp or hashcode of the original source file is changed. +The hashcode is generated based only on the Python source file by calling source.hashCode(), which is the JDK hash code over the array of source file bytes, calculated with java.util.Arrays.hashCode(byte[]).

+ +

The .pyc files are also regenerated if a magic number in the Python parser is changed. +The magic number is hard-coded in the Python source and can not be changed by the user (unless of course that user has access to the bytecode of Python).

+ +

The developers of GraalPy change the magic number when the bytecode format changes. +This is an implementation detail, so the magic number does not have to correspond to the version of GraalPy (as in CPython). +The magic number of pyc is a function of the actual Python runtime Java code that is running. Magic number changes will be communicated in the release notes so that embedders or system administrators can delete old .pyc files when upgrading.

+ +

Note that if you use .pyc files, you must allow write-access to GraalPy at least when switching versions or modifying the original source code file. +Otherwise, the regeneration of source code files will fail and every import will have the overhead of accessing each old .pyc file, parsing the code, serializing it, and trying (and failing) to write out a new .pyc file.

+ +

The directory structure created for .pyc files is as follows:

+
top_directory
+  __pycache__
+    sourceA.graalpy.pyc
+    sourceB.graalpy.pyc
+  sourceA.py
+  sourceB.py
+  sub_directory
+    __pycache__
+      sourceX.graalpy.pyc
+    sourceX.py
+
+ +

By default, the __pycache__ directory is created on the same directory level as a source code file and in this directory all .pyc files from the same directory are stored. +This directory may store .pyc files created with different versions of Python (including, for example, CPython), so the user may see files ending in .cpython3-6.pyc, for example.

+ +

.pyc files are largely managed automatically by GraalPy in a manner compatible to CPython. GraalPy provides options similar to CPython to specify the location of the .pyc files, and if they should be written at all, and both of these options can be changed by guest code.

+ +

The creation of .pyc files can be controlled in the same way as CPython +(c.f. https://docs.python.org/3/using/cmdline.html):

+ +
    +
  • The GraalPy launcher (graalpy) reads the PYTHONDONTWRITEBYTECODE +environment variable. If this is set to a non-empty string, Python will not +try to write .pyc files when importing modules.
  • +
  • The launcher command line option -B, if given, has the same effect as the +above.
  • +
  • A guest language code can change the attribute dont_write_bytecode of the +sys built-in module at runtime to change the behavior for subsequent +imports.
  • +
  • The launcher reads the PYTHONPYCACHEPREFIX environment variable. If set, +the __pycache__ directory will be created at the path specified by the +prefix, and a mirror of the directory structure of the source tree will be +created on-demand to store the .pyc files.
  • +
  • A guest language code can change the attribute pycache_prefix of the sys +module at runtime to change the location for subsequent imports.
  • +
+ +

Since the embedder cannot use environment variables or CPython options to +communicate these options to GraalPy, these options are made available as language options:

+ +
    +
  • python.DontWriteBytecodeFlag - equivalent to -B or PYTHONDONTWRITEBYTECODE
  • +
  • python.PyCachePrefix - equivalent to PYTHONPYCACHEPREFIX
  • +
+ +

Note that a Python context will not enable writing .pyc files by default. +The graalpy launcher enables it by default, but if this is desired in the embedding use case, care should be taken to ensure that the __pycache__ location is properly managed and the files in that location are secured against manipulation in the same way as the source code files (.py) from which they were derived.

+ +

Note also that to upgrade the application sources to Oracle GraalPy, old .pyc +files must be removed by the embedder as required.

+ +

Security Considerations

+ +

All file operations (obtaining the data, timestamps, and writing .pyc files) +are achieved through the FileSystem API. Embedders can modify all of these operations by means of custom (for example, read-only) FileSystem implementations. +The embedder can also effectively disable the creation of .pyc files by disabling I/O permissions for GraalPy.

+ +

If .pyc files are not readable, their location is not writable. +If the .pyc files’ serialization data or magic numbers are corrupted in any way, the deserialization fails and GraalPy parses the .py source code file again. +This comes with a minor performance hit only for the parsing of modules, which should not be significant for most applications (provided the application performs actual work in addition to loading Python code).

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Tooling/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Tooling/index.html new file mode 100644 index 0000000..e8426f0 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/Tooling/index.html @@ -0,0 +1,196 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Tooling Support for Python

+

GraalPy can run many standard Python tools as well as tools from the GraalVM ecosystem. +The graalpy --help:tools command will give you more information about GraalVM tools currently supported on Python.

+ +

Debugger

+

The built-in breakpoint() function uses pdb by default.

+ +

PDB

+

The standard python debugger pdb is supported on GraalPy. Refer to the official PDB documentation for usage.

+ +

Chrome Inspector

+

To enable GraalVM’s Chrome Inspector debugger, pass the --inspect option to the graalpy launcher. +The built-in breakpoint() function will work using the Chrome Inspector implementation.

+ +

Code Coverage

+ +

GraalPy comes with a coverage instrument that can be used with the --coverage option. +Use the graalpy --help:tools command to see usage details.

+ +

In order to work better with existing Python code, the standard library trace module is partially supported with this low-overhead coverage instrument. +So, for example, you trace a script as follows:

+ +
graalpy -m trace -m -c -s my_script.py
+
+ +

This works in the same way as CPython.

+ +

The programmatic API also works, with some limitations. +For example, it does not currently track calls, only line counts and called functions.

+ +

Profiling

+ +

The _lsprof built-in module is implemented using the GraalVM cpusampler tool. +Not all profiling features are currently supported, but basic profiling works. For example:

+ +
graalpy -m cProfile -s calls -m ginstall --help
+
+ +

The interactive exploration of a stats output file also works:

+ +
graalpy -m cProfile -o ginstall.profile -m ginstall --help
+graalpy -m pstats ginstall.profile
+ginstall.profile%
+callers
+[...]
+
+ +

The profile module works as well:

+ +
graalpy -m profile -s calls -m ginstall --help
+
+ +

or

+ +
>>> import profile
+>>> profile.run('l = []; l.append(1)')
+         5 function calls in 0.002 seconds
+
+   Ordered by: standard name
+
+   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
+        1    0.000    0.000    0.000    0.000 :0(_setprofile)
+        1    0.000    0.000    0.000    0.000 :0(append)
+        1    0.000    0.000    0.001    0.001 :0(exec)
+        1    0.000    0.000    0.000    0.000 <string>:1(<module>)
+        1    0.001    0.001    0.002    0.002 profile:0(l = []; l.append(1))
+        0    0.000             0.000          profile:0(profiler)
+
+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/index.html new file mode 100644 index 0000000..c10709a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/index.html @@ -0,0 +1,238 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

GraalVM Python Runtime

+ +

GraalPy provides a Python 3.10 compliant runtime. +A primary goal is to support PyTorch, SciPy, and their constituent libraries, as well as to work with other data science and machine learning libraries from the rich Python ecosystem.. +GraalPy can usually execute pure Python code faster than CPython, and nearly match CPython performance when C extensions are involved. +While many workloads run fine, any Python program that uses external packages could hit something unsupported. +At this point, the Python implementation is made available for experimentation and curious end-users. +See the FAQ for commonly asked questions about this implementation.

+ +

Installing GraalPy

+ +

Linux and macOS

+ +

The easiest way to install GraalPy on Linux and macOS platforms is to use pyenv, the Python version manager. +For example, to install version 22.3.0, for example, run the following commands:

+ +
pyenv install graalpy-22.3.0
+pyenv shell graalpy-22.3.0
+
+ +

Another option is to use Conda-Forge. +To get an environment with the latest version of GraalPy, use the following command:

+ +
conda create -c conda-forge -n graalpy graalpy
+
+ +

Alternatively, download a compressed GraalPy installation file appropriate for your platform. +For example, for Linux, download a file that matches the pattern graalpy-XX.Y.Z-linux-amd64.tar.gz. +Uncompress the file and update your PATH variable as necessary. +If you are using macOS Catalina or later, you may need to remove the quarantine attribute. +To do this, run the following command:

+ +
sudo xattr -r -d com.apple.quarantine /path/to/GRAALPY_HOME
+
+ +

To try GraalPy with a full GraalVM, including support for Java embedding and interoperability with other languages, you can use the bundled releases from www.graalvm.org.

+ +

Windows

+ +

There is currently no installer for Windows. +Instead, follow these instructions to build GraalPy from source.

+ +

Running Python

+ +

The best way of using GraalPy is from a venv virtual environment. +This generates wrapper scripts and makes the implementation usable from a shell as the standard Python interpreter. +To create a venv virtual environment with GraalPy, run the following command in your project directory:

+ +
graalpy -m venv <venv-dir>
+
+ +

To activate the environment in your shell session run:

+ +
source <venv-dir>/bin/activate
+
+ +

Several executables are available in the virtual environment, including python, python3, and graalpy.

+ +

You can run simple Python commands or programs with the graalpy launcher:

+ +
graalpy [options] [-c cmd | filename]
+
+ +

For example, start the Python interactive shell from the command line using the command graalpy, then enter the following line at the Python shell prompt (identified by >>>), followed by CR.

+ +
>>> print("Hello World!")
+
+ +

You should see the output displayed directly, followed by the Python interactive shell prompt

+ +
Hello World!
+>>>
+
+ +

Alternatively, you can invoke a Python script. +Copy the following content into a file named helloworld.py:

+ +
print("Hello World!")
+
+ +

Start graalpy and pass the filename as an argument:

+ +
graalpy helloworld.py
+
+ +

You should see the following output

+ +
Hello World!
+
+ +

Python Options

+ +

GraalPy supports some of the same options as Python 3.10 as well as some additional options to control the underlying Python runtime, GraalVM’s tools, and the execution engine. +These can be viewed using the following command:

+ +
graalpy --help --help:tools --help:languages
+
+ +

Native Image and JVM Runtime

+ +

By default, GraalVM runs GraalPy from a binary, compiled ahead-of-time with Native Image, yielding faster startup time and lower footprint. +Although the ahead-of-time compiled binary includes the Python and LLVM interpreters, to interoperate with +other languages you must supply the --jvm option. +This instructs the launcher to run on the JVM instead of in Native Image mode. +Thus, you will notice a longer startup time.

+ + + + +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/native-image/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/native-image/index.html new file mode 100644 index 0000000..bec8a14 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/native-image/index.html @@ -0,0 +1,183 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Native Executables with Python

+ +

Python is a large language. +“Batteries included” has long been a core tenet of CPython. +As a compatible replacement, GraalPy includes most of those “batteries” as well. +GraalPy binaries are much bigger than all CPython binaries combined, however. +The data-structures differ, there is extra metadata, and in general, more code is required to support the just-in-time (JIT) compiler for Python code. +GraalPy also includes many standard Python modules in the main binary that are external C modules for CPython. +All this means that the binary sizes for a GraalPy distribution are about 10 times larger than the CPython executable.

+ +

In embedding scenarios, the defaults for GraalPy builds may include much more than needed for any specific application. +Java embeddings usually have more limited use cases for the Python interpreter than the full GraalPy distribution, and often can know ahead of time whether certain features are needed or may even be undesired. +Thus, when embedding GraalPy in a Java application, the binary size can be reduced to some extent.

+ +

First and foremost, GraalPy accepts system properties on the native-image command line that exclude parts of the core runtime from the build. +The options currently provided can, when taken together, reduce the size of the executable by around 20%. +These are:

+ +
    +
  • python.WithoutSSL=true - This option removes the ssl module from the build. +If no secure network access or certificate checking is required, this removes Java’s SSL classes and the BouncyCastle library.
  • +
  • python.WithoutDigest=true - This option removes the _md5, _sha1, _sha256, _sha512, _sha3, and _hashlib modules from the build. +This removes the direct usages of java.security.MessageDigest and javax.crypto.Mac from GraalPy.
  • +
  • python.WithoutPlatformAccess=true - This removes the signal and subprocess modules, removes accesses to process properties such as the Unix UID and GID, or setting the Java default time zone. +This has no significant impact on binary size, but if these are unwanted capabilities that are dynamically disabled with context options anyway, they can also be removed ahead of time.
  • +
  • python.WithoutCompressionLibraries=true - This removes the zlib, lzma, bzip2, and zipimporter modules and related classes. +These modules have both native and pure Java implementations (the former for performance, the latter for better sandboxing); however, if they are not needed, they can be removed entirely.
  • +
  • python.WithoutNativePosix=true - The default os module backend is a pure Java implementation when GraalPy is embedded rather than run via its launcher. +The native POSIX backend of GraalPy is recommended only for 100% compatibility with CPython’s POSIX interfaces, and if not used, can be removed from the build with this option.
  • +
  • python.WithoutJavaInet=true - The Java implementation of Python’s socket module is based on Java’s networking classes. +If network access is denied for an embedding scenario anyway, this option can reduce the binary size further.
  • +
  • python.AutomaticAsyncActions=false - Signal handling, Python weak reference callbacks, and cleaning up native resources is usually achieved automatically by spawning GraalPy daemon threads that submit safepoint actions to the Python main thread. +This uses an ExecutorService with a thread pool. +If embedders want to disallow such extra threads or avoid pulling in ExecutorService and related classes, then set this property to false and retrieve the PollPythonAsyncActions object from the context’s polyglot bindings. +This object is executable and can be used to trigger Python asynchronous actions at locations the embedder desires.
  • +
+ +

Another useful option to reduce the size of the native executable is to omit a pre-initialized Python context from the executable. +By default, a default Python context is already pre-initialized and ready for immediate execution. +In embeddings that use a custom polyglot engine to allow context sharing, the pre-initialized context cannot be used, however. +It can be omitted by explicitly passing

+ +
-Dimage-build-time.PreinitializeContexts=
+
+ +

If binary size is significantly more important than execution speed (which may be the case if all Python scripts are expected to be very short running and scripts are rarely executed more than once), it may make sense to disable JIT compilation entirely. +Be aware that this will significantly impact your Python performance, so be sure to test the runtime behavior of your actual use cases when choosing to use this option. +This can be achieved by passing the following options:

+ +
-Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime \
+-Dpolyglot.engine.WarnInterpreterOnly=false
+
+ +

Using all of these combined can halve the size of the GraalPy binary. +Every embedding is different and the code pulled in by the rest of the Java code also matters, so combinations of these options should be tried to determine which effect they have in a specific instance.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/standalone-binaries/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/standalone-binaries/index.html new file mode 100644 index 0000000..9ab1ed3 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/python/standalone-binaries/index.html @@ -0,0 +1,183 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Standalone Applications with Python

+ +

With GraalPy, you can distribute Python applications or libraries as standalone binaries or JAR files without any external dependencies. +The Truffle framework on which GraalPy is built, and the Sulong LLVM runtime that GraalPy leverages for managed execution of Python’s native extensions enables users to completely virtualize all filesystem accesses of Python programs, including those to the standard library and installed packages.

+ +

GraalPy comes with a module that can create standalone binaries or Java project skeletons. +The binaries bundle everything into one native executable. +The Java skeletons are set up with Maven to build and run self-contained JAR files. +They can also be used to generate a standalone binary from those JAR files later, so Java skeletons offer more flexibility and control over the steps.

+ +

Prerequisite

+ +

Set JAVA_HOME to use a GraalVM distribution.

+ +

Creating GraalPy Binaries

+ +

Suppose there is a simple Python script, my_script.py, that does some useful work when run directly. +To distribute it as a standalone native binary, run the following command:

+ +
graalpy -m standalone binary \
+      --module my_script.py \
+      --output my_binary
+
+ +

It generates a standalone my_binary file which includes the Python code, the GraalPy runtime, and the Python standard library in a single, self-contained executable. +Use graalpy -m standalone binary --help for further options.

+ +

Embedding GraalPy in a Java Application

+ +

You can distribute the Python script as a JAR file that runs on GraalVM and includes GraalPy. +To achieve this, run the java subcommand of GraalPy’s standalone module:

+ +
graalpy -m standalone java \
+      --module my_script.py \
+      --output-directory MyJavaApplication
+
+ +

It creates a Java project MyJavaApplication. It includes a pom.xml file that makes it easy to generate a JAR file or a GraalVM native executable with Maven. +You can open this Maven project with any Java IDE and edit the main class that was created to modify the Python embedding. +To build the application, either use ./mvnw -Pjar package to create a JAR file, or ./mvnw -Pnative package to create a GraalVM native executable.

+ +

Take a look at the generated pom.xml file. +There are some options to tweak the performance and footprint trade-off. +Review the Python Native Images documentation to find out how to remove other unwanted components and further reduce the binary size.

+ +

The generated project should be viewed as a starting point. +It includes the entire Python standard library, so the Python code can invoke all of the standard library code. +The resources can be manually pruned to reduce the included Python libraries to the necessary amount, reducing both the size of the package and the time to start up. +This Java example demonstrates some useful default options for the Python context, but other settings may be desirable to further control what the Python code is allowed to do.

+ +

Security Considerations

+ +

Creating a native executable or a JAR file that includes the Python code could be seen as a mild form of obfuscation, but it does not protect your source code. +While the Python sources are not stored verbatim into the executable (only the GraalPy bytecode is stored), that bytecode is easy to convert back into Python sources. +If stronger protection for the included Python source code is required, consider, for example, encryption of the resources before building the native executable, and adding appropriate decryption into the generated virtual file system.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/Compatibility/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/Compatibility/index.html new file mode 100644 index 0000000..df8be90 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/Compatibility/index.html @@ -0,0 +1,160 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

GraalVM R Runtime Compatibility

+ +

The GraalVM R runtime is based on GNU R and reuses the base packages. +It is currently based on GNU R 4.0.3, and moves to new major versions of R as they become available and stable. +GraalVM’s R runtime developers maintain an extensive set of unit tests for all aspects of the R language and the builtin functionality, and these tests are available as part of the R source code.

+ +

GraalVM’s R runtime aims to be fully compatible with GNU R, including its native interface as used by R extensions. +It can install and run unmodified, complex R packages like ggplot2, Shiny, or Rcpp. +As some packages rely on unspecified behaviour or implementation details of GNU R, support for packages is a work in progress, and some packages might not install successfully or work as expected.

+

Installing Packages

+ +

Packages can be installed using the install.packages function or the R CMD INSTALL shell command. +By default, GraalVM’s R runtime uses a fixed snapshot of the CRAN repository1. +This behavior can be overridden by explicitly setting the repos argument of the install.packages function. +This functionality does not interfere with the checkpoint package. If you are behind a proxy server, make sure to configure the proxy either with environment variables or using the JVM options, e.g., --vm.Djava.net.useSystemProxies=true.

+ +

The versions of some packages specifically patched for GraalVM’s R runtime can be installed using the install.fastr.packages function that downloads them from the GitHub repository. +Currently, those are rJava and data.table.

+ +

Limitations

+

There are some limitations of the GraalVM R runtime compared to GNU R:

+
    +
  • Only small parts of the low-level graphics package are functional. However, the grid package is supported and R can install and run packages based on it, like ggplot2. Support for the graphics package in R is planned for future releases.
  • +
  • Encoding of character vectors: related builtins (e.g., Encoding) are available, + but do not execute any useful code. Character vectors are represented as Java Strings and therefore encoded in the UTF-16 format. GraalVM’s R runtime will add support for encoding in future releases.
  • +
  • Some parts of the native API (e.g., DATAPTR) expose implementation details that are hard to emulate for alternative implementations of R. These are implemented as needed while testing the GraalVM R runtime with various CRAN packages.
  • +
+ +


+
+
+1 More technically, GraalVM’s R runtime uses a fixed MRAN URL from $R_HOME/etc/DEFAULT_CRAN_MIRROR, which is a snapshot of the CRAN repository as it was visible at a given date from the URL string.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/ExtensionsSupport/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/ExtensionsSupport/index.html new file mode 100644 index 0000000..1608aeb --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/ExtensionsSupport/index.html @@ -0,0 +1,185 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

R Extensions Support

+ +

The GraalVM R runtime can run R extensions in two modes:

+ +
    +
  • native: the native machine code is run directly on your CPU, the same way GNU R runs R extensions.
  • +
  • llvm: if the LLVM bitcode is available, it can be interpreted by the LLVM interpreter shipped with GraalVM.
  • +
+ +

The native mode is better suited for code that does not extensively interact with the R API, for example, plain C or Fortran numerical computations working on primitive arrays. +The llvm mode provides significantly better performance for extensions that frequently call between R and the C/C++ code, because GraalVM’s LLVM runtime is also partially evaluated like the R code. +Both can be inlined and optimized as one compilation unit. +Moreover, GraalVM’s LLVM runtime is supported by GraalVM tools which allows users to, for instance, debug R and C code together.

+ +

In one GraalVM R process, any R package can be loaded in either mode. +That is, GraalVM’s R runtime supports mixing packages loaded in the native mode with packages loaded in the llvm mode in one process.

+ +

Generating LLVM Bitcode

+ +

As of version 19.3.0, the GraalVM R runtime is configured to use the LLVM toolchain to compile R packages’ native code. +This toolchain produces standard executable binaries for a given system, but it also embeds the corresponding LLVM bitcode into them. +The binaries produced by the LLVM toolchain can be loaded in both modes: native or llvm.

+ +

The GraalVM R runtime can be reconfigured to use your system default compilers when installing R packages by running:

+
# use local installation of GGC:
+R -e 'fastr.setToolchain("native")'
+# to revert back to using the GraalVM's LLVM toolchain:
+R -e 'fastr.setToolchain("llvm")'
+
+ +

Using the system default compilers may be more reliable, but you lose the ability to load the R packages built with the LLVM toolchain in the llvm mode, because they will not contain the embedded bitcode. +Moreover, mixing packages built by the local system default compilers and packages built by the LLVM toolchain in one R process may cause linking issues.

+ +

Fortran Compiler

+ +

As of version 20.1.0, the GraalVM R runtime uses gfortran as the default Fortran compiler when installing R packages. +Since gfortran cannot produce bitcode, packages that contain Fortran code will not work in the llvm mode.

+ +

The GraalVM R runtime contains the F2C tool, which can convert Fortran code to C and then compile it with the LLVM toolchain. +Users can configure GraalVM’s R runtime to use this tool by editing the configuration file R_HOME/etc/Makeconf, variable FC.

+ +

Choosing the Running Mode

+ +

Starting with version 19.3.0, GraalVM’s R runtime uses the following defaults:

+
    +
  • native mode to load the packages
  • +
  • llvm toolchain to build their sources
  • +
+ +

To enable the llvm mode for loading the packages, use --R.BackEnd=llvm. +You can also enable each mode selectively for the given R packages by using:

+
    +
  • --R.BackEndLLVM=package1,package2
  • +
  • --R.BackEndNative=package1,package2
  • +
+ +

Moreover, you can configure which packages will be always run in the native mode in file R_HOME/etc/native-packages. GraalVM’s R runtime comes with a default configuration that covers some popular R packages that are known to not work yet in the llvm mode.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/Interoperability/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/Interoperability/index.html new file mode 100644 index 0000000..5dbbb4f --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/Interoperability/index.html @@ -0,0 +1,263 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Interoperability

+ +

GraalVM supports several other programming languages including JavaScript, Ruby, Python, and those that compile to LLVM bitcode. +GraalVM’s R runtime provides an API for programming language interoperability that lets you execute code from any other GraalVM-supported language. +Note that you must start the R script with --polyglot to have access to other languages.

+ +

GraalVM’s R runtime allows the following interoperability primitives:

+
    +
  • eval.polyglot('languageId', 'code') evaluates code in some other language. The languageId can be, e.g., js.
  • +
  • eval.polyglot(path = '/path/to/file.extension') evaluates code loaded from a file. The language is recognized from the extension.
  • +
  • export('polyglot-value-name', rObject) exports an R object so that it can be imported by other languages.
  • +
  • import('exported-polyglot-value-name') imports a polyglot value exported by some other language.
  • +
+ +

Use the ?functionName syntax to learn more. The following example demonstrates the interoperability features:

+
# get an array from Ruby
+x <- eval.polyglot('ruby', '[1,2,3]')
+print(x[[1]])
+# [1] 1
+
+# get a JavaScript object
+x <- eval.polyglot(path='r_example.js')
+print(x$a)
+# [1] "value"
+
+# use R vector in JavaScript
+export('robj', c(1,2,3))
+eval.polyglot('js', paste0(
+    'rvalue = Polyglot.import("robj"); ',
+    'console.log("JavaScript: " + rvalue.length);'))
+# JavaScript: 3
+# NULL -- the return value of eval.polyglot
+
+

(Uses r_example.js.)

+ +

R vectors are presented as arrays to other languages. +This includes single element vectors, e.g., 42L or NA. +However, single element vectors that do not contain NA can be typically used in places where the other languages expect a scalar value. +An array subscript or similar operation can be used in other languages to access individual elements of an R vector. +If the element of the vector is not NA, the actual value is returned as a scalar value, e.g., int. +If the element is NA, then a special object that looks like null is returned. +The following Ruby code demonstrates this:

+ +
vec = Polyglot.eval("R", "c(NA, 42)")
+p vec[0].nil?
+# true
+p vec[1]
+# 42
+
+vec = Polyglot.eval("R", "42")
+p vec.to_s
+# "[42]"
+p vec[0]
+# 42
+
+ +

The foreign objects passed to R are implicitly treated as specific R types. + +The following table gives some examples:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Example of foreign object (Java)Viewed ‘as if’ on the R side
int[] {1,2,3}c(1L,2L,3L)
int[][] { {1, 2, 3}, {1, 2, 3} }matrix(c(1:3,1:3),nrow=3)
int[][] { {1, 2, 3}, {1, 3} }not supported: raises error
Object[] {1, ‘a’, ‘1’}list(1L, ‘a’, ‘1’)
4242L
+ +

In the following example, we can simply pass the Ruby array to the R built-in function sum, which will work with the Ruby array as if it was an integer vector.

+
sum(eval.polyglot('ruby', '[1,2,3]'))
+
+ +

Foreign objects can be also explicitly wrapped into adapters that make them look like the desired R type. +In such a case, no data copying occurs, if possible. +The code snippet below shows the most common use cases:

+
# gives list instead of an integer vector
+as.list(eval.polyglot('ruby', '[1,2,3]'))
+
+# assume the following Java code:
+# public class ClassWithArrays {
+#   public boolean[] b = {true, false, true};
+#   public int[] i = {1, 2, 3};
+# }
+
+x <- new('ClassWithArrays'); # see Java interop below
+as.list(x)
+
+# gives: list(c(T,F,T), c(1L,2L,3L))
+
+ +

For more details, refer to the executable specification of the implicit and explicit foreign objects conversions.

+ +

Note: R contexts started from other languages, or Java (as opposed to via the R script), will default to non interactive mode, similar to Rscript. +This has implications on console output (the results are not echoed) and graphics (the output defaults to a file instead of a window), and some packages may behave differently in non-interactive mode.

+ +

Bellow is a list of available R interoperability builtin functions. +For more information see the R --help message and try these examples:

+
> help(java.type)
+> ?java.type
+> example(java.type)
+
+ +
    +
  • java.type
  • +
  • java.addToClasspath
  • +
  • is.polyglot.value
  • +
  • eval.polyglot
  • +
  • export
  • +
  • import
  • +
+ +

See the Polyglot Programming reference for more information about interoperability with other programming languages.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/JavaInteroperability/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/JavaInteroperability/index.html new file mode 100644 index 0000000..e49fc90 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/JavaInteroperability/index.html @@ -0,0 +1,363 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Interoperability with Java

+ +

The GraalVM R runtime provides the built-in interoperability with Java. +Java class objects can be obtained via java.type(...). +In order to run R with the Java interoperability features, the R or Rscript commands have to be started with the --jvm option.

+
R --jvm
+
+ +

Note: All of the following examples are meant to be executed in the R REPL; no additional Java dependencies are necessary.

+ +
    +
  • The standard new function interprets String arguments as a Java class if such class exists.
  • +
  • new also accepts Java types returned from java.type.
  • +
  • The fields and methods of Java objects can be accessed using the $ operator.
  • +
  • Additionally, you can use awt(...) to open an R drawing device +directly on a Java Graphics surface. For more details see Java Graphics Interoperability.
  • +
+ +

The following example creates a new Java BufferedImage object, plots random data to it using R’s grid package, +and shows an image in a window using Java’s AWT framework. +Note that you must start the R script with --jvm to have access to Java interoperability.

+ +
library(grid)
+openJavaWindow <- function () {
+   # create image and register graphics
+   imageClass <- java.type('java.awt.image.BufferedImage')
+   image <- new(imageClass, 450, 450, imageClass$TYPE_INT_RGB);
+   graphics <- image$getGraphics()
+   graphics$setBackground(java.type('java.awt.Color')$white);
+   grDevices:::awt(image$getWidth(), image$getHeight(), graphics)
+
+   # draw image
+   grid.newpage()
+   pushViewport(plotViewport(margins = c(5.1, 4.1, 4.1, 2.1)))
+   grid.xaxis(); grid.yaxis()
+   grid.points(x = runif(10, 0, 1), y = runif(10, 0, 1),
+        size = unit(0.01, "npc"))
+
+   # open frame with image
+   imageIcon <- new("javax.swing.ImageIcon", image)
+   label <- new("javax.swing.JLabel", imageIcon)
+   panel <- new("javax.swing.JPanel")
+   panel$add(label)
+   frame <- new("javax.swing.JFrame")
+   frame$setMinimumSize(new("java.awt.Dimension",
+                image$getWidth(), image$getHeight()))
+   frame$add(panel)
+   frame$setVisible(T)
+   while (frame$isVisible()) Sys.sleep(1)
+}
+openJavaWindow()
+
+ +

GraalVM’s R runtime provides its own rJava-compatible replacement package available at GitHub, which can be installed using:

+
R -e "install.fastr.packages('rJava')"
+
+ +

In order for third party Java libraries to be accessed, they have to be placed on R’s class path:

+
> java.addToClasspath("/foo/bar.jar")
+> java.addToClasspath(c("/foo/bar.jar", "/foo/bar2.jar"))
+
+ +

Getting a Java Class

+ +

The access to a Java type is achieved by providing a fully qualified class name to the java.type function:

+
> calendarClass <- java.type('java.util.GregorianCalendar')
+
+

The returned value is a polyglot object representing a Java type.

+ +

The respective Java class is then available through the class property:

+
> calendarClass$class
+
+ +

The same works also for static class members:

+
> calendarClass$getInstance()
+
+ +

Every requested class has to be on the R classpath. +The JDK classes, like GregorianCalendar used above, work out of the box.

+ +

Creating a New Java Object

+ +

A new Java object can be created by providing a Java type to the new function:

+
> calendar <- new(calendarClass)
+
+ +

It is also possible to pass over additional constructor arguments:

+
> calendar <- new(calendarClass, year=2042L, moth=3L, day=1L)
+
+ +

Alternately, you can use just a class name:

+
calendar <- new("java.util.GregorianCalendar")
+calendar <- new("java.util.GregorianCalendar", year=2042L, moth=3L, day=1L)
+
+ +

Accessing Fields and Methods

+ +

The access to static and instance fields and methods is provided by the $ and [ operators.

+ +

To access Java fields:

+
> calendarClass$SUNDAY
+> calendarClass["SUNDAY"]
+
+ +

To invoke Java methods:

+
> currentTime <- calendar$getTime()
+> currentTime["toString"]()
+> calendar$setTime(currentTime)
+
+ +

Polyglot objects returned from a field or method, or created via new, are either automatically converted into corresponding R values or they live on as polyglot objects in the GraalVM R runtime. +If necessary, they can be passed over to Java:

+
> cet <- java.type("java.util.TimeZone")$getTimeZone("CET")
+> cetCalendar <- new(calendarClass, cet)
+
+ +

Handling of Java Primitives

+ +

The returned Java primitives, primitive wrappers, and String instances are automatically converted into corresponding R values and map as follows:

+ +
    +
  • R integer values map directly to Java int/Integer
  • +
  • R numeric to Java double/Double
  • +
  • R logical to Java boolean/Boolean
  • +
  • R character to Java String
  • +
  • If necessary R integer and double are converted to the expected Java type
  • +
+ +

Inspecting Polyglot Objects

+ +

The names function can be used to obtain a list of instance and static members from a polyglot Java object or Java class:

+
> names(calendar)
+> names(calendarClass)
+
+ +

Code completion works as well:

+
> calendar$a<TAB>
+
+ +

Working with Java Arrays

+ +

The need for Java arrays appears when they have to be passed over to java as arguments.

+ +

You can create an array by creating an array class and instantiating an array from it:

+
> arrayClass <- java.type('int[]')
+> intArray <- new(arrayClass, 3)
+
+ +

The component type names of primitive arrays are boolean, byte, char, double, float, int, long, +and short – the same as in each particular primitive wrapper TYPE constant (see, e.g., Integer.TYPE.getName()). +Note that it is possible to pass an R vector into a Java method in case the expected Java array is of a primitive component type or String. +Then, the conversion happens automatically in the background.

+
> integerArray <- new(java.type('java.lang.Integer[]'), 3L)
+> integer2DimArray <- new('java.lang.Integer[][]', c(2L, 3L))
+> stringArray <- new(java.type('java.lang.String[]'), 3L)
+
+ +

The access to array elements is provided by the [ operator:

+
> stringArray[1] <- 'a'
+> string2DimArray[1,1] <- 'a'
+> element <- stringArray[1]
+> element <- string2DimArray[1,1]
+
+ +

Converting Java Arrays into R Objects

+ +

Unlike Java primitives or their wrappers, Java arrays are not automatically converted into an R vector. +Nevertheless, when appropriate, they can be handled by R builtin functions the same way as native R objects:

+
> sapply(intArray, function(e) { e })
+> length(stringArray)
+> length(string2DimArray[1])
+
+ +

Explicit Java Array Conversion

+ +

A Java array conversion can be done explicitly by providing a Java array to the as.vector function:

+
> intVec <- as.vector(intArray)
+
+ +

Arrays having a Java primitive component type are converted into an R vector. +Otherwise a list containing the array elements is created:

+
> characterVector <- as.character(intArray)
+> logicalVector <- as.logical(intArray)
+> ...
+
+ +

Java Iterable Interface

+ +

When appropriate, Java objects implementing java.lang.Iterable are handled in the same way as Java arrays when passed as arguments to functions:

+
> javaList <- new(java.type('java.util.ArrayList'))
+> javaList$add(0);
+> javaList$add(1)
+> length(javaList)
+> as.integer(javaList)
+> as.logical(javaList)
+
+ +

Compatibility with rJava

+ +

The GraalVM R runtime comes with an rJava compatibility layer based on the Java interoperability features. +Most of the officially documented rJava functions are supported. +For more information, see the rJava CRAN documentation.

+ +
    +
  • You can install the GraalVM R runtime’s rJava replacement using install.packages("rJava"). +The install.packages function in R has special handling for some packages, including rJava, and it downloads rJava from the source repository on GitHub instead of from MRAN.
  • +
  • As with any other R package, before executing any rJava functions, the package has to be loaded first: +
    > library(rJava)
    +
    +
  • +
+ +

Supported rJava features:

+
    +
  • The $ and [ operators work the same as described above.
  • +
+ +

Java Graphics Interoperability

+

GraalVM R runtime’s graphics subsystem is mostly compatible with GNU-R’s graphics subsystem, i.e., most of the functions provided by grid and graphics base packages are supported. +See Reference of GNU-R’s graphical subsystem.

+ +

Aside from all the GNU-R graphics functionality, GraalVM R runtime provides an additional awt device and functions tailored to manipulate the SVG device: svg.off and sv.string. +The awt device is based on the Java Graphics2D object and users can pass it to their own Graphics2D object instance when opening the device using the awt function, as shown in the Java interop example. +When the Graphics2D object is not provided to awt, it opens a new window similar to X11.

+ +

The SVG device is demonstrated in the following code sample:

+ +
library(lattice)
+svg()
+mtcars$cars <- rownames(mtcars)
+print(barchart(cars~mpg, data=mtcars))
+svgCode <- svg.off()
+cat(svgCode)
+
+

To learn more, see the ?functionName syntax.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/ParallelExecution/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/ParallelExecution/index.html new file mode 100644 index 0000000..1551fb4 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/ParallelExecution/index.html @@ -0,0 +1,135 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Parallel Execution

+ +

The FORK cluster and functions depending solely on forking (e.g., mcparallel) are not supported by the GraalVM R runtime at the moment. +However, users can use the PSOCK cluster, which should work in the same way on the GraalVM R runtime as on GNU R.

+ +

Moreover, R can be used as a worker node in a PSOCK cluster computation driven from GNU R. +See FastRCluster package for GNU R, which provides helper functions to create PSOCK cluster nodes that run the GraalVM R runtime.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/Performance/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/Performance/index.html new file mode 100644 index 0000000..d5d4ca7 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/Performance/index.html @@ -0,0 +1,188 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Runtime Performance

+ +

GraalVM optimizes R code that runs for extended periods of time. +The speculative optimizations based on the runtime behaviour of the R code and dynamic compilation employed by the GraalVM runtime are capable of removing most of the abstraction penalties incurred by the dynamism and complexity of the R language.

+ +

Examine the algorithm in the following example which calculates the mutual information of a large matrix:

+
x <- matrix(runif(1000000), 1000, 1000)
+mutual_R <- function(joint_dist) {
+ joint_dist <- joint_dist/sum(joint_dist)
+ mutual_information <- 0
+ num_rows <- nrow(joint_dist)
+ num_cols <- ncol(joint_dist)
+ colsums <- colSums(joint_dist)
+ rowsums <- rowSums(joint_dist)
+ for(i in seq_along(1:num_rows)){
+  for(j in seq_along(1:num_cols)){
+   temp <- log((joint_dist[i,j]/(colsums[j]*rowsums[i])))
+   if(!is.finite(temp)){
+    temp = 0
+   }
+   mutual_information <-
+    mutual_information + joint_dist[i,j] * temp
+  }
+ }
+ mutual_information
+}
+system.time(mutual_R(x))
+#   user  system elapsed
+#  1.321   0.010   1.279
+
+ +

Algorithms such as this one usually require C/C++ code to run efficiently:1

+
if (!require('RcppArmadillo')) {
+    install.packages('RcppArmadillo')
+    library(RcppArmadillo)
+}
+library(Rcpp)
+sourceCpp("r_mutual.cpp")
+x <- matrix(runif(1000000), 1000, 1000)
+system.time(mutual_cpp(x))
+#   user  system elapsed
+#  0.037   0.003   0.040
+
+

(Uses r_mutual.cpp.)

+ +

However, after a few iterations, GraalVM runs the R code efficiently enough to make the performance advantage of C/C++ negligible:

+
system.time(mutual_R(x))
+#   user  system elapsed
+#  0.063   0.001   0.077
+
+ +

The GraalVM R runtime is primarily aimed at long-running applications. +Therefore, the peak performance is usually only achieved after a warmup period. +While startup time is currently slower than GNU R’s, due to the overhead from Java class loading and compilation, future releases will contain a native image of R with improved startup.

+ +


+
+
+1 When this example is run for the first time, it installs the RcppArmadillo package,which may take a few minutes. +Note that this example can be run in both GraalVM’s R runtime and GNU R.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/index.html new file mode 100644 index 0000000..59a7feb --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/r/index.html @@ -0,0 +1,8 @@ +

GraalVM R Runtime

+ +

GraalVM R runtime, also known as FastR, is a GNU-compatible implementation of the R programming language. +FastR is no longer under active development and is in maintenance mode, however we always welcome contributions from the community!

+ +

The last released version is 22.3.1, but FastR continues to be available in a source form. +See how to build FastR from source. +Find FastR 22.3.1 user documentation here.

diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Benchmarking/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Benchmarking/index.html new file mode 100644 index 0000000..3a69c38 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Benchmarking/index.html @@ -0,0 +1,224 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Benchmarking TruffleRuby

+ +

This document lists the most important points to consider when benchmarking TruffleRuby.

+ +

Guidelines for Benchmarking TruffleRuby

+ +

We expect anyone publishing benchmark numbers about TruffleRuby to follow these guidelines.

+ +

Use TruffleRuby EE

+ +

Use TruffleRuby EE, it is faster than CE overall and represents what TruffleRuby is capable of.

+ +

Use the Latest Release

+ +

Always use the latest release at the time of benchmarking (so it does not misrepresent TruffleRuby by using an old release which may have known performance issues).

+ +

Use the Correct Runtime Configuration

+ +

TruffleRuby has two Runtime Configurations, Native and JVM, see this comparison.

+ +

If you want to benchmark peak performance, you should use the JVM configuration. +To do so, set the environment variable TRUFFLERUBYOPT=--jvm so it affects all TruffleRuby processes. +You can also pass --jvm as an argument to TruffleRuby if you are sure there are no subprocesses.

+ +

The Native configuration provides better startup and warmup but has slower peak performance.

+ +

Of course you can also benchmark both configurations and see which one is better for what you are benchmarking.

+ +

Run with Enough Warmup

+ +

TruffleRuby like other runtimes with a just-in-time compiler needs some time (called warmup) to reach peak performance, +because it takes time to just-in-time compile the relevant methods of the benchmark. +The easiest way to check if there was enough warmup is to run the benchmark workload repeatedly inside a process and print the times of each run. +The times should be very stable once it’s warmed up and conversely keep changing while it is not warmed up yet. +See this documentation for more details about warmup.

+ +

Consider Disabling the Global C-Extension Lock

+ +

On TruffleRuby, C extensions by default use a global lock for maximum compatibility with CRuby. +If you are benchmarking a multi-threaded Ruby program (e.g. Rails on a multi-threaded server), it is worth trying +TRUFFLERUBYOPT="--experimental-options --cexts-lock=false". +This issue tracks a way to automatically not use the lock for extensions which do not need it.

+ +

Recommendations

+ +

These are more general recommendations about benchmarking.

+ +

Avoid Benchmarking on a Laptop

+ +

Performance on laptops is very sensitive to heat, and so overall quite unstable. +As an example, if the CPU gets too warm the operating system will throttle it, making the benchmark results unfair and unstable. +So benchmarking should be done on on a desktop computer or server.

+ +

Avoid Other Running Processes

+ +

As those would cause extra noise in benchmarking results. +Definitely no browser, slack, IDE, etc as those use a lot of CPU.

+ +

Disable Frequency Scaling

+ +

CPU frequency scaling and boost generally just increases noise in benchmarking results, +so it is recommended to disable them when benchmarking for more stable results.

+ +

For Intel CPUs use:

+ +
sudo sh -c 'echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo'
+
+ +

For AMD CPUs use:

+ +
sudo sh -c 'echo 0 > /sys/devices/system/cpu/cpufreq/boost'
+
+ +

Also make sure the performance governor is used on Linux:

+ +
sudo cpupower frequency-set -g performance
+cpupower frequency-info
+
+ +

Do not pin TruffleRuby to a Single Core

+ +

TruffleRuby uses multiple threads for the JIT Compiler, the GC, etc. +Restricting it to a single core for benchmarking does not make sense, it would cause a lot of contention.

+ +

Avoid Benchmarking on macOS

+ +

macOS’s memory management is subpar and can cause unnecessary memory swapping even when there is enough memory (e.g. it sometimes keeps terminated processes in memory needlessly).

+ +

macOS’s TCP stack is also subpar, see the Passenger docs on this subject.

+ +

If you have no choice but to benchmark on macOS then mention that with the results +and ensure there is plenty free memory and no swap while benchmarking. +Use vm_stat (Pages free) or top (PhysMem -> unused) to check the amount of free memory. +Unfortunately neither of these tools show the swap usage, illustrating how difficult it is to ensure there is no swapping going on when benchmarking on macOS. +Use Activity Monitor before and after benchmarking to ensure there is no swap (Swap Used) as a workaround. +Activity Monitor is too heavy to be used during benchmarking.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Compatibility/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Compatibility/index.html new file mode 100644 index 0000000..1b949a4 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Compatibility/index.html @@ -0,0 +1,359 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Compatibility

+ +

TruffleRuby aims to be fully compatible with the standard implementation of +Ruby, MRI, version 3.1.3, including C extensions. +TruffleRuby is still in development, so it is not 100% compatible yet.

+ +

TruffleRuby can run Rails and is compatible with many gems, including C extensions. +TruffleRuby passes around 97% of ruby/spec, +more than any other alternative Ruby implementation.

+ +

Any incompatibility with MRI is considered a bug, except for rare cases detailed below. +If you find an incompatibility with MRI, please report it.

+ +

TruffleRuby tries to match the behavior of MRI as much as possible. +In a few limited cases, TruffleRuby is deliberately incompatible with MRI in order to provide a greater capability.

+ +

Identification

+ +

TruffleRuby defines these constants for identification:

+ +
    +
  • RUBY_ENGINE is 'truffleruby'.
  • +
  • RUBY_VERSION is the compatible MRI version.
  • +
  • RUBY_REVISION is the full git commit hash used to build TruffleRuby (similar to MRI 2.7+).
  • +
  • RUBY_RELEASE_DATE is the git commit date.
  • +
  • RUBY_PATCHLEVEL is always zero.
  • +
  • RUBY_ENGINE_VERSION is the GraalVM version, or 0.0- and the Git commit hash if your build is not part of a GraalVM release.
  • +
+ +

In the C API, the preprocessor macro TRUFFLERUBY is defined, which can be checked with #ifdef TRUFFLERUBY.

+ +

Features Entirely Missing

+ +

Continuations and callcc

+ +

Continuations are obsolete in MRI, and Fibers are recommended instead. +Continuations and callcc are unlikely to ever be implemented in TruffleRuby, as their semantics fundamentally do not match the JVM architecture.

+ +

Fork

+ +

You cannot fork the TruffleRuby interpreter. +The feature is unlikely to ever be supported when running on the JVM but could be supported in the future in the native configuration. +The correct and portable way to test if fork is available is:

+
Process.respond_to?(:fork)
+
+ +

Standard libraries

+ +

The following standard libraries are unsupported:

+ +
    +
  • continuation: obsolete in MRI
  • +
  • debug: it relies on RubyVM::InstructionSequence, use the VSCode extension or --inspect instead
  • +
  • io/console: partially implemented
  • +
  • io/wait: partially implemented
  • +
  • pty: could be implemented in the future
  • +
+ +

TruffleRuby provides its own backend implementation for the ffi gem, similar to JRuby. +This should be completely transparent and behave the same as on MRI. +The implementation should be fairly complete and passes all the specs of the ffi gem except for some rarely-used corner cases.

+ +

Internal MRI functionality

+ +

RubyVM is not intended for users and is not implemented.

+ +

Features with Major Differences

+ +

Threads run in parallel

+ +

In MRI, threads are scheduled concurrently but not in parallel. +In TruffleRuby threads are scheduled in parallel. +As in JRuby and Rubinius, you are responsible for correctly synchronising access to your own shared mutable data structures, and TruffleRuby will be responsible for correctly synchronising the state of the interpreter.

+ +

Fibers do not have the same performance characteristics as in MRI

+ +

Most use cases of fibers rely on them being easy and cheap to start up, and with low memory overheads. +In TruffleRuby, fibers are currently implemented using operating system threads, so they have the same performance characteristics as Ruby threads. +This will be addressed once the Loom project becomes stable and available in JVM releases.

+ +

Some classes marked as internal will be different

+ +

MRI provides some classes that are described in the documentation as being available only on MRI (CRuby). +These classes are implemented if it is practical to do so, but this is not always the case. For example, RubyVM is not available.

+ +

Regexp

+ +

Regexp instances are always immutable in TruffleRuby. +In CRuby 3.1, all literal Regexp are immutable, but non-literal are still mutable. +This limitation means that one cannot define singleton methods on a Regexp instance, and cannot create instances of subclasses of Regexp on TruffleRuby.

+ +

Features with Subtle Differences

+ +

Command line switches

+ +

The -y, --yydebug, --dump=, and --debug-frozen-string-literal switches are ignored with a warning as they are unsupported development tools.

+ +

Programs passed in -e arguments with magic-comments must have an encoding that is UTF-8 or a subset of UTF-8, as the JVM has already decoded arguments by the time we get them.

+ +

The --jit option and the jit feature have no effect on TruffleRuby and warn. The GraalVM compiler is always used when available.

+ +

Strings have a maximum bytesize of 231-1

+ +

Ruby Strings are represented as a Java byte[]. +The JVM enforces a maximum array size of 231-1 (by storing the size in a 32-bit signed int), and therefore Ruby Strings cannot be longer than 231-1 bytes. +That is, Strings must be smaller than 2GB. This is the same restriction as JRuby. +A possible workaround could be to use natively-allocated strings, but it would be a large effort to support every Ruby String operation on native strings.

+ +

Threads detect interrupts at different points

+ +

TruffleRuby threads may detect that they have been interrupted at different points in the program compared to where they would on MRI. +In general, TruffleRuby seems to detect an interrupt sooner than MRI. +JRuby and Rubinius are also different from MRI; the behavior is not documented in MRI, and it is likely to change between MRI versions, so it is not recommended to depend on interrupt points.

+ +

Polyglot standard I/O streams

+ +

If you use standard I/O streams provided by the Polyglot engine, via the experimental --polyglot-stdio option, reads and writes to file descriptors 0, 1, and 2 will be redirected to these streams. +That means that other I/O operations on these file descriptors, such as isatty, may not be relevant for where these streams actually end up, and operations like dup may lose the connection to the polyglot stream. +For example, if you $stdout.reopen, as some logging frameworks do, you will get the native standard-out, not the polyglot out.

+ +

Also, I/O buffer drains, writes on I/O objects with sync set, and write_nonblock will not retry the write on EAGAIN and EWOULDBLOCK, as the streams do not provide a way to detect this.

+ +

Error messages

+ +

Error message strings will sometimes differ from MRI, as these are not generally covered by the Ruby Spec Suite or tests.

+ +

Signals

+ +

The set of signals that TruffleRuby can handle is different from MRI. +When using the native configuration, TruffleRuby allows trapping all the same signals that MRI does, as well as a few that MRI does not. +The only signals that can’t be trapped are KILL, STOP, and VTALRM. +Consequently, any signal handling code that runs on MRI can run on TruffleRuby without modification in the native configuration.

+ +

However, when run on the JVM, TruffleRuby is unable to trap USR1 or QUIT, as these signals are reserved by the JVM. +In such a case trap(:USR1) {} will raise an ArgumentError. +Any code that relies on being able to trap those signals will need to fall back to another available signal. +Additionally, FPE, ILL, KILL, SEGV, STOP, and VTALRM cannot be trapped, but these signals are also unavailable on MRI.

+ +

When TruffleRuby is run as part of a polyglot application, any signals that are handled by another language become unavailable for TruffleRuby to trap.

+ +

GC statistics

+ +

TruffleRuby provides similar GC.stat statistics to MRI, but not all statistics are available, and some statistics may be approximations. Use GC.stat.keys to see which are provided with real or approximate values. Missing values will return 0.

+ +

Features with Very Low Performance

+ +

ObjectSpace

+ +

ObjectSpace#each_object is implemented but is fairly slow due to needing to iterate the whole heap and essentially doing the equivalent of a GC marking phase. +ObjectSpace#trace_object_allocations_start slows down all allocations, similar to the behavior on CRuby. +Using most methods on ObjectSpace will temporarily lower the performance of your program. +Using them in test cases and other similar ‘offline’ operations is fine, but you probably do not want to use them in the inner loop of your production application.

+ +

set_trace_func

+ +

Using set_trace_func will temporarily lower the performance of your program. +As with ObjectSpace, it is recommended that you do not use this in the inner loop of your production application.

+ +

Backtraces

+ +

Throwing exceptions and other operations which need to create a backtrace are in general slower than on MRI. +This is because TruffleRuby needs to undo optimizations that have been applied to run your Ruby code fast in order to recreate the backtrace entries. +It is not recommended to use exceptions for control flow on any implementation of Ruby anyway.

+ +

To help alleviate this problem, backtraces are automatically disabled in cases where we can detect that they will not be used.

+ +

C Extension Compatibility

+ +

Identifiers may be macros or functions

+ +

Identifiers which are normally macros may be functions, functions may be macros, and global variables may be macros. +This may cause problems where they are used in a context which relies on a particular implementation (e.g., taking the +address of it, assigning to a function pointer variable, and using defined() to check if a macro exists). +These issues should all be considered bugs and be fixed. +Please report these cases.

+ +

rb_scan_args

+ +

rb_scan_args only supports up to 10 pointers.

+ +

rb_funcall

+ +

rb_funcall only supports up to 15 arguments.

+ +

mark functions of RDATA and RTYPEDDATA

+ +

The mark functions of RDATA and RTYPEDDATA are not called during garbage collection, but called periodically. +The information about objects is cached as they are assigned to structs, and TruffleRuby periodically runs all mark functions when the cache has become full to represent those object relationships in a way that the garbage collector will +understand. +The process should behave identically to MRI.

+ +

Compatibility with JRuby

+ +

Ruby to Java interoperability

+ +

TruffleRuby does not support the same interoperability interface to Java as JRuby does. +TruffleRuby provides an alternate polyglot API for interoperating with multiple languages, including Java, instead.

+ +

Java to Ruby interop

+ +

Calling Ruby code from Java is supported by the +GraalVM Polyglot API.

+ +

Java extensions

+ +

Using Java extensions written for JRuby is not supported.

+ +

Features Not Yet Supported in Native Configuration

+ +

Running TruffleRuby in the native configuration is mostly the same as running +on the JVM. There are differences in resource management, as both VMs use +different garbage collectors, but functionality-wise, they are essentially on +par with one another.

+ +

Java Interoperability With the Native Configuration

+ +

Java interoperability works in the native configuration but requires more setup. +By default, only some array classes are available in the image for Java interoperability. +You can add more classes by compiling a native image including TruffleRuby. +See here for more details.

+ +

Spec Completeness

+ +

“How many specs are there?” is not a question with an easy, precise answer. The +number of specs varies for the different versions of the Ruby language, different +platforms, and different versions of the specs. +The specs for the standard library and C extension API are also +very uneven and can give misleading results.

+ +

This blog post +summarizes how many specs TruffleRuby passes.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Debugging/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Debugging/index.html new file mode 100644 index 0000000..4d84802 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Debugging/index.html @@ -0,0 +1,208 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Debugging TruffleRuby

+ +

TruffleRuby, like other GraalVM languages, supports 2 standard debugging protocols:

+ + +

Also see Tools for more tools besides just debuggers.

+ +

VSCode

+ +

First install the GraalVM VSCode extension.

+ +

Then follow this documentation to debug TruffleRuby with VSCode.

+ +

RubyMine

+ +

Unfortunately RubyMine / IntelliJ IDEA do not support the Debug Adapter Protocol yet for Ruby debugging.

+ +

Please vote or comment on the feature request to share your interest.

+ +

Command-line Debugging Options

+ +

Printing Exceptions

+ +

There are two ways to print exceptions, which can be useful to find the source of an error:

+ +
    +
  • the standard Ruby -d flag which prints the file:line where each exception was raised
  • +
  • --backtraces-raise which show the full backtrace on each exception raised
  • +
+ +

Both print all exceptions even if the exceptions are later rescued.

+ +

Java exceptions can be printed with --exceptions-print-uncaught-java or +--exceptions-print-java.

+ +

See other --backtraces-* and --exceptions-* options for more possibilities.

+ +

Printing Stacktraces and Backtraces of a Running Process

+ +

One can send the SIGQUIT signal to TruffleRuby to make it print the Java stacktraces of all threads. +Ctrl + \ can be used to send SIGQUIT to the current process in a terminal. +This is useful to debug hangs and deadlocks, or to know what the process is doing. +This works on both TruffleRuby Native and JVM.

+ +

Sending SIGALRM to a TruffleRuby process will print the Ruby backtraces of all threads.

+ +

More Information in Backtraces

+ +

TruffleRuby tries to match MRI’s backtrace format as closely as possible. +This sometimes means that extra available information is not displayed. +When debugging you may want to see this information.

+ +

An option to show more information is --backtraces-interleave-java=true, which shows you the Java methods involved in executing each Ruby method.

+ +

When you are interoperating with other languages, including C extensions, backtraces for Java exceptions may be missing information, as the Java frames are gone by the time Ruby has a chance to format them into a backtrace.

+ +

Printing Subprocesses

+ +

You can log subprocesses created by TruffleRuby using the option --log-subprocess.

+ +
$ ruby --log-subprocess -e '`ls .`'
+[ruby] INFO: spawn: ls .
+
+ +

This is not transitive though, unless you set this option in TRUFFLERUBYOPT.

+ +

Printing TruffleRuby Processes and Arguments

+ +

You can log TruffleRuby processes created using the bin/truffleruby launcher and their arguments with --log-process-args.

+ +
$ ruby --log-process-args -e 0
+[ruby] INFO: new process: truffleruby --log-process-args -e 0
+
+ +

You can set this option in TRUFFLERUBYOPT to make it apply to TruffleRuby subprocess as well. +Separate log files will be used for different subprocesses running at the same time when using --log.file=PATH. +These log files start with the same path but end with 1, 2, etc suffixes.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/FAQ/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/FAQ/index.html new file mode 100644 index 0000000..d48229c --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/FAQ/index.html @@ -0,0 +1,253 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Frequently Asked Questions

+ +

What is TruffleRuby?

+ +

TruffleRuby is a high-performance implementation of the Ruby programming language built on GraalVM using the Truffle language +implementation framework and the GraalVM compiler. +TruffleRuby is one part of GraalVM, a platform for high-performance polyglot programming.

+ +

What is Truffle?

+ +

The Truffle language implementation framework is a Java framework for writing AST interpreters. +To implement a language using Truffle, you write an AST for your language and add methods to interpret – perform the action of – each node.

+ +

Truffle also incorporates the concept of specialization. +In most AST interpreters the nodes are megamorphic – they handle all possible types and other possible conditions. +In the Truffle framework you write several different nodes for the same semantic action, but for different types and conditions. +As runtime conditions change, you switch which nodes you are using. +After the program has warmed up you should end up with an AST that is precisely tailored for the types and conditions that you are actually using. +If these conditions change, you can just switch nodes again.

+ +

What is the GraalVM compiler?

+ +

The GraalVM compiler is a new implementation of a just-in-time compiler (JIT compiler, or we’d normally say dynamic compiler) in the OpenJDK Java Virtual Machine. +Unlike the current compilers, Graal is written in Java, and exposes a Java API to the running program. +This means that instead of emitting bytecode, a JVM language can directly control the compiler. +However this is complicated, so normally the Truffle framework uses the GraalVM compiler on your behalf to partially evaluate your AST interpreter into machine code.

+ +

What is GraalVM?

+ +

GraalVM is the platform on which TruffleRuby runs. It is a system for high-performance polyglot programming.

+ +

More concretely, GraalVM is a modified version of the OracleJDK that includes the Truffle framework, the GraalVM compiler, TruffleRuby, and other languages supported by GraalVM including JavaScript, Python, and R.

+ +

See how to install GraalVM and TruffleRuby.

+ +

How do I get TruffleRuby?

+ +

There are three ways to get TruffleRuby. Please see Getting Started.

+ +

Why is TruffleRuby slow on a standard JVM?

+ +

The expected way to run TruffleRuby is using the GraalVM compiler. +TruffleRuby isn’t designed to be efficient on a JVM without this.

+ +

Why is TruffleRuby faster on the GraalVM?

+ +

When running with the GraalVM compiler, the Truffle framework can use the API exposed by the GraalVM compiler. +The Truffle framework gets the bytecode representation of all of the AST interpreter methods involved in running your Ruby method, combines them into something like a single Java method, optimizes them together, and emits a single machine code function. +The Truffle framework also provides wrappers for JVM functionality not normally available to Java applications, such as code deoptimization. +TruffleRuby uses this to provide a dramatically simpler and faster implementation of Ruby.

+ +

Where did this code come from?

+ +

Chris Seaton wrote an implementation of Ruby on Truffle and Graal as part of an internship at Oracle Labs in the first half of 2013. The code was merged into JRuby in early 2014. Benoit Daloze and Kevin Menard joined as researchers in the second half of 2014, then Petr Chalupa in 2015, Brandon Fish in 2016, and Duncan MacGregor in 2017. +Since then we have also accepted contributions from people outside Oracle Labs. +In 2017 the code was forked back out of JRuby after it had matured.

+ +

Who do I ask about TruffleRuby?

+ +

See the Contact section of this README page.

+ +

How do I know if I’m using TruffleRuby?

+ +

RUBY_ENGINE will be 'truffleruby'.

+ +

How do I know if I’m using a VM that has the GraalVM compiler?

+ +

ruby --version will report GraalVM CE or EE.

+ +

Also, TruffleRuby.jit? will tell you if you are running with the GraalVM compiler.

+ +

How do I know that I’m using the Community Edition of GraalVM?

+ +

ruby --version will report GraalVM CE.

+ +

How do I know that I’m using the Enterprise Edition of GraalVM?

+ +

ruby --version will report GraalVM EE.

+ +

How do I know that I’m using the native version of TruffleRuby?

+ +

ruby --version will report Native.

+ +

TruffleRuby.native? will return true.

+ +

How can I see the GraalVM compiler is working?

+ +

Put this program into test.rb:

+ +
loop do
+  14 + 2
+end
+
+ +

We’ll use the --engine.TraceCompilation to ask the Truffle framework to tell us when it compiles something using the GraalVM compiler.

+ +
ruby --engine.TraceCompilation test.rb
+[truffle] opt done         block in <main> test.rb:1 <opt> <split-3a9ffa1b>         |ASTSize       8/    8 |Time   103(  99+4   )ms |DirectCallNodes I    0/D    0 |GraalNodes    24/    3 |CodeSize           69 |CodeAddress 0x11245cf50 |Source   ../test.rb:1
+
+ +

Here you can see that Truffle has decided to use the GraalVM compiler to compile the block of 127 - the loop to machine code - just 69 bytes of machine code in all.

+ +

Why doesn’t TruffleRuby perform well for my benchmark?

+ +

Benchmarks that we haven’t looked at yet may require new code paths to be specialized. +Currently we’ve added specialization for the code paths in the benchmarks and applications that we’ve been using. +Adding them is generally not complicated and over time we will have specializations to cover a broad range of applications.

+ +

Make sure that you are using the Enterprise Edition of GraalVM, and have rebuilt the executable images for the best performance.

+ + + +

TruffleRuby doesn’t use invokedynamic, as it doesn’t emit bytecode. +However it does have an optimizing method dispatch mechanism that achieves a similar result.

+ +

Why doesn’t JRuby switch to Truffle as well?

+ +

JRuby is taking a different approach to optimizing and adding new functionality to Ruby. +Both JRuby and TruffleRuby are important projects.

+ +

Why did you fork from JRuby?

+ +

We merged into JRuby in order to be able to use large parts of their Java implementation code. +We forked back out of JRuby when we had reached the point where the code that we were using needed to be modified for our purposes and we no longer had any dependency on the core part of JRuby. +Forking also allowed us to simplify our code base.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/InstallingGraalVM/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/InstallingGraalVM/index.html new file mode 100644 index 0000000..71ea011 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/InstallingGraalVM/index.html @@ -0,0 +1,184 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Using TruffleRuby with GraalVM

+ +

GraalVM is the platform on which TruffleRuby runs.

+ +

Installing GraalVM enables you to run TruffleRuby both in the --native and --jvm runtime configurations.

+ +

Dependencies

+ +

TruffleRuby’s dependencies need to be installed for TruffleRuby to run correctly.

+ +

Community Edition and Enterprise Edition

+ +

GraalVM is available in a Community Edition, which is open-source, and an Enterprise Edition which has better performance and scalability. +See the website for a comparison.

+ +

Installing the Base Image

+ +

GraalVM starts with a base image which provides the platform for high-performance language runtimes.

+ +

The Community Edition base image can be installed from GitHub, under an open source licence.

+ +

The Enterprise Edition base image can be installed from Oracle Downloads page by accepting the Oracle License Agreement.

+ +

Nightly builds of the GraalVM Community Edition are also available.

+ +

Whichever edition you choose, you will obtain a tarball which you can extract. +There will be a bin directory (Contents/Home/bin on macOS) which you can add to your $PATH if you want to.

+ +

Installing with asdf

+ +

Using asdf and asdf-java installation is as easy as +asdf install java graalvm-20.1.0+java11 (look up versions via asdf list-all java | grep graalvm).

+ +

Installing Ruby and Other Languages

+ +

After installing GraalVM you then need to install the Ruby language into it. +This is done using the gu command. +The Ruby package is the same for both editions of GraalVM and comes from GitHub:

+
gu install ruby
+
+ +

This command will show a message regarding running a post-install script. +This is necessary to make the Ruby openssl C extension work with your system libssl. +Please run that script now. +The path of the script will be:

+
languages/ruby/lib/truffle/post_install_hook.sh
+
+ +

You can also download the latest Ruby component (ruby-installable-...) manually from GitHub (CE) +or from Oracle Downloads (EE). +Then install it with gu install --file path/to/ruby-installable-....

+ +

Using a Ruby Manager

+ +

Inside GraalVM is a jre/languages/ruby or languages/ruby directory which has the usual structure of a Ruby implementation. It is recommended to add this directory to a Ruby manager. +See configuring Ruby managers for more information.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/InstallingLLVM/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/InstallingLLVM/index.html new file mode 100644 index 0000000..1b63c75 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/InstallingLLVM/index.html @@ -0,0 +1,169 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Installing Make and GCC

+ +

Since TruffleRuby 19.3.0, TruffleRuby ships with its own LLVM toolchain. +Therefore, it is no longer necessary to install LLVM. +If you are using an older version, see the documentation for that version.

+ +

The make utility as well as the dependencies of the toolchain still need to be available to build C and C++ extensions.

+ +

Fedora-based: RHEL, Oracle Linux, etc

+ +
sudo dnf install make gcc
+
+ +

Debian-based: Ubuntu, etc

+ +
sudo apt-get install make gcc
+
+ +

Mandriva-based and other Linux distributions

+ +

Note: Such distributions are not tested and not supported.

+ +

First, install the make and gcc dependencies.

+ +

Mandriva uses a not-yet-upstreamed patch to let clang find the GCC installation (see this comment). +Therefore the internal LLVM toolchain cannot find the necessary libgcc_s by default. +The proper fix is for those distributions to upstream their changes to LLVM.

+ +

A workaround is to create a symlink explicitly so that the LLVM toolchain can find libgcc_s:

+
cd /usr/lib/gcc
+sudo ln -s x86_64-mandriva-linux-gnu x86_64-linux-gnu
+
+ +

macOS

+ +

On macOS, make sure you have installed the command line developer tools from Xcode:

+ +
xcode-select --install
+
+ +

You might need to add export SDKROOT=$(xcrun --show-sdk-path) in your shell profile.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/InstallingLibYAML/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/InstallingLibYAML/index.html new file mode 100644 index 0000000..c3ea891 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/InstallingLibYAML/index.html @@ -0,0 +1,172 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Installing LibYAML

+ +

TruffleRuby requires to have libyaml installed, much like CRuby 3.2+ and Psych 5+.

+ +

If you experience psych-related errors saying it cannot find libyaml, it might help to recompile the psych gem by running lib/truffle/post_install_hook.sh. +This is done automatically by Ruby managers, and mentioned in the post-install message when installing TruffleRuby via gu install in GraalVM.

+ +

Fedora-based: RHEL, Oracle Linux, etc

+ +

Fedora

+ +
sudo dnf install libyaml-devel
+
+ +

Oracle Linux 7

+ +
sudo yum install --enablerepo=ol7_optional_latest libyaml-devel
+
+ +

Oracle Linux 8

+ +
sudo dnf install --enablerepo=ol8_codeready_builder libyaml-devel
+
+ +

Debian-based: Ubuntu, etc

+ +
sudo apt-get install libyaml-dev
+
+ +

macOS

+ +

Homebrew

+ +

We recommend installing libssl via Homebrew.

+ +
brew install libyaml
+
+ +

MacPorts

+ +

MacPorts should also work but is not actively tested.

+ +
sudo port install libyaml
+
+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Installinglibssl/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Installinglibssl/index.html new file mode 100644 index 0000000..7be9032 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Installinglibssl/index.html @@ -0,0 +1,167 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Installing libssl

+ +

TruffleRuby provides the openssl gem but not the native libssl system library that the gem uses. +TruffleRuby supports libssl versions 1.0.2, 1.1.0 and 3.0.0.

+ +

If you experience openssl-related errors, it might help to recompile the openssl gem by running lib/truffle/post_install_hook.sh. +This is done automatically by Ruby managers, and mentioned in the post-install message when installing TruffleRuby via gu install in GraalVM.

+ +

To compile TruffleRuby against a non-system libssl, set OPENSSL_PREFIX while installing TruffleRuby:

+
export OPENSSL_PREFIX=/path/to/my/openssl-1.1.0
+
+ +

Fedora-based: RHEL, Oracle Linux, etc

+ +
sudo dnf install openssl-devel
+
+ +

Debian-based: Ubuntu, etc

+ +
sudo apt-get install libssl-dev
+
+ +

macOS

+ +

On macOS the system version is too old.

+ +

Homebrew

+ +

We recommend installing libssl via Homebrew.

+ +
brew install openssl
+
+ +

MacPorts

+ +

MacPorts should also work but is not actively tested.

+ +
sudo port install openssl
+
+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Installingzlib/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Installingzlib/index.html new file mode 100644 index 0000000..d643d2d --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Installingzlib/index.html @@ -0,0 +1,145 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Installing zlib

+ +

TruffleRuby provides the zlib module but not the native zlib system library that the module uses.

+ +

Fedora-based: RHEL, Oracle Linux, etc

+ +
sudo dnf install zlib-devel
+
+ +

Debian-based: Ubuntu, etc

+ +
sudo apt-get install libz-dev
+
+ +

macOS

+ +

On macOS the system version can be used.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/JRubyMigration/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/JRubyMigration/index.html new file mode 100644 index 0000000..0adf870 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/JRubyMigration/index.html @@ -0,0 +1,580 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Migration from JRuby to TruffleRuby

+ +

When trying TruffleRuby on your gems and applications, you are encouraged to get in touch with the TruffleRuby team for help.

+ +

Deployment

+ +

If you are migrating from JRuby, probably the easiest way to use TruffleRuby is via GraalVM, which gives you a JVM, JavaScript, Ruby, and other languages in one package.

+ +

If you do not need the Java interoperability capabilities of TruffleRuby, then you could also install via your Ruby manager/installer as with any other implementation of Ruby.

+ +

You can also use the standalone distribution as a simple tarball. +The standalone distribution does not allow for Java interoperability.

+ +

Using Ruby from Java

+ +

JRuby supports many different ways to embed Ruby in Java, including JSR 223 (also know as javax.script), the Bean Scripting Framework (BSF), JRuby Embed (also known as Red Bridge), and the JRuby direct embedding API.

+ +

Thes best way to embed TruffleRuby is to use the Polyglot API, which is part of GraalVM. +The API is different because it is designed to support many languages, not just Ruby.

+ +

TruffleRuby also supports JSR 223, compatible with JRuby, to make it easier to run legacy JRuby code.

+ +

You will need to use GraalVM to use both of these APIs.

+ +

See the polyglot documentation for more information about how to use Ruby from other languages including Java; this document only shows the comparison to JRuby.

+ +

Creating a Context

+ +

In JRuby with JSR 223 you would have written:

+ +
ScriptEngineManager m = new ScriptEngineManager();
+ScriptEngine scriptEngine = m.getEngineByName("ruby");
+
+ +

Or with BSF you would have written:

+ +
BSFManager.registerScriptingEngine("jruby", "org.jruby.embed.bsf.JRubyEngine", null);
+BSFManager bsfManager = new BSFManager();
+
+ +

Or with JRuby Embed you would have written:

+ +
ScriptingContainer container = new ScriptingContainer();
+
+ +

Or with the direct embedding API you would have written:

+ +
Ruby ruby = Ruby.newInstance(new RubyInstanceConfig());
+
+ +

In TruffleRuby you now write:

+ +
Context polyglot = Context.newBuilder().allowAllAccess(true).build();
+
+ +

The allowAllAccess(true) method allows the permissive access privileges that Ruby needs for full functionality. +GraalVM by default disallows many privileges which may not be safe, such as native file access, but a normal Ruby installation uses these so we enable them. +You can decide not to grant those privileges, but this will restrict some of Ruby’s functionality.

+ +
// No privileges granted, restricts functionality
+Context polyglot = Context.newBuilder().build();
+
+ +

You would normally create your context inside a try block to ensure it is properly disposed:

+ +
try (Context polyglot = Context.newBuilder().allowAllAccess(true).build()) {
+}
+
+ +

See the Context API for detailed documentation about Context.

+ +

Setting Options

+ +

You can set TruffleRuby options via system properties, or via the .option(name, value) builder method.

+ +

Evaluating Code

+ +

In JRuby where you would have written one of these JRuby examples, the options available are given:

+ +
scriptEngine.eval("puts 'hello'");
+bsfManager.exec("jruby", "<script>", 1, 0, "puts 'hello'");
+container.runScriptlet("puts 'hello'");
+ruby.evalScriptlet("puts 'hello'");
+
+ +

In TruffleRuby you now write this:

+ +
polyglot.eval("ruby", "puts 'hello'");
+
+ +

Note that eval supports multiple languages, so you need to specify the language each time.

+ +

Evaluating Code with Parameters

+ +

In JRuby with JSR 223 you can pass parameters, called bindings, into a script:

+ +
Bindings bindings = scriptEngine.createBindings();
+bindings.put("a", 14);
+bindings.put("b", 2);
+scriptEngine.eval("puts a + b", bindings);
+
+ +

In TruffleRuby the eval method does not take parameters. Instead you should return a proc which does take parameters, and then call execute on this value:

+ +
polyglot.eval("ruby", "-> a, b { puts a + b }").execute(14, 2);
+
+ +

Primitive Values

+ +

The different embedding APIs handle primitive values in different ways. +In JSR 223, BSF, and JRuby Embed, the return type is Object and can be cast to a primitive like long and checked with instanceof. +In the direct embedding API the return is the root IRubyObject interface and you will need to convert a primitive to an Integer, and from there to a Java long:

+ +
(long) scriptEngine.eval("14 + 2");
+(long) bsfManager.eval("jruby", "<script>", 1, 0, "14 + 2");
+(long) container.runScriptlet("14 + 2");
+ruby.evalScriptlet("14 + 2").convertToInteger().getLongValue();
+
+ +

In TruffleRuby the return value is always an encapsulated Value object, which can be accessed as a long if that is possible for the object. fitsInLong() can test this:

+ +
polyglot.eval("ruby", "14 + 2").asLong();
+
+ +

Calling Methods

+ +

To call a method on an object you get from an eval, or any other object, in the JRuby embedding APIs you either need to ask the context to invoke the method, or in the case of direct embedding you need to call a method on the receiver and marshal the arguments into JRuby types yourself. +The BSF does not appear to have a way to call methods:

+ +
((Invocable) scriptEngine).invokeMethod(scriptEngine.eval("Math"), "sin", 2);
+container.callMethod(container.runScriptlet("Math"), "sin", 2);
+ruby.evalScriptlet("Math").callMethod(ruby.getCurrentContext(), "sin", new IRubyObject[]{ruby.newFixnum(2)})
+
+ +

In TruffleRuby the Value class has a getMember method to return Ruby methods on an object, which you can then call by calling execute. +You do not need to marshal the arguments:

+ +
polyglot.eval("ruby", "Math").getMember("sin").execute(2);
+
+ +

To call methods on a primitive, use a lambda:

+ +
polyglot.eval("ruby", "-> x { x.succ }").execute(2).asInt();
+
+ +

Passing Blocks

+ +

Blocks are a Ruby-specific language feature, so they don’t appear in language agnostic APIs like JSR 223 and BSF. +The JRuby Embed API and direct embedding do allow passing a Block parameter to the callMethod method, but it is not clear how you would create a Block object to use this.

+ +

In TruffleRuby you should return a Ruby lambda that performs your call, passing a block that executes a Java lambda that you pass in:

+ +
polyglot.eval("ruby", "-> block { (1..3).each { |n| block.call n } }")
+  .execute(polyglot.asValue((IntConsumer) n -> System.out.println(n)));
+
+ +

Creating Objects

+ +

JRuby embedding APIs don’t have support for creating new objects, but you can just call the new method yourself:

+ +
((Invocable) scriptEngine).invokeMethod(scriptEngine.eval("Time"), "new", 2021, 3, 18);
+container.callMethod(container.runScriptlet("Time"), "new", 2021, 3, 18)
+ruby.evalScriptlet("Time").callMethod(ruby.getCurrentContext(), "new",
+  new IRubyObject[]{ruby.newFixnum(2021), ruby.newFixnum(3), ruby.newFixnum(8)})
+
+ +

In TruffleRuby you can create an object from a Ruby class using newInstance. +You can use canInstantiate to see if this will be possible:

+ +
polyglot.eval("ruby", "Time").newInstance(2021, 3, 18);
+
+ +

Handling Strings

+ +

In JRuby’s embedding APIs you would use toString to convert to a Java String. +Use asString in TruffleRuby (and isString to check).

+ +

Accessing Arrays

+ +

JRuby’s arrays implement List<Object>, so you can cast to this interface to access them:

+ +
((List) scriptEngine.eval("[3, 4, 5]")).get(1);
+((List) container.runScriptlet("[3, 4, 5]")).get(1);
+((List) bsfManager.eval("jruby", "<script>", 1, 0, "[3, 4, 5]")).get(1);
+((List) ruby.evalScriptlet("[3, 4, 5]")).get(1);
+
+ +

In TruffleRuby you can use getArrayElement, setArrayElement, and +getArraySize, or you can use as(List.class) to get a List<Object>:

+ +
polyglot.eval("ruby", "[3, 4, 5]").getArrayElement(1);
+polyglot.eval("ruby", "[3, 4, 5]").as(List.class).get(1);
+
+ +

Accessing Hashes

+ +

JRuby’s hashes implement Map<Object, Object>, so you can cast to this +interface to access them:

+ +
((Map) scriptEngine.eval("{'a' => 3, 'b' => 4, 'c' => 5}")).get("b");
+((Map) scriptEngine.eval("{3 => 'a', 4 => 'b', 5 => 'c'}")).get(4);
+
+ +

In TruffleRuby there is currently no uniform way to access hashes or dictionary-like data structures. +At the moment we recommend using a lambda accessor:

+ +
Value hash = polyglot.eval("ruby", "{'a' => 3, 'b' => 4, 'c' => 5}");
+Value accessor = polyglot.eval("ruby", "-> hash, key { hash[key] }");
+accessor.execute(hash, "b");
+
+ +

Implementing Interfaces

+ +

You may want to implement a Java interface using a Ruby object (example copied from the JRuby wiki):

+ +
interface FluidForce {
+  double getFluidForce(double a, double b, double depth);
+}
+
+ +
class EthylAlcoholFluidForce
+  def getFluidForce(x, y, depth)
+    area = Math::PI * x * y
+    49.4 * area * depth
+  end
+end
+
+EthylAlcoholFluidForce.new
+
+ +
String RUBY_SOURCE = "class EthylAlcoholFluidForce\n  def getFluidForce...";
+
+ +

In JSR 223 you can use getInterface(object, Interface.class). +In JRuby Embed you can use getInstance(object, Interface.class). +In direct embedding you can use toJava(Interface.class). +BSF does not appear to support implementing interfaces:

+ +
FluidForce fluidForce = ((Invocable) scriptEngine).getInterface(scriptEngine.eval(RUBY_SOURCE), FluidForce.class);
+FluidForce fluidForce = container.getInstance(container.runScriptlet(RUBY_SOURCE), FluidForce.class);
+FluidForce fluidForce = ruby.evalScriptlet(RUBY_SOURCE).toJava(FluidForce.class);
+fluidForce.getFluidForce(2.0, 3.0, 6.0);
+
+ +

In TruffleRuby you can get an interface implemented by your Ruby object by using as(Interface.class):

+ +
FluidForce fluidForce = polyglot.eval("ruby", RUBY_SOURCE).as(FluidForce.class);
+fluidForce.getFluidForce(2.0, 3.0, 6.0);
+
+ +

JRuby allows the name of the Ruby method to be get_fluid_force, using Ruby conventions, instead of getFluidForce, using Java conventions. +TruffleRuby does not support this at the moment.

+ +

Implementing Lambdas

+ +

As far as we know, JSR 223, BSF, JRuby Embed, and direct embedding do not have a convenient way to get a Java lambda from a Ruby lambda.

+ +

In TruffleRuby you can get a Java lambda (really an implementation of a functional interface) from a Ruby lambda by using +as(FunctionalInterface.class):

+ +
BiFunction<Integer, Integer, Integer> adder = polyglot.eval("ruby", "-> a, b { a + b }").as(BiFunction.class);
+adder.apply(14, 2).intValue();
+
+ +

Parse Once Run Many Times

+ +

Some of the JRuby embedding APIs allow a script to be compiled once and then eval’d several times:

+ +
CompiledScript compiled = ((Compilable) scriptEngine).compile("puts 'hello'");
+compiled.eval();
+
+ +

In TruffleRuby you can simply return a lambda from parsing and execute this many times. +It will be subject to optimization like any other Ruby code:

+ +
Value parsedOnce = polyglot.eval("ruby", "-> { run many times }");
+parsedOnce.execute();
+
+ +

Using Java from Ruby

+ +

TruffleRuby provides its own scheme for Java interoperability that is consistent for use from any GraalVM language, to any other GraalVM language. +This is not compatible with existing JRuby-Java interoperability, so you will need to migrate.

+ +

Polyglot programming in general is documented elsewhere - this section describes it relative to JRuby.

+ +

This example is from the JRuby wiki:

+ +
require 'java'
+
+# With the 'require' above, you now can refer to things that are part of the
+# standard Java platform via their full paths.
+frame = javax.swing.JFrame.new("Window") # Creating a Java JFrame
+label = javax.swing.JLabel.new("Hello")
+
+# You can transparently call Java methods on Java objects, just as if they were defined in Ruby.
+frame.add(label)  # Invoking the Java method 'add'.
+frame.setDefaultCloseOperation(javax.swing.JFrame::EXIT_ON_CLOSE)
+frame.pack
+frame.setVisible(true)
+
+ +

In TruffleRuby we would write that this way instead:

+ +
Java.import 'javax.swing.JFrame'
+Java.import 'javax.swing.JLabel'
+
+frame = JFrame.new("Window")
+label = JLabel.new("Hello")
+
+frame.add(label)
+frame.setDefaultCloseOperation(JFrame[:EXIT_ON_CLOSE])
+frame.pack
+frame.setVisible(true)
+
+ +

Instead of using Ruby metaprogramming to simulate a Java package name, we explicitly import classes. +Java.import is similar to JRuby’s java_import, and does ClassName = Java.type('package.ClassName').

+ +

Constants are read by reading properties of the class rather than using Ruby notation.

+ +

Require Java

+ +

Do not require 'java' in TruffleRuby. However, you do need to run in --jvm mode. +This is only available in GraalVM - not in the standalone distribution installed by Ruby version managers and installers.

+ +

Referring to Classes

+ +

In JRuby, Java classes can either be referenced in the Java module, such as Java::ComFoo::Bar, or if they have a common TLD they can be referenced as com.foo.Bar. java_import com.foo.Bar will define Bar as a top-level constant.

+ +

In TruffleRuby, Java classes are referred to using either Java.type('com.foo.Bar'), which you would then normally assign to a constant, or you can use Java.import 'com.foo.Bar' to have Bar defined in the enclosing module.

+ +

Wildcard Package Imports

+ +

JRuby lets you include_package 'com.foo' which will make all classes in that package available as constants in the current scope.

+ +

In TruffleRuby you refer to classes explicitly.

+ +

Calling Methods and Creating Instances

+ +

In both JRuby and TruffleRuby you call Java methods as you would a Ruby method.

+ +

JRuby will rewrite method names such as my_method to the Java convention of myMethod, and convert getFoo to foo, and setFoo to foo=. +TruffleRuby does not perform these conversions.

+ +

Referring to Constants

+ +

In JRuby, Java constants are modelled as Ruby constants, MyClass::FOO. +In TruffleRuby you use the read notation to read them as a property, +MyClass[:FOO].

+ +

Using Classes from JAR files

+ +

In JRuby you can add classes and JARs to the classpath using require. +In TruffleRuby at the moment you use the -classpath JVM flag as normal.

+ +

Additional Java-Specific Methods

+ +

JRuby defines these methods on Java objects; use these equivalents instead.

+ +

java_class - use class.

+ +

java_kind_of? - use is_a?

+ +

java_object - not supported.

+ +

java_send - use __send__.

+ +

java_method - not supported.

+ +

java_alias - not supported.

+ +

Creating Java Arrays

+ +

In JRuby you use Java::byte[1024].new.

+ +

In TruffleRuby you would use Java.type('byte[]').new(1024).

+ +

Implementing Java Interfaces

+ +

JRuby has several ways to implement an interface. +For example, to add an action listener to a Swing button we could do any of the following three things:

+ +
class ClickAction
+  include java.awt.event.ActionListener
+
+  def actionPerformed(event)
+   javax.swing.JOptionPane.showMessageDialog nil, 'hello'
+  end
+end
+
+button.addActionListener ClickAction.new
+
+ +
button.addActionListener do |event|
+  javax.swing.JOptionPane.showMessageDialog nil, 'hello'
+end
+
+ +
button.addActionListener -> event {
+  javax.swing.JOptionPane.showMessageDialog nil, 'hello'
+}
+
+ +

In TruffleRuby we’d always use the last option to generate an interface:

+ +
button.addActionListener -> event {
+  JOptionPane.showMessageDialog nil, 'hello'
+}
+
+ +

Generating Java Classes at Runtime

+ +

JRuby supports converting a Ruby class to a concrete Java class using become_java!.

+ +

TruffleRuby does not support this. +We recommend using a proper Java interface as your interface between Java and Ruby.

+ +

Reopening Java Classes

+ +

Java classes cannot be reopened in TruffleRuby.

+ +

Subclassing Java Classes

+ +

Java classes cannot be subclassed in TruffleRuby. Use composition or interfaces instead.

+ +

Extending TruffleRuby Using Java

+ +

JRuby supports extensions written in Java. These extensions are written against an informal interface that is simply the entire internals of JRuby, similar to how the MRI C extension interface works.

+ +

TruffleRuby does not support writing these kind of Java extensions at the moment. +We recommend using Java interop as described above.

+ +

Tooling

+ +

Standalone Classes and JARs

+ +

JRuby supports compiling to standalone source classes and compiled JARs from Ruby using jrubyc.

+ +

TruffleRuby does not support compiling Ruby code to Java. We recommend using the Polyglot API as your entry point from Java to Ruby.

+ +

Warbler

+ +

JRuby supports building WAR files for loading into enterprise Java web servers.

+ +

TruffleRuby does not support this at the moment.

+ +

VisualVM

+ +

VisualVM works for TruffleRuby as for JRuby.

+ +

Additionally, the VisualVM included in GraalVM understands Ruby objects, rather than Java objects, when you use the heap dump tool.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/KnownCVEs/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/KnownCVEs/index.html new file mode 100644 index 0000000..0efe001 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/KnownCVEs/index.html @@ -0,0 +1,796 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Security

+ +

Please report security vulnerabilities via the process outlined in the reporting vulnerabilities guide. +Specific guidelines for reporting security issues of the GraalVM project, including TruffleRuby, can be found in the SECURITY file.

+ +

MRI Vulnerabilities

+ +

Vulnerabilities reported against MRI may apply to the design of Ruby or to code that we share with MRI. +We list reported MRI vulnerabilities here and document how MRI has mitigated the vulnerability, if the mitigation is tested +by anything, and how TruffleRuby has mitigated. We have not investigated all legacy vulnerabilities, as it is often very hard to work out the details from older reports.

+ +

Cross-reference with the details on the MRI website.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NumberDescriptionTheir MitigationTestOur Mitigation
CVE-2021-33621HTTP response splitting in CGIFixTestSame
CVE-2022-28739Buffer overrun in String-to-Float conversion  Not applicable thanks to Java semantics
CVE-2022-28738Double free in Regexp compilation  Not applicable thanks to Java semantics
CVE-2021-41819Cookie Prefix Spoofing in CGI::Cookie.parseFixTestSame
CVE-2021-41817Regular expression denial of service vulnerability (ReDoS) in dateFixTestNot applicable thanks to TRegex
CVE-2021-41816Buffer Overrun in CGI.escape_htmlFixTestNot applicable, we use the Ruby definition of CGI.escape_html
CVE-2021-31810Trusting FTP PASV responses vulnerability in Net::FTPFixTestSame
CVE-2021-32066A StartTLS stripping vulnerability in Net::IMAPFixTestSame
CVE-2021-31799A command injection vulnerability in RDocFix BackportTestSame
CVE-2021-28966Path traversal in Tempfile on WindowsSanitization of paths in tmpdir.rbIn test/mri/tests/test_tmpdir.rbSanitization of paths in tmpdir.rb
CVE-2021-28965XML round-trip vulnerability in REXMLUpdate to REXML 3.2.5In ruby/rexmlUpdate to REXML 3.2.5
CVE-2020-10663Unsafe Object Creation Vulnerability in JSON (Additional fix)FixSpecThe pure Ruby version of JSON we use is safe
CVE-2019-16255A code injection vulnerability of Shell#[] and Shell#testFixMRI testSame
CVE-2019-16254HTTP response splitting in WEBrick (Additional fix)FixMRI testSame
CVE-2019-15845A NUL injection vulnerability of File.fnmatch and File.fnmatch?FixMRI testCheck for NUL bytes
CVE-2019-16201Regular Expression Denial of Service vulnerability of WEBrick’s Digest access authenticationFixMRI testSame
CVE-2012-6708Multiple jQuery vulnerabilities in RDocRemove jquery.jsN/ASame
CVE-2015-9251Multiple jQuery vulnerabilities in RDocRemove jquery.jsN/ASame
CVE-2019-8320Delete directory using symlink when decompressing tarCheck the expanded pathTested in MRI test/rubygems/test_gem_package.rbApplied the same patch
CVE-2019-8321Escape sequence injection in verboseSanitise messageTested in ruby/spec :securityApplied the same patch
CVE-2019-8322Escape sequence injection in gem ownerSanitise messageTested in ruby/spec :securityApplied the same patch
CVE-2019-8323Escape sequence injection vulnerability in API response handlingSanitise messageTested in ruby/spec :securityApplied the same patch
CVE-2019-8324Installing a malicious gem may lead to arbitrary code executionVerifying gems before pre-install checksTested in MRI test/rubygems/test_gem_installer.rbApplied the same patch
CVE-2019-8325Escape sequence injection in errorsSanitise error messagesTested in ruby/spec :securityApplied the same patch
CVE-2018-16395OpenSSL::X509::Name equality check does not work correctly   
CVE-2018-16396Tainted flags are not propagated in Array#pack and String#unpack with some directivesAdditional taint operationsTested in ruby/spec :securityAdditional taint operations
CVE-2018-6914Unintentional file and directory creation with directory traversal in tempfile and tmpdirSanitization of pathsTested in ruby/spec :securitySanitization of paths
CVE-2018-8779Unintentional socket creation by poisoned NUL byte in UNIXServer and UNIXSocketCheck for NUL bytesTested in ruby/spec :securityCheck for NUL bytes
CVE-2018-8780Unintentional directory traversal by poisoned NUL byte in DirCheck for NUL bytesTested in ruby/spec :securityCheck for NUL bytes
CVE-2018-8777DoS by large request in WEBrickLogic for header lengthTested in MRI test/webrick/test_httpserver.rbApplied the same mitigation
CVE-2017-17742HTTP response splitting in WEBrickLogic for invalid headersTested in ruby/spec :securityApplied the same mitigation
CVE-2018-8778Buffer under-read in String#unpackA range checkTested in ruby/spec :securityA range check
CVE-2017-17405Command injection vulnerability in Net::FTPTreat paths in commands explicitly as paths, not general IO commandsTested in MRI test/net/ftp/test_ftp.rbApplied the same mitigation
CVE-2017-10784Escape sequence injection vulnerability in the Basic authentication of WEBrickProper escaping of logsTested in MRI test/webrick/test_httpauth.rbApplied the same mitigation
CVE-2017-0898Buffer underrun vulnerability in Kernel.sprintf   
CVE-2017-14033Buffer underrun vulnerability in OpenSSL ASN1 decode   
CVE-2017-14064Heap exposure vulnerability in generating JSON   
CVE-2017-0902, CVE-2017-0899, CVE-2017-0900, CVE-2017-0901Multiple vulnerabilities in RubyGems   
CVE-2015-7551Unsafe tainted string usage in Fiddle and DL (regression of the mitigation of CVE-2009-5147)Additional taint checksTested in MRI test/mri/tests/fiddle/test_handle.rbNot applicable as we do not support $SAFE, and the DL module was removed in Ruby 2.2.0
CVE-2015-1855Ruby OpenSSL Hostname Verification   
CVE-2014-8090Another Denial of Service XML Expansion   
CVE-2014-8080Denial of Service XML Expansion Tested in ruby/spec :security 
NoneChanged default settings of ext/openssl   
CVE-2014-2734Dispute of Vulnerability   
CVE-2014-0160OpenSSL Severe Vulnerability in TLS Heartbeat Extension   
CVE-2014-2525Heap Overflow in YAML URI Escape Parsing   
CVE-2013-4164Heap Overflow in Floating Point Parsing Tested in ruby/spec :security 
CVE-2013-4073Hostname check bypassing vulnerability in SSL client   
CVE-2013-2065Object taint bypassing in DL and Fiddle in RubyAdditional taint checksTested in MRI test/mri/tests/fiddle/test_func.rbNot applicable as we do not support $SAFE, and the DL module was removed in Ruby 2.2.0
CVE-2013-1821Entity expansion DoS vulnerability in REXML   
CVE-2013-0269Denial of Service and Unsafe Object Creation Vulnerability in JSON   
CVE-2013-0256XSS exploit of RDoc documentation generated by rdoc   
CVE-2012-5371Hash-flooding DoS vulnerability for ruby 1.9   
CVE-2012-4522Unintentional file creation caused by inserting a illegal NUL character   
CVE-2012-4464, CVE-2012-4466$SAFE escaping vulnerability about Exception#to_s / NameError#to_s  Not applicable as we do not support $SAFE
NoneSecurity Fix for RubyGems: SSL server verification failure for remote repository   
CVE-2011-3389Security Fix for Ruby OpenSSL module: Allow 0/n splitting as a prevention for the TLS BEAST attack   
CVE-2011-4815Denial of service attack was found for Ruby’s Hash algorithm (cross-reference CVE-2011-4838, CVE-2012-5370, CVE-2012-5372)Hashes are made non-deterministic by incorporating process start timeTested in ruby/spec :securityHashes are made non-deterministic by incorporating a seed from /dev/urandom
NoneException methods can bypass $SAFE  Not applicable as we do not support $SAFE
NoneFileUtils is vulnerable to symlink race attacks   
CVE-2010-0541XSS in WEBrick  WEBrick no longer shipped
NoneBuffer over-run in ARGF.inplace_mode=   
NoneWEBrick has an Escape Sequence Injection vulnerability  WEBrick no longer shipped
CVE-2009-5147DL::dlopen opens libraries with tainted namesAdditional taint checksThe DL module does not exist in modern RubyNot applicable as we do not support $SAFE, and the DL module was removed in Ruby 2.2.0
CVE-2009-4124Heap overflow in String   
NoneDoS vulnerability in BigDecimal   
NoneDoS vulnerability in REXML   
CVE-2008-1447Multiple vulnerabilities in Ruby   
CVE-2008-2662, CVE-2008-2663, CVE-2008-2725, CVE-2008-2726, CVE-2008-2664, CVE-2008-1891Arbitrary code execution vulnerabilities   
NoneFile access vulnerability of WEBrick  WEBrick no longer shipped
NoneNet::HTTPS Vulnerability   
JVN#84798830Another DoS Vulnerability in CGI Library   
CVE-2006-5467DoS Vulnerability in CGI Library   
VU#160012Ruby vulnerability in the safe level settings  Not applicable as we do not support $SAFE
+ +

JRuby Vulnerabilities

+ +

TruffleRuby uses code from JRuby, so vulnerabilities reported against JRuby may apply to TruffleRuby.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
NumberDescriptionTheir MitigationTestOur Mitigation
CVE-2012-5370JRuby computes hash values without properly restricting the ability to trigger hash collisions predictably (cross-reference CVE-2011-4815, CVE-2011-4838, CVE-2012-5372)Hashes are made non-deterministic by incorporating process start timeTested in ruby/spec :securityHashes are made non-deterministic by incorporating a seed from /dev/urandom
CVE-2011-4838JRuby before 1.6.5.1 computes hash values without restricting the ability to trigger hash collisions predictably (cross-reference CVE-2011-4815, CVE-2012-5370, CVE-2012-5372)Hashes are made non-deterministic by incorporating process start timeTested in ruby/spec :securityHashes are made non-deterministic by incorporating a seed from /dev/urandom
+ +

Rubinius Vulnerabilities

+ +

TruffleRuby uses code from Rubinius, so vulnerabilities reported against Rubinius may apply to TruffleRuby.

+ + + + + + + + + + + + + + + + + + + + +
NumberDescriptionTheir MitigationTestOur Mitigation
CVE-2012-5372Rubinius computes hash values without properly restricting the ability to trigger hash collisions predictably (cross-reference CVE-2011-4815, CVE-2011-4838, CVE-2012-5370)Hashes are made non-deterministic by incorporating output from /dev/urandomTested in ruby/spec :securityHashes are made non-deterministic by incorporating a seed from /dev/urandom
+ +

Java Dependency Vulnerabilities

+ +

JONI

+ +

No vulnerabilities are known.

+ +

JCodings

+ + + + + + + + + + + + + + + + + + + + +
NumberDescriptionTheir MitigationTestOur Mitigation
CVE-2010-1330The regular expression engine in JRuby before 1.4.1, when $KCODE is set to 'u', does not properly handle characters immediately after a UTF-8 characterCheck byte sequences for the UTF-8 encoding when perform regexp operationsTested in ruby/spec :securityApplied the same mitigation
+ +

Other Dependency Vulnerabilities

+ +

zlib

+ +

No vulnerabilities are known, but consider potential vulnerabilities in your system zlib.

+ +

libssl

+ +

Consider potential vulnerabilities in your system libssl.

+ +

FFI

+ + + + + + + + + + + + + + + + + + + + +
NumberDescriptionTheir MitigationTestOur Mitigation
CVE-2018-1000201A DLL loading issue can be hijacked on Windows when a Symbol is used for the library nameTreat Symbols the same as Strings in ffi_lib Applied the same mitigation, by using a version of FFI which fixed this vulnerability
+ +

Notes on Hashing

+ +

TruffleRuby uses MurmurHash2 hashing with a seed from /dev/urandom - it cannot be configured to use any other hashing algorithm. +For hashing strings, TruffleRuby uses Java’s hash algorithm (and then MurmurHash2 on top).

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Optcarrot/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Optcarrot/index.html new file mode 100644 index 0000000..89e99b7 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Optcarrot/index.html @@ -0,0 +1,184 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Running Optcarrot

+ +

Running the Demo

+ +

Start by installing GraalVM.

+ +

Then add GraalVM/bin in PATH (or use a Ruby manager):

+
export PATH="/path/to/graalvm/bin:$PATH"
+
+ +

You also need to install SDL2:

+
    +
  • brew install sdl2 on macOS
  • +
  • sudo yum install SDL2-devel for RedHat-based Linux
  • +
  • sudo apt-get install libsdl2-dev for Debian-based Linux
  • +
+ +

Then clone the optcarrot repository:

+
git clone https://github.com/eregon/optcarrot.git
+cd optcarrot
+
+ +

Then you can play the Lan Master game.

+ +

On Linux:

+
ruby --jvm bin/optcarrot --print-fps --sdl2 --audio=none examples/Lan_Master.nes
+
+ +

On macOS, you need an extra flag, --vm.XstartOnFirstThread, for the GUI to appear:

+
ruby --jvm --vm.XstartOnFirstThread bin/optcarrot --print-fps --sdl2 --audio=none examples/Lan_Master.nes
+
+ +

Note: --audio=none is used since the audio it not nice at non-60FPS speeds.

+ +

To play, use keys 17 (not on numpad) to scale the screen, X for OK/turn right, S for turn left, arrows to move around, and Q to quit.

+ +

More information can be found in this blog post.

+ +

Here is a recording of a talk running the demo on TruffleRuby and MRI.

+ +

Running on Other Ruby implementations

+ +

You can also run it on MRI for comparison. +You will need to install the FFI gem with:

+
gem install --user ffi
+ruby bin/optcarrot --print-fps --sdl2 --audio=none examples/Lan_Master.nes
+
+ +

You can also run it on JRuby if desired:

+
jruby bin/optcarrot --print-fps --sdl2 --audio=none examples/Lan_Master.nes
+
+ +

Running as a Benchmark from the TruffleRuby Repository

+ +

If you have a local checkout of TruffleRuby, you can also use the version of OptCarrot under bench/optcarrot. +See the Benchmarking documentation for details.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Options/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Options/index.html new file mode 100644 index 0000000..1d188e6 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Options/index.html @@ -0,0 +1,271 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

TruffleRuby Options and Command Line

+ +

TruffleRuby has the same command-line interface as our compatible MRI version.

+ +
Usage: truffleruby [switches] [--] [programfile] [arguments]
+  -0[octal]       specify record separator (\0, if no argument)
+  -a              autosplit mode with -n or -p (splits $_ into $F)
+  -c              check syntax only
+  -Cdirectory     cd to directory before executing your script
+  -d, --debug     set debugging flags (set $DEBUG to true)
+  -e 'command'    one line of script. Several -e's allowed. Omit [programfile]
+  -Eex[:in], --encoding=ex[:in]
+                  specify the default external and internal character encodings
+  -Fpattern       split() pattern for autosplit (-a)
+  -i[extension]   edit ARGV files in place (make backup if extension supplied)
+  -Idirectory     specify $LOAD_PATH directory (may be used more than once)
+  -l              enable line ending processing
+  -n              assume 'while gets(); ... end' loop around your script
+  -p              assume loop like -n but print line also like sed
+  -rlibrary       require the library before executing your script
+  -s              enable some switch parsing for switches after script name
+  -S              look for the script using PATH environment variable
+  -v              print the version number, then turn on verbose mode
+  -w              turn warnings on for your script
+  -W[level=2|:category]
+                  set warning level; 0=silence, 1=medium, 2=verbose
+  -x[directory]   strip off text before #!ruby line and perhaps cd to directory
+  --copyright     print the copyright
+  --enable={rubyopt|...}[,...], --disable={rubyopt|...}[,...]
+                  enable or disable features. see below for available features
+  --external-encoding=encoding, --internal-encoding=encoding
+                  specify the default external or internal character encoding
+  --backtrace-limit=num
+                  limit the maximum length of backtrace
+  --verbose       turn on verbose mode and disable script from stdin
+  --version       print the version number, then exit
+  --help          show this message, -h for short message
+
+Features:
+  gems            rubygems (only for debugging, default: enabled)
+  did_you_mean    did_you_mean (default: enabled)
+  rubyopt         RUBYOPT environment variable (default: enabled)
+  frozen-string-literal
+                  freeze all string literals (default: disabled)
+
+Warning categories:
+  deprecated      deprecated features
+  experimental    experimental features
+
+Runtime options:
+  --polyglot                                   Run with all other guest languages accessible.
+  --native                                     Run using the native launcher with limited access to Java libraries
+                                               (default).
+  --jvm                                        Run on the Java Virtual Machine with access to Java libraries.
+  --vm.[option]                                Pass options to the host VM. To see available options, use '--help:vm'.
+  --log.file=<String>                          Redirect guest languages logging into a given file.
+  --log.[logger].level=<String>                Set language log level to OFF, SEVERE, WARNING, INFO, CONFIG, FINE,
+                                               FINER, FINEST or ALL.
+  --help                                       Print this help message.
+  --help:vm                                    Print options for the host VM.
+  --help:engine                                Print engine options.
+  --help:all                                   Print all options.
+  --version:graalvm                            Print GraalVM version information and exit.
+  --show-version:graalvm                       Print GraalVM version information and continue execution.
+
+Languages:
+  [id]        [name]                  [website]
+  llvm        LLVM                    https://www.graalvm.org/22.1/reference-manual/llvm/
+  ruby        Ruby                    https://www.graalvm.org/ruby/
+
+Tools:
+  [id]        [name]                  [website]
+  agentscript Agent Script            
+  coverage    Code Coverage           https://www.graalvm.org/tools/code-coverage/
+  cpusampler  CPU Sampler             https://www.graalvm.org/tools/profiling/
+  cputracer   CPU Tracer              https://www.graalvm.org/tools/profiling/
+  dap         Debug Protocol Server   https://www.graalvm.org/tools/dap/
+  heap        Heap Dump               
+  heapmonitor Heap Allocation Monitor 
+  insight     Insight                 https://www.graalvm.org/tools/graalvm-insight/
+  inspect     Chrome Inspector        https://www.graalvm.org/tools/chrome-debugger/
+  lsp         Language Server         https://www.graalvm.org/tools/lsp/
+  memtracer   Memory Tracer           https://www.graalvm.org/tools/profiling/
+
+  Use --help:[id] for component options.
+
+See http://www.graalvm.org for more information.
+
+ +

TruffleRuby also reads the RUBYOPT environment variable, as in standard +Ruby, if run from the Ruby launcher.

+ +

Unlisted Ruby Switches

+ +

MRI has some extra Ruby switches which are not normally listed in help output +but are documented in the Ruby manual page.

+ +
  -Xdirectory     cd to directory before executing your script (same as -C)
+  -U              set the internal encoding to UTF-8
+  -K[EeSsUuNnAa]  sets the source and external encoding
+  --encoding=external[:internal]
+                  the same as --external-encoding=external and optionally --internal-encoding=internal
+
+ +

TruffleRuby Options

+ +

TruffleRuby options are set via --option=value, or you can use --ruby.option=value from any launcher. +You can omit =value to set to true.

+ +

Available options and documentation can be seen with --help:languages. +Additionally, set --help:expert and --help:internal to see those categories of options. +All options all experimental and subject to change at any time.

+ +

Options can also be set as JVM system properties, where they have a prefix polyglot.ruby.. +For example, --vm.Dpolyglot.ruby.cexts.remap=true, or via any other way of setting JVM system properties. +Finally, options can be set as GraalVM polyglot API configuration options.

+ +

The priority for options is the command line first, then the Graal-SDK polyglot API configuration, then system properties last.

+ +

TruffleRuby options, as well as conventional Ruby options and VM options, can also be set in the TRUFFLERUBYOPT and RUBYOPT environment variables, if run from the Ruby launcher.

+ +

-- or the first non-option argument stops processing of TrufflRuby and VM options in the same way it stops processing of Ruby arguments.

+ +

VM Options

+ +

To set options in the underlying VM, use --vm., valid for both the native configuration and the JVM configuration. +For example, --vm.Dsystem_property=value or --vm.ea.

+ +

To set the classpath, use the = notation, rather than two separate arguments. +For example, --vm.cp=lib.jar or --vm.classpath=lib.jar.

+ +

Other Binary Switches

+ +

Other binaries, such as irb, gem, and so on, support exactly the same switches as in standard Ruby.

+ +

Determining the TruffleRuby Home

+ +

TruffleRuby needs to know where to locate files such as the standard library. +These are stored in the TruffleRuby home directory. +The Ruby home is always the one that the Truffle framework reports.

+ +

If the Ruby home appears not to be correct, or is unset, a warning will be given but the program will continue and you will not be able to require standard libraries. +You can tell TruffleRuby not to try to find a home at all using the no-home-provided option.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Polyglot/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Polyglot/index.html new file mode 100644 index 0000000..4a885cf --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Polyglot/index.html @@ -0,0 +1,368 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Polyglot Programming

+ +

TruffleRuby allows you to interface with any other Truffle language to create polyglot programs – programs written in more than one language.

+ +

This guide describes how to load code written in foreign languages, how to export and import objects between languages, how to use Ruby objects from a foreign language, how to use foreign objects from Ruby, how to load Java types to interface with Java, and how to embed in Java.

+ +

If you are using the native configuration, you will need to use the --polyglot flag to get access to other languages. +The JVM configuration automatically has access to other languages.

+ + + +

Running Ruby Code from Another Language

+ +

When you eval Ruby code from the Context API in another language and mark the Source as interactive, the same interactive top-level binding is used each time. +This means that if you set a local variable in one eval, you will be able to use it from the next.

+ +

The semantics are the same as the Ruby semantics of calling INTERACTIVE_BINDING.eval(code) for every Context.eval() call with an interactive Source. +This is similar to most REPL semantics.

+ +

Loading Code Written in Foreign Languages

+ +

Polyglot.eval(id, string) executes code in a foreign language identified by its ID.

+ +

Polyglot.eval_file(id, path) executes code in a foreign language from a file, identified by its language ID.

+ +

Polyglot.eval_file(path) executes code in a foreign language from a file, automatically determining the language.

+ +

Exporting Ruby Objects to Foreign Languages

+ +

Polyglot.export(name, value) exports a value with a given name.

+ +

Polyglot.export_method(name) exports a method, defined in the top-level object.

+ +

Importing Foreign Objects to Ruby

+ +

Polyglot.import(name) imports and returns a value with a given name.

+ +

Polyglot.import_method(name) imports a value, which should be IS_EXECUTABLE, with a given name, and defines it in the top-level object.

+ +

Using Ruby Objects from a Foreign Language

+ +

Using JavaScript as an example: the left example is JavaScript, the right one is the corresponding action it takes on the Ruby object expressed in Ruby code.

+ +

object[name/index] calls object[name/index] if the object has a method [], or reads an instance variable if the name starts with @, or returns a bound method with the name.

+ +

object[name/index] = value calls object[name/index] = value if the object has a method []=, or sets an instance variable if the name starts with @.

+ +

delete object.name calls object.delete(name).

+ +

delete object[name/index] calls object.delete(name).

+ +

object.length calls object.size.

+ +

Object.keys(hash) gives the hash keys as strings.

+ +

Object.keys(object) gives the methods of an object as functions, unless the object has a [] method, in which case it returns an empty array.

+ +

object(args...) calls a Ruby Proc, Method, UnboundMethod, etc.

+ +

object.name(args...) calls a method on the Ruby object.

+ +

new object(args...) calls object.new(args...).

+ +

"length" in obj returns true for a Ruby Array.

+ +

object == null calls object.nil?.

+ +

Notes on Creating Ruby Objects for Use in Foreign Languages

+ +

If you want to pass a Ruby object to another language for fields to be read and written, a good object to pass is usually a Struct, as this will have both the object.foo and object.foo = value accessors for you to use from Ruby, and they will also respond to object['foo'] and object['foo'] = value, which means they will work from other languages sending read and write messages.

+ +

Using Foreign Objects from Ruby

+ +

object[name/index] will read a member from the foreign object.

+ +

object[name/index] = value will write a value to the foreign object.

+ +

object.delete(name/index) will remove a value from the foreign object.

+ +

object.size will get the size or length of the foreign object.

+ +

object.keys will get an array of the members of the foreign object.

+ +

object.call(*args) will execute the foreign object.

+ +

object.name(*args) will invoke a method called name on the foreign object.

+ +

object.new(*args) will create a new object from the foreign object (as if it is some kind of class).

+ +

object.respond_to?(:size) will tell you if the foreign object has a size or length.

+ +

object.nil? will tell you if the foreign object represents the language’s equivalent of null or nil.

+ +

object.respond_to?(:call) will tell you if a foreign object can be executed.

+ +

object.respond_to?(:new) will tell you if a foreign object can be used to create a new object (if it’s a class).

+ +

Polyglot.as_enumerable(object) will create a Ruby Enumerable from the foreign object, using its size or length, and reading from it.

+ +

Where boolean value is expected (e.g., in if conditions) the foreign value is converted to boolean if possible or considered to be true.

+ +

Rescuing Foreign Exceptions

+ +

Foreign exceptions can be caught by rescue Polyglot::ForeignException => e or by rescue foreign_meta_object. +It is possible to rescue any exception (Ruby or foreign) with rescue Exception => e.

+ +

This naturally stems from the ancestors of a foreign exception:

+
Java.type("java.lang.RuntimeException").new.class.ancestors
+# => [Polyglot::ForeignException, Polyglot::ExceptionTrait, Polyglot::ObjectTrait, Exception, Object, Kernel, BasicObject]
+
+ +

Accessing Java Objects

+ +

TruffleRuby’s Java interoperability interface is similar to the interface from the Nashorn JavaScript implementation, as also implemented by GraalVM’s JavaScript implementation.

+ +

It is easier to use Java interoperability in JVM mode (--jvm). Java interoperability is also supported in native mode but requires more setup. +See here for more details.

+ +

Java.type('name') returns a Java type, given a name such as java.lang.Integer or int[]. +With the type object, .new will create an instance, .foo will call the static method foo, [:FOO] will read the static field +FOO, and so on. +To access methods of the java.lang.Class instance, use [:class], such as MyClass[:class].getName. +You can also go from the java.lang.Class instance to the Java type by using [:static].

+ +

To import a Java class in the enclosing module, use MyClass = Java.type 'java.lang.MyClass' or Java.import 'java.lang.MyClass'.

+ +

Embedding in Java

+ +

TruffleRuby is embedded via the Polyglot API, which is part of GraalVM. +You will need to use GraalVM to use this API.

+ +
import org.graalvm.polyglot.*;
+
+class Embedding {
+    public static void main(String[] args) {
+        Context polyglot = Context.newBuilder().allowAllAccess(true).build();
+        Value array = polyglot.eval("ruby", "[1,2,42,4]");
+        int result = array.getArrayElement(2).asInt();
+        System.out.println(result);
+    }
+}
+
+ +

Using Ruby Objects from Embedding Java

+ +

Ruby objects are represented by the Value class when embedded in Java.

+ +

Accessing Arrays

+ +
boolean hasArrayElements()
+Value getArrayElement(long index)
+void setArrayElement(long index, Object value)
+boolean removeArrayElement(long index)
+long getArraySize()
+
+ +

Accessing Methods in Objects

+ +
boolean hasMembers()
+boolean hasMember(String identifier)
+Value getMember(String identifier)
+Set<String> getMemberKeys
+void putMember(String identifier, Object value
+boolean removeMember(String identifier)
+
+ +

Executing Procs, Lambdas, and Methods

+ +
boolean canExecute()
+Value execute(Object... arguments)
+void executeVoid(Object... arguments)
+
+ +

Instantiating Classes

+ +
boolean canInstantiate() {
+Value newInstance(Object... arguments)
+
+ +

Accessing Primitives

+ +
boolean isString()
+String asString()
+boolean isBoolean()
+boolean asBoolean()
+boolean isNumber()
+boolean fitsInByte()
+byte asByte()
+boolean fitsInShort()
+short asShort()
+boolean fitsInInt()
+int asInt()
+boolean fitsInLong()
+long asLong()
+boolean fitsInDouble()
+double asDouble()
+boolean fitsInFloat()
+float asFloat()
+boolean isNull()
+
+ +

The JRuby migration guide includes some more examples.

+ +

Threading and Interop

+ +

Ruby is designed to be a multi-threaded language and much of the ecosystem expects threads to be available. +This may be incompatible with other Truffle languages which do not support threading, so you can disable the creation of +multiple threads with the option --single-threaded. +This option is set by default unless the Ruby launcher is used, as part of the embedded configuration, described below.

+ +

When this option is enabled, the timeout module will warn that the timeouts are being ignored, and signal handlers will warn that a signal has been caught but will not run the handler, as both of these features would require starting new threads.

+ +

Embedded Configuration

+ +

When used outside of the Ruby launcher - such as from another language’s launcher via the polyglot interface, embedded using the native polyglot library, or embedded in a Java application via the GraalVM SDK - TruffleRuby will be automatically configured to work more cooperatively within another application. +This includes options such as not installing an interrupt signal handler, and using the I/O streams from the Graal SDK. +It also turns on the single-threaded mode, as described above.

+ +

It will also warn when you explicitly do things that may not work well when embedded, such as installing your own signal handlers.

+ +

This can be turned off even when embedded, with the embedded option (--ruby.embedded=false from another launcher, or +-Dpolyglot.ruby.embedded=false from a normal Java application).

+ +

It is a separate option, but in an embedded configuration you may want to set allowNativeAccess(false) in your Context.Builder, or use the experimental --platform-native=false option, to disable use of the NFI for internal +functionality.

+ +

Also, the experimental option --cexts=false can disable C extensions.

+ +

Note: Unlike for example pure JavaScript, Ruby is more than a self-contained expression language. +It has a large core library that includes low-level I/O and system and native-memory routines which may interfere with other embedded contexts or the host system.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/ReportingPerformanceProblems/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/ReportingPerformanceProblems/index.html new file mode 100644 index 0000000..08277b1 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/ReportingPerformanceProblems/index.html @@ -0,0 +1,218 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Reporting Performance Problems

+ +

We are interested in hearing from you if you experience lower performance with TruffleRuby than with other implementations of Ruby. +Please report any performance issues you might find on GitHub.

+ +

The Compatibility guide lists some features which we know are slow and are unlikely to get faster.

+ +

Common Problems and Why

+ +

TruffleRuby uses extremely sophisticated techniques to optimize your Ruby program. +These optimizations take time to apply, so TruffleRuby is often a lot slower than other implementations until it has had time to warm up.

+ +

Also, TruffleRuby tries to find a “stable state” of your program and then automatically remove the dynamism of Ruby where it is not needed, but this then means that if the stable state is disturbed, performance lowers again until TruffleRuby can adapt to the new stable state.

+ +

Another problem is that TruffleRuby is very good at removing unnecessary work, such as calculations that +are not needed or loops that contain no work.

+ +

All of these issues make it hard to benchmark TruffleRuby. This is not a problem that is unique to us - it applies to many sophisticated virtual machines - but most Ruby implementations are not yet performing optimizations powerful enough to show these problems, so they may be new to some people in the Ruby community.

+ +

Using the Enterprise Edition of GraalVM

+ +

To experiment with how fast TruffleRuby can be, we recommend using the Enterprise Edition of GraalVM.

+ +

Using the JVM Configuration

+ +

For the best peak performance, use the JVM configuration, using --jvm. +The default native configuration starts faster but does not quite reach the same peak performance. +However, you must then use a good benchmarking tool, like benchmark-ips described below, to run the benchmark, or the slower +warmup time will mean that you do not see TruffleRuby’s true performance in the benchmark. +If you want to write simpler benchmarks that just run a while loop with a simple timer (which we would not recommend anyway), then use the default native mode so that startup and warmup time is shorter.

+ +

How to Check for Basic Performance Problems

+ +

If you are examining the performance of TruffleRuby, we would recommend that you always run with the --engine.TraceCompilation flag. +If you see compilation failures or repeated compilation of the same methods, this is an indicator that something is not working as intended and you may need to examine why, or ask us to help you do so. +If you do not run with this flag, TruffleRuby will try to work around errors and you will not see that there is a problem.

+ +

How to Write a Performance Benchmark

+ +

The TruffleRuby team recommends that you use benchmark-ips to check the performance of TruffleRuby. +It makes things easier for us if you report any potential performance problems using a report from benchmark-ips.

+ +

A benchmark could look like this:

+ +
require 'benchmark/ips'
+
+Benchmark.ips do |x|
+  x.iterations = 2
+
+  x.report("adding") do
+    14 + 2
+  end
+end
+
+ +

We use the x.iterations = extension in benchmark-ips to run the warmup and measurement cycles of benchmark-ips two times, to ensure the results are stable and that enough warmup was provided (which can be tweaked with x.warmup = 5).

+ +

You should see something like this:

+ +
Warming up --------------------------------------
+              adding    20.933k i/100ms
+              adding     1.764M i/100ms
+Calculating -------------------------------------
+              adding      2.037B (±12.7%) i/s -      9.590B in   4.965741s
+              adding      2.062B (±11.5%) i/s -     10.123B in   4.989398s
+
+ +

We want to look at the last line, which says that TruffleRuby runs 2.062 billion iterations of this block per second, with a margin of error of ±11.5%.

+ +

Compare that to an implementation like Rubinius:

+ +
Warming up --------------------------------------
+              adding    71.697k i/100ms
+              adding    74.983k i/100ms
+Calculating -------------------------------------
+              adding      2.111M (±12.2%) i/s -     10.302M
+              adding      2.126M (±10.6%) i/s -     10.452M
+
+ +

Here, TruffleRuby’s performance can be described as a thousand times faster than Rubinius. +That seems like a lot - and what is actually happening here is that TruffleRuby is optimizing +away your benchmark. +The effect is less pronounced with complex code that cannot be optimized away.

+ +

Final Technical Note: Blackholes and Value Profiling

+ +

Some benchmarking tools for other languages have features called “blackholes.” +These surround a value and make it appear to be a variable at runtime, even if it is in fact a constant, so that the optimizer does not remove it and actually performs any computations that use it. +However, TruffleRuby uses extensive value profiling (caching of values and turning them into constants), so that even if you make a value appear to be a variable at its source, it is likely to be value-profiled at an intermediate stage. +In general, more complex benchmarks that naturally defeat value profiling are preferable, rather than manually adding annotations to turn off important features.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/RubyManagers/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/RubyManagers/index.html new file mode 100644 index 0000000..eaefd88 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/RubyManagers/index.html @@ -0,0 +1,304 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Ruby Managers and Installers

+ +

If you have installed GraalVM, it is recommended to add TruffleRuby to a Ruby manager. +See +Configuring Ruby managers for the full GraalVM distribution below.

+ +

Installing TruffleRuby with rvm, ruby-build, asdf or ruby-install

+ +

TruffleRuby is supported by the 3 major Ruby installers.

+ +

rvm

+ +

Upgrade rvm to let rvm know about the latest TruffleRuby release:

+ +
rvm get head
+
+ +

Install the latest TruffleRuby standalone release with:

+ +
rvm install truffleruby
+
+ +

You can also install the latest nightly standalone build of TruffleRuby with:

+ +
rvm install truffleruby-head
+
+ +

ruby-build and rbenv

+ +

We assume you already have ruby-build installed as a plugin for rbenv.

+ +

First, you need to upgrade ruby-build to get the latest TruffleRuby definition. +See ruby-build’s instructions for upgrading.

+ +

On macOS, if ruby-build is installed via Homebrew and you do not see the latest TruffleRuby release, you might need to install the HEAD version of ruby-build with:

+ +
brew reinstall --HEAD ruby-build
+
+ +

Check the latest available version of TruffleRuby with:

+ +
rbenv install --list
+
+ +

Then install the latest TruffleRuby standalone release with:

+ +
rbenv install truffleruby-[LATEST_VERSION]
+
+ +

You can also install the latest nightly standalone build of TruffleRuby with:

+ +
rbenv install truffleruby-dev
+
+ +

You can also install TruffleRuby+GraalVM with:

+ +
rbenv install truffleruby+graalvm-[LATEST_VERSION] OR truffleruby+graalvm-dev
+
+ +

asdf (with asdf-ruby plugin)

+ +

See https://github.com/asdf-vm/asdf-ruby for installing and updating asdf-ruby.

+ +

You can install a TruffleRuby standalone release or nightly build with:

+ +
asdf install ruby truffleruby-VERSION OR truffleruby-dev
+
+ +

You can install TruffleRuby+GraalVM with:

+ +
asdf install ruby truffleruby+graalvm-VERSION OR truffleruby+graalvm-dev
+
+ +

ruby-install and chruby

+ +

See https://github.com/postmodern/ruby-install#install for installing and updating ruby-install.

+ +

First, ensure you have the latest ruby-install release. +Check your version with:

+ +
ruby-install --version
+
+ +

And compare to the latest tag. +If it is older, you should update to the latest ruby-install (e.g. 0.8.4 is necessary for aarch64 support). +Follow the installation instructions, since the steps for upgrading ruby-install are the same as the steps for installing it.

+ +

Then install the latest TruffleRuby standalone release with:

+ +
ruby-install --latest
+ruby-install truffleruby
+
+ +

You can also install TruffleRuby+GraalVM with:

+ +
ruby-install truffleruby-graalvm
+
+ +

ruby-install does not support installing nightly builds. +Please use ruby-build (which also works with chruby) if you want to install nightly builds:

+ +
ruby-build truffleruby-dev ~/.rubies/truffleruby-dev
+OR
+ruby-build truffleruby+graalvm-dev ~/.rubies/truffleruby+graalvm-dev
+
+ +

There are also instructions on the chruby wiki if you prefer to install TruffleRuby manually.

+ +

Configuring Ruby Managers for the Full GraalVM Distribution

+ +

When installing GraalVM and Ruby, it is recommended to add TruffleRuby to a Ruby manager for ease of use.

+ +

Make sure you ran the post-install script before adding GraalVM to Ruby managers.

+ +

Then follow these steps to integrate GraalVM with your Ruby manager.

+ +

The first step is to find the TruffleRuby home. +On macOS, run:

+
ruby_home=$(path/to/graalvm/Contents/Home/bin/ruby -e 'print RbConfig::CONFIG["prefix"]')
+
+ +

On Linux and other platforms, run:

+
ruby_home=$(path/to/graalvm/bin/ruby -e 'print RbConfig::CONFIG["prefix"]')
+
+ +

rbenv

+ +

To add TruffleRuby to rbenv, create a symbolic link in the versions directory of rbenv:

+ +
ln -s "$ruby_home" "$RBENV_ROOT/versions/truffleruby"
+rbenv shell truffleruby
+ruby --version
+
+ +

chruby

+ +

To add TruffleRuby to chruby, create a symbolic link to the $HOME/.rubies directory:

+ +
ln -s "$ruby_home" "$HOME/.rubies/truffleruby"
+chruby truffleruby
+ruby --version
+
+ +

rvm

+ +

rvm has a command for adding a precompiled Ruby to the list of available rubies:

+ +
rvm mount "$ruby_home" -n truffleruby
+rvm use ext-truffleruby
+ruby --version
+
+ +

asdf (with asdf-ruby plugin)

+ +

To add TruffleRuby to asdf, create a symbolic link in the .installs/ruby directory. +You also need to reshim:

+ +
ln -s "$ruby_home" "$HOME/.asdf/installs/ruby/truffleruby"
+asdf reshim ruby truffleruby
+asdf local ruby truffleruby
+ruby --version
+
+ +

Using TruffleRuby without a Ruby Manager

+ +

If you are using a Ruby manager like rvm, rbenv, or chruby and wish not to add TruffleRuby to one of them, make sure that the manager does not set environment variables GEM_HOME and GEM_PATH. +The variables are picked up by TruffleRuby (as any other Ruby implementation would do) causing TruffleRuby to pickup the wrong gem home instead of its own.

+ +

It can be fixed for the current terminal by clearing the environment with one of the following commands:

+ +
rbenv system
+chruby system
+rvm use system
+# Or manually:
+unset GEM_HOME GEM_PATH
+
+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/RuntimeConfigurations/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/RuntimeConfigurations/index.html new file mode 100644 index 0000000..1eecad4 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/RuntimeConfigurations/index.html @@ -0,0 +1,187 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Deploying TruffleRuby

+ +

If you are attempting to experiment with deploying TruffleRuby to production we would encourage you to contact us so we can help you understand what is possible at the moment and to help solve any issues for you.

+ +

This document details TruffleRuby’s different runtime configurations.

+ +

TruffleRuby Runtime Configurations

+ +

There are two main configurations of TruffleRuby - native and JVM. +It is important to understand the different configurations of TruffleRuby, as each has different capabilities and performance characteristics. +You should pick the execution mode that is appropriate for your application.

+ +

Native Configuration

+ +

When distributed as part of GraalVM, TruffleRuby by default runs in the native configuration. +In this configuration, TruffleRuby is ahead-of-time compiled to a standalone native executable. +This means that you do not need a JVM installed on your system to use it.

+ +

The advantages of the native configuration are that it starts about as fast as MRI, it may use less memory, and it becomes fast in less time than the JVM +configuration. +The disadvantages are that you can’t use Java tools like VisualVM, it is less convenient for Java interoperability (see the details here), and peak performance may be lower than on the JVM.

+ +

The native configuration is used by default, but you can also request it using --native. +To use polyglot programming with the native configuration, you need to pass the --polyglot flag.

+ +

JVM Configuration

+ +

TruffleRuby can also be used in the JVM configuration, where it runs as a normal Java application on the JVM. +The advantages of the JVM configuration are that you can use Java interoperability easily, and peak performance may be higher than the native configuration. +The disadvantages are that it takes much longer to start and to get fast, and may use more memory. +You can select the JVM configuration by passing --jvm.

+ +

Selecting the Best Configuration

+ +

If you are running a short-running program you probably want the default, native, configuration. +If you are running a long-running program and want the highest possible performance you probably want the JVM configuration, by using --jvm.

+ +

Getting the Best Startup Time Performance

+ +

To get the best startup time in most cases you want to use the native configuration, which is the default.

+ +

Getting the Lowest Memory Footprint

+ +

To get the lowest memory footprint you probably initially want to use the native configuration, but as you get a larger working set of objects you may find that the simpler garbage collector and current lack of compressed ordinary object pointers (OOPS) actually increases your memory footprint and you will be better off with the JVM configuration using --jvm to reduce memory use.

+ +

Getting the Best Peak Performance from TruffleRuby

+ +

To get the best peak performance from TruffleRuby for longer-running applications we would in most cases recommend the JVM configuration with --jvm.

+ +

However to reach this peak performance you need to warm-up TruffleRuby, as you do with most heavily optimising virtual machines. +This is done by running the application under load for a period of time. +If you monitor the performance (by measuring operation time or response time) you will see it reduce over time and then probably stabilise.

+ +

Logging

+ +

Ruby application logging and warning works as in the standard implementation of Ruby.

+ +

For logging of TruffleRuby internals, standard Java logging is used. +The logging level can be set with --log.level=INFO, =FINEST, or so on.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/StandaloneDistribution/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/StandaloneDistribution/index.html new file mode 100644 index 0000000..8fb1060 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/StandaloneDistribution/index.html @@ -0,0 +1,214 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Standalone Distribution

+ +

For details regarding the three ways to install TruffleRuby, see Getting Started. +The recommended way is to install GraalVM as it provides the most flexibility. +However, you can also use what we call the standalone distribution of TruffleRuby, either via your Ruby manager/installer, or as a simple binary tarball.

+ +

Releases of the standalone distribution are available on GitHub. +Nightly builds are also available.

+ +

The standalone distributions are the files:

+
truffleruby-VERSION-linux-amd64.tar.gz
+truffleruby-VERSION-macos-amd64.tar.gz
+
+ +

Testing TruffleRuby in TravisCI

+ +

TruffleRuby is now integrated in TravisCI. +Just add truffleruby in the build matrix, such as:

+ +
language: ruby
+rvm:
+  - 2.6.1
+  - truffleruby
+  - truffleruby-head
+
+ +

See the TravisCI documentation for details. +Please report any issue you might find.

+ +

Testing TruffleRuby in GitHub Actions

+ +

In GitHub Actions, you can easily setup TruffleRuby with ruby/setup-ruby:

+ +
name: My workflow
+on: [push]
+jobs:
+  test:
+    strategy:
+      fail-fast: false
+      matrix:
+        ruby: [ 2.6, truffleruby, truffleruby-head ]
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: ruby/setup-ruby@v1
+      with:
+        ruby-version: $
+    - run: ruby -v
+
+ +

See the README of that action for more documentation.

+ +

Testing TruffleRuby in CI

+ +

If you use another continuous integration system, simply follow these instructions to run TruffleRuby in CI: download and extract the archive, add it to PATH, and run the post-install script.

+ +

Latest Release

+ +

Set TRUFFLERUBY_VERSION to the latest TruffleRuby version from GitHub releases.

+ +
export TRUFFLERUBY_VERSION=<desired_version>
+curl -L https://github.com/oracle/truffleruby/releases/download/vm-$TRUFFLERUBY_VERSION/truffleruby-$TRUFFLERUBY_VERSION-linux-amd64.tar.gz | tar xz
+export PATH="$PWD/truffleruby-$TRUFFLERUBY_VERSION-linux-amd64/bin:$PATH"
+$PWD/truffleruby-$TRUFFLERUBY_VERSION-linux-amd64/lib/truffle/post_install_hook.sh
+ruby -v
+
+ +

Latest Nightly Build

+ +

Here are the instructions for manually installing the latest nightly build:

+ +
curl -L https://github.com/ruby/truffleruby-dev-builder/releases/latest/download/truffleruby-head-ubuntu-18.04.tar.gz | tar xz
+export PATH="$PWD/truffleruby-head/bin:$PATH"
+$PWD/truffleruby-head/lib/truffle/post_install_hook.sh
+ruby -v
+
+ +

RubyGems Configuration

+ +

Note that you also need to ensure GEM_HOME and GEM_PATH are not set, so TruffleRuby uses the correct GEM_HOME and GEM_PATH. +See Using TruffleRuby without a Ruby manager +for details.

+ +

Dependencies

+ +

TruffleRuby’s dependencies need to be installed for TruffleRuby to run correctly.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Tools/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Tools/index.html new file mode 100644 index 0000000..019c6d7 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/Tools/index.html @@ -0,0 +1,363 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Development Tools for Ruby

+ +

TruffleRuby ships with tools automatically provided by GraalVM. Run --help:tools to see a full list of options.

+ +

The following program is used for illustration:

+ +
require 'chunky_png'
+
+a = ChunkyPNG::Image.from_file('a.png')
+b = ChunkyPNG::Image.from_file('b.png')
+c = a.compose(b, 0, 0)
+c.save('c.png')
+
+ +

VisualVM

+ +

You need to use the --jvm runtime configuration and install GraalVM to use VisualVM.

+ +

VisualVM is a GUI with many tools:

+ +
    +
  • monitoring such as CPU usage, heap size, time spent in GC, etc (tab: Monitor)
  • +
  • capturing and exploring heap dumps (tab: Monitor)
  • +
  • a list of threads and their status, and thread dumps (tab: Threads)
  • +
  • CPU and memory sampling profilers at the Java level (tab: Sampler)
  • +
  • a CPU sampling profiler at the Ruby level (tab: Polyglot Sampler)
  • +
+ +

There is more documentation about VisualVM on the GraalVM website.

+ +

Profiling

+ +

CPU Tracer

+ +

The CPU tracer records the number of times methods, blocks, or statements that are run, and prints a histogram. +Enable it with --cputracer:

+ +
-----------------------------------------------------------------------------------------------------------------------------------
+Tracing Histogram. Counted a total of 41663 element executions.
+  Total Count: Number of times the element was executed and percentage of total executions.
+  Interpreted Count: Number of times the element was interpreted and percentage of total executions of this element.
+  Compiled Count: Number of times the compiled element was executed and percentage of total executions of this element.
+-----------------------------------------------------------------------------------------------------------------------------------
+ Name                                               |          Total Count |    Interpreted Count |       Compiled Count | Location
+-----------------------------------------------------------------------------------------------------------------------------------
+ ChunkyPNG::Color#a                                 |          6474  15.5% |          6474 100.0% |             0   0.0% | chunky_png/color.rb~296-298:12189-12237
+ block in ChunkyPNG::Canvas::PNGDecoding#decode_png_str_scanline_paeth |          5248  12.6% |          5248 100.0% |             0   0.0% | chunky_png/canvas/png_decoding.rb~476:25098-25131
+ ChunkyPNG::Color#int8_mult                         |          3619   8.7% |          3619 100.0% |             0   0.0% | chunky_png/color.rb~344-347:13821-13900
+ ChunkyPNG::Canvas#width                            |          3205   7.7% |          3205 100.0% |             0   0.0% | chunky_png/canvas.rb~50:1853-1874
+ block in ChunkyPNG::Canvas::PNGEncoding#encode_png_str_scanline_up |          3072   7.4% |          3072 100.0% |             0   0.0% | chunky_png/canvas/png_encoding.rb~404:20111-20145
+ ChunkyPNG::Canvas#get_pixel                        |          2048   4.9% |          2048 100.0% |             0   0.0% | chunky_png/canvas.rb~184-186:7144-7203
+ ChunkyPNG::Color#opaque?                           |          1872   4.5% |          1872 100.0% |             0   0.0% | chunky_png/color.rb~304-306:12410-12468
+ ChunkyPNG::Color#fully_transparent?                |          1500   3.6% |          1500 100.0% |             0   0.0% | chunky_png/color.rb~327-329:13138-13207
+ block in ChunkyPNG::Canvas::PNGDecoding#decode_png_str_scanline_up |          1408   3.4% |          1408 100.0% |             0   0.0% | chunky_png/canvas/png_decoding.rb~453:24009-24042
+ block in ChunkyPNG::Canvas::PNGDecoding#decode_png_str_scanline_sub |          1280   3.1% |          1280 100.0% |             0   0.0% | chunky_png/canvas/png_decoding.rb~443:23517-23550
+ ChunkyPNG::Color#b                                 |          1037   2.5% |          1037 100.0% |             0   0.0% | chunky_png/color.rb~288-290:11969-12024
+ ChunkyPNG::Color#r                                 |          1036   2.5% |          1036 100.0% |             0   0.0% | chunky_png/color.rb~272-274:11534-11590
+ ChunkyPNG::Color#g                                 |          1035   2.5% |          1035 100.0% |             0   0.0% | chunky_png/color.rb~280-282:11752-11808
+ ChunkyPNG::Color#compose_quick                     |          1024   2.5% |          1024 100.0% |             0   0.0% | chunky_png/color.rb~358-368:14300-14740
+ ChunkyPNG::Canvas#set_pixel                        |          1024   2.5% |          1024 100.0% |             0   0.0% | chunky_png/canvas.rb~149-151:5706-5780
+ block (2 levels) in ChunkyPNG::Canvas::Operations#compose! |          1024   2.5% |          1024 100.0% |             0   0.0% | chunky_png/canvas/operations.rb~58:2331-2367
+ block in ChunkyPNG::Palette#opaque?                |           848   2.0% |           848 100.0% |             0   0.0% | chunky_png/palette.rb~89:3319-3361
+ SortedSet#add                                      |           848   2.0% |           848 100.0% |             0   0.0% | lib/mri/set.rb~741-745:1000-1169
+ ChunkyPNG::Color#rgba                              |           518   1.2% |           518 100.0% |             0   0.0% | chunky_png/color.rb~100-102:3905-3973
+ block in ChunkyPNG::Canvas::PNGDecoding#decode_png_str_scanline_average |           256   0.6% |           256 100.0% |             0   0.0% | chunky_png/canvas/png_decoding.rb~464:24513-24546
+ rb_str_new                                         |           199   0.5% |           199 100.0% |             0   0.0% | src/main/c/cext/ruby.c~837:23634-23684
+ is_managed_rstring_ptr                             |           193   0.5% |           193 100.0% |             0   0.0% | src/main/c/cext/ruby.c~832:23499-23538
+ is_rstring_ptr                                     |           193   0.5% |           193 100.0% |             0   0.0% | src/main/c/cext/ruby.c~828:23430-23461
+ strlen                                             |           190   0.5% |           190 100.0% |             0   0.0% | string.c~56:0
+ rb_str_new_cstr                                    |           190   0.5% |           190 100.0% |             0   0.0% | src/main/c/cext/ruby.c~858:24268-24310
+ RB_NIL_P                                           |           138   0.3% |           138 100.0% |             0   0.0% | src/main/c/cext/ruby.c~413:11068-11094
+
+ +

Note how the C function strlen and the C preprocessor macro RB_NIL_P show up, being called by the zlib C extension.

+ +

CPU Sampler

+ +

The CPU tracer records the cumulative time spent executing methods, blocks, or statements, and prints a histogram. Enable it with --cpusampler:

+ +
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+Sampling Histogram. Recorded 3895 samples with period 1ms.
+  Self Time: Time spent on the top of the stack.
+  Total Time: Time spent somewhere on the stack.
+  Opt %: Percent of time spent in compiled and therefore non-interpreted code.
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Thread: Thread[main,5,main]
+ Name                                                                                |      Total Time     |  Opt % ||       Self Time     |  Opt % | Location
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ <main>                                                                              |       3635ms  93.3% |   0.0% ||       1086ms  27.9% |   0.0% | test.rb~1-6:0-140
+ <top (required)>                                                                    |       1068ms  27.4% |   0.0% ||        708ms  18.2% |   0.0% | chunky_png.rb~1-179:0-5528
+ block in ChunkyPNG::Canvas::PNGEncoding#encode_png_str_scanline_up                  |        643ms  16.5% |   0.0% ||        643ms  16.5% |   0.0% | chunky_png/canvas/png_encoding.rb~404:20111-20145
+ __sulong_dispose_context                                                            |        260ms   6.7% |   0.0% ||        257ms   6.6% |   0.0% | sulong_dispose_context.c~33:0
+ block in ChunkyPNG::Canvas::PNGDecoding#decode_png_str_scanline_paeth               |         58ms   1.5% |   0.0% ||         58ms   1.5% |   0.0% | chunky_png/canvas/png_decoding.rb~476:25098-25131
+ do_checksum                                                                         |         77ms   2.0% |   0.0% ||         47ms   1.2% |   0.0% | src/main/c/zlib/zlib.c~389:12721-12796
+ ChunkyPNG::Palette#initialize                                                       |         52ms   1.3% |   0.0% ||         41ms   1.1% |   0.0% | chunky_png/palette.rb~21-24:954-1074
+ block in Gem::Specification.load                                                    |         39ms   1.0% |   0.0% ||         39ms   1.0% |   0.0% | bigdecimal-1.4.1.gemspec~5:126-154
+ rb_define_method                                                                    |         50ms   1.3% |   0.0% ||         36ms   0.9% |   0.0% | src/main/c/cext/ruby.c~2185:61849-61941
+ rb_tr_init                                                                          |         44ms   1.1% |   0.0% ||         34ms   0.9% |   0.0% | src/main/c/cext/ruby.c~93:2351-2384
+ <top (required)>                                                                    |         42ms   1.1% |   0.0% ||         33ms   0.8% |   0.0% | chunky_png/canvas.rb~1-372:0-13593
+ __sulong_byte_array_to_native                                                       |         30ms   0.8% |   0.0% ||         30ms   0.8% |   0.0% | crt0.c~59:0
+ rb_str_new                                                                          |         33ms   0.8% |   0.0% ||         29ms   0.7% |   0.0% | src/main/c/cext/ruby.c~837:23634-23684
+ __sulong_init_context                                                               |         68ms   1.7% |   0.0% ||         26ms   0.7% |   0.0% | crt0.c~80:0
+ Init_zlib                                                                           |        145ms   3.7% |   0.0% ||         24ms   0.6% |   0.0% | src/main/c/zlib/zlib.c~4465:112360-112374
+ rb_zlib_crc32                                                                       |        101ms   2.6% |   0.0% ||         24ms   0.6% |   0.0% | src/main/c/zlib/zlib.c~473:14760-14808
+ rb_data_typed_object_wrap                                                           |         21ms   0.5% |   0.0% ||         21ms   0.5% |   0.0% | src/main/c/cext/ruby.c~2834:79544-79639
+ rb_tr_load_library                                                                  |         22ms   0.6% |   0.0% ||         20ms   0.5% |   0.0% | src/main/c/cext/ruby.c~2906:81858-81897
+ ChunkyPNG::Canvas::PNGEncoding#encode_png_str_scanline_up                           |        662ms  17.0% |   0.0% ||         19ms   0.5% |   0.0% | chunky_png/canvas/png_encoding.rb~403-409:20027-20347
+ rb_define_class_id_under                                                            |         18ms   0.5% |   0.0% ||         18ms   0.5% |   0.0% | src/main/c/cext/ruby.c~2165:61106-61178
+ ChunkyPNG::Chunk.read                                                               |        137ms   3.5% |   0.0% ||         17ms   0.4% |   0.0% | chunky_png/chunk.rb~18-26:783-1054
+ Datastream                                                                          |         15ms   0.4% |   0.0% ||         15ms   0.4% |   0.0% | chunky_png/datastream.rb~11:364-381
+ ChunkyPNG::Color#compose_quick                                                      |         37ms   0.9% |   0.0% ||         14ms   0.4% |   0.0% | chunky_png/color.rb~358-368:14300-14740
+ rb_deflate_s_deflate                                                                |         27ms   0.7% |   0.0% ||         13ms   0.3% |   0.0% | src/main/c/zlib/zlib.c~1622:44042-44097
+ ChunkyPNG::Canvas::PNGDecoding#decode_png_str_scanline_paeth                        |         71ms   1.8% |   0.0% ||         13ms   0.3% |   0.0% | chunky_png/canvas/png_decoding.rb~475-488:25010-25650
+ ChunkyPNG::Palette.from_canvas                                                      |         64ms   1.6% |   0.0% ||         12ms   0.3% |   0.0% | chunky_png/palette.rb~63-68:2377-2629
+ __sulong_byte_arrays_to_native                                                      |         42ms   1.1% |   0.0% ||         12ms   0.3% |   0.0% | crt0.c~69:0
+ ChunkyPNG::Chunk::ImageData.combine_chunks                                          |        167ms   4.3% |   0.0% ||         12ms   0.3% |   0.0% | chunky_png/chunk.rb~247-253:9766-9980
+ rb_inflate_initialize                                                               |         19ms   0.5% |   0.0% ||         12ms   0.3% |   0.0% | src/main/c/zlib/zlib.c~1887:51522-51576
+ strlen                                                                              |         12ms   0.3% |   0.0% ||         12ms   0.3% |   0.0% | string.c~56:0
+ zstream_run                                                                         |         76ms   2.0% |   0.0% ||         12ms   0.3% |   0.0% | src/main/c/zlib/zlib.c~1024:27867-27929
+
+ +

By default you see this histogram, but you can also see a call tree with --cpusampler.Output=calltree.

+ +
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+Sampling CallTree. Recorded 3102 samples with period 1ms.
+  Self Time: Time spent on the top of the stack.
+  Total Time: Time spent somewhere on the stack.
+  Opt %: Percent of time spent in compiled and therefore non-interpreted code.
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Thread: Thread[main,5,main]
+ Name                                                                                        |      Total Time     |  Opt % ||       Self Time     |  Opt % | Location
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+  ChunkyPNG::Canvas::PNGEncoding#save                                                        |        582ms  18.8% |   0.0% ||          5ms   0.2% |   0.0% | chunky_png/canvas/png_encoding.rb~42-44:1754-1871
+   block in ChunkyPNG::Canvas::PNGEncoding#save                                              |        577ms  18.6% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/canvas/png_encoding.rb~43:1797-1861
+    ChunkyPNG::Canvas::PNGEncoding#write                                                     |        577ms  18.6% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/canvas/png_encoding.rb~34-36:1430-1521
+     ChunkyPNG::Image#to_datastream                                                          |        571ms  18.4% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/image.rb~61-65:2314-2447
+      ChunkyPNG::Canvas::PNGEncoding#to_datastream                                           |        569ms  18.3% |   0.0% ||          1ms   0.0% |   0.0% | chunky_png/canvas/png_encoding.rb~74-89:3715-4561
+       ChunkyPNG::Canvas::PNGEncoding#encode_png_pixelstream                                 |        489ms  15.8% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/canvas/png_encoding.rb~153-165:7702-8570
+        ChunkyPNG::Canvas::PNGEncoding#encode_png_image_without_interlacing                  |        489ms  15.8% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/canvas/png_encoding.rb~172-176:8925-9195
+         ChunkyPNG::Canvas::PNGEncoding#encode_png_image_pass_to_stream                      |        489ms  15.8% |   0.0% ||          3ms   0.1% |   0.0% | chunky_png/canvas/png_encoding.rb~203-231:10502-11685
+          block in ChunkyPNG::Canvas::PNGEncoding#encode_png_image_pass_to_stream            |        481ms  15.5% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/canvas/png_encoding.rb~225:11418-11456
+           ChunkyPNG::Canvas::PNGEncoding#encode_png_str_scanline_up                         |        481ms  15.5% |   0.0% ||         11ms   0.4% |   0.0% | chunky_png/canvas/png_encoding.rb~403-409:20027-20347
+            block in ChunkyPNG::Canvas::PNGEncoding#encode_png_str_scanline_up               |        470ms  15.2% |   0.0% ||        470ms  15.2% |   0.0% | chunky_png/canvas/png_encoding.rb~404:20111-20145
+          block in ChunkyPNG::Canvas::PNGEncoding#encode_png_image_pass_to_stream            |          5ms   0.2% |   0.0% ||          1ms   0.0% |   0.0% | chunky_png/canvas/png_encoding.rb~219:11252-11284
+           ChunkyPNG::Canvas::PNGEncoding#encode_png_pixels_to_scanline_truecolor_8bit       |          4ms   0.1% |   0.0% ||          4ms   0.1% |   0.0% | chunky_png/canvas/png_encoding.rb~236-238:11896-12009
+       ChunkyPNG::Canvas::PNGEncoding#determine_png_encoding                                 |         60ms   1.9% |   0.0% ||          2ms   0.1% |   0.0% | chunky_png/canvas/png_encoding.rb~102-145:5085-7303
+        ChunkyPNG::Canvas#palette                                                            |         51ms   1.6% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/canvas.rb~268-270:10089-10154
+         ChunkyPNG::Palette.from_canvas                                                      |         51ms   1.6% |   0.0% ||          9ms   0.3% |   0.0% | chunky_png/palette.rb~63-68:2377-2629
+          ChunkyPNG::Palette#initialize                                                      |         42ms   1.4% |   0.0% ||         30ms   1.0% |   0.0% | chunky_png/palette.rb~21-24:954-1074
+           SortedSet#initialize                                                              |         12ms   0.4% |   0.0% ||          0ms   0.0% |   0.0% | lib/mri/set.rb~726-729:725-819
+            SortedSet#merge                                                                  |         12ms   0.4% |   0.0% ||          6ms   0.2% |   0.0% | lib/mri/set.rb~770-773:1759-1847
+             SortedSet#add                                                                   |          6ms   0.2% |   0.0% ||          6ms   0.2% |   0.0% | lib/mri/set.rb~741-745:1000-1169
+        ChunkyPNG::Palette#best_color_settings                                               |          7ms   0.2% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/palette.rb~195-211:7150-7613
+         ChunkyPNG::Palette#opaque?                                                          |          3ms   0.1% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/palette.rb~88-90:3303-3369
+          SortedSet#each                                                                     |          3ms   0.1% |   0.0% ||          2ms   0.1% |   0.0% | lib/mri/set.rb~775-779:1850-2004
+           block in ChunkyPNG::Palette#opaque?                                               |          1ms   0.0% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/palette.rb~89:3319-3361
+            ChunkyPNG::Color#opaque?                                                         |          1ms   0.0% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/color.rb~304-306:12410-12468
+             ChunkyPNG::Color#a                                                              |          1ms   0.0% |   0.0% ||          1ms   0.0% |   0.0% | chunky_png/color.rb~296-298:12189-12237
+         ChunkyPNG::Palette#black_and_white?                                                 |          3ms   0.1% |   0.0% ||          1ms   0.0% |   0.0% | chunky_png/palette.rb~104-106:3841-3940
+          SortedSet#each                                                                     |          2ms   0.1% |   0.0% ||          1ms   0.0% |   0.0% | lib/mri/set.rb~775-779:1850-2004
+           SortedSet#to_a                                                                    |          1ms   0.0% |   0.0% ||          1ms   0.0% |   0.0% | lib/mri/set.rb~781-784:2007-2116
+         ChunkyPNG::Palette#grayscale?                                                       |          1ms   0.0% |   0.0% ||          0ms   0.0% |   0.0% | chunky_png/palette.rb~96-98:3570-3642
+          SortedSet#each                                                                     |          1ms   0.0% |   0.0% ||          1ms   0.0% |   0.0% | lib/mri/set.rb~775-779:1850-2004
+       ChunkyPNG::Chunk::ImageData.split_in_chunks                                           |         19ms   0.6% |   0.0% ||          7ms   0.2% |   0.0% | chunky_png/chunk.rb~255-259:9983-10267
+        rb_deflate_s_deflate                                                                 |         12ms   0.4% |   0.0% ||          4ms   0.1% |   0.0% | src/main/c/zlib/zlib.c~1622:44042-44097
+         rb_ensure                                                                           |          5ms   0.2% |   0.0% ||          1ms   0.0% |   0.0% | src/main/c/cext/ruby.c~2107:59266-59360
+          deflate_run                                                                        |          4ms   0.1% |   0.0% ||          0ms   0.0% |   0.0% | src/main/c/zlib/zlib.c~1589:43218-43240
+           zstream_run                                                                       |          4ms   0.1% |   0.0% ||          0ms   0.0% |   0.0% | src/main/c/zlib/zlib.c~1024:27867-27929
+            zstream_append_input                                                             |          1ms   0.0% |   0.0% ||          0ms   0.0% |   0.0% | src/main/c/zlib/zlib.c~827:23738-23804
+             rb_str_buf_cat                                                                  |          1ms   0.0% |   0.0% ||          0ms   0.0% |   0.0% | src/main/c/cext/ruby.c~981:27520-27591
+              rb_str_cat                                                                     |          1ms   0.0% |   0.0% ||          0ms   0.0% |   0.0% | src/main/c/cext/ruby.c~882:24901-24968
+               rb_enc_str_new                                                                |          1ms   0.0% |   0.0% ||          0ms   0.0% |   0.0% | src/main/c/cext/ruby.c~1301:37035-37101
+
+ +

Coverage

+ +

The coverage tool reports coverage by statement, line, and root. +Root means the root of a function, i.e., how many methods and blocks were covered. +Enable it with --coverage:

+ +
-------------------------------------------------------------------------------------------------------------------------------------
+Code coverage histogram.
+  Shows what percent of each element was covered during execution
+-------------------------------------------------------------------------------------------------------------------------------------
+ Path                                                                                            |  Statements |    Lines |    Roots
+-------------------------------------------------------------------------------------------------------------------------------------
+ chunky_png-1.3.11/lib/chunky_png.rb                                                             |     100.00% |  100.00% |  100.00%
+ chunky_png-1.3.11/lib/chunky_png/canvas.rb                                                      |      58.40% |   69.41% |   35.90%
+ chunky_png-1.3.11/lib/chunky_png/canvas/adam7_interlacing.rb                                    |      28.57% |   50.00% |   28.57%
+ chunky_png-1.3.11/lib/chunky_png/canvas/data_url_exporting.rb                                   |      80.00% |   83.33% |   80.00%
+ chunky_png-1.3.11/lib/chunky_png/canvas/data_url_importing.rb                                   |      57.14% |   70.00% |   80.00%
+ chunky_png-1.3.11/lib/chunky_png/canvas/drawing.rb                                              |       8.28% |   44.02% |   13.33%
+ chunky_png-1.3.11/lib/chunky_png/canvas/masking.rb                                              |      28.57% |   51.72% |   44.44%
+ chunky_png-1.3.11/lib/chunky_png/canvas/operations.rb                                           |      42.42% |   65.07% |   21.95%
+ chunky_png-1.3.11/lib/chunky_png/canvas/png_decoding.rb                                         |      52.84% |   68.27% |   26.98%
+ chunky_png-1.3.11/lib/chunky_png/canvas/png_encoding.rb                                         |      44.44% |   62.07% |   40.43%
+ chunky_png-1.3.11/lib/chunky_png/canvas/resampling.rb                                           |      17.46% |   48.51% |   25.00%
+ chunky_png-1.3.11/lib/chunky_png/canvas/stream_exporting.rb                                     |      61.54% |   72.22% |   44.44%
+ chunky_png-1.3.11/lib/chunky_png/canvas/stream_importing.rb                                     |      31.82% |   45.83% |   40.00%
+ chunky_png-1.3.11/lib/chunky_png/chunk.rb                                                       |      82.84% |   86.32% |   68.42%
+ chunky_png-1.3.11/lib/chunky_png/color.rb                                                       |      41.82% |   59.00% |   33.93%
+ chunky_png-1.3.11/lib/chunky_png/compatibility.rb                                               |      75.00% |   66.67% |   75.00%
+ chunky_png-1.3.11/lib/chunky_png/datastream.rb                                                  |      83.56% |   87.50% |   80.00%
+ chunky_png-1.3.11/lib/chunky_png/dimension.rb                                                   |      42.11% |   62.07% |   23.08%
+ chunky_png-1.3.11/lib/chunky_png/image.rb                                                       |      85.00% |   90.32% |   90.00%
+ chunky_png-1.3.11/lib/chunky_png/palette.rb                                                     |      41.18% |   63.46% |   42.31%
+ chunky_png-1.3.11/lib/chunky_png/point.rb                                                       |      42.86% |   62.96% |   25.00%
+ chunky_png-1.3.11/lib/chunky_png/vector.rb                                                      |      40.98% |   63.10% |   10.34%
+ chunky_png-1.3.11/lib/chunky_png/version.rb                                                     |     100.00% |  100.00% |  100.00%
+-------------------------------------------------------------------------------------------------------------------------------------
+
+ +

Debugging

+ +

Debugging with VSCode

+ +

This is the best-supported debugger for TruffleRuby, see this documentation.

+ +

Chrome Inspector

+ +

GraalVM lets you debug Ruby programs, and any other language supported by GraalVM, using the Chrome DevTools Protocol to attach to debuggers such as Chrome Developer Tools.

+ +

Run with --inspect, open the given URL in Chrome, drag your file system into the sources list, and then set a breakpoint and resume execution. +Having the debugger attached and simple breakpoints set should not reduce performance.

+ +

Chrome Developer Tools debugger session

+ +

NetBeans

+ +

You can also debug GraalVM languages using NetBeans. +Run with --jvm --vm.agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=y and attach to the process as if you were debugging a Java program. +Then click Toggle pause in GraalVM script (the pause icon with a G superimposed) and break, and you will break into the Ruby code. +You will see Ruby stack frames and local variables rather than Java stack frames and local variables.

+ +

NetBeans debugger session

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/TruffleRubyAdditions/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/TruffleRubyAdditions/index.html new file mode 100644 index 0000000..46c902a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/TruffleRubyAdditions/index.html @@ -0,0 +1,273 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

TruffleRuby Additional Functionality

+ +

TruffleRuby is intended to be usable as a standard Ruby implementation that runs programs developed on other implementations, but it also provides additional functionality beyond that of other implementations.

+ +

See the Compatibility guide for compatibility with other Ruby implementations.

+ +

Detecting If You Run on TruffleRuby

+ +

You can use the --version command-line option. TruffleRuby will report for example:

+
truffleruby ..., like ruby ..., GraalVM CE Native [x86_64-darwin]
+
+ +

In Ruby code, you can look at the standard RUBY_ENGINE constant, which will be 'truffleruby'. +In C code TRUFFLERUBY is defined.

+ +

It is also possible to use feature-detection instead of looking at RUBY_ENGINE.

+ +

TruffleRuby is an integral part of GraalVM, so the version number of TruffleRuby is always the same as the version of GraalVM that contains it. +If you are using TruffleRuby outside of GraalVM, such as a standard JVM, the version will be '0.0'. +You can find the version number of GraalVM and TruffleRuby using the standard RUBY_ENGINE_VERSION constant.

+ +

TruffleRuby Methods and Classes

+ +

TruffleRuby provides these non-standard methods and classes that provide additional functionality in the TruffleRuby module:

+ +
    +
  • +

    TruffleRuby.jit? reports if the GraalVM Compiler is available and will be used.

    +
  • +
  • +

    TruffleRuby.native? reports if TruffleRuby is compiled as a native executable.

    +
  • +
  • +

    TruffleRuby.cexts? reports if TruffleRuby has the GraalVM LLVM Runtime for C extensions available.

    +
  • +
  • +

    TruffleRuby.revision reports the source control revision used to build TruffleRuby as a String. Also available as RUBY_REVISION, like CRuby 2.7+.

    +
  • +
  • +

    TruffleRuby.full_memory_barrier ensures lack of reordering of loads or stores before the barrier with loads or stores after the barrier.

    +
  • +
  • +

    TruffleRuby.graalvm_home returns the GraalVM home or nil if running outside of GraalVM (e.g., TruffleRuby standalone).

    +
  • +
  • +

    TruffleRuby.synchronized(object) { } will run the block while holding an implicit lock per object instance.

    +
  • +
+ +

Atomic References

+ +
    +
  • +

    atomic_reference = TruffleRuby::AtomicReference.new(value=nil) creates a new atomic reference with a reference to a given object.

    +
  • +
  • +

    atomic_reference.get gets the value of an atomic reference, returning the value.

    +
  • +
  • +

    atomic_reference.set(new_value) sets the value of an atomic reference and causes a memory barrier on writes involving new_value.

    +
  • +
  • +

    atomic_reference.get_and_set(new_value) sets the value of an atomic reference, returns the previous value, and causes a memory barrier on writes involving new_value.

    +
  • +
  • +

    atomic_reference.compare_and_set(expected_value, new_value) sets the value of an atomic reference, only if it currently has the expected value, returning a boolean to say whether or not it was set, and causes a memory barrier on writes involving new_value. For numeric objects it will get the current value and then check that the current value is also a numeric and that it is equal to the expected value by calling ==, then perform an atomic compare and set.

    +
  • +
  • +

    AtomicReference is marshalable.

    +
  • +
+ +

Concurrent Maps

+ +

TruffleRuby::ConcurrentMap is a key-value data structure, like a Hash and using #hash and #eql? to compare keys and identity to compare values. Unlike Hash it is unordered. All methods on TruffleRuby::ConcurrentMap are thread-safe but should have higher concurrency than a fully syncronized implementation. It is intended to be used by gems such as concurrent-ruby - please use via this gem rather than using directly.

+ +
    +
  • +

    map = TruffleRuby::ConcurrentMap.new([initial_capacity: ...], [load_factor: ...])

    +
  • +
  • +

    map[key] = new_value

    +
  • +
  • +

    map[key]

    +
  • +
  • +

    map.compute_if_absent(key) { computed_value } if the key is not found, run the block and store the result. The block is run at most once. Returns the computed value.

    +
  • +
  • +

    map.compute_if_present(key) { |current_value| computed_value } if the key is found, run the block and store the result. If the block returns nil the entry for that key is removed. The block is run at most once. Returns the final value, or nil if the block returned nil.

    +
  • +
  • +

    map.compute(key) { |current_value| computed_value } run the block, passing the current value if there is one or nil, and store the result. If the block returns nil the entry for that key is removed. Returns the computed value.

    +
  • +
  • +

    map.merge_pair(key, new_value) { |existing_value| merged_value } if key is not found or is nil, store the new value, otherwise call the block and store the result, or remove the entry if the block returns nil. Returns the final value for that entry, or nil if the block returned nil.

    +
  • +
  • +

    map.replace_pair(key, expected_value, new_value) replace the value for key but only if the existing value for it is the same as expected_value (compared by identity). Returns if the value was replaced or not.

    +
  • +
  • +

    map.replace_if_exists(key, value) replace the value for key but only if it was found. Returns value if the key exists or nil.

    +
  • +
  • +

    map.get_and_set(key, new_value) sets the value for a key and returns the previous value.

    +
  • +
  • +

    map.key?(key) returns if a key is in the map.

    +
  • +
  • +

    map.delete(key) removes a key from the map if it exists, returning the value or nil if it did not exist.

    +
  • +
  • +

    map.delete_pair(key, expected_value) removes a key but only if the existing value for it is the same as expected_value (compared by identity). Returns if the key was deleted.

    +
  • +
  • +

    map.clear removes all entries from the map.

    +
  • +
  • +

    map.size gives the number of entries in the map.

    +
  • +
  • +

    map.get_or_default(key, default_value)

    +
  • +
  • +

    map.each_pair { |key, value| ... }

    +
  • +
+ +

FFI

+ +

TruffleRuby includes a Ruby-FFI backend. This should be transparent: you can just install the ffi gem as normal, and it will use TruffleRuby’s FFI backend. TruffleRuby also includes a default version of the FFI gem, so require "ffi" always works on TruffleRuby, even if the gem is not installed.

+ +

Polyglot Programming

+ +

The Polyglot and Java modules provide access to the polyglot programming functionality of GraalVM. +They are described in the Polyglot Programming guide.

+ +

Unsupported Additional Functionality

+ +

You may be able to find some other modules and methods not listed here that look interesting, such as Truffle::POSIX or Truffle::FFI. +Additional modules and methods not listed in this document are designed to support the implementation of TruffleRuby and should not be used. They may be modified or made not visible to user programs in the future, and you should not use them.

+ +

Extra macros, functions, and variables in TruffleRuby C extension headers beyond those provided by MRI, such as those starting with rb_tr_*, are unsupported and should not be used by any C extension.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/UTF8Locale/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/UTF8Locale/index.html new file mode 100644 index 0000000..a703c16 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/UTF8Locale/index.html @@ -0,0 +1,176 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

Setting Up a UTF-8 Locale

+ +

You need a UTF-8 locale to run some Ruby applications. +For example, we have found that RubyGems and ruby/spec need such a locale.

+ +

This is not needed if the $LANG environment variable is already set and:

+ +
locale
+
+ +

shows no ="C" and no warning. +Instead, all values should be "en_US.UTF-8" or other regions but still .UTF-8.

+ +

Fedora-based: RHEL, Oracle Linux, etc

+ +
export LANG=en_US.UTF-8
+
+ +

Debian-based: Ubuntu, etc

+ +

Ubuntu

+ +

The Ubuntu version of locale-gen supports arguments, so it is easy:

+
sudo apt-get install -y locales
+sudo locale-gen en_US.UTF-8
+export LANG=en_US.UTF-8
+
+ +

Non-Ubuntu

+ +

Debian and other non-Ubuntu do not support locale-gen arguments. +Instead you need to modify /etc/locale.gen:

+
# Uncomment the en_US.UTF-8 line in /etc/locale.gen
+sudo sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen
+
+# locale-gen generates locales for all uncommented locales in /etc/locale.gen
+sudo locale-gen
+export LANG=en_US.UTF-8
+
+ +

For Dockerfile’s:

+ +
# Uncomment the en_US.UTF-8 line in /etc/locale.gen
+RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen
+# locale-gen generates locales for all uncommented locales in /etc/locale.gen
+RUN locale-gen
+ENV LANG=en_US.UTF-8
+
+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/index.html new file mode 100644 index 0000000..0d17721 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/ruby/index.html @@ -0,0 +1,373 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

TruffleRuby

+ +

TruffleRuby logo

+ +

TruffleRuby is the GraalVM high-performance implementation +of the Ruby programming language.

+ +

Getting Started

+ +

TruffleRuby comes in two distributions:

+ +
    +
  • Standalone: This only contains TruffleRuby in the Native configuration, making it a smaller download.
  • +
  • GraalVM: This includes support for other languages such as JavaScript, Python and R, and supports both the Native and JVM configurations. +We recommend that you use a Ruby manager to use TruffleRuby inside GraalVM.
  • +
+ +

You can install either of those:

+ +
    +
  • Via your Ruby manager/installer (RVM, rbenv, chruby, asdf, ruby-build, ruby-install). +We recommend trying TruffleRuby dev builds which contain the latest fixes and improvements (replace VERSION by dev).
  • +
+ +

Standalone:

+
RVM:    $ rvm install truffleruby
+rbenv:  $ rbenv install truffleruby-VERSION
+asdf:   $ asdf install ruby truffleruby-VERSION
+chruby: $ ruby-install truffleruby
+        $ ruby-build truffleruby-VERSION ~/.rubies/truffleruby-VERSION
+
+

GraalVM:

+
rbenv:  $ rbenv install truffleruby+graalvm-VERSION
+asdf:   $ asdf install ruby truffleruby+graalvm-VERSION
+chruby: $ ruby-install truffleruby-graalvm
+        $ ruby-build truffleruby+graalvm-VERSION ~/.rubies/truffleruby+graalvm-VERSION
+
+ + + +
- uses: ruby/setup-ruby@v1
+  with:
+    ruby-version: truffleruby # or truffleruby-head, or truffleruby+graalvm or truffleruby+graalvm-head
+
+ + + +

You can use gem and bundle to install gems, as usual.

+ +

Please report any issues you might find on GitHub.

+ +

Aim

+ +

TruffleRuby aims to:

+ +
    +
  • Run idiomatic Ruby code faster. + +
  • +
  • Run Ruby code in parallel. +
      +
    • TruffleRuby does not have a global interpreter lock and runs Ruby code in parallel.
    • +
    +
  • +
  • Support C extensions. +
      +
    • Many C extensions work out of the box, including database drivers.
    • +
    +
  • +
  • Add fast and low-overhead interoperability with languages like Java, JavaScript, Python, and R. + +
  • +
  • Provide new tooling, such as debuggers and monitoring, that works across languages. + +
  • +
  • Provide all of the above while maintaining very high compatibility with the standard implementation of Ruby.
  • +
+ +

TruffleRuby Runtime Configurations

+ +

There are two main runtime configurations of TruffleRuby, Native and JVM, which have different trade-offs.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Configuration:Native (--native, default)JVM (--jvm)
Time to start TruffleRubyabout as fast as MRI startupslower
Time to reach peak performancefasterslower
Peak performance (also considering GC)goodbest
Java host interoperabilityneeds reflection configurationjust works
+ +

To find out which runtime configuration is being used, run ruby --version on the command line, +or check the value of RUBY_DESCRIPTION or TruffleRuby.native? in Ruby code. +Runtime configurations are further detailed in Deploying TruffleRuby.

+ +

System Compatibility

+ +

TruffleRuby is actively tested on the following systems:

+ +
    +
  • Oracle Linux 7, 8
  • +
  • Ubuntu 16.04, 18.04, 20.04, 22.04 (all LTS)
  • +
  • Fedora 35, 36
  • +
  • macOS 10.14 (Mojave), 12 (Monterey)
  • +
+ +

Architectures:

+ +
    +
  • AMD64 (aka x86_64): Supported
  • +
  • AArch64 (aka arm64): Supported on Linux (from 21.2) and on macOS (from 22.2)
  • +
+ +

You may find that TruffleRuby will not work if you severely restrict the +environment, for example, by unmounting system filesystems such as /dev/shm.

+ +

Dependencies

+ +
    +
  • make and gcc for building C and C++ extensions
  • +
  • libssl for the openssl C extension
  • +
  • libyaml for the psych C extension
  • +
  • zlib for the zlib C extension
  • +
+ +

Without these dependencies, many libraries including RubyGems will not work. +TruffleRuby will try to print a nice error message if a dependency is missing, but this can only be done on a best effort basis.

+ +

You also need to set up a UTF-8 locale if not already done.

+ +

See the contributor workflow document if you wish to build TruffleRuby from source.

+ +

Current Status

+ +

We recommend that people trying TruffleRuby on their gems and applications get in touch with us for any help they might need.

+ +

TruffleRuby runs Rails and is compatible with many gems, including C extensions. +TruffleRuby is not 100% compatible with MRI 3.1 yet. Please report any compatibility issues you might find. +TruffleRuby passes around 97% of ruby/spec, +more than any other alternative Ruby implementation.

+ +

Regarding performance, TruffleRuby is by far +the fastest Ruby implementation on the yjit-bench benchmark suite which includes railsbench, etc. +To achieve this performance TruffleRuby needs a fair amount of warmup, as other advanced JIT compilers do. +If you find any performance issue, please see this guide.

+ +

Releases

+ +

TruffleRuby has the same version and is released at the same time as GraalVM. +See the release roadmap for the release dates and information about how long releases are supported. +GraalVM CE releases are supported at most one year. +Longer support is available for GraalVM EE.

+ +

Migration from MRI

+ +

TruffleRuby should in most cases work as a drop-in replacement for MRI, but you +should read about our compatibility.

+ +

Migration from JRuby

+ +

For many use cases TruffleRuby should work as a drop-in replacement for JRuby. +However, our approach to integration with Java is different to JRuby so you +should read our migration guide.

+ +

Contact

+ +

The best way to get in touch with us is to join the #truffleruby channel on +GraalVM Slack. +You can also Tweet to @TruffleRuby, or email +benoit.daloze@oracle.com.

+ +

Please report security vulnerabilities via the process outlined in the reporting vulnerabilities guide, rather than by something public such as a GitHub issue or a Gitter conversation.

+ +

Mailing List

+ +

Announcements about GraalVM, including TruffleRuby, are made on the +graal-dev mailing list.

+ +

Authors

+ +

The main authors of TruffleRuby ordered by first contribution are: +Chris Seaton, Benoit Daloze, Kevin Menard, Petr Chalupa, Brandon Fish, Duncan MacGregor, Christian Wirth, Rafael França, Alan Wu, Nicolas Laurent, Carol Chen, Nikolay Sverchkov, Lillian Zhang, Tom Stuart, and Maple Ong.

+ +

Security

+ +

See SECURITY for how to report security vulnerabilities to Oracle. +For known vulnerabilities in Ruby, please refer to the known-cves file.

+ +

Licence

+ +

TruffleRuby is copyright (c) 2013-2023 Oracle and/or its affiliates, and is made +available to you under the terms of any one of the following three licenses:

+ +
    +
  • Eclipse Public License version 2.0, or
  • +
  • GNU General Public License version 2, or
  • +
  • GNU Lesser General Public License version 2.1.
  • +
+ +

For further licensing information, see LICENCE, 3rd_party_licenses, and doc/legal/legal.

+ +

Attribution

+ +

TruffleRuby includes infrastructure code from JRuby (e.g. parser, JCodings, Joni), core library code from the Rubinius project, as well as code from the standard implementation of Ruby, MRI.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/wasm/index.html b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/wasm/index.html new file mode 100644 index 0000000..5753f70 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/reference-manual/wasm/index.html @@ -0,0 +1,193 @@ + + + + + + + + + + + + + GraalVM + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+
+
+ Table of Contents + +

GraalVM Implementation of WebAssembly

+ +

GraalVM can run programs compiled to WebAssembly. +It can interpret and compile WebAssembly code in the binary format or embed it into other programs. +The support for WebAssembly is in the early stages of its development.

+ +

Installing Wasm

+ +

The support is not available by default, but you can add it to GraalVM using the GraalVM Updater tool:

+
gu install wasm
+
+

The above command will install a community version of a component from the GitHub catalog.

+ +

Then wasm launcher, which can run compiled WebAssembly binary code, becomes available.

+ +

Running WebAssembly Programs

+ +

You can run a program written in the language that compiles to WebAssembly on GraalVM. +For example, put the following C program in a file named floyd.c:

+
#include <stdio.h>
+
+int main() {
+  int number = 1;
+  int rows = 10;
+  for (int i = 1; i <= rows; i++) {
+    for (int j = 1; j <= i; j++) {
+      printf("%d ", number);
+      ++number;
+    }
+    printf(".\n");
+  }
+  return 0;
+}
+
+ +

Compile it using the most recent Emscripten compiler frontend version. It should produce a standalone floyd.wasm file in the current working directory:

+
emcc -o floyd.wasm floyd.c
+
+ +

Then you can run the compiled WebAssembly binary on GraalVM as follows:

+
wasm --Builtins=wasi_snapshot_preview1 floyd.wasm
+
+ +

In this example, the flag --Builtins specifies builtin modules that the Emscripten toolchain requires.

+ +

Embedding WebAssembly Programs

+ +

The compiled WebAssembly binary code can be accessed programmatically with GraalVM Polyglot API, which allows embedding GraalVM WebAssembly into user programs. Here is a simple example of how to call WebAssembly code from a Java application:

+ +
import org.graalvm.polyglot.*;
+import org.graalvm.polyglot.io.ByteSequence;
+//Load the WASM contents into a byte array
+byte[] binary = readBytes("example.wasm");
+Context.Builder contextBuilder = Context.newBuilder("wasm");
+Source.Builder sourceBuilder = Source.newBuilder("wasm", ByteSequence.create(binary), "example");
+Source source = sourceBuilder.build();
+Context context = contextBuilder.build();
+
+context.eval(source);
+
+Value mainFunction = context.getBindings("wasm").getMember("main").getMember("_start");
+mainFunction.execute();
+
+ +

For more polyglot examples, visit the Polyglot Programming page.

+ +
+
+
+ +
+ +
+ + + + +
+ +
+ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_Bd.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_Bd.ttf new file mode 100755 index 0000000..cb413bd Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_Bd.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_BdIt.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_BdIt.ttf new file mode 100755 index 0000000..cad8b44 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_BdIt.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_It.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_It.ttf new file mode 100755 index 0000000..46e5149 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_It.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_Lt.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_Lt.ttf new file mode 100755 index 0000000..5807513 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_Lt.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_LtIt.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_LtIt.ttf new file mode 100755 index 0000000..b367c35 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_LtIt.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_Rg.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_Rg.ttf new file mode 100755 index 0000000..fc18732 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_Rg.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_SBd.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_SBd.ttf new file mode 100755 index 0000000..60fb479 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_SBd.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_SBdIt.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_SBdIt.ttf new file mode 100755 index 0000000..d7e18bf Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_SBdIt.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_ULt.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_ULt.ttf new file mode 100755 index 0000000..80676af Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_ULt.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_ULtIt.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_ULtIt.ttf new file mode 100755 index 0000000..b4d55f7 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_ULtIt.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_XBd.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_XBd.ttf new file mode 100755 index 0000000..e0bdbe9 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_XBd.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_XBdIt.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_XBdIt.ttf new file mode 100755 index 0000000..a984b84 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/oracle-sans/OracleSans_XBdIt.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.eot b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.eot new file mode 100644 index 0000000..2cbab9c Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.eot differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.svg b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.svg new file mode 100644 index 0000000..b36a66a --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.svg @@ -0,0 +1,14 @@ + + + +Generated by Fontastic.me + + + + + + + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.ttf new file mode 100644 index 0000000..9d03461 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.woff b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.woff new file mode 100644 index 0000000..8ee9972 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/fonts/slick-fonts/slick.woff differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/copy.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/copy.png new file mode 100644 index 0000000..d2af57e Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/copy.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/copy.svg b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/copy.svg new file mode 100644 index 0000000..22a16c8 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/copy.svg @@ -0,0 +1,19 @@ + + + + Mask + Created with Sketch. + + + + + + + + + + + + + + \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/android-chrome-192x192.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/android-chrome-192x192.png new file mode 100644 index 0000000..94bbfb7 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/android-chrome-192x192.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/android-chrome-512x512.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/android-chrome-512x512.png new file mode 100644 index 0000000..85aeacb Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/android-chrome-512x512.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/apple-touch-icon.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/apple-touch-icon.png new file mode 100644 index 0000000..0445623 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/apple-touch-icon.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/favicon-16x16.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/favicon-16x16.png new file mode 100644 index 0000000..7cf0510 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/favicon-16x16.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/favicon-32x32.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/favicon-32x32.png new file mode 100644 index 0000000..32076cb Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/favicon-32x32.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/favicon-dark.ico b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/favicon-dark.ico new file mode 100644 index 0000000..3b71c06 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/favicon-dark.ico differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/site.webmanifest b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-dark/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/android-chrome-192x192.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/android-chrome-192x192.png new file mode 100644 index 0000000..daea931 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/android-chrome-192x192.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/android-chrome-512x512.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/android-chrome-512x512.png new file mode 100644 index 0000000..e2aa2f9 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/android-chrome-512x512.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/apple-touch-icon.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/apple-touch-icon.png new file mode 100644 index 0000000..44d0262 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/apple-touch-icon.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/favicon-16x16.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/favicon-16x16.png new file mode 100644 index 0000000..6b8d915 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/favicon-16x16.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/favicon-32x32.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/favicon-32x32.png new file mode 100644 index 0000000..8d2fa09 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/favicon-32x32.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/favicon-light.ico b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/favicon-light.ico new file mode 100644 index 0000000..fdcd2ff Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/favicon-light.ico differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/site.webmanifest b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/favicon-light/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/site.webmanifest b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/favicon/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/fiddle.svg b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/fiddle.svg new file mode 100644 index 0000000..7b0e4ae --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/fiddle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/graalvm.png b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/graalvm.png new file mode 100644 index 0000000..97c0717 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/graalvm.png differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/logo.svg b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/logo.svg new file mode 100644 index 0000000..539a3db --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/img/logo_header.svg b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/logo_header.svg new file mode 100644 index 0000000..1756e51 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/img/logo_header.svg @@ -0,0 +1,4 @@ + + + + diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/js/favicon.js b/native-image/spring-boot-webserver/src/main/resources/static/resources/js/favicon.js new file mode 100644 index 0000000..3223568 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/js/favicon.js @@ -0,0 +1,15 @@ +function setLightFavicon() { + var favicon = document.getElementById('favicon'); + favicon.href = '/resources/img/favicon/favicon-light/favicon-light.ico'; +} +function setDarkFavicon() { + var favicon = document.getElementById('favicon'); + favicon.href = '/resources/img/favicon/favicon-dark/favicon-dark.ico'; +} +var isDarkTheme = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + +if (isDarkTheme) { + setDarkFavicon(); +} else { + setLightFavicon(); +} \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/js/main.js b/native-image/spring-boot-webserver/src/main/resources/static/resources/js/main.js new file mode 100644 index 0000000..14eef19 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/js/main.js @@ -0,0 +1,290 @@ +// On-load actions +$(window).on("load", function () { + $("body").removeClass("preload"); +}); + +function copySnippet(elem) { + var div = $(elem.parentElement.parentElement); + text = ""; + div.find("figure").each(function () { + var currentText = $(this).text(); + text += currentText; + }); + copyTextToClipboard(text); +} + +function copyTextToClipboard(text) { + var textArea = document.createElement("textarea"); + + textArea.style.position = 'fixed'; + textArea.style.top = 0; + textArea.style.left = 0; + textArea.style.width = '2em'; + textArea.style.height = '2em'; + textArea.style.padding = 0; + textArea.style.border = 'none'; + textArea.style.outline = 'none'; + textArea.style.boxShadow = 'none'; + textArea.style.background = 'transparent'; + textArea.value = text; + document.body.appendChild(textArea); + textArea.select(); + try { + var successful = document.execCommand('copy'); + var msg = successful ? 'successful' : 'unsuccessful'; + // console.log('Copying text command was ' + msg); + } catch (err) { + // console.log('Oops, unable to copy'); + } + document.body.removeChild(textArea); +} + +function fiddleSnippet(elem, fileName) { + elem.onclick = null; // do not call this function on the same elem again + var className = fileName.match(/([^/]*)\.java$/)[1]; + var div = elem.parentElement.parentElement; + displayFiddleLoadingMessage(div); + GraalVMFiddle.replace(div, div._snippets, {className: className}); +} + +function displayFiddleLoadingMessage(elem) { + var w = elem.clientWidth; + var h = elem.clientHeight; + var outer = document.createElement('div'); + outer.style.position = 'relative'; + var inner = document.createElement('div'); + inner.style.position = 'absolute'; + inner.style.left = (w/4) + 'px'; + inner.style.top = (h/3) + 'px'; + inner.style.width = (w/2) + 'px'; + inner.style.height = (h/3) + 'px'; + inner.style.lineHeight = (h/3) + 'px'; + inner.className = 'fiddle-loading-message'; + inner.appendChild(document.createTextNode('Loading...')); + outer.appendChild(inner); + elem.prepend(outer); +} + +// Create terminal function +function createTerminal(serverUrl, containerId, terminalId, uid) { + var appearDuration = 350; + var revealDuration = 750; + var bootTerminalDuration = 750; + + $(containerId).animate({ + padding: "8px", + opacity: 1.0 + }, appearDuration) + + $(terminalId).animate({ + height: "260px" + }, revealDuration, function () { + $(terminalId).terminal(function (command) { + var shell = this; + + if (command !== "") { + shell.freeze(true); + var currentPrompt = shell.get_prompt(); + shell.set_prompt(""); + var escapedLine = encodeURIComponent(command); + var url = serverUrl + "/send-line?uid=" + uid + "&line=" + escapedLine; + $.get(url).done(function (data) { + console.log(data); + data = JSON.parse(data); + if (data["language"]) { + currentPrompt = data["language"] + "> "; + } + if (data["error"]) { + shell.echo(data["error"]); + if (!data["terminate"]) { + shell.freeze(false); + shell.set_prompt(currentPrompt); + } + } else { + console.log(data) + var output = JSON.parse("\"" + data["output"] + "\""); + shell.echo(output); + shell.freeze(false); + shell.set_prompt(currentPrompt); + } + }).fail(function (r) { + shell.echo("Server error code " + r.status + ": " + r.statusText); + shell.enable(); + }) + } else { + shell.echo(""); + } + }, { + greetings: "GraalVM Shell (type 'js', 'ruby' or 'R' to switch languages)", + name: "graalvm_shell", + height: 240, + prompt: "js> ", + onInit: function () { + var shell = this; + $(terminalId).click(function () { + console.log("Focused terminal."); + shell.focus(true); + }); + shell.disable(); + shell.enable(); + } + }); + + $(terminalId).animate({ + opacity: 1.0 + }, bootTerminalDuration); + }); +} + +function establishSessionAndCreateTerminal(serverUrl) { + console.log("Establishing shell session with " + serverUrl); + $.get(serverUrl + "/start-session").done(function (data) { + console.log(data); + data = JSON.parse(data); + if (data["error"]) { + console.error(data["error"]); + } else { + console.log("Starting terminal session..."); + createTerminal(serverUrl, "#terminal-container", "#terminal", data["uid"]); + } + }).fail(function (r) { + console.error(r); + }); +} + +$(document).ready(function () { + +// Highlightjs init +$('pre code').each(function (i, block) { + hljs.highlightBlock(block); +}); + + +//Header loading +(document, 'script', 'twitter-wjs'); +(function () { + var topBanner = $('.showcase-banner'); + var header = $('.header'); + var topBannerHeight = topBanner.innerHeight(); + var showed = false; + + $(window).scroll(function () { + var scrollTop = $(document).scrollTop(); + + if (scrollTop > topBannerHeight && !showed) { + header.addClass('header--filled animated fadeIn'); + showed = true; + } else if (scrollTop <= topBannerHeight && showed) { + header.removeClass('header--filled animated fadeIn').addClass('header--filled animated fadeOut'); + setTimeout(function () { + header.removeClass('header--filled animated fadeOut'); + }, 500); + showed = false; + } + }); + }()); + + // Sticky content menu + (function () { + var headerHeight = $('.header').innerHeight(); + var sectionHeadingHeight = $('.section-heading').innerHeight(); + var offsetTop = parseInt(headerHeight) + parseInt(sectionHeadingHeight); + + var contentMenuHorixontal = $(".toc-bullets--horizontal"); + var showed = false; + + $(window).scroll(function () { + var scrollTop = $(document).scrollTop(); + + if (scrollTop > offsetTop && !showed) { + contentMenuHorixontal.addClass('toc-bullets--horizontal-stiky'); + showed = true; + } else if (scrollTop <= sectionHeadingHeight && showed) { + contentMenuHorixontal.removeClass('toc-bullets--horizontal-stiky'); + showed = false; + } + }); + }()); + + + // Mobile menu + if ($('.js-show-menu').length) { + $(".js-show-menu, .overlay").click(function (e) { + e.preventDefault(); + $('body').toggleClass('overflow-hidden'); + $(".menu-btn--menu").toggleClass('menu-open').toggleClass('close'); + $('.menu__list').toggleClass('open'); + $('.menu__logo').toggleClass('open'); + $('.menu__downloads').toggleClass('open'); + $('.menu').toggleClass('open'); + $('.overlay').fadeToggle(); + }); + } + + if ($(".js-show-sidebar").length) { + $(".js-show-sidebar, .overlay").click(function (e) { + e.preventDefault(); + $('body').toggleClass('overflow-hidden'); + $(".menu-btn--sidebar").toggleClass('menu-open'); + $('.sidebar').toggleClass('open'); + $('.overlay').fadeToggle(); + }); + } + +}); + +// Sticky sidebar +var sidebar = document.querySelector('.sidebar-wrap'); + +if (sidebar) { + var stickySidebar = new StickySidebar(sidebar, { + topSpacing: 74, + bottomSpacing: 40 + }); + + sidebar.addEventListener('affix.container-bottom.stickySidebar', function (event) { + window.dispatchEvent(new Event('resize')); + }); +} + +// Highlight active header menu item +$("a").each(function() { + if ((window.location.pathname.indexOf($(this).attr('href'))) > -1) { + $(this).addClass('activeMenuItem'); + } +}); + +// Copy to clipboard +const copyButtonLabel = "Copy"; + +// Only add a button if browser supports Clipboard API +if (navigator.clipboard) { + + let blocks = document.querySelectorAll("pre"); + + blocks.forEach((block) => { + let button = document.createElement("button"); + button.innerText = copyButtonLabel; + button.addEventListener("click", copyCode); + block.appendChild(button); + }); +} + +async function copyCode(event) { + const button = event.srcElement; + const pre = button.parentElement; + let code = pre.querySelector("code"); + let text = code.innerText; + await navigator.clipboard.writeText(text); + + button.innerText = "Copied"; + + setTimeout(() => { + button.innerText = copyButtonLabel; + }, 1000) +} + +// Highlightjs init +$('pre code').each(function (i, block) { + hljs.highlightBlock(block); +}); \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/js/sidebar.js b/native-image/spring-boot-webserver/src/main/resources/static/resources/js/sidebar.js new file mode 100644 index 0000000..3cca7b1 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/js/sidebar.js @@ -0,0 +1,11 @@ +//Expand&Collapse sidebar&changing icon onclick + +const menuabout = document.querySelector(".menuabout"); +menuabout && menuabout.addEventListener("click", function () { + expandSidebar(); +}); +function expandSidebar() { + document.querySelector("article").classList.toggle("short"); + $('#sidebarClose').toggleClass("no-display") + $('#sidebarOpen').toggleClass("no-display") +} \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/config.json b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/config.json new file mode 100644 index 0000000..2f64ad9 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/config.json @@ -0,0 +1,426 @@ +{ + "vars": { + "@gray-base": "#000", + "@gray-darker": "lighten(@gray-base, 13.5%)", + "@gray-dark": "lighten(@gray-base, 20%)", + "@gray": "lighten(@gray-base, 33.5%)", + "@gray-light": "lighten(@gray-base, 46.7%)", + "@gray-lighter": "lighten(@gray-base, 93.5%)", + "@brand-primary": "darken(#428bca, 6.5%)", + "@brand-success": "#5cb85c", + "@brand-info": "#5bc0de", + "@brand-warning": "#f0ad4e", + "@brand-danger": "#d9534f", + "@body-bg": "#fff", + "@text-color": "@gray-dark", + "@link-color": "@brand-primary", + "@link-hover-color": "darken(@link-color, 15%)", + "@link-hover-decoration": "underline", + "@font-family-sans-serif": "\"Helvetica Neue\", Helvetica, Arial, sans-serif", + "@font-family-serif": "Georgia, \"Times New Roman\", Times, serif", + "@font-family-monospace": "Menlo, Monaco, Consolas, \"Courier New\", monospace", + "@font-family-base": "@font-family-sans-serif", + "@font-size-base": "14px", + "@font-size-large": "ceil((@font-size-base * 1.25))", + "@font-size-small": "ceil((@font-size-base * 0.85))", + "@font-size-h1": "floor((@font-size-base * 2.6))", + "@font-size-h2": "floor((@font-size-base * 2.15))", + "@font-size-h3": "ceil((@font-size-base * 1.7))", + "@font-size-h4": "ceil((@font-size-base * 1.25))", + "@font-size-h5": "@font-size-base", + "@font-size-h6": "ceil((@font-size-base * 0.85))", + "@line-height-base": "1.428571429", + "@line-height-computed": "floor((@font-size-base * @line-height-base))", + "@headings-font-family": "inherit", + "@headings-font-weight": "500", + "@headings-line-height": "1.1", + "@headings-color": "inherit", + "@icon-font-path": "\"../fonts/\"", + "@icon-font-name": "\"glyphicons-halflings-regular\"", + "@icon-font-svg-id": "\"glyphicons_halflingsregular\"", + "@padding-base-vertical": "6px", + "@padding-base-horizontal": "12px", + "@padding-large-vertical": "10px", + "@padding-large-horizontal": "16px", + "@padding-small-vertical": "5px", + "@padding-small-horizontal": "10px", + "@padding-xs-vertical": "1px", + "@padding-xs-horizontal": "5px", + "@line-height-large": "1.3333333", + "@line-height-small": "1.5", + "@border-radius-base": "4px", + "@border-radius-large": "6px", + "@border-radius-small": "3px", + "@component-active-color": "#fff", + "@component-active-bg": "@brand-primary", + "@caret-width-base": "4px", + "@caret-width-large": "5px", + "@table-cell-padding": "8px", + "@table-condensed-cell-padding": "5px", + "@table-bg": "transparent", + "@table-bg-accent": "#f9f9f9", + "@table-bg-hover": "#f5f5f5", + "@table-bg-active": "@table-bg-hover", + "@table-border-color": "#ddd", + "@btn-font-weight": "normal", + "@btn-default-color": "#333", + "@btn-default-bg": "#fff", + "@btn-default-border": "#ccc", + "@btn-primary-color": "#fff", + "@btn-primary-bg": "@brand-primary", + "@btn-primary-border": "darken(@btn-primary-bg, 5%)", + "@btn-success-color": "#fff", + "@btn-success-bg": "@brand-success", + "@btn-success-border": "darken(@btn-success-bg, 5%)", + "@btn-info-color": "#fff", + "@btn-info-bg": "@brand-info", + "@btn-info-border": "darken(@btn-info-bg, 5%)", + "@btn-warning-color": "#fff", + "@btn-warning-bg": "@brand-warning", + "@btn-warning-border": "darken(@btn-warning-bg, 5%)", + "@btn-danger-color": "#fff", + "@btn-danger-bg": "@brand-danger", + "@btn-danger-border": "darken(@btn-danger-bg, 5%)", + "@btn-link-disabled-color": "@gray-light", + "@btn-border-radius-base": "@border-radius-base", + "@btn-border-radius-large": "@border-radius-large", + "@btn-border-radius-small": "@border-radius-small", + "@input-bg": "#fff", + "@input-bg-disabled": "@gray-lighter", + "@input-color": "@gray", + "@input-border": "#ccc", + "@input-border-radius": "@border-radius-base", + "@input-border-radius-large": "@border-radius-large", + "@input-border-radius-small": "@border-radius-small", + "@input-border-focus": "#66afe9", + "@input-color-placeholder": "#999", + "@input-height-base": "(@line-height-computed + (@padding-base-vertical * 2) + 2)", + "@input-height-large": "(ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2)", + "@input-height-small": "(floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2)", + "@form-group-margin-bottom": "15px", + "@legend-color": "@gray-dark", + "@legend-border-color": "#e5e5e5", + "@input-group-addon-bg": "@gray-lighter", + "@input-group-addon-border-color": "@input-border", + "@cursor-disabled": "not-allowed", + "@dropdown-bg": "#fff", + "@dropdown-border": "rgba(0,0,0,.15)", + "@dropdown-fallback-border": "#ccc", + "@dropdown-divider-bg": "#e5e5e5", + "@dropdown-link-color": "@gray-dark", + "@dropdown-link-hover-color": "darken(@gray-dark, 5%)", + "@dropdown-link-hover-bg": "#f5f5f5", + "@dropdown-link-active-color": "@component-active-color", + "@dropdown-link-active-bg": "@component-active-bg", + "@dropdown-link-disabled-color": "@gray-light", + "@dropdown-header-color": "@gray-light", + "@dropdown-caret-color": "#000", + "@screen-xs": "480px", + "@screen-xs-min": "@screen-xs", + "@screen-phone": "@screen-xs-min", + "@screen-sm": "768px", + "@screen-sm-min": "@screen-sm", + "@screen-tablet": "@screen-sm-min", + "@screen-md": "992px", + "@screen-md-min": "@screen-md", + "@screen-desktop": "@screen-md-min", + "@screen-lg": "1200px", + "@screen-lg-min": "@screen-lg", + "@screen-lg-desktop": "@screen-lg-min", + "@screen-xs-max": "(@screen-sm-min - 1)", + "@screen-sm-max": "(@screen-md-min - 1)", + "@screen-md-max": "(@screen-lg-min - 1)", + "@grid-columns": "12", + "@grid-gutter-width": "30px", + "@grid-float-breakpoint": "@screen-sm-min", + "@grid-float-breakpoint-max": "(@grid-float-breakpoint - 1)", + "@container-tablet": "(720px + @grid-gutter-width)", + "@container-sm": "@container-tablet", + "@container-desktop": "(940px + @grid-gutter-width)", + "@container-md": "@container-desktop", + "@container-large-desktop": "(1140px + @grid-gutter-width)", + "@container-lg": "@container-large-desktop", + "@navbar-height": "50px", + "@navbar-margin-bottom": "@line-height-computed", + "@navbar-border-radius": "@border-radius-base", + "@navbar-padding-horizontal": "floor((@grid-gutter-width / 2))", + "@navbar-padding-vertical": "((@navbar-height - @line-height-computed) / 2)", + "@navbar-collapse-max-height": "340px", + "@navbar-default-color": "#777", + "@navbar-default-bg": "#f8f8f8", + "@navbar-default-border": "darken(@navbar-default-bg, 6.5%)", + "@navbar-default-link-color": "#777", + "@navbar-default-link-hover-color": "#333", + "@navbar-default-link-hover-bg": "transparent", + "@navbar-default-link-active-color": "#555", + "@navbar-default-link-active-bg": "darken(@navbar-default-bg, 6.5%)", + "@navbar-default-link-disabled-color": "#ccc", + "@navbar-default-link-disabled-bg": "transparent", + "@navbar-default-brand-color": "@navbar-default-link-color", + "@navbar-default-brand-hover-color": "darken(@navbar-default-brand-color, 10%)", + "@navbar-default-brand-hover-bg": "transparent", + "@navbar-default-toggle-hover-bg": "#ddd", + "@navbar-default-toggle-icon-bar-bg": "#888", + "@navbar-default-toggle-border-color": "#ddd", + "@navbar-inverse-color": "lighten(@gray-light, 15%)", + "@navbar-inverse-bg": "#222", + "@navbar-inverse-border": "darken(@navbar-inverse-bg, 10%)", + "@navbar-inverse-link-color": "lighten(@gray-light, 15%)", + "@navbar-inverse-link-hover-color": "#fff", + "@navbar-inverse-link-hover-bg": "transparent", + "@navbar-inverse-link-active-color": "@navbar-inverse-link-hover-color", + "@navbar-inverse-link-active-bg": "darken(@navbar-inverse-bg, 10%)", + "@navbar-inverse-link-disabled-color": "#444", + "@navbar-inverse-link-disabled-bg": "transparent", + "@navbar-inverse-brand-color": "@navbar-inverse-link-color", + "@navbar-inverse-brand-hover-color": "#fff", + "@navbar-inverse-brand-hover-bg": "transparent", + "@navbar-inverse-toggle-hover-bg": "#333", + "@navbar-inverse-toggle-icon-bar-bg": "#fff", + "@navbar-inverse-toggle-border-color": "#333", + "@nav-link-padding": "10px 15px", + "@nav-link-hover-bg": "@gray-lighter", + "@nav-disabled-link-color": "@gray-light", + "@nav-disabled-link-hover-color": "@gray-light", + "@nav-tabs-border-color": "#ddd", + "@nav-tabs-link-hover-border-color": "@gray-lighter", + "@nav-tabs-active-link-hover-bg": "@body-bg", + "@nav-tabs-active-link-hover-color": "@gray", + "@nav-tabs-active-link-hover-border-color": "#ddd", + "@nav-tabs-justified-link-border-color": "#ddd", + "@nav-tabs-justified-active-link-border-color": "@body-bg", + "@nav-pills-border-radius": "@border-radius-base", + "@nav-pills-active-link-hover-bg": "@component-active-bg", + "@nav-pills-active-link-hover-color": "@component-active-color", + "@pagination-color": "@link-color", + "@pagination-bg": "#fff", + "@pagination-border": "#ddd", + "@pagination-hover-color": "@link-hover-color", + "@pagination-hover-bg": "@gray-lighter", + "@pagination-hover-border": "#ddd", + "@pagination-active-color": "#fff", + "@pagination-active-bg": "@brand-primary", + "@pagination-active-border": "@brand-primary", + "@pagination-disabled-color": "@gray-light", + "@pagination-disabled-bg": "#fff", + "@pagination-disabled-border": "#ddd", + "@pager-bg": "@pagination-bg", + "@pager-border": "@pagination-border", + "@pager-border-radius": "15px", + "@pager-hover-bg": "@pagination-hover-bg", + "@pager-active-bg": "@pagination-active-bg", + "@pager-active-color": "@pagination-active-color", + "@pager-disabled-color": "@pagination-disabled-color", + "@jumbotron-padding": "30px", + "@jumbotron-color": "inherit", + "@jumbotron-bg": "@gray-lighter", + "@jumbotron-heading-color": "inherit", + "@jumbotron-font-size": "ceil((@font-size-base * 1.5))", + "@jumbotron-heading-font-size": "ceil((@font-size-base * 4.5))", + "@state-success-text": "#3c763d", + "@state-success-bg": "#dff0d8", + "@state-success-border": "darken(spin(@state-success-bg, -10), 5%)", + "@state-info-text": "#31708f", + "@state-info-bg": "#d9edf7", + "@state-info-border": "darken(spin(@state-info-bg, -10), 7%)", + "@state-warning-text": "#8a6d3b", + "@state-warning-bg": "#fcf8e3", + "@state-warning-border": "darken(spin(@state-warning-bg, -10), 5%)", + "@state-danger-text": "#a94442", + "@state-danger-bg": "#f2dede", + "@state-danger-border": "darken(spin(@state-danger-bg, -10), 5%)", + "@tooltip-max-width": "200px", + "@tooltip-color": "#fff", + "@tooltip-bg": "#000", + "@tooltip-opacity": ".9", + "@tooltip-arrow-width": "5px", + "@tooltip-arrow-color": "@tooltip-bg", + "@popover-bg": "#fff", + "@popover-max-width": "276px", + "@popover-border-color": "rgba(0,0,0,.2)", + "@popover-fallback-border-color": "#ccc", + "@popover-title-bg": "darken(@popover-bg, 3%)", + "@popover-arrow-width": "10px", + "@popover-arrow-color": "@popover-bg", + "@popover-arrow-outer-width": "(@popover-arrow-width + 1)", + "@popover-arrow-outer-color": "fadein(@popover-border-color, 5%)", + "@popover-arrow-outer-fallback-color": "darken(@popover-fallback-border-color, 20%)", + "@label-default-bg": "@gray-light", + "@label-primary-bg": "@brand-primary", + "@label-success-bg": "@brand-success", + "@label-info-bg": "@brand-info", + "@label-warning-bg": "@brand-warning", + "@label-danger-bg": "@brand-danger", + "@label-color": "#fff", + "@label-link-hover-color": "#fff", + "@modal-inner-padding": "15px", + "@modal-title-padding": "15px", + "@modal-title-line-height": "@line-height-base", + "@modal-content-bg": "#fff", + "@modal-content-border-color": "rgba(0,0,0,.2)", + "@modal-content-fallback-border-color": "#999", + "@modal-backdrop-bg": "#000", + "@modal-backdrop-opacity": ".5", + "@modal-header-border-color": "#e5e5e5", + "@modal-footer-border-color": "@modal-header-border-color", + "@modal-lg": "900px", + "@modal-md": "600px", + "@modal-sm": "300px", + "@alert-padding": "15px", + "@alert-border-radius": "@border-radius-base", + "@alert-link-font-weight": "bold", + "@alert-success-bg": "@state-success-bg", + "@alert-success-text": "@state-success-text", + "@alert-success-border": "@state-success-border", + "@alert-info-bg": "@state-info-bg", + "@alert-info-text": "@state-info-text", + "@alert-info-border": "@state-info-border", + "@alert-warning-bg": "@state-warning-bg", + "@alert-warning-text": "@state-warning-text", + "@alert-warning-border": "@state-warning-border", + "@alert-danger-bg": "@state-danger-bg", + "@alert-danger-text": "@state-danger-text", + "@alert-danger-border": "@state-danger-border", + "@progress-bg": "#f5f5f5", + "@progress-bar-color": "#fff", + "@progress-border-radius": "@border-radius-base", + "@progress-bar-bg": "@brand-primary", + "@progress-bar-success-bg": "@brand-success", + "@progress-bar-warning-bg": "@brand-warning", + "@progress-bar-danger-bg": "@brand-danger", + "@progress-bar-info-bg": "@brand-info", + "@list-group-bg": "#fff", + "@list-group-border": "#ddd", + "@list-group-border-radius": "@border-radius-base", + "@list-group-hover-bg": "#f5f5f5", + "@list-group-active-color": "@component-active-color", + "@list-group-active-bg": "@component-active-bg", + "@list-group-active-border": "@list-group-active-bg", + "@list-group-active-text-color": "lighten(@list-group-active-bg, 40%)", + "@list-group-disabled-color": "@gray-light", + "@list-group-disabled-bg": "@gray-lighter", + "@list-group-disabled-text-color": "@list-group-disabled-color", + "@list-group-link-color": "#555", + "@list-group-link-hover-color": "@list-group-link-color", + "@list-group-link-heading-color": "#333", + "@panel-bg": "#fff", + "@panel-body-padding": "15px", + "@panel-heading-padding": "10px 15px", + "@panel-footer-padding": "@panel-heading-padding", + "@panel-border-radius": "@border-radius-base", + "@panel-inner-border": "#ddd", + "@panel-footer-bg": "#f5f5f5", + "@panel-default-text": "@gray-dark", + "@panel-default-border": "#ddd", + "@panel-default-heading-bg": "#f5f5f5", + "@panel-primary-text": "#fff", + "@panel-primary-border": "@brand-primary", + "@panel-primary-heading-bg": "@brand-primary", + "@panel-success-text": "@state-success-text", + "@panel-success-border": "@state-success-border", + "@panel-success-heading-bg": "@state-success-bg", + "@panel-info-text": "@state-info-text", + "@panel-info-border": "@state-info-border", + "@panel-info-heading-bg": "@state-info-bg", + "@panel-warning-text": "@state-warning-text", + "@panel-warning-border": "@state-warning-border", + "@panel-warning-heading-bg": "@state-warning-bg", + "@panel-danger-text": "@state-danger-text", + "@panel-danger-border": "@state-danger-border", + "@panel-danger-heading-bg": "@state-danger-bg", + "@thumbnail-padding": "4px", + "@thumbnail-bg": "@body-bg", + "@thumbnail-border": "#ddd", + "@thumbnail-border-radius": "@border-radius-base", + "@thumbnail-caption-color": "@text-color", + "@thumbnail-caption-padding": "9px", + "@well-bg": "#f5f5f5", + "@well-border": "darken(@well-bg, 7%)", + "@badge-color": "#fff", + "@badge-link-hover-color": "#fff", + "@badge-bg": "@gray-light", + "@badge-active-color": "@link-color", + "@badge-active-bg": "#fff", + "@badge-font-weight": "bold", + "@badge-line-height": "1", + "@badge-border-radius": "10px", + "@breadcrumb-padding-vertical": "8px", + "@breadcrumb-padding-horizontal": "15px", + "@breadcrumb-bg": "#f5f5f5", + "@breadcrumb-color": "#ccc", + "@breadcrumb-active-color": "@gray-light", + "@breadcrumb-separator": "\"/\"", + "@carousel-text-shadow": "0 1px 2px rgba(0,0,0,.6)", + "@carousel-control-color": "#fff", + "@carousel-control-width": "15%", + "@carousel-control-opacity": ".5", + "@carousel-control-font-size": "20px", + "@carousel-indicator-active-bg": "#fff", + "@carousel-indicator-border-color": "#fff", + "@carousel-caption-color": "#fff", + "@close-font-weight": "bold", + "@close-color": "#000", + "@close-text-shadow": "0 1px 0 #fff", + "@code-color": "#c7254e", + "@code-bg": "#f9f2f4", + "@kbd-color": "#fff", + "@kbd-bg": "#333", + "@pre-bg": "#f5f5f5", + "@pre-color": "@gray-dark", + "@pre-border-color": "#ccc", + "@pre-scrollable-max-height": "340px", + "@component-offset-horizontal": "180px", + "@text-muted": "@gray-light", + "@abbr-border-color": "@gray-light", + "@headings-small-color": "@gray-light", + "@blockquote-small-color": "@gray-light", + "@blockquote-font-size": "(@font-size-base * 1.25)", + "@blockquote-border-color": "@gray-lighter", + "@page-header-border-color": "@gray-lighter", + "@dl-horizontal-offset": "@component-offset-horizontal", + "@dl-horizontal-breakpoint": "@grid-float-breakpoint", + "@hr-border": "@gray-lighter" + }, + "css": [ + "grid.less", + "responsive-utilities.less", + "glyphicons.less", + "navs.less", + "breadcrumbs.less", + "pagination.less", + "pager.less", + "labels.less", + "badges.less", + "jumbotron.less", + "thumbnails.less", + "alerts.less", + "progress-bars.less", + "media.less", + "list-group.less", + "panels.less", + "responsive-embed.less", + "wells.less", + "close.less", + "component-animations.less", + "dropdowns.less", + "tooltip.less", + "popovers.less", + "modals.less", + "carousel.less" + ], + "js": [ + "alert.js", + "button.js", + "carousel.js", + "dropdown.js", + "modal.js", + "tooltip.js", + "popover.js", + "tab.js", + "affix.js", + "collapse.js", + "scrollspy.js", + "transition.js" + ], + "customizerUrl": "http://getbootstrap.com/customize/?id=e2d40346490b4fcc3281498b1601840e" +} \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.eot b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000..b93a495 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.eot differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.svg b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..94fb549 --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000..1413fc6 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.woff b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000..9e61285 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.woff differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 0000000..64539b5 Binary files /dev/null and b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/js/bootstrap.bundle.js b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/js/bootstrap.bundle.js new file mode 100644 index 0000000..d9236da --- /dev/null +++ b/native-image/spring-boot-webserver/src/main/resources/static/resources/lib/bootstrap/js/bootstrap.bundle.js @@ -0,0 +1,7033 @@ +/*! + * Bootstrap v4.5.0 (https://getbootstrap.com/) + * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('jquery')) : + typeof define === 'function' && define.amd ? define(['exports', 'jquery'], factory) : + (global = global || self, factory(global.bootstrap = {}, global.jQuery)); +}(this, (function (exports, $) { 'use strict'; + + $ = $ && Object.prototype.hasOwnProperty.call($, 'default') ? $['default'] : $; + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object); + + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object); + if (enumerableOnly) symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable; + }); + keys.push.apply(keys, symbols); + } + + return keys; + } + + function _objectSpread2(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + + if (i % 2) { + ownKeys(Object(source), true).forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); + } else { + ownKeys(Object(source)).forEach(function (key) { + Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); + }); + } + } + + return target; + } + + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + subClass.__proto__ = superClass; + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap (v4.5.0): util.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + /** + * ------------------------------------------------------------------------ + * Private TransitionEnd Helpers + * ------------------------------------------------------------------------ + */ + + var TRANSITION_END = 'transitionend'; + var MAX_UID = 1000000; + var MILLISECONDS_MULTIPLIER = 1000; // Shoutout AngusCroll (https://goo.gl/pxwQGp) + + function toType(obj) { + if (obj === null || typeof obj === 'undefined') { + return "" + obj; + } + + return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase(); + } + + function getSpecialTransitionEndEvent() { + return { + bindType: TRANSITION_END, + delegateType: TRANSITION_END, + handle: function handle(event) { + if ($(event.target).is(this)) { + return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params + } + + return undefined; + } + }; + } + + function transitionEndEmulator(duration) { + var _this = this; + + var called = false; + $(this).one(Util.TRANSITION_END, function () { + called = true; + }); + setTimeout(function () { + if (!called) { + Util.triggerTransitionEnd(_this); + } + }, duration); + return this; + } + + function setTransitionEndSupport() { + $.fn.emulateTransitionEnd = transitionEndEmulator; + $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent(); + } + /** + * -------------------------------------------------------------------------- + * Public Util Api + * -------------------------------------------------------------------------- + */ + + + var Util = { + TRANSITION_END: 'bsTransitionEnd', + getUID: function getUID(prefix) { + do { + // eslint-disable-next-line no-bitwise + prefix += ~~(Math.random() * MAX_UID); // "~~" acts like a faster Math.floor() here + } while (document.getElementById(prefix)); + + return prefix; + }, + getSelectorFromElement: function getSelectorFromElement(element) { + var selector = element.getAttribute('data-target'); + + if (!selector || selector === '#') { + var hrefAttr = element.getAttribute('href'); + selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''; + } + + try { + return document.querySelector(selector) ? selector : null; + } catch (err) { + return null; + } + }, + getTransitionDurationFromElement: function getTransitionDurationFromElement(element) { + if (!element) { + return 0; + } // Get transition-duration of the element + + + var transitionDuration = $(element).css('transition-duration'); + var transitionDelay = $(element).css('transition-delay'); + var floatTransitionDuration = parseFloat(transitionDuration); + var floatTransitionDelay = parseFloat(transitionDelay); // Return 0 if element or transition duration is not found + + if (!floatTransitionDuration && !floatTransitionDelay) { + return 0; + } // If multiple durations are defined, take the first + + + transitionDuration = transitionDuration.split(',')[0]; + transitionDelay = transitionDelay.split(',')[0]; + return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; + }, + reflow: function reflow(element) { + return element.offsetHeight; + }, + triggerTransitionEnd: function triggerTransitionEnd(element) { + $(element).trigger(TRANSITION_END); + }, + // TODO: Remove in v5 + supportsTransitionEnd: function supportsTransitionEnd() { + return Boolean(TRANSITION_END); + }, + isElement: function isElement(obj) { + return (obj[0] || obj).nodeType; + }, + typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) { + for (var property in configTypes) { + if (Object.prototype.hasOwnProperty.call(configTypes, property)) { + var expectedTypes = configTypes[property]; + var value = config[property]; + var valueType = value && Util.isElement(value) ? 'element' : toType(value); + + if (!new RegExp(expectedTypes).test(valueType)) { + throw new Error(componentName.toUpperCase() + ": " + ("Option \"" + property + "\" provided type \"" + valueType + "\" ") + ("but expected type \"" + expectedTypes + "\".")); + } + } + } + }, + findShadowRoot: function findShadowRoot(element) { + if (!document.documentElement.attachShadow) { + return null; + } // Can find the shadow root otherwise it'll return the document + + + if (typeof element.getRootNode === 'function') { + var root = element.getRootNode(); + return root instanceof ShadowRoot ? root : null; + } + + if (element instanceof ShadowRoot) { + return element; + } // when we don't find a shadow root + + + if (!element.parentNode) { + return null; + } + + return Util.findShadowRoot(element.parentNode); + }, + jQueryDetection: function jQueryDetection() { + if (typeof $ === 'undefined') { + throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.'); + } + + var version = $.fn.jquery.split(' ')[0].split('.'); + var minMajor = 1; + var ltMajor = 2; + var minMinor = 9; + var minPatch = 1; + var maxMajor = 4; + + if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) { + throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0'); + } + } + }; + Util.jQueryDetection(); + setTransitionEndSupport(); + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME = 'alert'; + var VERSION = '4.5.0'; + var DATA_KEY = 'bs.alert'; + var EVENT_KEY = "." + DATA_KEY; + var DATA_API_KEY = '.data-api'; + var JQUERY_NO_CONFLICT = $.fn[NAME]; + var SELECTOR_DISMISS = '[data-dismiss="alert"]'; + var EVENT_CLOSE = "close" + EVENT_KEY; + var EVENT_CLOSED = "closed" + EVENT_KEY; + var EVENT_CLICK_DATA_API = "click" + EVENT_KEY + DATA_API_KEY; + var CLASS_NAME_ALERT = 'alert'; + var CLASS_NAME_FADE = 'fade'; + var CLASS_NAME_SHOW = 'show'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Alert = /*#__PURE__*/function () { + function Alert(element) { + this._element = element; + } // Getters + + + var _proto = Alert.prototype; + + // Public + _proto.close = function close(element) { + var rootElement = this._element; + + if (element) { + rootElement = this._getRootElement(element); + } + + var customEvent = this._triggerCloseEvent(rootElement); + + if (customEvent.isDefaultPrevented()) { + return; + } + + this._removeElement(rootElement); + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY); + this._element = null; + } // Private + ; + + _proto._getRootElement = function _getRootElement(element) { + var selector = Util.getSelectorFromElement(element); + var parent = false; + + if (selector) { + parent = document.querySelector(selector); + } + + if (!parent) { + parent = $(element).closest("." + CLASS_NAME_ALERT)[0]; + } + + return parent; + }; + + _proto._triggerCloseEvent = function _triggerCloseEvent(element) { + var closeEvent = $.Event(EVENT_CLOSE); + $(element).trigger(closeEvent); + return closeEvent; + }; + + _proto._removeElement = function _removeElement(element) { + var _this = this; + + $(element).removeClass(CLASS_NAME_SHOW); + + if (!$(element).hasClass(CLASS_NAME_FADE)) { + this._destroyElement(element); + + return; + } + + var transitionDuration = Util.getTransitionDurationFromElement(element); + $(element).one(Util.TRANSITION_END, function (event) { + return _this._destroyElement(element, event); + }).emulateTransitionEnd(transitionDuration); + }; + + _proto._destroyElement = function _destroyElement(element) { + $(element).detach().trigger(EVENT_CLOSED).remove(); + } // Static + ; + + Alert._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var $element = $(this); + var data = $element.data(DATA_KEY); + + if (!data) { + data = new Alert(this); + $element.data(DATA_KEY, data); + } + + if (config === 'close') { + data[config](this); + } + }); + }; + + Alert._handleDismiss = function _handleDismiss(alertInstance) { + return function (event) { + if (event) { + event.preventDefault(); + } + + alertInstance.close(this); + }; + }; + + _createClass(Alert, null, [{ + key: "VERSION", + get: function get() { + return VERSION; + } + }]); + + return Alert; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert._handleDismiss(new Alert())); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME] = Alert._jQueryInterface; + $.fn[NAME].Constructor = Alert; + + $.fn[NAME].noConflict = function () { + $.fn[NAME] = JQUERY_NO_CONFLICT; + return Alert._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$1 = 'button'; + var VERSION$1 = '4.5.0'; + var DATA_KEY$1 = 'bs.button'; + var EVENT_KEY$1 = "." + DATA_KEY$1; + var DATA_API_KEY$1 = '.data-api'; + var JQUERY_NO_CONFLICT$1 = $.fn[NAME$1]; + var CLASS_NAME_ACTIVE = 'active'; + var CLASS_NAME_BUTTON = 'btn'; + var CLASS_NAME_FOCUS = 'focus'; + var SELECTOR_DATA_TOGGLE_CARROT = '[data-toggle^="button"]'; + var SELECTOR_DATA_TOGGLES = '[data-toggle="buttons"]'; + var SELECTOR_DATA_TOGGLE = '[data-toggle="button"]'; + var SELECTOR_DATA_TOGGLES_BUTTONS = '[data-toggle="buttons"] .btn'; + var SELECTOR_INPUT = 'input:not([type="hidden"])'; + var SELECTOR_ACTIVE = '.active'; + var SELECTOR_BUTTON = '.btn'; + var EVENT_CLICK_DATA_API$1 = "click" + EVENT_KEY$1 + DATA_API_KEY$1; + var EVENT_FOCUS_BLUR_DATA_API = "focus" + EVENT_KEY$1 + DATA_API_KEY$1 + " " + ("blur" + EVENT_KEY$1 + DATA_API_KEY$1); + var EVENT_LOAD_DATA_API = "load" + EVENT_KEY$1 + DATA_API_KEY$1; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Button = /*#__PURE__*/function () { + function Button(element) { + this._element = element; + } // Getters + + + var _proto = Button.prototype; + + // Public + _proto.toggle = function toggle() { + var triggerChangeEvent = true; + var addAriaPressed = true; + var rootElement = $(this._element).closest(SELECTOR_DATA_TOGGLES)[0]; + + if (rootElement) { + var input = this._element.querySelector(SELECTOR_INPUT); + + if (input) { + if (input.type === 'radio') { + if (input.checked && this._element.classList.contains(CLASS_NAME_ACTIVE)) { + triggerChangeEvent = false; + } else { + var activeElement = rootElement.querySelector(SELECTOR_ACTIVE); + + if (activeElement) { + $(activeElement).removeClass(CLASS_NAME_ACTIVE); + } + } + } + + if (triggerChangeEvent) { + // if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input + if (input.type === 'checkbox' || input.type === 'radio') { + input.checked = !this._element.classList.contains(CLASS_NAME_ACTIVE); + } + + $(input).trigger('change'); + } + + input.focus(); + addAriaPressed = false; + } + } + + if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) { + if (addAriaPressed) { + this._element.setAttribute('aria-pressed', !this._element.classList.contains(CLASS_NAME_ACTIVE)); + } + + if (triggerChangeEvent) { + $(this._element).toggleClass(CLASS_NAME_ACTIVE); + } + } + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$1); + this._element = null; + } // Static + ; + + Button._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$1); + + if (!data) { + data = new Button(this); + $(this).data(DATA_KEY$1, data); + } + + if (config === 'toggle') { + data[config](); + } + }); + }; + + _createClass(Button, null, [{ + key: "VERSION", + get: function get() { + return VERSION$1; + } + }]); + + return Button; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE_CARROT, function (event) { + var button = event.target; + var initialButton = button; + + if (!$(button).hasClass(CLASS_NAME_BUTTON)) { + button = $(button).closest(SELECTOR_BUTTON)[0]; + } + + if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) { + event.preventDefault(); // work around Firefox bug #1540995 + } else { + var inputBtn = button.querySelector(SELECTOR_INPUT); + + if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) { + event.preventDefault(); // work around Firefox bug #1540995 + + return; + } + + if (initialButton.tagName === 'LABEL' && inputBtn && inputBtn.type === 'checkbox') { + event.preventDefault(); // work around event sent to label and input + } + + Button._jQueryInterface.call($(button), 'toggle'); + } + }).on(EVENT_FOCUS_BLUR_DATA_API, SELECTOR_DATA_TOGGLE_CARROT, function (event) { + var button = $(event.target).closest(SELECTOR_BUTTON)[0]; + $(button).toggleClass(CLASS_NAME_FOCUS, /^focus(in)?$/.test(event.type)); + }); + $(window).on(EVENT_LOAD_DATA_API, function () { + // ensure correct active class is set to match the controls' actual values/states + // find all checkboxes/readio buttons inside data-toggle groups + var buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLES_BUTTONS)); + + for (var i = 0, len = buttons.length; i < len; i++) { + var button = buttons[i]; + var input = button.querySelector(SELECTOR_INPUT); + + if (input.checked || input.hasAttribute('checked')) { + button.classList.add(CLASS_NAME_ACTIVE); + } else { + button.classList.remove(CLASS_NAME_ACTIVE); + } + } // find all button toggles + + + buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE)); + + for (var _i = 0, _len = buttons.length; _i < _len; _i++) { + var _button = buttons[_i]; + + if (_button.getAttribute('aria-pressed') === 'true') { + _button.classList.add(CLASS_NAME_ACTIVE); + } else { + _button.classList.remove(CLASS_NAME_ACTIVE); + } + } + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$1] = Button._jQueryInterface; + $.fn[NAME$1].Constructor = Button; + + $.fn[NAME$1].noConflict = function () { + $.fn[NAME$1] = JQUERY_NO_CONFLICT$1; + return Button._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$2 = 'carousel'; + var VERSION$2 = '4.5.0'; + var DATA_KEY$2 = 'bs.carousel'; + var EVENT_KEY$2 = "." + DATA_KEY$2; + var DATA_API_KEY$2 = '.data-api'; + var JQUERY_NO_CONFLICT$2 = $.fn[NAME$2]; + var ARROW_LEFT_KEYCODE = 37; // KeyboardEvent.which value for left arrow key + + var ARROW_RIGHT_KEYCODE = 39; // KeyboardEvent.which value for right arrow key + + var TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch + + var SWIPE_THRESHOLD = 40; + var Default = { + interval: 5000, + keyboard: true, + slide: false, + pause: 'hover', + wrap: true, + touch: true + }; + var DefaultType = { + interval: '(number|boolean)', + keyboard: 'boolean', + slide: '(boolean|string)', + pause: '(string|boolean)', + wrap: 'boolean', + touch: 'boolean' + }; + var DIRECTION_NEXT = 'next'; + var DIRECTION_PREV = 'prev'; + var DIRECTION_LEFT = 'left'; + var DIRECTION_RIGHT = 'right'; + var EVENT_SLIDE = "slide" + EVENT_KEY$2; + var EVENT_SLID = "slid" + EVENT_KEY$2; + var EVENT_KEYDOWN = "keydown" + EVENT_KEY$2; + var EVENT_MOUSEENTER = "mouseenter" + EVENT_KEY$2; + var EVENT_MOUSELEAVE = "mouseleave" + EVENT_KEY$2; + var EVENT_TOUCHSTART = "touchstart" + EVENT_KEY$2; + var EVENT_TOUCHMOVE = "touchmove" + EVENT_KEY$2; + var EVENT_TOUCHEND = "touchend" + EVENT_KEY$2; + var EVENT_POINTERDOWN = "pointerdown" + EVENT_KEY$2; + var EVENT_POINTERUP = "pointerup" + EVENT_KEY$2; + var EVENT_DRAG_START = "dragstart" + EVENT_KEY$2; + var EVENT_LOAD_DATA_API$1 = "load" + EVENT_KEY$2 + DATA_API_KEY$2; + var EVENT_CLICK_DATA_API$2 = "click" + EVENT_KEY$2 + DATA_API_KEY$2; + var CLASS_NAME_CAROUSEL = 'carousel'; + var CLASS_NAME_ACTIVE$1 = 'active'; + var CLASS_NAME_SLIDE = 'slide'; + var CLASS_NAME_RIGHT = 'carousel-item-right'; + var CLASS_NAME_LEFT = 'carousel-item-left'; + var CLASS_NAME_NEXT = 'carousel-item-next'; + var CLASS_NAME_PREV = 'carousel-item-prev'; + var CLASS_NAME_POINTER_EVENT = 'pointer-event'; + var SELECTOR_ACTIVE$1 = '.active'; + var SELECTOR_ACTIVE_ITEM = '.active.carousel-item'; + var SELECTOR_ITEM = '.carousel-item'; + var SELECTOR_ITEM_IMG = '.carousel-item img'; + var SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'; + var SELECTOR_INDICATORS = '.carousel-indicators'; + var SELECTOR_DATA_SLIDE = '[data-slide], [data-slide-to]'; + var SELECTOR_DATA_RIDE = '[data-ride="carousel"]'; + var PointerType = { + TOUCH: 'touch', + PEN: 'pen' + }; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Carousel = /*#__PURE__*/function () { + function Carousel(element, config) { + this._items = null; + this._interval = null; + this._activeElement = null; + this._isPaused = false; + this._isSliding = false; + this.touchTimeout = null; + this.touchStartX = 0; + this.touchDeltaX = 0; + this._config = this._getConfig(config); + this._element = element; + this._indicatorsElement = this._element.querySelector(SELECTOR_INDICATORS); + this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0; + this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent); + + this._addEventListeners(); + } // Getters + + + var _proto = Carousel.prototype; + + // Public + _proto.next = function next() { + if (!this._isSliding) { + this._slide(DIRECTION_NEXT); + } + }; + + _proto.nextWhenVisible = function nextWhenVisible() { + // Don't call next when the page isn't visible + // or the carousel or its parent isn't visible + if (!document.hidden && $(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden') { + this.next(); + } + }; + + _proto.prev = function prev() { + if (!this._isSliding) { + this._slide(DIRECTION_PREV); + } + }; + + _proto.pause = function pause(event) { + if (!event) { + this._isPaused = true; + } + + if (this._element.querySelector(SELECTOR_NEXT_PREV)) { + Util.triggerTransitionEnd(this._element); + this.cycle(true); + } + + clearInterval(this._interval); + this._interval = null; + }; + + _proto.cycle = function cycle(event) { + if (!event) { + this._isPaused = false; + } + + if (this._interval) { + clearInterval(this._interval); + this._interval = null; + } + + if (this._config.interval && !this._isPaused) { + this._interval = setInterval((document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval); + } + }; + + _proto.to = function to(index) { + var _this = this; + + this._activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM); + + var activeIndex = this._getItemIndex(this._activeElement); + + if (index > this._items.length - 1 || index < 0) { + return; + } + + if (this._isSliding) { + $(this._element).one(EVENT_SLID, function () { + return _this.to(index); + }); + return; + } + + if (activeIndex === index) { + this.pause(); + this.cycle(); + return; + } + + var direction = index > activeIndex ? DIRECTION_NEXT : DIRECTION_PREV; + + this._slide(direction, this._items[index]); + }; + + _proto.dispose = function dispose() { + $(this._element).off(EVENT_KEY$2); + $.removeData(this._element, DATA_KEY$2); + this._items = null; + this._config = null; + this._element = null; + this._interval = null; + this._isPaused = null; + this._isSliding = null; + this._activeElement = null; + this._indicatorsElement = null; + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2({}, Default), config); + Util.typeCheckConfig(NAME$2, config, DefaultType); + return config; + }; + + _proto._handleSwipe = function _handleSwipe() { + var absDeltax = Math.abs(this.touchDeltaX); + + if (absDeltax <= SWIPE_THRESHOLD) { + return; + } + + var direction = absDeltax / this.touchDeltaX; + this.touchDeltaX = 0; // swipe left + + if (direction > 0) { + this.prev(); + } // swipe right + + + if (direction < 0) { + this.next(); + } + }; + + _proto._addEventListeners = function _addEventListeners() { + var _this2 = this; + + if (this._config.keyboard) { + $(this._element).on(EVENT_KEYDOWN, function (event) { + return _this2._keydown(event); + }); + } + + if (this._config.pause === 'hover') { + $(this._element).on(EVENT_MOUSEENTER, function (event) { + return _this2.pause(event); + }).on(EVENT_MOUSELEAVE, function (event) { + return _this2.cycle(event); + }); + } + + if (this._config.touch) { + this._addTouchEventListeners(); + } + }; + + _proto._addTouchEventListeners = function _addTouchEventListeners() { + var _this3 = this; + + if (!this._touchSupported) { + return; + } + + var start = function start(event) { + if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { + _this3.touchStartX = event.originalEvent.clientX; + } else if (!_this3._pointerEvent) { + _this3.touchStartX = event.originalEvent.touches[0].clientX; + } + }; + + var move = function move(event) { + // ensure swiping with one touch and not pinching + if (event.originalEvent.touches && event.originalEvent.touches.length > 1) { + _this3.touchDeltaX = 0; + } else { + _this3.touchDeltaX = event.originalEvent.touches[0].clientX - _this3.touchStartX; + } + }; + + var end = function end(event) { + if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { + _this3.touchDeltaX = event.originalEvent.clientX - _this3.touchStartX; + } + + _this3._handleSwipe(); + + if (_this3._config.pause === 'hover') { + // If it's a touch-enabled device, mouseenter/leave are fired as + // part of the mouse compatibility events on first tap - the carousel + // would stop cycling until user tapped out of it; + // here, we listen for touchend, explicitly pause the carousel + // (as if it's the second time we tap on it, mouseenter compat event + // is NOT fired) and after a timeout (to allow for mouse compatibility + // events to fire) we explicitly restart cycling + _this3.pause(); + + if (_this3.touchTimeout) { + clearTimeout(_this3.touchTimeout); + } + + _this3.touchTimeout = setTimeout(function (event) { + return _this3.cycle(event); + }, TOUCHEVENT_COMPAT_WAIT + _this3._config.interval); + } + }; + + $(this._element.querySelectorAll(SELECTOR_ITEM_IMG)).on(EVENT_DRAG_START, function (e) { + return e.preventDefault(); + }); + + if (this._pointerEvent) { + $(this._element).on(EVENT_POINTERDOWN, function (event) { + return start(event); + }); + $(this._element).on(EVENT_POINTERUP, function (event) { + return end(event); + }); + + this._element.classList.add(CLASS_NAME_POINTER_EVENT); + } else { + $(this._element).on(EVENT_TOUCHSTART, function (event) { + return start(event); + }); + $(this._element).on(EVENT_TOUCHMOVE, function (event) { + return move(event); + }); + $(this._element).on(EVENT_TOUCHEND, function (event) { + return end(event); + }); + } + }; + + _proto._keydown = function _keydown(event) { + if (/input|textarea/i.test(event.target.tagName)) { + return; + } + + switch (event.which) { + case ARROW_LEFT_KEYCODE: + event.preventDefault(); + this.prev(); + break; + + case ARROW_RIGHT_KEYCODE: + event.preventDefault(); + this.next(); + break; + } + }; + + _proto._getItemIndex = function _getItemIndex(element) { + this._items = element && element.parentNode ? [].slice.call(element.parentNode.querySelectorAll(SELECTOR_ITEM)) : []; + return this._items.indexOf(element); + }; + + _proto._getItemByDirection = function _getItemByDirection(direction, activeElement) { + var isNextDirection = direction === DIRECTION_NEXT; + var isPrevDirection = direction === DIRECTION_PREV; + + var activeIndex = this._getItemIndex(activeElement); + + var lastItemIndex = this._items.length - 1; + var isGoingToWrap = isPrevDirection && activeIndex === 0 || isNextDirection && activeIndex === lastItemIndex; + + if (isGoingToWrap && !this._config.wrap) { + return activeElement; + } + + var delta = direction === DIRECTION_PREV ? -1 : 1; + var itemIndex = (activeIndex + delta) % this._items.length; + return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex]; + }; + + _proto._triggerSlideEvent = function _triggerSlideEvent(relatedTarget, eventDirectionName) { + var targetIndex = this._getItemIndex(relatedTarget); + + var fromIndex = this._getItemIndex(this._element.querySelector(SELECTOR_ACTIVE_ITEM)); + + var slideEvent = $.Event(EVENT_SLIDE, { + relatedTarget: relatedTarget, + direction: eventDirectionName, + from: fromIndex, + to: targetIndex + }); + $(this._element).trigger(slideEvent); + return slideEvent; + }; + + _proto._setActiveIndicatorElement = function _setActiveIndicatorElement(element) { + if (this._indicatorsElement) { + var indicators = [].slice.call(this._indicatorsElement.querySelectorAll(SELECTOR_ACTIVE$1)); + $(indicators).removeClass(CLASS_NAME_ACTIVE$1); + + var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)]; + + if (nextIndicator) { + $(nextIndicator).addClass(CLASS_NAME_ACTIVE$1); + } + } + }; + + _proto._slide = function _slide(direction, element) { + var _this4 = this; + + var activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM); + + var activeElementIndex = this._getItemIndex(activeElement); + + var nextElement = element || activeElement && this._getItemByDirection(direction, activeElement); + + var nextElementIndex = this._getItemIndex(nextElement); + + var isCycling = Boolean(this._interval); + var directionalClassName; + var orderClassName; + var eventDirectionName; + + if (direction === DIRECTION_NEXT) { + directionalClassName = CLASS_NAME_LEFT; + orderClassName = CLASS_NAME_NEXT; + eventDirectionName = DIRECTION_LEFT; + } else { + directionalClassName = CLASS_NAME_RIGHT; + orderClassName = CLASS_NAME_PREV; + eventDirectionName = DIRECTION_RIGHT; + } + + if (nextElement && $(nextElement).hasClass(CLASS_NAME_ACTIVE$1)) { + this._isSliding = false; + return; + } + + var slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName); + + if (slideEvent.isDefaultPrevented()) { + return; + } + + if (!activeElement || !nextElement) { + // Some weirdness is happening, so we bail + return; + } + + this._isSliding = true; + + if (isCycling) { + this.pause(); + } + + this._setActiveIndicatorElement(nextElement); + + var slidEvent = $.Event(EVENT_SLID, { + relatedTarget: nextElement, + direction: eventDirectionName, + from: activeElementIndex, + to: nextElementIndex + }); + + if ($(this._element).hasClass(CLASS_NAME_SLIDE)) { + $(nextElement).addClass(orderClassName); + Util.reflow(nextElement); + $(activeElement).addClass(directionalClassName); + $(nextElement).addClass(directionalClassName); + var nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10); + + if (nextElementInterval) { + this._config.defaultInterval = this._config.defaultInterval || this._config.interval; + this._config.interval = nextElementInterval; + } else { + this._config.interval = this._config.defaultInterval || this._config.interval; + } + + var transitionDuration = Util.getTransitionDurationFromElement(activeElement); + $(activeElement).one(Util.TRANSITION_END, function () { + $(nextElement).removeClass(directionalClassName + " " + orderClassName).addClass(CLASS_NAME_ACTIVE$1); + $(activeElement).removeClass(CLASS_NAME_ACTIVE$1 + " " + orderClassName + " " + directionalClassName); + _this4._isSliding = false; + setTimeout(function () { + return $(_this4._element).trigger(slidEvent); + }, 0); + }).emulateTransitionEnd(transitionDuration); + } else { + $(activeElement).removeClass(CLASS_NAME_ACTIVE$1); + $(nextElement).addClass(CLASS_NAME_ACTIVE$1); + this._isSliding = false; + $(this._element).trigger(slidEvent); + } + + if (isCycling) { + this.cycle(); + } + } // Static + ; + + Carousel._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$2); + + var _config = _objectSpread2(_objectSpread2({}, Default), $(this).data()); + + if (typeof config === 'object') { + _config = _objectSpread2(_objectSpread2({}, _config), config); + } + + var action = typeof config === 'string' ? config : _config.slide; + + if (!data) { + data = new Carousel(this, _config); + $(this).data(DATA_KEY$2, data); + } + + if (typeof config === 'number') { + data.to(config); + } else if (typeof action === 'string') { + if (typeof data[action] === 'undefined') { + throw new TypeError("No method named \"" + action + "\""); + } + + data[action](); + } else if (_config.interval && _config.ride) { + data.pause(); + data.cycle(); + } + }); + }; + + Carousel._dataApiClickHandler = function _dataApiClickHandler(event) { + var selector = Util.getSelectorFromElement(this); + + if (!selector) { + return; + } + + var target = $(selector)[0]; + + if (!target || !$(target).hasClass(CLASS_NAME_CAROUSEL)) { + return; + } + + var config = _objectSpread2(_objectSpread2({}, $(target).data()), $(this).data()); + + var slideIndex = this.getAttribute('data-slide-to'); + + if (slideIndex) { + config.interval = false; + } + + Carousel._jQueryInterface.call($(target), config); + + if (slideIndex) { + $(target).data(DATA_KEY$2).to(slideIndex); + } + + event.preventDefault(); + }; + + _createClass(Carousel, null, [{ + key: "VERSION", + get: function get() { + return VERSION$2; + } + }, { + key: "Default", + get: function get() { + return Default; + } + }]); + + return Carousel; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API$2, SELECTOR_DATA_SLIDE, Carousel._dataApiClickHandler); + $(window).on(EVENT_LOAD_DATA_API$1, function () { + var carousels = [].slice.call(document.querySelectorAll(SELECTOR_DATA_RIDE)); + + for (var i = 0, len = carousels.length; i < len; i++) { + var $carousel = $(carousels[i]); + + Carousel._jQueryInterface.call($carousel, $carousel.data()); + } + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$2] = Carousel._jQueryInterface; + $.fn[NAME$2].Constructor = Carousel; + + $.fn[NAME$2].noConflict = function () { + $.fn[NAME$2] = JQUERY_NO_CONFLICT$2; + return Carousel._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$3 = 'collapse'; + var VERSION$3 = '4.5.0'; + var DATA_KEY$3 = 'bs.collapse'; + var EVENT_KEY$3 = "." + DATA_KEY$3; + var DATA_API_KEY$3 = '.data-api'; + var JQUERY_NO_CONFLICT$3 = $.fn[NAME$3]; + var Default$1 = { + toggle: true, + parent: '' + }; + var DefaultType$1 = { + toggle: 'boolean', + parent: '(string|element)' + }; + var EVENT_SHOW = "show" + EVENT_KEY$3; + var EVENT_SHOWN = "shown" + EVENT_KEY$3; + var EVENT_HIDE = "hide" + EVENT_KEY$3; + var EVENT_HIDDEN = "hidden" + EVENT_KEY$3; + var EVENT_CLICK_DATA_API$3 = "click" + EVENT_KEY$3 + DATA_API_KEY$3; + var CLASS_NAME_SHOW$1 = 'show'; + var CLASS_NAME_COLLAPSE = 'collapse'; + var CLASS_NAME_COLLAPSING = 'collapsing'; + var CLASS_NAME_COLLAPSED = 'collapsed'; + var DIMENSION_WIDTH = 'width'; + var DIMENSION_HEIGHT = 'height'; + var SELECTOR_ACTIVES = '.show, .collapsing'; + var SELECTOR_DATA_TOGGLE$1 = '[data-toggle="collapse"]'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Collapse = /*#__PURE__*/function () { + function Collapse(element, config) { + this._isTransitioning = false; + this._element = element; + this._config = this._getConfig(config); + this._triggerArray = [].slice.call(document.querySelectorAll("[data-toggle=\"collapse\"][href=\"#" + element.id + "\"]," + ("[data-toggle=\"collapse\"][data-target=\"#" + element.id + "\"]"))); + var toggleList = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE$1)); + + for (var i = 0, len = toggleList.length; i < len; i++) { + var elem = toggleList[i]; + var selector = Util.getSelectorFromElement(elem); + var filterElement = [].slice.call(document.querySelectorAll(selector)).filter(function (foundElem) { + return foundElem === element; + }); + + if (selector !== null && filterElement.length > 0) { + this._selector = selector; + + this._triggerArray.push(elem); + } + } + + this._parent = this._config.parent ? this._getParent() : null; + + if (!this._config.parent) { + this._addAriaAndCollapsedClass(this._element, this._triggerArray); + } + + if (this._config.toggle) { + this.toggle(); + } + } // Getters + + + var _proto = Collapse.prototype; + + // Public + _proto.toggle = function toggle() { + if ($(this._element).hasClass(CLASS_NAME_SHOW$1)) { + this.hide(); + } else { + this.show(); + } + }; + + _proto.show = function show() { + var _this = this; + + if (this._isTransitioning || $(this._element).hasClass(CLASS_NAME_SHOW$1)) { + return; + } + + var actives; + var activesData; + + if (this._parent) { + actives = [].slice.call(this._parent.querySelectorAll(SELECTOR_ACTIVES)).filter(function (elem) { + if (typeof _this._config.parent === 'string') { + return elem.getAttribute('data-parent') === _this._config.parent; + } + + return elem.classList.contains(CLASS_NAME_COLLAPSE); + }); + + if (actives.length === 0) { + actives = null; + } + } + + if (actives) { + activesData = $(actives).not(this._selector).data(DATA_KEY$3); + + if (activesData && activesData._isTransitioning) { + return; + } + } + + var startEvent = $.Event(EVENT_SHOW); + $(this._element).trigger(startEvent); + + if (startEvent.isDefaultPrevented()) { + return; + } + + if (actives) { + Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide'); + + if (!activesData) { + $(actives).data(DATA_KEY$3, null); + } + } + + var dimension = this._getDimension(); + + $(this._element).removeClass(CLASS_NAME_COLLAPSE).addClass(CLASS_NAME_COLLAPSING); + this._element.style[dimension] = 0; + + if (this._triggerArray.length) { + $(this._triggerArray).removeClass(CLASS_NAME_COLLAPSED).attr('aria-expanded', true); + } + + this.setTransitioning(true); + + var complete = function complete() { + $(_this._element).removeClass(CLASS_NAME_COLLAPSING).addClass(CLASS_NAME_COLLAPSE + " " + CLASS_NAME_SHOW$1); + _this._element.style[dimension] = ''; + + _this.setTransitioning(false); + + $(_this._element).trigger(EVENT_SHOWN); + }; + + var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); + var scrollSize = "scroll" + capitalizedDimension; + var transitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + this._element.style[dimension] = this._element[scrollSize] + "px"; + }; + + _proto.hide = function hide() { + var _this2 = this; + + if (this._isTransitioning || !$(this._element).hasClass(CLASS_NAME_SHOW$1)) { + return; + } + + var startEvent = $.Event(EVENT_HIDE); + $(this._element).trigger(startEvent); + + if (startEvent.isDefaultPrevented()) { + return; + } + + var dimension = this._getDimension(); + + this._element.style[dimension] = this._element.getBoundingClientRect()[dimension] + "px"; + Util.reflow(this._element); + $(this._element).addClass(CLASS_NAME_COLLAPSING).removeClass(CLASS_NAME_COLLAPSE + " " + CLASS_NAME_SHOW$1); + var triggerArrayLength = this._triggerArray.length; + + if (triggerArrayLength > 0) { + for (var i = 0; i < triggerArrayLength; i++) { + var trigger = this._triggerArray[i]; + var selector = Util.getSelectorFromElement(trigger); + + if (selector !== null) { + var $elem = $([].slice.call(document.querySelectorAll(selector))); + + if (!$elem.hasClass(CLASS_NAME_SHOW$1)) { + $(trigger).addClass(CLASS_NAME_COLLAPSED).attr('aria-expanded', false); + } + } + } + } + + this.setTransitioning(true); + + var complete = function complete() { + _this2.setTransitioning(false); + + $(_this2._element).removeClass(CLASS_NAME_COLLAPSING).addClass(CLASS_NAME_COLLAPSE).trigger(EVENT_HIDDEN); + }; + + this._element.style[dimension] = ''; + var transitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + }; + + _proto.setTransitioning = function setTransitioning(isTransitioning) { + this._isTransitioning = isTransitioning; + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$3); + this._config = null; + this._parent = null; + this._element = null; + this._triggerArray = null; + this._isTransitioning = null; + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2({}, Default$1), config); + config.toggle = Boolean(config.toggle); // Coerce string values + + Util.typeCheckConfig(NAME$3, config, DefaultType$1); + return config; + }; + + _proto._getDimension = function _getDimension() { + var hasWidth = $(this._element).hasClass(DIMENSION_WIDTH); + return hasWidth ? DIMENSION_WIDTH : DIMENSION_HEIGHT; + }; + + _proto._getParent = function _getParent() { + var _this3 = this; + + var parent; + + if (Util.isElement(this._config.parent)) { + parent = this._config.parent; // It's a jQuery object + + if (typeof this._config.parent.jquery !== 'undefined') { + parent = this._config.parent[0]; + } + } else { + parent = document.querySelector(this._config.parent); + } + + var selector = "[data-toggle=\"collapse\"][data-parent=\"" + this._config.parent + "\"]"; + var children = [].slice.call(parent.querySelectorAll(selector)); + $(children).each(function (i, element) { + _this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]); + }); + return parent; + }; + + _proto._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) { + var isOpen = $(element).hasClass(CLASS_NAME_SHOW$1); + + if (triggerArray.length) { + $(triggerArray).toggleClass(CLASS_NAME_COLLAPSED, !isOpen).attr('aria-expanded', isOpen); + } + } // Static + ; + + Collapse._getTargetFromElement = function _getTargetFromElement(element) { + var selector = Util.getSelectorFromElement(element); + return selector ? document.querySelector(selector) : null; + }; + + Collapse._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var $this = $(this); + var data = $this.data(DATA_KEY$3); + + var _config = _objectSpread2(_objectSpread2(_objectSpread2({}, Default$1), $this.data()), typeof config === 'object' && config ? config : {}); + + if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) { + _config.toggle = false; + } + + if (!data) { + data = new Collapse(this, _config); + $this.data(DATA_KEY$3, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + _createClass(Collapse, null, [{ + key: "VERSION", + get: function get() { + return VERSION$3; + } + }, { + key: "Default", + get: function get() { + return Default$1; + } + }]); + + return Collapse; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$1, function (event) { + // preventDefault only for elements (which change the URL) not inside the collapsible element + if (event.currentTarget.tagName === 'A') { + event.preventDefault(); + } + + var $trigger = $(this); + var selector = Util.getSelectorFromElement(this); + var selectors = [].slice.call(document.querySelectorAll(selector)); + $(selectors).each(function () { + var $target = $(this); + var data = $target.data(DATA_KEY$3); + var config = data ? 'toggle' : $trigger.data(); + + Collapse._jQueryInterface.call($target, config); + }); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$3] = Collapse._jQueryInterface; + $.fn[NAME$3].Constructor = Collapse; + + $.fn[NAME$3].noConflict = function () { + $.fn[NAME$3] = JQUERY_NO_CONFLICT$3; + return Collapse._jQueryInterface; + }; + + /**! + * @fileOverview Kickass library to create and place poppers near their reference elements. + * @version 1.16.0 + * @license + * Copyright (c) 2016 Federico Zivolo and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof navigator !== 'undefined'; + + var timeoutDuration = function () { + var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox']; + for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) { + if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) { + return 1; + } + } + return 0; + }(); + + function microtaskDebounce(fn) { + var called = false; + return function () { + if (called) { + return; + } + called = true; + window.Promise.resolve().then(function () { + called = false; + fn(); + }); + }; + } + + function taskDebounce(fn) { + var scheduled = false; + return function () { + if (!scheduled) { + scheduled = true; + setTimeout(function () { + scheduled = false; + fn(); + }, timeoutDuration); + } + }; + } + + var supportsMicroTasks = isBrowser && window.Promise; + + /** + * Create a debounced version of a method, that's asynchronously deferred + * but called in the minimum time possible. + * + * @method + * @memberof Popper.Utils + * @argument {Function} fn + * @returns {Function} + */ + var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce; + + /** + * Check if the given variable is a function + * @method + * @memberof Popper.Utils + * @argument {Any} functionToCheck - variable to check + * @returns {Boolean} answer to: is a function? + */ + function isFunction(functionToCheck) { + var getType = {}; + return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; + } + + /** + * Get CSS computed property of the given element + * @method + * @memberof Popper.Utils + * @argument {Eement} element + * @argument {String} property + */ + function getStyleComputedProperty(element, property) { + if (element.nodeType !== 1) { + return []; + } + // NOTE: 1 DOM access here + var window = element.ownerDocument.defaultView; + var css = window.getComputedStyle(element, null); + return property ? css[property] : css; + } + + /** + * Returns the parentNode or the host of the element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} parent + */ + function getParentNode(element) { + if (element.nodeName === 'HTML') { + return element; + } + return element.parentNode || element.host; + } + + /** + * Returns the scrolling parent of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} scroll parent + */ + function getScrollParent(element) { + // Return body, `getScroll` will take care to get the correct `scrollTop` from it + if (!element) { + return document.body; + } + + switch (element.nodeName) { + case 'HTML': + case 'BODY': + return element.ownerDocument.body; + case '#document': + return element.body; + } + + // Firefox want us to check `-x` and `-y` variations as well + + var _getStyleComputedProp = getStyleComputedProperty(element), + overflow = _getStyleComputedProp.overflow, + overflowX = _getStyleComputedProp.overflowX, + overflowY = _getStyleComputedProp.overflowY; + + if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) { + return element; + } + + return getScrollParent(getParentNode(element)); + } + + /** + * Returns the reference node of the reference object, or the reference object itself. + * @method + * @memberof Popper.Utils + * @param {Element|Object} reference - the reference element (the popper will be relative to this) + * @returns {Element} parent + */ + function getReferenceNode(reference) { + return reference && reference.referenceNode ? reference.referenceNode : reference; + } + + var isIE11 = isBrowser && !!(window.MSInputMethodContext && document.documentMode); + var isIE10 = isBrowser && /MSIE 10/.test(navigator.userAgent); + + /** + * Determines if the browser is Internet Explorer + * @method + * @memberof Popper.Utils + * @param {Number} version to check + * @returns {Boolean} isIE + */ + function isIE(version) { + if (version === 11) { + return isIE11; + } + if (version === 10) { + return isIE10; + } + return isIE11 || isIE10; + } + + /** + * Returns the offset parent of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} offset parent + */ + function getOffsetParent(element) { + if (!element) { + return document.documentElement; + } + + var noOffsetParent = isIE(10) ? document.body : null; + + // NOTE: 1 DOM access here + var offsetParent = element.offsetParent || null; + // Skip hidden elements which don't have an offsetParent + while (offsetParent === noOffsetParent && element.nextElementSibling) { + offsetParent = (element = element.nextElementSibling).offsetParent; + } + + var nodeName = offsetParent && offsetParent.nodeName; + + if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') { + return element ? element.ownerDocument.documentElement : document.documentElement; + } + + // .offsetParent will return the closest TH, TD or TABLE in case + // no offsetParent is present, I hate this job... + if (['TH', 'TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') { + return getOffsetParent(offsetParent); + } + + return offsetParent; + } + + function isOffsetContainer(element) { + var nodeName = element.nodeName; + + if (nodeName === 'BODY') { + return false; + } + return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element; + } + + /** + * Finds the root node (document, shadowDOM root) of the given element + * @method + * @memberof Popper.Utils + * @argument {Element} node + * @returns {Element} root node + */ + function getRoot(node) { + if (node.parentNode !== null) { + return getRoot(node.parentNode); + } + + return node; + } + + /** + * Finds the offset parent common to the two provided nodes + * @method + * @memberof Popper.Utils + * @argument {Element} element1 + * @argument {Element} element2 + * @returns {Element} common offset parent + */ + function findCommonOffsetParent(element1, element2) { + // This check is needed to avoid errors in case one of the elements isn't defined for any reason + if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) { + return document.documentElement; + } + + // Here we make sure to give as "start" the element that comes first in the DOM + var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING; + var start = order ? element1 : element2; + var end = order ? element2 : element1; + + // Get common ancestor container + var range = document.createRange(); + range.setStart(start, 0); + range.setEnd(end, 0); + var commonAncestorContainer = range.commonAncestorContainer; + + // Both nodes are inside #document + + if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) { + if (isOffsetContainer(commonAncestorContainer)) { + return commonAncestorContainer; + } + + return getOffsetParent(commonAncestorContainer); + } + + // one of the nodes is inside shadowDOM, find which one + var element1root = getRoot(element1); + if (element1root.host) { + return findCommonOffsetParent(element1root.host, element2); + } else { + return findCommonOffsetParent(element1, getRoot(element2).host); + } + } + + /** + * Gets the scroll value of the given element in the given side (top and left) + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @argument {String} side `top` or `left` + * @returns {number} amount of scrolled pixels + */ + function getScroll(element) { + var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top'; + + var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft'; + var nodeName = element.nodeName; + + if (nodeName === 'BODY' || nodeName === 'HTML') { + var html = element.ownerDocument.documentElement; + var scrollingElement = element.ownerDocument.scrollingElement || html; + return scrollingElement[upperSide]; + } + + return element[upperSide]; + } + + /* + * Sum or subtract the element scroll values (left and top) from a given rect object + * @method + * @memberof Popper.Utils + * @param {Object} rect - Rect object you want to change + * @param {HTMLElement} element - The element from the function reads the scroll values + * @param {Boolean} subtract - set to true if you want to subtract the scroll values + * @return {Object} rect - The modifier rect object + */ + function includeScroll(rect, element) { + var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var scrollTop = getScroll(element, 'top'); + var scrollLeft = getScroll(element, 'left'); + var modifier = subtract ? -1 : 1; + rect.top += scrollTop * modifier; + rect.bottom += scrollTop * modifier; + rect.left += scrollLeft * modifier; + rect.right += scrollLeft * modifier; + return rect; + } + + /* + * Helper to detect borders of a given element + * @method + * @memberof Popper.Utils + * @param {CSSStyleDeclaration} styles + * Result of `getStyleComputedProperty` on the given element + * @param {String} axis - `x` or `y` + * @return {number} borders - The borders size of the given axis + */ + + function getBordersSize(styles, axis) { + var sideA = axis === 'x' ? 'Left' : 'Top'; + var sideB = sideA === 'Left' ? 'Right' : 'Bottom'; + + return parseFloat(styles['border' + sideA + 'Width'], 10) + parseFloat(styles['border' + sideB + 'Width'], 10); + } + + function getSize(axis, body, html, computedStyle) { + return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE(10) ? parseInt(html['offset' + axis]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')]) + parseInt(computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')]) : 0); + } + + function getWindowSizes(document) { + var body = document.body; + var html = document.documentElement; + var computedStyle = isIE(10) && getComputedStyle(html); + + return { + height: getSize('Height', body, html, computedStyle), + width: getSize('Width', body, html, computedStyle) + }; + } + + var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + + var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + }(); + + + + + + var defineProperty = function (obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + }; + + var _extends = Object.assign || function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + /** + * Given element offsets, generate an output similar to getBoundingClientRect + * @method + * @memberof Popper.Utils + * @argument {Object} offsets + * @returns {Object} ClientRect like output + */ + function getClientRect(offsets) { + return _extends({}, offsets, { + right: offsets.left + offsets.width, + bottom: offsets.top + offsets.height + }); + } + + /** + * Get bounding client rect of given element + * @method + * @memberof Popper.Utils + * @param {HTMLElement} element + * @return {Object} client rect + */ + function getBoundingClientRect(element) { + var rect = {}; + + // IE10 10 FIX: Please, don't ask, the element isn't + // considered in DOM in some circumstances... + // This isn't reproducible in IE10 compatibility mode of IE11 + try { + if (isIE(10)) { + rect = element.getBoundingClientRect(); + var scrollTop = getScroll(element, 'top'); + var scrollLeft = getScroll(element, 'left'); + rect.top += scrollTop; + rect.left += scrollLeft; + rect.bottom += scrollTop; + rect.right += scrollLeft; + } else { + rect = element.getBoundingClientRect(); + } + } catch (e) {} + + var result = { + left: rect.left, + top: rect.top, + width: rect.right - rect.left, + height: rect.bottom - rect.top + }; + + // subtract scrollbar size from sizes + var sizes = element.nodeName === 'HTML' ? getWindowSizes(element.ownerDocument) : {}; + var width = sizes.width || element.clientWidth || result.width; + var height = sizes.height || element.clientHeight || result.height; + + var horizScrollbar = element.offsetWidth - width; + var vertScrollbar = element.offsetHeight - height; + + // if an hypothetical scrollbar is detected, we must be sure it's not a `border` + // we make this check conditional for performance reasons + if (horizScrollbar || vertScrollbar) { + var styles = getStyleComputedProperty(element); + horizScrollbar -= getBordersSize(styles, 'x'); + vertScrollbar -= getBordersSize(styles, 'y'); + + result.width -= horizScrollbar; + result.height -= vertScrollbar; + } + + return getClientRect(result); + } + + function getOffsetRectRelativeToArbitraryNode(children, parent) { + var fixedPosition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + + var isIE10 = isIE(10); + var isHTML = parent.nodeName === 'HTML'; + var childrenRect = getBoundingClientRect(children); + var parentRect = getBoundingClientRect(parent); + var scrollParent = getScrollParent(children); + + var styles = getStyleComputedProperty(parent); + var borderTopWidth = parseFloat(styles.borderTopWidth, 10); + var borderLeftWidth = parseFloat(styles.borderLeftWidth, 10); + + // In cases where the parent is fixed, we must ignore negative scroll in offset calc + if (fixedPosition && isHTML) { + parentRect.top = Math.max(parentRect.top, 0); + parentRect.left = Math.max(parentRect.left, 0); + } + var offsets = getClientRect({ + top: childrenRect.top - parentRect.top - borderTopWidth, + left: childrenRect.left - parentRect.left - borderLeftWidth, + width: childrenRect.width, + height: childrenRect.height + }); + offsets.marginTop = 0; + offsets.marginLeft = 0; + + // Subtract margins of documentElement in case it's being used as parent + // we do this only on HTML because it's the only element that behaves + // differently when margins are applied to it. The margins are included in + // the box of the documentElement, in the other cases not. + if (!isIE10 && isHTML) { + var marginTop = parseFloat(styles.marginTop, 10); + var marginLeft = parseFloat(styles.marginLeft, 10); + + offsets.top -= borderTopWidth - marginTop; + offsets.bottom -= borderTopWidth - marginTop; + offsets.left -= borderLeftWidth - marginLeft; + offsets.right -= borderLeftWidth - marginLeft; + + // Attach marginTop and marginLeft because in some circumstances we may need them + offsets.marginTop = marginTop; + offsets.marginLeft = marginLeft; + } + + if (isIE10 && !fixedPosition ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') { + offsets = includeScroll(offsets, parent); + } + + return offsets; + } + + function getViewportOffsetRectRelativeToArtbitraryNode(element) { + var excludeScroll = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var html = element.ownerDocument.documentElement; + var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html); + var width = Math.max(html.clientWidth, window.innerWidth || 0); + var height = Math.max(html.clientHeight, window.innerHeight || 0); + + var scrollTop = !excludeScroll ? getScroll(html) : 0; + var scrollLeft = !excludeScroll ? getScroll(html, 'left') : 0; + + var offset = { + top: scrollTop - relativeOffset.top + relativeOffset.marginTop, + left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft, + width: width, + height: height + }; + + return getClientRect(offset); + } + + /** + * Check if the given element is fixed or is inside a fixed parent + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @argument {Element} customContainer + * @returns {Boolean} answer to "isFixed?" + */ + function isFixed(element) { + var nodeName = element.nodeName; + if (nodeName === 'BODY' || nodeName === 'HTML') { + return false; + } + if (getStyleComputedProperty(element, 'position') === 'fixed') { + return true; + } + var parentNode = getParentNode(element); + if (!parentNode) { + return false; + } + return isFixed(parentNode); + } + + /** + * Finds the first parent of an element that has a transformed property defined + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Element} first transformed parent or documentElement + */ + + function getFixedPositionOffsetParent(element) { + // This check is needed to avoid errors in case one of the elements isn't defined for any reason + if (!element || !element.parentElement || isIE()) { + return document.documentElement; + } + var el = element.parentElement; + while (el && getStyleComputedProperty(el, 'transform') === 'none') { + el = el.parentElement; + } + return el || document.documentElement; + } + + /** + * Computed the boundaries limits and return them + * @method + * @memberof Popper.Utils + * @param {HTMLElement} popper + * @param {HTMLElement} reference + * @param {number} padding + * @param {HTMLElement} boundariesElement - Element used to define the boundaries + * @param {Boolean} fixedPosition - Is in fixed position mode + * @returns {Object} Coordinates of the boundaries + */ + function getBoundaries(popper, reference, padding, boundariesElement) { + var fixedPosition = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; + + // NOTE: 1 DOM access here + + var boundaries = { top: 0, left: 0 }; + var offsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference)); + + // Handle viewport case + if (boundariesElement === 'viewport') { + boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent, fixedPosition); + } else { + // Handle other cases based on DOM element used as boundaries + var boundariesNode = void 0; + if (boundariesElement === 'scrollParent') { + boundariesNode = getScrollParent(getParentNode(reference)); + if (boundariesNode.nodeName === 'BODY') { + boundariesNode = popper.ownerDocument.documentElement; + } + } else if (boundariesElement === 'window') { + boundariesNode = popper.ownerDocument.documentElement; + } else { + boundariesNode = boundariesElement; + } + + var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent, fixedPosition); + + // In case of HTML, we need a different computation + if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) { + var _getWindowSizes = getWindowSizes(popper.ownerDocument), + height = _getWindowSizes.height, + width = _getWindowSizes.width; + + boundaries.top += offsets.top - offsets.marginTop; + boundaries.bottom = height + offsets.top; + boundaries.left += offsets.left - offsets.marginLeft; + boundaries.right = width + offsets.left; + } else { + // for all the other DOM elements, this one is good + boundaries = offsets; + } + } + + // Add paddings + padding = padding || 0; + var isPaddingNumber = typeof padding === 'number'; + boundaries.left += isPaddingNumber ? padding : padding.left || 0; + boundaries.top += isPaddingNumber ? padding : padding.top || 0; + boundaries.right -= isPaddingNumber ? padding : padding.right || 0; + boundaries.bottom -= isPaddingNumber ? padding : padding.bottom || 0; + + return boundaries; + } + + function getArea(_ref) { + var width = _ref.width, + height = _ref.height; + + return width * height; + } + + /** + * Utility used to transform the `auto` placement to the placement with more + * available space. + * @method + * @memberof Popper.Utils + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) { + var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0; + + if (placement.indexOf('auto') === -1) { + return placement; + } + + var boundaries = getBoundaries(popper, reference, padding, boundariesElement); + + var rects = { + top: { + width: boundaries.width, + height: refRect.top - boundaries.top + }, + right: { + width: boundaries.right - refRect.right, + height: boundaries.height + }, + bottom: { + width: boundaries.width, + height: boundaries.bottom - refRect.bottom + }, + left: { + width: refRect.left - boundaries.left, + height: boundaries.height + } + }; + + var sortedAreas = Object.keys(rects).map(function (key) { + return _extends({ + key: key + }, rects[key], { + area: getArea(rects[key]) + }); + }).sort(function (a, b) { + return b.area - a.area; + }); + + var filteredAreas = sortedAreas.filter(function (_ref2) { + var width = _ref2.width, + height = _ref2.height; + return width >= popper.clientWidth && height >= popper.clientHeight; + }); + + var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key; + + var variation = placement.split('-')[1]; + + return computedPlacement + (variation ? '-' + variation : ''); + } + + /** + * Get offsets to the reference element + * @method + * @memberof Popper.Utils + * @param {Object} state + * @param {Element} popper - the popper element + * @param {Element} reference - the reference element (the popper will be relative to this) + * @param {Element} fixedPosition - is in fixed position mode + * @returns {Object} An object containing the offsets which will be applied to the popper + */ + function getReferenceOffsets(state, popper, reference) { + var fixedPosition = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; + + var commonOffsetParent = fixedPosition ? getFixedPositionOffsetParent(popper) : findCommonOffsetParent(popper, getReferenceNode(reference)); + return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent, fixedPosition); + } + + /** + * Get the outer sizes of the given element (offset size + margins) + * @method + * @memberof Popper.Utils + * @argument {Element} element + * @returns {Object} object containing width and height properties + */ + function getOuterSizes(element) { + var window = element.ownerDocument.defaultView; + var styles = window.getComputedStyle(element); + var x = parseFloat(styles.marginTop || 0) + parseFloat(styles.marginBottom || 0); + var y = parseFloat(styles.marginLeft || 0) + parseFloat(styles.marginRight || 0); + var result = { + width: element.offsetWidth + y, + height: element.offsetHeight + x + }; + return result; + } + + /** + * Get the opposite placement of the given one + * @method + * @memberof Popper.Utils + * @argument {String} placement + * @returns {String} flipped placement + */ + function getOppositePlacement(placement) { + var hash = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' }; + return placement.replace(/left|right|bottom|top/g, function (matched) { + return hash[matched]; + }); + } + + /** + * Get offsets to the popper + * @method + * @memberof Popper.Utils + * @param {Object} position - CSS position the Popper will get applied + * @param {HTMLElement} popper - the popper element + * @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this) + * @param {String} placement - one of the valid placement options + * @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper + */ + function getPopperOffsets(popper, referenceOffsets, placement) { + placement = placement.split('-')[0]; + + // Get popper node sizes + var popperRect = getOuterSizes(popper); + + // Add position, width and height to our offsets object + var popperOffsets = { + width: popperRect.width, + height: popperRect.height + }; + + // depending by the popper placement we have to compute its offsets slightly differently + var isHoriz = ['right', 'left'].indexOf(placement) !== -1; + var mainSide = isHoriz ? 'top' : 'left'; + var secondarySide = isHoriz ? 'left' : 'top'; + var measurement = isHoriz ? 'height' : 'width'; + var secondaryMeasurement = !isHoriz ? 'height' : 'width'; + + popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2; + if (placement === secondarySide) { + popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement]; + } else { + popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)]; + } + + return popperOffsets; + } + + /** + * Mimics the `find` method of Array + * @method + * @memberof Popper.Utils + * @argument {Array} arr + * @argument prop + * @argument value + * @returns index or -1 + */ + function find(arr, check) { + // use native find if supported + if (Array.prototype.find) { + return arr.find(check); + } + + // use `filter` to obtain the same behavior of `find` + return arr.filter(check)[0]; + } + + /** + * Return the index of the matching object + * @method + * @memberof Popper.Utils + * @argument {Array} arr + * @argument prop + * @argument value + * @returns index or -1 + */ + function findIndex(arr, prop, value) { + // use native findIndex if supported + if (Array.prototype.findIndex) { + return arr.findIndex(function (cur) { + return cur[prop] === value; + }); + } + + // use `find` + `indexOf` if `findIndex` isn't supported + var match = find(arr, function (obj) { + return obj[prop] === value; + }); + return arr.indexOf(match); + } + + /** + * Loop trough the list of modifiers and run them in order, + * each of them will then edit the data object. + * @method + * @memberof Popper.Utils + * @param {dataObject} data + * @param {Array} modifiers + * @param {String} ends - Optional modifier name used as stopper + * @returns {dataObject} + */ + function runModifiers(modifiers, data, ends) { + var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends)); + + modifiersToRun.forEach(function (modifier) { + if (modifier['function']) { + // eslint-disable-line dot-notation + console.warn('`modifier.function` is deprecated, use `modifier.fn`!'); + } + var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation + if (modifier.enabled && isFunction(fn)) { + // Add properties to offsets to make them a complete clientRect object + // we do this before each modifier to make sure the previous one doesn't + // mess with these values + data.offsets.popper = getClientRect(data.offsets.popper); + data.offsets.reference = getClientRect(data.offsets.reference); + + data = fn(data, modifier); + } + }); + + return data; + } + + /** + * Updates the position of the popper, computing the new offsets and applying + * the new style.
+ * Prefer `scheduleUpdate` over `update` because of performance reasons. + * @method + * @memberof Popper + */ + function update() { + // if popper is destroyed, don't perform any further update + if (this.state.isDestroyed) { + return; + } + + var data = { + instance: this, + styles: {}, + arrowStyles: {}, + attributes: {}, + flipped: false, + offsets: {} + }; + + // compute reference element offsets + data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference, this.options.positionFixed); + + // compute auto placement, store placement inside the data object, + // modifiers will be able to edit `placement` if needed + // and refer to originalPlacement to know the original value + data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding); + + // store the computed placement inside `originalPlacement` + data.originalPlacement = data.placement; + + data.positionFixed = this.options.positionFixed; + + // compute the popper offsets + data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement); + + data.offsets.popper.position = this.options.positionFixed ? 'fixed' : 'absolute'; + + // run the modifiers + data = runModifiers(this.modifiers, data); + + // the first `update` will call `onCreate` callback + // the other ones will call `onUpdate` callback + if (!this.state.isCreated) { + this.state.isCreated = true; + this.options.onCreate(data); + } else { + this.options.onUpdate(data); + } + } + + /** + * Helper used to know if the given modifier is enabled. + * @method + * @memberof Popper.Utils + * @returns {Boolean} + */ + function isModifierEnabled(modifiers, modifierName) { + return modifiers.some(function (_ref) { + var name = _ref.name, + enabled = _ref.enabled; + return enabled && name === modifierName; + }); + } + + /** + * Get the prefixed supported property name + * @method + * @memberof Popper.Utils + * @argument {String} property (camelCase) + * @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix) + */ + function getSupportedPropertyName(property) { + var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O']; + var upperProp = property.charAt(0).toUpperCase() + property.slice(1); + + for (var i = 0; i < prefixes.length; i++) { + var prefix = prefixes[i]; + var toCheck = prefix ? '' + prefix + upperProp : property; + if (typeof document.body.style[toCheck] !== 'undefined') { + return toCheck; + } + } + return null; + } + + /** + * Destroys the popper. + * @method + * @memberof Popper + */ + function destroy() { + this.state.isDestroyed = true; + + // touch DOM only if `applyStyle` modifier is enabled + if (isModifierEnabled(this.modifiers, 'applyStyle')) { + this.popper.removeAttribute('x-placement'); + this.popper.style.position = ''; + this.popper.style.top = ''; + this.popper.style.left = ''; + this.popper.style.right = ''; + this.popper.style.bottom = ''; + this.popper.style.willChange = ''; + this.popper.style[getSupportedPropertyName('transform')] = ''; + } + + this.disableEventListeners(); + + // remove the popper if user explicitly asked for the deletion on destroy + // do not use `remove` because IE11 doesn't support it + if (this.options.removeOnDestroy) { + this.popper.parentNode.removeChild(this.popper); + } + return this; + } + + /** + * Get the window associated with the element + * @argument {Element} element + * @returns {Window} + */ + function getWindow(element) { + var ownerDocument = element.ownerDocument; + return ownerDocument ? ownerDocument.defaultView : window; + } + + function attachToScrollParents(scrollParent, event, callback, scrollParents) { + var isBody = scrollParent.nodeName === 'BODY'; + var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent; + target.addEventListener(event, callback, { passive: true }); + + if (!isBody) { + attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents); + } + scrollParents.push(target); + } + + /** + * Setup needed event listeners used to update the popper position + * @method + * @memberof Popper.Utils + * @private + */ + function setupEventListeners(reference, options, state, updateBound) { + // Resize event listener on window + state.updateBound = updateBound; + getWindow(reference).addEventListener('resize', state.updateBound, { passive: true }); + + // Scroll event listener on scroll parents + var scrollElement = getScrollParent(reference); + attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents); + state.scrollElement = scrollElement; + state.eventsEnabled = true; + + return state; + } + + /** + * It will add resize/scroll events and start recalculating + * position of the popper element when they are triggered. + * @method + * @memberof Popper + */ + function enableEventListeners() { + if (!this.state.eventsEnabled) { + this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate); + } + } + + /** + * Remove event listeners used to update the popper position + * @method + * @memberof Popper.Utils + * @private + */ + function removeEventListeners(reference, state) { + // Remove resize event listener on window + getWindow(reference).removeEventListener('resize', state.updateBound); + + // Remove scroll event listener on scroll parents + state.scrollParents.forEach(function (target) { + target.removeEventListener('scroll', state.updateBound); + }); + + // Reset state + state.updateBound = null; + state.scrollParents = []; + state.scrollElement = null; + state.eventsEnabled = false; + return state; + } + + /** + * It will remove resize/scroll events and won't recalculate popper position + * when they are triggered. It also won't trigger `onUpdate` callback anymore, + * unless you call `update` method manually. + * @method + * @memberof Popper + */ + function disableEventListeners() { + if (this.state.eventsEnabled) { + cancelAnimationFrame(this.scheduleUpdate); + this.state = removeEventListeners(this.reference, this.state); + } + } + + /** + * Tells if a given input is a number + * @method + * @memberof Popper.Utils + * @param {*} input to check + * @return {Boolean} + */ + function isNumeric(n) { + return n !== '' && !isNaN(parseFloat(n)) && isFinite(n); + } + + /** + * Set the style to the given popper + * @method + * @memberof Popper.Utils + * @argument {Element} element - Element to apply the style to + * @argument {Object} styles + * Object with a list of properties and values which will be applied to the element + */ + function setStyles(element, styles) { + Object.keys(styles).forEach(function (prop) { + var unit = ''; + // add unit if the value is numeric and is one of the following + if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) { + unit = 'px'; + } + element.style[prop] = styles[prop] + unit; + }); + } + + /** + * Set the attributes to the given popper + * @method + * @memberof Popper.Utils + * @argument {Element} element - Element to apply the attributes to + * @argument {Object} styles + * Object with a list of properties and values which will be applied to the element + */ + function setAttributes(element, attributes) { + Object.keys(attributes).forEach(function (prop) { + var value = attributes[prop]; + if (value !== false) { + element.setAttribute(prop, attributes[prop]); + } else { + element.removeAttribute(prop); + } + }); + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} data.styles - List of style properties - values to apply to popper element + * @argument {Object} data.attributes - List of attribute properties - values to apply to popper element + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The same data object + */ + function applyStyle(data) { + // any property present in `data.styles` will be applied to the popper, + // in this way we can make the 3rd party modifiers add custom styles to it + // Be aware, modifiers could override the properties defined in the previous + // lines of this modifier! + setStyles(data.instance.popper, data.styles); + + // any property present in `data.attributes` will be applied to the popper, + // they will be set as HTML attributes of the element + setAttributes(data.instance.popper, data.attributes); + + // if arrowElement is defined and arrowStyles has some properties + if (data.arrowElement && Object.keys(data.arrowStyles).length) { + setStyles(data.arrowElement, data.arrowStyles); + } + + return data; + } + + /** + * Set the x-placement attribute before everything else because it could be used + * to add margins to the popper margins needs to be calculated to get the + * correct popper offsets. + * @method + * @memberof Popper.modifiers + * @param {HTMLElement} reference - The reference element used to position the popper + * @param {HTMLElement} popper - The HTML element used as popper + * @param {Object} options - Popper.js options + */ + function applyStyleOnLoad(reference, popper, options, modifierOptions, state) { + // compute reference element offsets + var referenceOffsets = getReferenceOffsets(state, popper, reference, options.positionFixed); + + // compute auto placement, store placement inside the data object, + // modifiers will be able to edit `placement` if needed + // and refer to originalPlacement to know the original value + var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding); + + popper.setAttribute('x-placement', placement); + + // Apply `position` to popper before anything else because + // without the position applied we can't guarantee correct computations + setStyles(popper, { position: options.positionFixed ? 'fixed' : 'absolute' }); + + return options; + } + + /** + * @function + * @memberof Popper.Utils + * @argument {Object} data - The data object generated by `update` method + * @argument {Boolean} shouldRound - If the offsets should be rounded at all + * @returns {Object} The popper's position offsets rounded + * + * The tale of pixel-perfect positioning. It's still not 100% perfect, but as + * good as it can be within reason. + * Discussion here: https://github.com/FezVrasta/popper.js/pull/715 + * + * Low DPI screens cause a popper to be blurry if not using full pixels (Safari + * as well on High DPI screens). + * + * Firefox prefers no rounding for positioning and does not have blurriness on + * high DPI screens. + * + * Only horizontal placement and left/right values need to be considered. + */ + function getRoundedOffsets(data, shouldRound) { + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + var round = Math.round, + floor = Math.floor; + + var noRound = function noRound(v) { + return v; + }; + + var referenceWidth = round(reference.width); + var popperWidth = round(popper.width); + + var isVertical = ['left', 'right'].indexOf(data.placement) !== -1; + var isVariation = data.placement.indexOf('-') !== -1; + var sameWidthParity = referenceWidth % 2 === popperWidth % 2; + var bothOddWidth = referenceWidth % 2 === 1 && popperWidth % 2 === 1; + + var horizontalToInteger = !shouldRound ? noRound : isVertical || isVariation || sameWidthParity ? round : floor; + var verticalToInteger = !shouldRound ? noRound : round; + + return { + left: horizontalToInteger(bothOddWidth && !isVariation && shouldRound ? popper.left - 1 : popper.left), + top: verticalToInteger(popper.top), + bottom: verticalToInteger(popper.bottom), + right: horizontalToInteger(popper.right) + }; + } + + var isFirefox = isBrowser && /Firefox/i.test(navigator.userAgent); + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function computeStyle(data, options) { + var x = options.x, + y = options.y; + var popper = data.offsets.popper; + + // Remove this legacy support in Popper.js v2 + + var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) { + return modifier.name === 'applyStyle'; + }).gpuAcceleration; + if (legacyGpuAccelerationOption !== undefined) { + console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!'); + } + var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration; + + var offsetParent = getOffsetParent(data.instance.popper); + var offsetParentRect = getBoundingClientRect(offsetParent); + + // Styles + var styles = { + position: popper.position + }; + + var offsets = getRoundedOffsets(data, window.devicePixelRatio < 2 || !isFirefox); + + var sideA = x === 'bottom' ? 'top' : 'bottom'; + var sideB = y === 'right' ? 'left' : 'right'; + + // if gpuAcceleration is set to `true` and transform is supported, + // we use `translate3d` to apply the position to the popper we + // automatically use the supported prefixed version if needed + var prefixedProperty = getSupportedPropertyName('transform'); + + // now, let's make a step back and look at this code closely (wtf?) + // If the content of the popper grows once it's been positioned, it + // may happen that the popper gets misplaced because of the new content + // overflowing its reference element + // To avoid this problem, we provide two options (x and y), which allow + // the consumer to define the offset origin. + // If we position a popper on top of a reference element, we can set + // `x` to `top` to make the popper grow towards its top instead of + // its bottom. + var left = void 0, + top = void 0; + if (sideA === 'bottom') { + // when offsetParent is the positioning is relative to the bottom of the screen (excluding the scrollbar) + // and not the bottom of the html element + if (offsetParent.nodeName === 'HTML') { + top = -offsetParent.clientHeight + offsets.bottom; + } else { + top = -offsetParentRect.height + offsets.bottom; + } + } else { + top = offsets.top; + } + if (sideB === 'right') { + if (offsetParent.nodeName === 'HTML') { + left = -offsetParent.clientWidth + offsets.right; + } else { + left = -offsetParentRect.width + offsets.right; + } + } else { + left = offsets.left; + } + if (gpuAcceleration && prefixedProperty) { + styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)'; + styles[sideA] = 0; + styles[sideB] = 0; + styles.willChange = 'transform'; + } else { + // othwerise, we use the standard `top`, `left`, `bottom` and `right` properties + var invertTop = sideA === 'bottom' ? -1 : 1; + var invertLeft = sideB === 'right' ? -1 : 1; + styles[sideA] = top * invertTop; + styles[sideB] = left * invertLeft; + styles.willChange = sideA + ', ' + sideB; + } + + // Attributes + var attributes = { + 'x-placement': data.placement + }; + + // Update `data` attributes, styles and arrowStyles + data.attributes = _extends({}, attributes, data.attributes); + data.styles = _extends({}, styles, data.styles); + data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles); + + return data; + } + + /** + * Helper used to know if the given modifier depends from another one.
+ * It checks if the needed modifier is listed and enabled. + * @method + * @memberof Popper.Utils + * @param {Array} modifiers - list of modifiers + * @param {String} requestingName - name of requesting modifier + * @param {String} requestedName - name of requested modifier + * @returns {Boolean} + */ + function isModifierRequired(modifiers, requestingName, requestedName) { + var requesting = find(modifiers, function (_ref) { + var name = _ref.name; + return name === requestingName; + }); + + var isRequired = !!requesting && modifiers.some(function (modifier) { + return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order; + }); + + if (!isRequired) { + var _requesting = '`' + requestingName + '`'; + var requested = '`' + requestedName + '`'; + console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!'); + } + return isRequired; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function arrow(data, options) { + var _data$offsets$arrow; + + // arrow depends on keepTogether in order to work + if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) { + return data; + } + + var arrowElement = options.element; + + // if arrowElement is a string, suppose it's a CSS selector + if (typeof arrowElement === 'string') { + arrowElement = data.instance.popper.querySelector(arrowElement); + + // if arrowElement is not found, don't run the modifier + if (!arrowElement) { + return data; + } + } else { + // if the arrowElement isn't a query selector we must check that the + // provided DOM node is child of its popper node + if (!data.instance.popper.contains(arrowElement)) { + console.warn('WARNING: `arrow.element` must be child of its popper element!'); + return data; + } + } + + var placement = data.placement.split('-')[0]; + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var isVertical = ['left', 'right'].indexOf(placement) !== -1; + + var len = isVertical ? 'height' : 'width'; + var sideCapitalized = isVertical ? 'Top' : 'Left'; + var side = sideCapitalized.toLowerCase(); + var altSide = isVertical ? 'left' : 'top'; + var opSide = isVertical ? 'bottom' : 'right'; + var arrowElementSize = getOuterSizes(arrowElement)[len]; + + // + // extends keepTogether behavior making sure the popper and its + // reference have enough pixels in conjunction + // + + // top/left side + if (reference[opSide] - arrowElementSize < popper[side]) { + data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize); + } + // bottom/right side + if (reference[side] + arrowElementSize > popper[opSide]) { + data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide]; + } + data.offsets.popper = getClientRect(data.offsets.popper); + + // compute center of the popper + var center = reference[side] + reference[len] / 2 - arrowElementSize / 2; + + // Compute the sideValue using the updated popper offsets + // take popper margin in account because we don't have this info available + var css = getStyleComputedProperty(data.instance.popper); + var popperMarginSide = parseFloat(css['margin' + sideCapitalized], 10); + var popperBorderSide = parseFloat(css['border' + sideCapitalized + 'Width'], 10); + var sideValue = center - data.offsets.popper[side] - popperMarginSide - popperBorderSide; + + // prevent arrowElement from being placed not contiguously to its popper + sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0); + + data.arrowElement = arrowElement; + data.offsets.arrow = (_data$offsets$arrow = {}, defineProperty(_data$offsets$arrow, side, Math.round(sideValue)), defineProperty(_data$offsets$arrow, altSide, ''), _data$offsets$arrow); + + return data; + } + + /** + * Get the opposite placement variation of the given one + * @method + * @memberof Popper.Utils + * @argument {String} placement variation + * @returns {String} flipped placement variation + */ + function getOppositeVariation(variation) { + if (variation === 'end') { + return 'start'; + } else if (variation === 'start') { + return 'end'; + } + return variation; + } + + /** + * List of accepted placements to use as values of the `placement` option.
+ * Valid placements are: + * - `auto` + * - `top` + * - `right` + * - `bottom` + * - `left` + * + * Each placement can have a variation from this list: + * - `-start` + * - `-end` + * + * Variations are interpreted easily if you think of them as the left to right + * written languages. Horizontally (`top` and `bottom`), `start` is left and `end` + * is right.
+ * Vertically (`left` and `right`), `start` is top and `end` is bottom. + * + * Some valid examples are: + * - `top-end` (on top of reference, right aligned) + * - `right-start` (on right of reference, top aligned) + * - `bottom` (on bottom, centered) + * - `auto-end` (on the side with more space available, alignment depends by placement) + * + * @static + * @type {Array} + * @enum {String} + * @readonly + * @method placements + * @memberof Popper + */ + var placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start']; + + // Get rid of `auto` `auto-start` and `auto-end` + var validPlacements = placements.slice(3); + + /** + * Given an initial placement, returns all the subsequent placements + * clockwise (or counter-clockwise). + * + * @method + * @memberof Popper.Utils + * @argument {String} placement - A valid placement (it accepts variations) + * @argument {Boolean} counter - Set to true to walk the placements counterclockwise + * @returns {Array} placements including their variations + */ + function clockwise(placement) { + var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var index = validPlacements.indexOf(placement); + var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index)); + return counter ? arr.reverse() : arr; + } + + var BEHAVIORS = { + FLIP: 'flip', + CLOCKWISE: 'clockwise', + COUNTERCLOCKWISE: 'counterclockwise' + }; + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function flip(data, options) { + // if `inner` modifier is enabled, we can't use the `flip` modifier + if (isModifierEnabled(data.instance.modifiers, 'inner')) { + return data; + } + + if (data.flipped && data.placement === data.originalPlacement) { + // seems like flip is trying to loop, probably there's not enough space on any of the flippable sides + return data; + } + + var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement, data.positionFixed); + + var placement = data.placement.split('-')[0]; + var placementOpposite = getOppositePlacement(placement); + var variation = data.placement.split('-')[1] || ''; + + var flipOrder = []; + + switch (options.behavior) { + case BEHAVIORS.FLIP: + flipOrder = [placement, placementOpposite]; + break; + case BEHAVIORS.CLOCKWISE: + flipOrder = clockwise(placement); + break; + case BEHAVIORS.COUNTERCLOCKWISE: + flipOrder = clockwise(placement, true); + break; + default: + flipOrder = options.behavior; + } + + flipOrder.forEach(function (step, index) { + if (placement !== step || flipOrder.length === index + 1) { + return data; + } + + placement = data.placement.split('-')[0]; + placementOpposite = getOppositePlacement(placement); + + var popperOffsets = data.offsets.popper; + var refOffsets = data.offsets.reference; + + // using floor because the reference offsets may contain decimals we are not going to consider here + var floor = Math.floor; + var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom); + + var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left); + var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right); + var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top); + var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom); + + var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom; + + // flip the variation if required + var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; + + // flips variation if reference element overflows boundaries + var flippedVariationByRef = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom); + + // flips variation if popper content overflows boundaries + var flippedVariationByContent = !!options.flipVariationsByContent && (isVertical && variation === 'start' && overflowsRight || isVertical && variation === 'end' && overflowsLeft || !isVertical && variation === 'start' && overflowsBottom || !isVertical && variation === 'end' && overflowsTop); + + var flippedVariation = flippedVariationByRef || flippedVariationByContent; + + if (overlapsRef || overflowsBoundaries || flippedVariation) { + // this boolean to detect any flip loop + data.flipped = true; + + if (overlapsRef || overflowsBoundaries) { + placement = flipOrder[index + 1]; + } + + if (flippedVariation) { + variation = getOppositeVariation(variation); + } + + data.placement = placement + (variation ? '-' + variation : ''); + + // this object contains `position`, we want to preserve it along with + // any additional property we may add in the future + data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement)); + + data = runModifiers(data.instance.modifiers, data, 'flip'); + } + }); + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function keepTogether(data) { + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var placement = data.placement.split('-')[0]; + var floor = Math.floor; + var isVertical = ['top', 'bottom'].indexOf(placement) !== -1; + var side = isVertical ? 'right' : 'bottom'; + var opSide = isVertical ? 'left' : 'top'; + var measurement = isVertical ? 'width' : 'height'; + + if (popper[side] < floor(reference[opSide])) { + data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement]; + } + if (popper[opSide] > floor(reference[side])) { + data.offsets.popper[opSide] = floor(reference[side]); + } + + return data; + } + + /** + * Converts a string containing value + unit into a px value number + * @function + * @memberof {modifiers~offset} + * @private + * @argument {String} str - Value + unit string + * @argument {String} measurement - `height` or `width` + * @argument {Object} popperOffsets + * @argument {Object} referenceOffsets + * @returns {Number|String} + * Value in pixels, or original string if no values were extracted + */ + function toValue(str, measurement, popperOffsets, referenceOffsets) { + // separate value from unit + var split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/); + var value = +split[1]; + var unit = split[2]; + + // If it's not a number it's an operator, I guess + if (!value) { + return str; + } + + if (unit.indexOf('%') === 0) { + var element = void 0; + switch (unit) { + case '%p': + element = popperOffsets; + break; + case '%': + case '%r': + default: + element = referenceOffsets; + } + + var rect = getClientRect(element); + return rect[measurement] / 100 * value; + } else if (unit === 'vh' || unit === 'vw') { + // if is a vh or vw, we calculate the size based on the viewport + var size = void 0; + if (unit === 'vh') { + size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); + } else { + size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); + } + return size / 100 * value; + } else { + // if is an explicit pixel unit, we get rid of the unit and keep the value + // if is an implicit unit, it's px, and we return just the value + return value; + } + } + + /** + * Parse an `offset` string to extrapolate `x` and `y` numeric offsets. + * @function + * @memberof {modifiers~offset} + * @private + * @argument {String} offset + * @argument {Object} popperOffsets + * @argument {Object} referenceOffsets + * @argument {String} basePlacement + * @returns {Array} a two cells array with x and y offsets in numbers + */ + function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) { + var offsets = [0, 0]; + + // Use height if placement is left or right and index is 0 otherwise use width + // in this way the first offset will use an axis and the second one + // will use the other one + var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1; + + // Split the offset string to obtain a list of values and operands + // The regex addresses values with the plus or minus sign in front (+10, -20, etc) + var fragments = offset.split(/(\+|\-)/).map(function (frag) { + return frag.trim(); + }); + + // Detect if the offset string contains a pair of values or a single one + // they could be separated by comma or space + var divider = fragments.indexOf(find(fragments, function (frag) { + return frag.search(/,|\s/) !== -1; + })); + + if (fragments[divider] && fragments[divider].indexOf(',') === -1) { + console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.'); + } + + // If divider is found, we divide the list of values and operands to divide + // them by ofset X and Y. + var splitRegex = /\s*,\s*|\s+/; + var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments]; + + // Convert the values with units to absolute pixels to allow our computations + ops = ops.map(function (op, index) { + // Most of the units rely on the orientation of the popper + var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width'; + var mergeWithPrevious = false; + return op + // This aggregates any `+` or `-` sign that aren't considered operators + // e.g.: 10 + +5 => [10, +, +5] + .reduce(function (a, b) { + if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) { + a[a.length - 1] = b; + mergeWithPrevious = true; + return a; + } else if (mergeWithPrevious) { + a[a.length - 1] += b; + mergeWithPrevious = false; + return a; + } else { + return a.concat(b); + } + }, []) + // Here we convert the string values into number values (in px) + .map(function (str) { + return toValue(str, measurement, popperOffsets, referenceOffsets); + }); + }); + + // Loop trough the offsets arrays and execute the operations + ops.forEach(function (op, index) { + op.forEach(function (frag, index2) { + if (isNumeric(frag)) { + offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1); + } + }); + }); + return offsets; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @argument {Number|String} options.offset=0 + * The offset value as described in the modifier description + * @returns {Object} The data object, properly modified + */ + function offset(data, _ref) { + var offset = _ref.offset; + var placement = data.placement, + _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var basePlacement = placement.split('-')[0]; + + var offsets = void 0; + if (isNumeric(+offset)) { + offsets = [+offset, 0]; + } else { + offsets = parseOffset(offset, popper, reference, basePlacement); + } + + if (basePlacement === 'left') { + popper.top += offsets[0]; + popper.left -= offsets[1]; + } else if (basePlacement === 'right') { + popper.top += offsets[0]; + popper.left += offsets[1]; + } else if (basePlacement === 'top') { + popper.left += offsets[0]; + popper.top -= offsets[1]; + } else if (basePlacement === 'bottom') { + popper.left += offsets[0]; + popper.top += offsets[1]; + } + + data.popper = popper; + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function preventOverflow(data, options) { + var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper); + + // If offsetParent is the reference element, we really want to + // go one step up and use the next offsetParent as reference to + // avoid to make this modifier completely useless and look like broken + if (data.instance.reference === boundariesElement) { + boundariesElement = getOffsetParent(boundariesElement); + } + + // NOTE: DOM access here + // resets the popper's position so that the document size can be calculated excluding + // the size of the popper element itself + var transformProp = getSupportedPropertyName('transform'); + var popperStyles = data.instance.popper.style; // assignment to help minification + var top = popperStyles.top, + left = popperStyles.left, + transform = popperStyles[transformProp]; + + popperStyles.top = ''; + popperStyles.left = ''; + popperStyles[transformProp] = ''; + + var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement, data.positionFixed); + + // NOTE: DOM access here + // restores the original style properties after the offsets have been computed + popperStyles.top = top; + popperStyles.left = left; + popperStyles[transformProp] = transform; + + options.boundaries = boundaries; + + var order = options.priority; + var popper = data.offsets.popper; + + var check = { + primary: function primary(placement) { + var value = popper[placement]; + if (popper[placement] < boundaries[placement] && !options.escapeWithReference) { + value = Math.max(popper[placement], boundaries[placement]); + } + return defineProperty({}, placement, value); + }, + secondary: function secondary(placement) { + var mainSide = placement === 'right' ? 'left' : 'top'; + var value = popper[mainSide]; + if (popper[placement] > boundaries[placement] && !options.escapeWithReference) { + value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height)); + } + return defineProperty({}, mainSide, value); + } + }; + + order.forEach(function (placement) { + var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary'; + popper = _extends({}, popper, check[side](placement)); + }); + + data.offsets.popper = popper; + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function shift(data) { + var placement = data.placement; + var basePlacement = placement.split('-')[0]; + var shiftvariation = placement.split('-')[1]; + + // if shift shiftvariation is specified, run the modifier + if (shiftvariation) { + var _data$offsets = data.offsets, + reference = _data$offsets.reference, + popper = _data$offsets.popper; + + var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1; + var side = isVertical ? 'left' : 'top'; + var measurement = isVertical ? 'width' : 'height'; + + var shiftOffsets = { + start: defineProperty({}, side, reference[side]), + end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement]) + }; + + data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]); + } + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by update method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function hide(data) { + if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) { + return data; + } + + var refRect = data.offsets.reference; + var bound = find(data.instance.modifiers, function (modifier) { + return modifier.name === 'preventOverflow'; + }).boundaries; + + if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) { + // Avoid unnecessary DOM access if visibility hasn't changed + if (data.hide === true) { + return data; + } + + data.hide = true; + data.attributes['x-out-of-boundaries'] = ''; + } else { + // Avoid unnecessary DOM access if visibility hasn't changed + if (data.hide === false) { + return data; + } + + data.hide = false; + data.attributes['x-out-of-boundaries'] = false; + } + + return data; + } + + /** + * @function + * @memberof Modifiers + * @argument {Object} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {Object} The data object, properly modified + */ + function inner(data) { + var placement = data.placement; + var basePlacement = placement.split('-')[0]; + var _data$offsets = data.offsets, + popper = _data$offsets.popper, + reference = _data$offsets.reference; + + var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1; + + var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1; + + popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0); + + data.placement = getOppositePlacement(placement); + data.offsets.popper = getClientRect(popper); + + return data; + } + + /** + * Modifier function, each modifier can have a function of this type assigned + * to its `fn` property.
+ * These functions will be called on each update, this means that you must + * make sure they are performant enough to avoid performance bottlenecks. + * + * @function ModifierFn + * @argument {dataObject} data - The data object generated by `update` method + * @argument {Object} options - Modifiers configuration and options + * @returns {dataObject} The data object, properly modified + */ + + /** + * Modifiers are plugins used to alter the behavior of your poppers.
+ * Popper.js uses a set of 9 modifiers to provide all the basic functionalities + * needed by the library. + * + * Usually you don't want to override the `order`, `fn` and `onLoad` props. + * All the other properties are configurations that could be tweaked. + * @namespace modifiers + */ + var modifiers = { + /** + * Modifier used to shift the popper on the start or end of its reference + * element.
+ * It will read the variation of the `placement` property.
+ * It can be one either `-end` or `-start`. + * @memberof modifiers + * @inner + */ + shift: { + /** @prop {number} order=100 - Index used to define the order of execution */ + order: 100, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: shift + }, + + /** + * The `offset` modifier can shift your popper on both its axis. + * + * It accepts the following units: + * - `px` or unit-less, interpreted as pixels + * - `%` or `%r`, percentage relative to the length of the reference element + * - `%p`, percentage relative to the length of the popper element + * - `vw`, CSS viewport width unit + * - `vh`, CSS viewport height unit + * + * For length is intended the main axis relative to the placement of the popper.
+ * This means that if the placement is `top` or `bottom`, the length will be the + * `width`. In case of `left` or `right`, it will be the `height`. + * + * You can provide a single value (as `Number` or `String`), or a pair of values + * as `String` divided by a comma or one (or more) white spaces.
+ * The latter is a deprecated method because it leads to confusion and will be + * removed in v2.
+ * Additionally, it accepts additions and subtractions between different units. + * Note that multiplications and divisions aren't supported. + * + * Valid examples are: + * ``` + * 10 + * '10%' + * '10, 10' + * '10%, 10' + * '10 + 10%' + * '10 - 5vh + 3%' + * '-10px + 5vh, 5px - 6%' + * ``` + * > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap + * > with their reference element, unfortunately, you will have to disable the `flip` modifier. + * > You can read more on this at this [issue](https://github.com/FezVrasta/popper.js/issues/373). + * + * @memberof modifiers + * @inner + */ + offset: { + /** @prop {number} order=200 - Index used to define the order of execution */ + order: 200, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: offset, + /** @prop {Number|String} offset=0 + * The offset value as described in the modifier description + */ + offset: 0 + }, + + /** + * Modifier used to prevent the popper from being positioned outside the boundary. + * + * A scenario exists where the reference itself is not within the boundaries.
+ * We can say it has "escaped the boundaries" — or just "escaped".
+ * In this case we need to decide whether the popper should either: + * + * - detach from the reference and remain "trapped" in the boundaries, or + * - if it should ignore the boundary and "escape with its reference" + * + * When `escapeWithReference` is set to`true` and reference is completely + * outside its boundaries, the popper will overflow (or completely leave) + * the boundaries in order to remain attached to the edge of the reference. + * + * @memberof modifiers + * @inner + */ + preventOverflow: { + /** @prop {number} order=300 - Index used to define the order of execution */ + order: 300, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: preventOverflow, + /** + * @prop {Array} [priority=['left','right','top','bottom']] + * Popper will try to prevent overflow following these priorities by default, + * then, it could overflow on the left and on top of the `boundariesElement` + */ + priority: ['left', 'right', 'top', 'bottom'], + /** + * @prop {number} padding=5 + * Amount of pixel used to define a minimum distance between the boundaries + * and the popper. This makes sure the popper always has a little padding + * between the edges of its container + */ + padding: 5, + /** + * @prop {String|HTMLElement} boundariesElement='scrollParent' + * Boundaries used by the modifier. Can be `scrollParent`, `window`, + * `viewport` or any DOM element. + */ + boundariesElement: 'scrollParent' + }, + + /** + * Modifier used to make sure the reference and its popper stay near each other + * without leaving any gap between the two. Especially useful when the arrow is + * enabled and you want to ensure that it points to its reference element. + * It cares only about the first axis. You can still have poppers with margin + * between the popper and its reference element. + * @memberof modifiers + * @inner + */ + keepTogether: { + /** @prop {number} order=400 - Index used to define the order of execution */ + order: 400, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: keepTogether + }, + + /** + * This modifier is used to move the `arrowElement` of the popper to make + * sure it is positioned between the reference element and its popper element. + * It will read the outer size of the `arrowElement` node to detect how many + * pixels of conjunction are needed. + * + * It has no effect if no `arrowElement` is provided. + * @memberof modifiers + * @inner + */ + arrow: { + /** @prop {number} order=500 - Index used to define the order of execution */ + order: 500, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: arrow, + /** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */ + element: '[x-arrow]' + }, + + /** + * Modifier used to flip the popper's placement when it starts to overlap its + * reference element. + * + * Requires the `preventOverflow` modifier before it in order to work. + * + * **NOTE:** this modifier will interrupt the current update cycle and will + * restart it if it detects the need to flip the placement. + * @memberof modifiers + * @inner + */ + flip: { + /** @prop {number} order=600 - Index used to define the order of execution */ + order: 600, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: flip, + /** + * @prop {String|Array} behavior='flip' + * The behavior used to change the popper's placement. It can be one of + * `flip`, `clockwise`, `counterclockwise` or an array with a list of valid + * placements (with optional variations) + */ + behavior: 'flip', + /** + * @prop {number} padding=5 + * The popper will flip if it hits the edges of the `boundariesElement` + */ + padding: 5, + /** + * @prop {String|HTMLElement} boundariesElement='viewport' + * The element which will define the boundaries of the popper position. + * The popper will never be placed outside of the defined boundaries + * (except if `keepTogether` is enabled) + */ + boundariesElement: 'viewport', + /** + * @prop {Boolean} flipVariations=false + * The popper will switch placement variation between `-start` and `-end` when + * the reference element overlaps its boundaries. + * + * The original placement should have a set variation. + */ + flipVariations: false, + /** + * @prop {Boolean} flipVariationsByContent=false + * The popper will switch placement variation between `-start` and `-end` when + * the popper element overlaps its reference boundaries. + * + * The original placement should have a set variation. + */ + flipVariationsByContent: false + }, + + /** + * Modifier used to make the popper flow toward the inner of the reference element. + * By default, when this modifier is disabled, the popper will be placed outside + * the reference element. + * @memberof modifiers + * @inner + */ + inner: { + /** @prop {number} order=700 - Index used to define the order of execution */ + order: 700, + /** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */ + enabled: false, + /** @prop {ModifierFn} */ + fn: inner + }, + + /** + * Modifier used to hide the popper when its reference element is outside of the + * popper boundaries. It will set a `x-out-of-boundaries` attribute which can + * be used to hide with a CSS selector the popper when its reference is + * out of boundaries. + * + * Requires the `preventOverflow` modifier before it in order to work. + * @memberof modifiers + * @inner + */ + hide: { + /** @prop {number} order=800 - Index used to define the order of execution */ + order: 800, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: hide + }, + + /** + * Computes the style that will be applied to the popper element to gets + * properly positioned. + * + * Note that this modifier will not touch the DOM, it just prepares the styles + * so that `applyStyle` modifier can apply it. This separation is useful + * in case you need to replace `applyStyle` with a custom implementation. + * + * This modifier has `850` as `order` value to maintain backward compatibility + * with previous versions of Popper.js. Expect the modifiers ordering method + * to change in future major versions of the library. + * + * @memberof modifiers + * @inner + */ + computeStyle: { + /** @prop {number} order=850 - Index used to define the order of execution */ + order: 850, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: computeStyle, + /** + * @prop {Boolean} gpuAcceleration=true + * If true, it uses the CSS 3D transformation to position the popper. + * Otherwise, it will use the `top` and `left` properties + */ + gpuAcceleration: true, + /** + * @prop {string} [x='bottom'] + * Where to anchor the X axis (`bottom` or `top`). AKA X offset origin. + * Change this if your popper should grow in a direction different from `bottom` + */ + x: 'bottom', + /** + * @prop {string} [x='left'] + * Where to anchor the Y axis (`left` or `right`). AKA Y offset origin. + * Change this if your popper should grow in a direction different from `right` + */ + y: 'right' + }, + + /** + * Applies the computed styles to the popper element. + * + * All the DOM manipulations are limited to this modifier. This is useful in case + * you want to integrate Popper.js inside a framework or view library and you + * want to delegate all the DOM manipulations to it. + * + * Note that if you disable this modifier, you must make sure the popper element + * has its position set to `absolute` before Popper.js can do its work! + * + * Just disable this modifier and define your own to achieve the desired effect. + * + * @memberof modifiers + * @inner + */ + applyStyle: { + /** @prop {number} order=900 - Index used to define the order of execution */ + order: 900, + /** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */ + enabled: true, + /** @prop {ModifierFn} */ + fn: applyStyle, + /** @prop {Function} */ + onLoad: applyStyleOnLoad, + /** + * @deprecated since version 1.10.0, the property moved to `computeStyle` modifier + * @prop {Boolean} gpuAcceleration=true + * If true, it uses the CSS 3D transformation to position the popper. + * Otherwise, it will use the `top` and `left` properties + */ + gpuAcceleration: undefined + } + }; + + /** + * The `dataObject` is an object containing all the information used by Popper.js. + * This object is passed to modifiers and to the `onCreate` and `onUpdate` callbacks. + * @name dataObject + * @property {Object} data.instance The Popper.js instance + * @property {String} data.placement Placement applied to popper + * @property {String} data.originalPlacement Placement originally defined on init + * @property {Boolean} data.flipped True if popper has been flipped by flip modifier + * @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper + * @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier + * @property {Object} data.styles Any CSS property defined here will be applied to the popper. It expects the JavaScript nomenclature (eg. `marginBottom`) + * @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow. It expects the JavaScript nomenclature (eg. `marginBottom`) + * @property {Object} data.boundaries Offsets of the popper boundaries + * @property {Object} data.offsets The measurements of popper, reference and arrow elements + * @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values + * @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values + * @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0 + */ + + /** + * Default options provided to Popper.js constructor.
+ * These can be overridden using the `options` argument of Popper.js.
+ * To override an option, simply pass an object with the same + * structure of the `options` object, as the 3rd argument. For example: + * ``` + * new Popper(ref, pop, { + * modifiers: { + * preventOverflow: { enabled: false } + * } + * }) + * ``` + * @type {Object} + * @static + * @memberof Popper + */ + var Defaults = { + /** + * Popper's placement. + * @prop {Popper.placements} placement='bottom' + */ + placement: 'bottom', + + /** + * Set this to true if you want popper to position it self in 'fixed' mode + * @prop {Boolean} positionFixed=false + */ + positionFixed: false, + + /** + * Whether events (resize, scroll) are initially enabled. + * @prop {Boolean} eventsEnabled=true + */ + eventsEnabled: true, + + /** + * Set to true if you want to automatically remove the popper when + * you call the `destroy` method. + * @prop {Boolean} removeOnDestroy=false + */ + removeOnDestroy: false, + + /** + * Callback called when the popper is created.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`. + * @prop {onCreate} + */ + onCreate: function onCreate() {}, + + /** + * Callback called when the popper is updated. This callback is not called + * on the initialization/creation of the popper, but only on subsequent + * updates.
+ * By default, it is set to no-op.
+ * Access Popper.js instance with `data.instance`. + * @prop {onUpdate} + */ + onUpdate: function onUpdate() {}, + + /** + * List of modifiers used to modify the offsets before they are applied to the popper. + * They provide most of the functionalities of Popper.js. + * @prop {modifiers} + */ + modifiers: modifiers + }; + + /** + * @callback onCreate + * @param {dataObject} data + */ + + /** + * @callback onUpdate + * @param {dataObject} data + */ + + // Utils + // Methods + var Popper = function () { + /** + * Creates a new Popper.js instance. + * @class Popper + * @param {Element|referenceObject} reference - The reference element used to position the popper + * @param {Element} popper - The HTML / XML element used as the popper + * @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults) + * @return {Object} instance - The generated Popper.js instance + */ + function Popper(reference, popper) { + var _this = this; + + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + classCallCheck(this, Popper); + + this.scheduleUpdate = function () { + return requestAnimationFrame(_this.update); + }; + + // make update() debounced, so that it only runs at most once-per-tick + this.update = debounce(this.update.bind(this)); + + // with {} we create a new object with the options inside it + this.options = _extends({}, Popper.Defaults, options); + + // init state + this.state = { + isDestroyed: false, + isCreated: false, + scrollParents: [] + }; + + // get reference and popper elements (allow jQuery wrappers) + this.reference = reference && reference.jquery ? reference[0] : reference; + this.popper = popper && popper.jquery ? popper[0] : popper; + + // Deep merge modifiers options + this.options.modifiers = {}; + Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) { + _this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {}); + }); + + // Refactoring modifiers' list (Object => Array) + this.modifiers = Object.keys(this.options.modifiers).map(function (name) { + return _extends({ + name: name + }, _this.options.modifiers[name]); + }) + // sort the modifiers by order + .sort(function (a, b) { + return a.order - b.order; + }); + + // modifiers have the ability to execute arbitrary code when Popper.js get inited + // such code is executed in the same order of its modifier + // they could add new properties to their options configuration + // BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`! + this.modifiers.forEach(function (modifierOptions) { + if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) { + modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state); + } + }); + + // fire the first update to position the popper in the right place + this.update(); + + var eventsEnabled = this.options.eventsEnabled; + if (eventsEnabled) { + // setup event listeners, they will take care of update the position in specific situations + this.enableEventListeners(); + } + + this.state.eventsEnabled = eventsEnabled; + } + + // We can't use class properties because they don't get listed in the + // class prototype and break stuff like Sinon stubs + + + createClass(Popper, [{ + key: 'update', + value: function update$$1() { + return update.call(this); + } + }, { + key: 'destroy', + value: function destroy$$1() { + return destroy.call(this); + } + }, { + key: 'enableEventListeners', + value: function enableEventListeners$$1() { + return enableEventListeners.call(this); + } + }, { + key: 'disableEventListeners', + value: function disableEventListeners$$1() { + return disableEventListeners.call(this); + } + + /** + * Schedules an update. It will run on the next UI update available. + * @method scheduleUpdate + * @memberof Popper + */ + + + /** + * Collection of utilities useful when writing custom modifiers. + * Starting from version 1.7, this method is available only if you + * include `popper-utils.js` before `popper.js`. + * + * **DEPRECATION**: This way to access PopperUtils is deprecated + * and will be removed in v2! Use the PopperUtils module directly instead. + * Due to the high instability of the methods contained in Utils, we can't + * guarantee them to follow semver. Use them at your own risk! + * @static + * @private + * @type {Object} + * @deprecated since version 1.8 + * @member Utils + * @memberof Popper + */ + + }]); + return Popper; + }(); + + /** + * The `referenceObject` is an object that provides an interface compatible with Popper.js + * and lets you use it as replacement of a real DOM node.
+ * You can use this method to position a popper relatively to a set of coordinates + * in case you don't have a DOM node to use as reference. + * + * ``` + * new Popper(referenceObject, popperNode); + * ``` + * + * NB: This feature isn't supported in Internet Explorer 10. + * @name referenceObject + * @property {Function} data.getBoundingClientRect + * A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method. + * @property {number} data.clientWidth + * An ES6 getter that will return the width of the virtual reference element. + * @property {number} data.clientHeight + * An ES6 getter that will return the height of the virtual reference element. + */ + + + Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils; + Popper.placements = placements; + Popper.Defaults = Defaults; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$4 = 'dropdown'; + var VERSION$4 = '4.5.0'; + var DATA_KEY$4 = 'bs.dropdown'; + var EVENT_KEY$4 = "." + DATA_KEY$4; + var DATA_API_KEY$4 = '.data-api'; + var JQUERY_NO_CONFLICT$4 = $.fn[NAME$4]; + var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key + + var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key + + var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key + + var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key + + var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key + + var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse) + + var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE); + var EVENT_HIDE$1 = "hide" + EVENT_KEY$4; + var EVENT_HIDDEN$1 = "hidden" + EVENT_KEY$4; + var EVENT_SHOW$1 = "show" + EVENT_KEY$4; + var EVENT_SHOWN$1 = "shown" + EVENT_KEY$4; + var EVENT_CLICK = "click" + EVENT_KEY$4; + var EVENT_CLICK_DATA_API$4 = "click" + EVENT_KEY$4 + DATA_API_KEY$4; + var EVENT_KEYDOWN_DATA_API = "keydown" + EVENT_KEY$4 + DATA_API_KEY$4; + var EVENT_KEYUP_DATA_API = "keyup" + EVENT_KEY$4 + DATA_API_KEY$4; + var CLASS_NAME_DISABLED = 'disabled'; + var CLASS_NAME_SHOW$2 = 'show'; + var CLASS_NAME_DROPUP = 'dropup'; + var CLASS_NAME_DROPRIGHT = 'dropright'; + var CLASS_NAME_DROPLEFT = 'dropleft'; + var CLASS_NAME_MENURIGHT = 'dropdown-menu-right'; + var CLASS_NAME_POSITION_STATIC = 'position-static'; + var SELECTOR_DATA_TOGGLE$2 = '[data-toggle="dropdown"]'; + var SELECTOR_FORM_CHILD = '.dropdown form'; + var SELECTOR_MENU = '.dropdown-menu'; + var SELECTOR_NAVBAR_NAV = '.navbar-nav'; + var SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'; + var PLACEMENT_TOP = 'top-start'; + var PLACEMENT_TOPEND = 'top-end'; + var PLACEMENT_BOTTOM = 'bottom-start'; + var PLACEMENT_BOTTOMEND = 'bottom-end'; + var PLACEMENT_RIGHT = 'right-start'; + var PLACEMENT_LEFT = 'left-start'; + var Default$2 = { + offset: 0, + flip: true, + boundary: 'scrollParent', + reference: 'toggle', + display: 'dynamic', + popperConfig: null + }; + var DefaultType$2 = { + offset: '(number|string|function)', + flip: 'boolean', + boundary: '(string|element)', + reference: '(string|element)', + display: 'string', + popperConfig: '(null|object)' + }; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Dropdown = /*#__PURE__*/function () { + function Dropdown(element, config) { + this._element = element; + this._popper = null; + this._config = this._getConfig(config); + this._menu = this._getMenuElement(); + this._inNavbar = this._detectNavbar(); + + this._addEventListeners(); + } // Getters + + + var _proto = Dropdown.prototype; + + // Public + _proto.toggle = function toggle() { + if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED)) { + return; + } + + var isActive = $(this._menu).hasClass(CLASS_NAME_SHOW$2); + + Dropdown._clearMenus(); + + if (isActive) { + return; + } + + this.show(true); + }; + + _proto.show = function show(usePopper) { + if (usePopper === void 0) { + usePopper = false; + } + + if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || $(this._menu).hasClass(CLASS_NAME_SHOW$2)) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var showEvent = $.Event(EVENT_SHOW$1, relatedTarget); + + var parent = Dropdown._getParentFromElement(this._element); + + $(parent).trigger(showEvent); + + if (showEvent.isDefaultPrevented()) { + return; + } // Disable totally Popper.js for Dropdown in Navbar + + + if (!this._inNavbar && usePopper) { + /** + * Check for Popper dependency + * Popper - https://popper.js.org + */ + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s dropdowns require Popper.js (https://popper.js.org/)'); + } + + var referenceElement = this._element; + + if (this._config.reference === 'parent') { + referenceElement = parent; + } else if (Util.isElement(this._config.reference)) { + referenceElement = this._config.reference; // Check if it's jQuery element + + if (typeof this._config.reference.jquery !== 'undefined') { + referenceElement = this._config.reference[0]; + } + } // If boundary is not `scrollParent`, then set position to `static` + // to allow the menu to "escape" the scroll parent's boundaries + // https://github.com/twbs/bootstrap/issues/24251 + + + if (this._config.boundary !== 'scrollParent') { + $(parent).addClass(CLASS_NAME_POSITION_STATIC); + } + + this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig()); + } // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + + + if ('ontouchstart' in document.documentElement && $(parent).closest(SELECTOR_NAVBAR_NAV).length === 0) { + $(document.body).children().on('mouseover', null, $.noop); + } + + this._element.focus(); + + this._element.setAttribute('aria-expanded', true); + + $(this._menu).toggleClass(CLASS_NAME_SHOW$2); + $(parent).toggleClass(CLASS_NAME_SHOW$2).trigger($.Event(EVENT_SHOWN$1, relatedTarget)); + }; + + _proto.hide = function hide() { + if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || !$(this._menu).hasClass(CLASS_NAME_SHOW$2)) { + return; + } + + var relatedTarget = { + relatedTarget: this._element + }; + var hideEvent = $.Event(EVENT_HIDE$1, relatedTarget); + + var parent = Dropdown._getParentFromElement(this._element); + + $(parent).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + return; + } + + if (this._popper) { + this._popper.destroy(); + } + + $(this._menu).toggleClass(CLASS_NAME_SHOW$2); + $(parent).toggleClass(CLASS_NAME_SHOW$2).trigger($.Event(EVENT_HIDDEN$1, relatedTarget)); + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$4); + $(this._element).off(EVENT_KEY$4); + this._element = null; + this._menu = null; + + if (this._popper !== null) { + this._popper.destroy(); + + this._popper = null; + } + }; + + _proto.update = function update() { + this._inNavbar = this._detectNavbar(); + + if (this._popper !== null) { + this._popper.scheduleUpdate(); + } + } // Private + ; + + _proto._addEventListeners = function _addEventListeners() { + var _this = this; + + $(this._element).on(EVENT_CLICK, function (event) { + event.preventDefault(); + event.stopPropagation(); + + _this.toggle(); + }); + }; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2(_objectSpread2({}, this.constructor.Default), $(this._element).data()), config); + Util.typeCheckConfig(NAME$4, config, this.constructor.DefaultType); + return config; + }; + + _proto._getMenuElement = function _getMenuElement() { + if (!this._menu) { + var parent = Dropdown._getParentFromElement(this._element); + + if (parent) { + this._menu = parent.querySelector(SELECTOR_MENU); + } + } + + return this._menu; + }; + + _proto._getPlacement = function _getPlacement() { + var $parentDropdown = $(this._element.parentNode); + var placement = PLACEMENT_BOTTOM; // Handle dropup + + if ($parentDropdown.hasClass(CLASS_NAME_DROPUP)) { + placement = $(this._menu).hasClass(CLASS_NAME_MENURIGHT) ? PLACEMENT_TOPEND : PLACEMENT_TOP; + } else if ($parentDropdown.hasClass(CLASS_NAME_DROPRIGHT)) { + placement = PLACEMENT_RIGHT; + } else if ($parentDropdown.hasClass(CLASS_NAME_DROPLEFT)) { + placement = PLACEMENT_LEFT; + } else if ($(this._menu).hasClass(CLASS_NAME_MENURIGHT)) { + placement = PLACEMENT_BOTTOMEND; + } + + return placement; + }; + + _proto._detectNavbar = function _detectNavbar() { + return $(this._element).closest('.navbar').length > 0; + }; + + _proto._getOffset = function _getOffset() { + var _this2 = this; + + var offset = {}; + + if (typeof this._config.offset === 'function') { + offset.fn = function (data) { + data.offsets = _objectSpread2(_objectSpread2({}, data.offsets), _this2._config.offset(data.offsets, _this2._element) || {}); + return data; + }; + } else { + offset.offset = this._config.offset; + } + + return offset; + }; + + _proto._getPopperConfig = function _getPopperConfig() { + var popperConfig = { + placement: this._getPlacement(), + modifiers: { + offset: this._getOffset(), + flip: { + enabled: this._config.flip + }, + preventOverflow: { + boundariesElement: this._config.boundary + } + } + }; // Disable Popper.js if we have a static display + + if (this._config.display === 'static') { + popperConfig.modifiers.applyStyle = { + enabled: false + }; + } + + return _objectSpread2(_objectSpread2({}, popperConfig), this._config.popperConfig); + } // Static + ; + + Dropdown._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$4); + + var _config = typeof config === 'object' ? config : null; + + if (!data) { + data = new Dropdown(this, _config); + $(this).data(DATA_KEY$4, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + Dropdown._clearMenus = function _clearMenus(event) { + if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) { + return; + } + + var toggles = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE$2)); + + for (var i = 0, len = toggles.length; i < len; i++) { + var parent = Dropdown._getParentFromElement(toggles[i]); + + var context = $(toggles[i]).data(DATA_KEY$4); + var relatedTarget = { + relatedTarget: toggles[i] + }; + + if (event && event.type === 'click') { + relatedTarget.clickEvent = event; + } + + if (!context) { + continue; + } + + var dropdownMenu = context._menu; + + if (!$(parent).hasClass(CLASS_NAME_SHOW$2)) { + continue; + } + + if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $.contains(parent, event.target)) { + continue; + } + + var hideEvent = $.Event(EVENT_HIDE$1, relatedTarget); + $(parent).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + continue; + } // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + + + if ('ontouchstart' in document.documentElement) { + $(document.body).children().off('mouseover', null, $.noop); + } + + toggles[i].setAttribute('aria-expanded', 'false'); + + if (context._popper) { + context._popper.destroy(); + } + + $(dropdownMenu).removeClass(CLASS_NAME_SHOW$2); + $(parent).removeClass(CLASS_NAME_SHOW$2).trigger($.Event(EVENT_HIDDEN$1, relatedTarget)); + } + }; + + Dropdown._getParentFromElement = function _getParentFromElement(element) { + var parent; + var selector = Util.getSelectorFromElement(element); + + if (selector) { + parent = document.querySelector(selector); + } + + return parent || element.parentNode; + } // eslint-disable-next-line complexity + ; + + Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) { + // If not input/textarea: + // - And not a key in REGEXP_KEYDOWN => not a dropdown command + // If input/textarea: + // - If space key => not a dropdown command + // - If key is other than escape + // - If key is not up or down => not a dropdown command + // - If trigger inside the menu => not a dropdown command + if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $(event.target).closest(SELECTOR_MENU).length) : !REGEXP_KEYDOWN.test(event.which)) { + return; + } + + if (this.disabled || $(this).hasClass(CLASS_NAME_DISABLED)) { + return; + } + + var parent = Dropdown._getParentFromElement(this); + + var isActive = $(parent).hasClass(CLASS_NAME_SHOW$2); + + if (!isActive && event.which === ESCAPE_KEYCODE) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { + if (event.which === ESCAPE_KEYCODE) { + $(parent.querySelector(SELECTOR_DATA_TOGGLE$2)).trigger('focus'); + } + + $(this).trigger('click'); + return; + } + + var items = [].slice.call(parent.querySelectorAll(SELECTOR_VISIBLE_ITEMS)).filter(function (item) { + return $(item).is(':visible'); + }); + + if (items.length === 0) { + return; + } + + var index = items.indexOf(event.target); + + if (event.which === ARROW_UP_KEYCODE && index > 0) { + // Up + index--; + } + + if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { + // Down + index++; + } + + if (index < 0) { + index = 0; + } + + items[index].focus(); + }; + + _createClass(Dropdown, null, [{ + key: "VERSION", + get: function get() { + return VERSION$4; + } + }, { + key: "Default", + get: function get() { + return Default$2; + } + }, { + key: "DefaultType", + get: function get() { + return DefaultType$2; + } + }]); + + return Dropdown; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$2, Dropdown._dataApiKeydownHandler).on(EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown._dataApiKeydownHandler).on(EVENT_CLICK_DATA_API$4 + " " + EVENT_KEYUP_DATA_API, Dropdown._clearMenus).on(EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$2, function (event) { + event.preventDefault(); + event.stopPropagation(); + + Dropdown._jQueryInterface.call($(this), 'toggle'); + }).on(EVENT_CLICK_DATA_API$4, SELECTOR_FORM_CHILD, function (e) { + e.stopPropagation(); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$4] = Dropdown._jQueryInterface; + $.fn[NAME$4].Constructor = Dropdown; + + $.fn[NAME$4].noConflict = function () { + $.fn[NAME$4] = JQUERY_NO_CONFLICT$4; + return Dropdown._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$5 = 'modal'; + var VERSION$5 = '4.5.0'; + var DATA_KEY$5 = 'bs.modal'; + var EVENT_KEY$5 = "." + DATA_KEY$5; + var DATA_API_KEY$5 = '.data-api'; + var JQUERY_NO_CONFLICT$5 = $.fn[NAME$5]; + var ESCAPE_KEYCODE$1 = 27; // KeyboardEvent.which value for Escape (Esc) key + + var Default$3 = { + backdrop: true, + keyboard: true, + focus: true, + show: true + }; + var DefaultType$3 = { + backdrop: '(boolean|string)', + keyboard: 'boolean', + focus: 'boolean', + show: 'boolean' + }; + var EVENT_HIDE$2 = "hide" + EVENT_KEY$5; + var EVENT_HIDE_PREVENTED = "hidePrevented" + EVENT_KEY$5; + var EVENT_HIDDEN$2 = "hidden" + EVENT_KEY$5; + var EVENT_SHOW$2 = "show" + EVENT_KEY$5; + var EVENT_SHOWN$2 = "shown" + EVENT_KEY$5; + var EVENT_FOCUSIN = "focusin" + EVENT_KEY$5; + var EVENT_RESIZE = "resize" + EVENT_KEY$5; + var EVENT_CLICK_DISMISS = "click.dismiss" + EVENT_KEY$5; + var EVENT_KEYDOWN_DISMISS = "keydown.dismiss" + EVENT_KEY$5; + var EVENT_MOUSEUP_DISMISS = "mouseup.dismiss" + EVENT_KEY$5; + var EVENT_MOUSEDOWN_DISMISS = "mousedown.dismiss" + EVENT_KEY$5; + var EVENT_CLICK_DATA_API$5 = "click" + EVENT_KEY$5 + DATA_API_KEY$5; + var CLASS_NAME_SCROLLABLE = 'modal-dialog-scrollable'; + var CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure'; + var CLASS_NAME_BACKDROP = 'modal-backdrop'; + var CLASS_NAME_OPEN = 'modal-open'; + var CLASS_NAME_FADE$1 = 'fade'; + var CLASS_NAME_SHOW$3 = 'show'; + var CLASS_NAME_STATIC = 'modal-static'; + var SELECTOR_DIALOG = '.modal-dialog'; + var SELECTOR_MODAL_BODY = '.modal-body'; + var SELECTOR_DATA_TOGGLE$3 = '[data-toggle="modal"]'; + var SELECTOR_DATA_DISMISS = '[data-dismiss="modal"]'; + var SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; + var SELECTOR_STICKY_CONTENT = '.sticky-top'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Modal = /*#__PURE__*/function () { + function Modal(element, config) { + this._config = this._getConfig(config); + this._element = element; + this._dialog = element.querySelector(SELECTOR_DIALOG); + this._backdrop = null; + this._isShown = false; + this._isBodyOverflowing = false; + this._ignoreBackdropClick = false; + this._isTransitioning = false; + this._scrollbarWidth = 0; + } // Getters + + + var _proto = Modal.prototype; + + // Public + _proto.toggle = function toggle(relatedTarget) { + return this._isShown ? this.hide() : this.show(relatedTarget); + }; + + _proto.show = function show(relatedTarget) { + var _this = this; + + if (this._isShown || this._isTransitioning) { + return; + } + + if ($(this._element).hasClass(CLASS_NAME_FADE$1)) { + this._isTransitioning = true; + } + + var showEvent = $.Event(EVENT_SHOW$2, { + relatedTarget: relatedTarget + }); + $(this._element).trigger(showEvent); + + if (this._isShown || showEvent.isDefaultPrevented()) { + return; + } + + this._isShown = true; + + this._checkScrollbar(); + + this._setScrollbar(); + + this._adjustDialog(); + + this._setEscapeEvent(); + + this._setResizeEvent(); + + $(this._element).on(EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, function (event) { + return _this.hide(event); + }); + $(this._dialog).on(EVENT_MOUSEDOWN_DISMISS, function () { + $(_this._element).one(EVENT_MOUSEUP_DISMISS, function (event) { + if ($(event.target).is(_this._element)) { + _this._ignoreBackdropClick = true; + } + }); + }); + + this._showBackdrop(function () { + return _this._showElement(relatedTarget); + }); + }; + + _proto.hide = function hide(event) { + var _this2 = this; + + if (event) { + event.preventDefault(); + } + + if (!this._isShown || this._isTransitioning) { + return; + } + + var hideEvent = $.Event(EVENT_HIDE$2); + $(this._element).trigger(hideEvent); + + if (!this._isShown || hideEvent.isDefaultPrevented()) { + return; + } + + this._isShown = false; + var transition = $(this._element).hasClass(CLASS_NAME_FADE$1); + + if (transition) { + this._isTransitioning = true; + } + + this._setEscapeEvent(); + + this._setResizeEvent(); + + $(document).off(EVENT_FOCUSIN); + $(this._element).removeClass(CLASS_NAME_SHOW$3); + $(this._element).off(EVENT_CLICK_DISMISS); + $(this._dialog).off(EVENT_MOUSEDOWN_DISMISS); + + if (transition) { + var transitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, function (event) { + return _this2._hideModal(event); + }).emulateTransitionEnd(transitionDuration); + } else { + this._hideModal(); + } + }; + + _proto.dispose = function dispose() { + [window, this._element, this._dialog].forEach(function (htmlElement) { + return $(htmlElement).off(EVENT_KEY$5); + }); + /** + * `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API` + * Do not move `document` in `htmlElements` array + * It will remove `EVENT_CLICK_DATA_API` event that should remain + */ + + $(document).off(EVENT_FOCUSIN); + $.removeData(this._element, DATA_KEY$5); + this._config = null; + this._element = null; + this._dialog = null; + this._backdrop = null; + this._isShown = null; + this._isBodyOverflowing = null; + this._ignoreBackdropClick = null; + this._isTransitioning = null; + this._scrollbarWidth = null; + }; + + _proto.handleUpdate = function handleUpdate() { + this._adjustDialog(); + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2({}, Default$3), config); + Util.typeCheckConfig(NAME$5, config, DefaultType$3); + return config; + }; + + _proto._triggerBackdropTransition = function _triggerBackdropTransition() { + var _this3 = this; + + if (this._config.backdrop === 'static') { + var hideEventPrevented = $.Event(EVENT_HIDE_PREVENTED); + $(this._element).trigger(hideEventPrevented); + + if (hideEventPrevented.defaultPrevented) { + return; + } + + this._element.classList.add(CLASS_NAME_STATIC); + + var modalTransitionDuration = Util.getTransitionDurationFromElement(this._element); + $(this._element).one(Util.TRANSITION_END, function () { + _this3._element.classList.remove(CLASS_NAME_STATIC); + }).emulateTransitionEnd(modalTransitionDuration); + + this._element.focus(); + } else { + this.hide(); + } + }; + + _proto._showElement = function _showElement(relatedTarget) { + var _this4 = this; + + var transition = $(this._element).hasClass(CLASS_NAME_FADE$1); + var modalBody = this._dialog ? this._dialog.querySelector(SELECTOR_MODAL_BODY) : null; + + if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { + // Don't move modal's DOM position + document.body.appendChild(this._element); + } + + this._element.style.display = 'block'; + + this._element.removeAttribute('aria-hidden'); + + this._element.setAttribute('aria-modal', true); + + if ($(this._dialog).hasClass(CLASS_NAME_SCROLLABLE) && modalBody) { + modalBody.scrollTop = 0; + } else { + this._element.scrollTop = 0; + } + + if (transition) { + Util.reflow(this._element); + } + + $(this._element).addClass(CLASS_NAME_SHOW$3); + + if (this._config.focus) { + this._enforceFocus(); + } + + var shownEvent = $.Event(EVENT_SHOWN$2, { + relatedTarget: relatedTarget + }); + + var transitionComplete = function transitionComplete() { + if (_this4._config.focus) { + _this4._element.focus(); + } + + _this4._isTransitioning = false; + $(_this4._element).trigger(shownEvent); + }; + + if (transition) { + var transitionDuration = Util.getTransitionDurationFromElement(this._dialog); + $(this._dialog).one(Util.TRANSITION_END, transitionComplete).emulateTransitionEnd(transitionDuration); + } else { + transitionComplete(); + } + }; + + _proto._enforceFocus = function _enforceFocus() { + var _this5 = this; + + $(document).off(EVENT_FOCUSIN) // Guard against infinite focus loop + .on(EVENT_FOCUSIN, function (event) { + if (document !== event.target && _this5._element !== event.target && $(_this5._element).has(event.target).length === 0) { + _this5._element.focus(); + } + }); + }; + + _proto._setEscapeEvent = function _setEscapeEvent() { + var _this6 = this; + + if (this._isShown) { + $(this._element).on(EVENT_KEYDOWN_DISMISS, function (event) { + if (_this6._config.keyboard && event.which === ESCAPE_KEYCODE$1) { + event.preventDefault(); + + _this6.hide(); + } else if (!_this6._config.keyboard && event.which === ESCAPE_KEYCODE$1) { + _this6._triggerBackdropTransition(); + } + }); + } else if (!this._isShown) { + $(this._element).off(EVENT_KEYDOWN_DISMISS); + } + }; + + _proto._setResizeEvent = function _setResizeEvent() { + var _this7 = this; + + if (this._isShown) { + $(window).on(EVENT_RESIZE, function (event) { + return _this7.handleUpdate(event); + }); + } else { + $(window).off(EVENT_RESIZE); + } + }; + + _proto._hideModal = function _hideModal() { + var _this8 = this; + + this._element.style.display = 'none'; + + this._element.setAttribute('aria-hidden', true); + + this._element.removeAttribute('aria-modal'); + + this._isTransitioning = false; + + this._showBackdrop(function () { + $(document.body).removeClass(CLASS_NAME_OPEN); + + _this8._resetAdjustments(); + + _this8._resetScrollbar(); + + $(_this8._element).trigger(EVENT_HIDDEN$2); + }); + }; + + _proto._removeBackdrop = function _removeBackdrop() { + if (this._backdrop) { + $(this._backdrop).remove(); + this._backdrop = null; + } + }; + + _proto._showBackdrop = function _showBackdrop(callback) { + var _this9 = this; + + var animate = $(this._element).hasClass(CLASS_NAME_FADE$1) ? CLASS_NAME_FADE$1 : ''; + + if (this._isShown && this._config.backdrop) { + this._backdrop = document.createElement('div'); + this._backdrop.className = CLASS_NAME_BACKDROP; + + if (animate) { + this._backdrop.classList.add(animate); + } + + $(this._backdrop).appendTo(document.body); + $(this._element).on(EVENT_CLICK_DISMISS, function (event) { + if (_this9._ignoreBackdropClick) { + _this9._ignoreBackdropClick = false; + return; + } + + if (event.target !== event.currentTarget) { + return; + } + + _this9._triggerBackdropTransition(); + }); + + if (animate) { + Util.reflow(this._backdrop); + } + + $(this._backdrop).addClass(CLASS_NAME_SHOW$3); + + if (!callback) { + return; + } + + if (!animate) { + callback(); + return; + } + + var backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop); + $(this._backdrop).one(Util.TRANSITION_END, callback).emulateTransitionEnd(backdropTransitionDuration); + } else if (!this._isShown && this._backdrop) { + $(this._backdrop).removeClass(CLASS_NAME_SHOW$3); + + var callbackRemove = function callbackRemove() { + _this9._removeBackdrop(); + + if (callback) { + callback(); + } + }; + + if ($(this._element).hasClass(CLASS_NAME_FADE$1)) { + var _backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop); + + $(this._backdrop).one(Util.TRANSITION_END, callbackRemove).emulateTransitionEnd(_backdropTransitionDuration); + } else { + callbackRemove(); + } + } else if (callback) { + callback(); + } + } // ---------------------------------------------------------------------- + // the following methods are used to handle overflowing modals + // todo (fat): these should probably be refactored out of modal.js + // ---------------------------------------------------------------------- + ; + + _proto._adjustDialog = function _adjustDialog() { + var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + + if (!this._isBodyOverflowing && isModalOverflowing) { + this._element.style.paddingLeft = this._scrollbarWidth + "px"; + } + + if (this._isBodyOverflowing && !isModalOverflowing) { + this._element.style.paddingRight = this._scrollbarWidth + "px"; + } + }; + + _proto._resetAdjustments = function _resetAdjustments() { + this._element.style.paddingLeft = ''; + this._element.style.paddingRight = ''; + }; + + _proto._checkScrollbar = function _checkScrollbar() { + var rect = document.body.getBoundingClientRect(); + this._isBodyOverflowing = Math.round(rect.left + rect.right) < window.innerWidth; + this._scrollbarWidth = this._getScrollbarWidth(); + }; + + _proto._setScrollbar = function _setScrollbar() { + var _this10 = this; + + if (this._isBodyOverflowing) { + // Note: DOMNode.style.paddingRight returns the actual value or '' if not set + // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set + var fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT)); + var stickyContent = [].slice.call(document.querySelectorAll(SELECTOR_STICKY_CONTENT)); // Adjust fixed content padding + + $(fixedContent).each(function (index, element) { + var actualPadding = element.style.paddingRight; + var calculatedPadding = $(element).css('padding-right'); + $(element).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + _this10._scrollbarWidth + "px"); + }); // Adjust sticky content margin + + $(stickyContent).each(function (index, element) { + var actualMargin = element.style.marginRight; + var calculatedMargin = $(element).css('margin-right'); + $(element).data('margin-right', actualMargin).css('margin-right', parseFloat(calculatedMargin) - _this10._scrollbarWidth + "px"); + }); // Adjust body padding + + var actualPadding = document.body.style.paddingRight; + var calculatedPadding = $(document.body).css('padding-right'); + $(document.body).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + this._scrollbarWidth + "px"); + } + + $(document.body).addClass(CLASS_NAME_OPEN); + }; + + _proto._resetScrollbar = function _resetScrollbar() { + // Restore fixed content padding + var fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT)); + $(fixedContent).each(function (index, element) { + var padding = $(element).data('padding-right'); + $(element).removeData('padding-right'); + element.style.paddingRight = padding ? padding : ''; + }); // Restore sticky content + + var elements = [].slice.call(document.querySelectorAll("" + SELECTOR_STICKY_CONTENT)); + $(elements).each(function (index, element) { + var margin = $(element).data('margin-right'); + + if (typeof margin !== 'undefined') { + $(element).css('margin-right', margin).removeData('margin-right'); + } + }); // Restore body padding + + var padding = $(document.body).data('padding-right'); + $(document.body).removeData('padding-right'); + document.body.style.paddingRight = padding ? padding : ''; + }; + + _proto._getScrollbarWidth = function _getScrollbarWidth() { + // thx d.walsh + var scrollDiv = document.createElement('div'); + scrollDiv.className = CLASS_NAME_SCROLLBAR_MEASURER; + document.body.appendChild(scrollDiv); + var scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth; + document.body.removeChild(scrollDiv); + return scrollbarWidth; + } // Static + ; + + Modal._jQueryInterface = function _jQueryInterface(config, relatedTarget) { + return this.each(function () { + var data = $(this).data(DATA_KEY$5); + + var _config = _objectSpread2(_objectSpread2(_objectSpread2({}, Default$3), $(this).data()), typeof config === 'object' && config ? config : {}); + + if (!data) { + data = new Modal(this, _config); + $(this).data(DATA_KEY$5, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](relatedTarget); + } else if (_config.show) { + data.show(relatedTarget); + } + }); + }; + + _createClass(Modal, null, [{ + key: "VERSION", + get: function get() { + return VERSION$5; + } + }, { + key: "Default", + get: function get() { + return Default$3; + } + }]); + + return Modal; + }(); + /** + * ------------------------------------------------------------------------ + * Data Api implementation + * ------------------------------------------------------------------------ + */ + + + $(document).on(EVENT_CLICK_DATA_API$5, SELECTOR_DATA_TOGGLE$3, function (event) { + var _this11 = this; + + var target; + var selector = Util.getSelectorFromElement(this); + + if (selector) { + target = document.querySelector(selector); + } + + var config = $(target).data(DATA_KEY$5) ? 'toggle' : _objectSpread2(_objectSpread2({}, $(target).data()), $(this).data()); + + if (this.tagName === 'A' || this.tagName === 'AREA') { + event.preventDefault(); + } + + var $target = $(target).one(EVENT_SHOW$2, function (showEvent) { + if (showEvent.isDefaultPrevented()) { + // Only register focus restorer if modal will actually get shown + return; + } + + $target.one(EVENT_HIDDEN$2, function () { + if ($(_this11).is(':visible')) { + _this11.focus(); + } + }); + }); + + Modal._jQueryInterface.call($(target), config, this); + }); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + $.fn[NAME$5] = Modal._jQueryInterface; + $.fn[NAME$5].Constructor = Modal; + + $.fn[NAME$5].noConflict = function () { + $.fn[NAME$5] = JQUERY_NO_CONFLICT$5; + return Modal._jQueryInterface; + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap (v4.5.0): tools/sanitizer.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * -------------------------------------------------------------------------- + */ + var uriAttrs = ['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']; + var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; + var DefaultWhitelist = { + // Global attributes allowed on any supplied element below. + '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN], + a: ['target', 'href', 'title', 'rel'], + area: [], + b: [], + br: [], + col: [], + code: [], + div: [], + em: [], + hr: [], + h1: [], + h2: [], + h3: [], + h4: [], + h5: [], + h6: [], + i: [], + img: ['src', 'srcset', 'alt', 'title', 'width', 'height'], + li: [], + ol: [], + p: [], + pre: [], + s: [], + small: [], + span: [], + sub: [], + sup: [], + strong: [], + u: [], + ul: [] + }; + /** + * A pattern that recognizes a commonly useful subset of URLs that are safe. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + + var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi; + /** + * A pattern that matches safe data URLs. Only matches image, video and audio types. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + + var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i; + + function allowedAttribute(attr, allowedAttributeList) { + var attrName = attr.nodeName.toLowerCase(); + + if (allowedAttributeList.indexOf(attrName) !== -1) { + if (uriAttrs.indexOf(attrName) !== -1) { + return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN)); + } + + return true; + } + + var regExp = allowedAttributeList.filter(function (attrRegex) { + return attrRegex instanceof RegExp; + }); // Check if a regular expression validates the attribute. + + for (var i = 0, len = regExp.length; i < len; i++) { + if (attrName.match(regExp[i])) { + return true; + } + } + + return false; + } + + function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) { + if (unsafeHtml.length === 0) { + return unsafeHtml; + } + + if (sanitizeFn && typeof sanitizeFn === 'function') { + return sanitizeFn(unsafeHtml); + } + + var domParser = new window.DOMParser(); + var createdDocument = domParser.parseFromString(unsafeHtml, 'text/html'); + var whitelistKeys = Object.keys(whiteList); + var elements = [].slice.call(createdDocument.body.querySelectorAll('*')); + + var _loop = function _loop(i, len) { + var el = elements[i]; + var elName = el.nodeName.toLowerCase(); + + if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) { + el.parentNode.removeChild(el); + return "continue"; + } + + var attributeList = [].slice.call(el.attributes); + var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []); + attributeList.forEach(function (attr) { + if (!allowedAttribute(attr, whitelistedAttributes)) { + el.removeAttribute(attr.nodeName); + } + }); + }; + + for (var i = 0, len = elements.length; i < len; i++) { + var _ret = _loop(i); + + if (_ret === "continue") continue; + } + + return createdDocument.body.innerHTML; + } + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$6 = 'tooltip'; + var VERSION$6 = '4.5.0'; + var DATA_KEY$6 = 'bs.tooltip'; + var EVENT_KEY$6 = "." + DATA_KEY$6; + var JQUERY_NO_CONFLICT$6 = $.fn[NAME$6]; + var CLASS_PREFIX = 'bs-tooltip'; + var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)" + CLASS_PREFIX + "\\S+", 'g'); + var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']; + var DefaultType$4 = { + animation: 'boolean', + template: 'string', + title: '(string|element|function)', + trigger: 'string', + delay: '(number|object)', + html: 'boolean', + selector: '(string|boolean)', + placement: '(string|function)', + offset: '(number|string|function)', + container: '(string|element|boolean)', + fallbackPlacement: '(string|array)', + boundary: '(string|element)', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + whiteList: 'object', + popperConfig: '(null|object)' + }; + var AttachmentMap = { + AUTO: 'auto', + TOP: 'top', + RIGHT: 'right', + BOTTOM: 'bottom', + LEFT: 'left' + }; + var Default$4 = { + animation: true, + template: '', + trigger: 'hover focus', + title: '', + delay: 0, + html: false, + selector: false, + placement: 'top', + offset: 0, + container: false, + fallbackPlacement: 'flip', + boundary: 'scrollParent', + sanitize: true, + sanitizeFn: null, + whiteList: DefaultWhitelist, + popperConfig: null + }; + var HOVER_STATE_SHOW = 'show'; + var HOVER_STATE_OUT = 'out'; + var Event = { + HIDE: "hide" + EVENT_KEY$6, + HIDDEN: "hidden" + EVENT_KEY$6, + SHOW: "show" + EVENT_KEY$6, + SHOWN: "shown" + EVENT_KEY$6, + INSERTED: "inserted" + EVENT_KEY$6, + CLICK: "click" + EVENT_KEY$6, + FOCUSIN: "focusin" + EVENT_KEY$6, + FOCUSOUT: "focusout" + EVENT_KEY$6, + MOUSEENTER: "mouseenter" + EVENT_KEY$6, + MOUSELEAVE: "mouseleave" + EVENT_KEY$6 + }; + var CLASS_NAME_FADE$2 = 'fade'; + var CLASS_NAME_SHOW$4 = 'show'; + var SELECTOR_TOOLTIP_INNER = '.tooltip-inner'; + var SELECTOR_ARROW = '.arrow'; + var TRIGGER_HOVER = 'hover'; + var TRIGGER_FOCUS = 'focus'; + var TRIGGER_CLICK = 'click'; + var TRIGGER_MANUAL = 'manual'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Tooltip = /*#__PURE__*/function () { + function Tooltip(element, config) { + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s tooltips require Popper.js (https://popper.js.org/)'); + } // private + + + this._isEnabled = true; + this._timeout = 0; + this._hoverState = ''; + this._activeTrigger = {}; + this._popper = null; // Protected + + this.element = element; + this.config = this._getConfig(config); + this.tip = null; + + this._setListeners(); + } // Getters + + + var _proto = Tooltip.prototype; + + // Public + _proto.enable = function enable() { + this._isEnabled = true; + }; + + _proto.disable = function disable() { + this._isEnabled = false; + }; + + _proto.toggleEnabled = function toggleEnabled() { + this._isEnabled = !this._isEnabled; + }; + + _proto.toggle = function toggle(event) { + if (!this._isEnabled) { + return; + } + + if (event) { + var dataKey = this.constructor.DATA_KEY; + var context = $(event.currentTarget).data(dataKey); + + if (!context) { + context = new this.constructor(event.currentTarget, this._getDelegateConfig()); + $(event.currentTarget).data(dataKey, context); + } + + context._activeTrigger.click = !context._activeTrigger.click; + + if (context._isWithActiveTrigger()) { + context._enter(null, context); + } else { + context._leave(null, context); + } + } else { + if ($(this.getTipElement()).hasClass(CLASS_NAME_SHOW$4)) { + this._leave(null, this); + + return; + } + + this._enter(null, this); + } + }; + + _proto.dispose = function dispose() { + clearTimeout(this._timeout); + $.removeData(this.element, this.constructor.DATA_KEY); + $(this.element).off(this.constructor.EVENT_KEY); + $(this.element).closest('.modal').off('hide.bs.modal', this._hideModalHandler); + + if (this.tip) { + $(this.tip).remove(); + } + + this._isEnabled = null; + this._timeout = null; + this._hoverState = null; + this._activeTrigger = null; + + if (this._popper) { + this._popper.destroy(); + } + + this._popper = null; + this.element = null; + this.config = null; + this.tip = null; + }; + + _proto.show = function show() { + var _this = this; + + if ($(this.element).css('display') === 'none') { + throw new Error('Please use show on visible elements'); + } + + var showEvent = $.Event(this.constructor.Event.SHOW); + + if (this.isWithContent() && this._isEnabled) { + $(this.element).trigger(showEvent); + var shadowRoot = Util.findShadowRoot(this.element); + var isInTheDom = $.contains(shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement, this.element); + + if (showEvent.isDefaultPrevented() || !isInTheDom) { + return; + } + + var tip = this.getTipElement(); + var tipId = Util.getUID(this.constructor.NAME); + tip.setAttribute('id', tipId); + this.element.setAttribute('aria-describedby', tipId); + this.setContent(); + + if (this.config.animation) { + $(tip).addClass(CLASS_NAME_FADE$2); + } + + var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement; + + var attachment = this._getAttachment(placement); + + this.addAttachmentClass(attachment); + + var container = this._getContainer(); + + $(tip).data(this.constructor.DATA_KEY, this); + + if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) { + $(tip).appendTo(container); + } + + $(this.element).trigger(this.constructor.Event.INSERTED); + this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment)); + $(tip).addClass(CLASS_NAME_SHOW$4); // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + + if ('ontouchstart' in document.documentElement) { + $(document.body).children().on('mouseover', null, $.noop); + } + + var complete = function complete() { + if (_this.config.animation) { + _this._fixTransition(); + } + + var prevHoverState = _this._hoverState; + _this._hoverState = null; + $(_this.element).trigger(_this.constructor.Event.SHOWN); + + if (prevHoverState === HOVER_STATE_OUT) { + _this._leave(null, _this); + } + }; + + if ($(this.tip).hasClass(CLASS_NAME_FADE$2)) { + var transitionDuration = Util.getTransitionDurationFromElement(this.tip); + $(this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + } else { + complete(); + } + } + }; + + _proto.hide = function hide(callback) { + var _this2 = this; + + var tip = this.getTipElement(); + var hideEvent = $.Event(this.constructor.Event.HIDE); + + var complete = function complete() { + if (_this2._hoverState !== HOVER_STATE_SHOW && tip.parentNode) { + tip.parentNode.removeChild(tip); + } + + _this2._cleanTipClass(); + + _this2.element.removeAttribute('aria-describedby'); + + $(_this2.element).trigger(_this2.constructor.Event.HIDDEN); + + if (_this2._popper !== null) { + _this2._popper.destroy(); + } + + if (callback) { + callback(); + } + }; + + $(this.element).trigger(hideEvent); + + if (hideEvent.isDefaultPrevented()) { + return; + } + + $(tip).removeClass(CLASS_NAME_SHOW$4); // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + + if ('ontouchstart' in document.documentElement) { + $(document.body).children().off('mouseover', null, $.noop); + } + + this._activeTrigger[TRIGGER_CLICK] = false; + this._activeTrigger[TRIGGER_FOCUS] = false; + this._activeTrigger[TRIGGER_HOVER] = false; + + if ($(this.tip).hasClass(CLASS_NAME_FADE$2)) { + var transitionDuration = Util.getTransitionDurationFromElement(tip); + $(tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration); + } else { + complete(); + } + + this._hoverState = ''; + }; + + _proto.update = function update() { + if (this._popper !== null) { + this._popper.scheduleUpdate(); + } + } // Protected + ; + + _proto.isWithContent = function isWithContent() { + return Boolean(this.getTitle()); + }; + + _proto.addAttachmentClass = function addAttachmentClass(attachment) { + $(this.getTipElement()).addClass(CLASS_PREFIX + "-" + attachment); + }; + + _proto.getTipElement = function getTipElement() { + this.tip = this.tip || $(this.config.template)[0]; + return this.tip; + }; + + _proto.setContent = function setContent() { + var tip = this.getTipElement(); + this.setElementContent($(tip.querySelectorAll(SELECTOR_TOOLTIP_INNER)), this.getTitle()); + $(tip).removeClass(CLASS_NAME_FADE$2 + " " + CLASS_NAME_SHOW$4); + }; + + _proto.setElementContent = function setElementContent($element, content) { + if (typeof content === 'object' && (content.nodeType || content.jquery)) { + // Content is a DOM node or a jQuery + if (this.config.html) { + if (!$(content).parent().is($element)) { + $element.empty().append(content); + } + } else { + $element.text($(content).text()); + } + + return; + } + + if (this.config.html) { + if (this.config.sanitize) { + content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn); + } + + $element.html(content); + } else { + $element.text(content); + } + }; + + _proto.getTitle = function getTitle() { + var title = this.element.getAttribute('data-original-title'); + + if (!title) { + title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title; + } + + return title; + } // Private + ; + + _proto._getPopperConfig = function _getPopperConfig(attachment) { + var _this3 = this; + + var defaultBsConfig = { + placement: attachment, + modifiers: { + offset: this._getOffset(), + flip: { + behavior: this.config.fallbackPlacement + }, + arrow: { + element: SELECTOR_ARROW + }, + preventOverflow: { + boundariesElement: this.config.boundary + } + }, + onCreate: function onCreate(data) { + if (data.originalPlacement !== data.placement) { + _this3._handlePopperPlacementChange(data); + } + }, + onUpdate: function onUpdate(data) { + return _this3._handlePopperPlacementChange(data); + } + }; + return _objectSpread2(_objectSpread2({}, defaultBsConfig), this.config.popperConfig); + }; + + _proto._getOffset = function _getOffset() { + var _this4 = this; + + var offset = {}; + + if (typeof this.config.offset === 'function') { + offset.fn = function (data) { + data.offsets = _objectSpread2(_objectSpread2({}, data.offsets), _this4.config.offset(data.offsets, _this4.element) || {}); + return data; + }; + } else { + offset.offset = this.config.offset; + } + + return offset; + }; + + _proto._getContainer = function _getContainer() { + if (this.config.container === false) { + return document.body; + } + + if (Util.isElement(this.config.container)) { + return $(this.config.container); + } + + return $(document).find(this.config.container); + }; + + _proto._getAttachment = function _getAttachment(placement) { + return AttachmentMap[placement.toUpperCase()]; + }; + + _proto._setListeners = function _setListeners() { + var _this5 = this; + + var triggers = this.config.trigger.split(' '); + triggers.forEach(function (trigger) { + if (trigger === 'click') { + $(_this5.element).on(_this5.constructor.Event.CLICK, _this5.config.selector, function (event) { + return _this5.toggle(event); + }); + } else if (trigger !== TRIGGER_MANUAL) { + var eventIn = trigger === TRIGGER_HOVER ? _this5.constructor.Event.MOUSEENTER : _this5.constructor.Event.FOCUSIN; + var eventOut = trigger === TRIGGER_HOVER ? _this5.constructor.Event.MOUSELEAVE : _this5.constructor.Event.FOCUSOUT; + $(_this5.element).on(eventIn, _this5.config.selector, function (event) { + return _this5._enter(event); + }).on(eventOut, _this5.config.selector, function (event) { + return _this5._leave(event); + }); + } + }); + + this._hideModalHandler = function () { + if (_this5.element) { + _this5.hide(); + } + }; + + $(this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler); + + if (this.config.selector) { + this.config = _objectSpread2(_objectSpread2({}, this.config), {}, { + trigger: 'manual', + selector: '' + }); + } else { + this._fixTitle(); + } + }; + + _proto._fixTitle = function _fixTitle() { + var titleType = typeof this.element.getAttribute('data-original-title'); + + if (this.element.getAttribute('title') || titleType !== 'string') { + this.element.setAttribute('data-original-title', this.element.getAttribute('title') || ''); + this.element.setAttribute('title', ''); + } + }; + + _proto._enter = function _enter(event, context) { + var dataKey = this.constructor.DATA_KEY; + context = context || $(event.currentTarget).data(dataKey); + + if (!context) { + context = new this.constructor(event.currentTarget, this._getDelegateConfig()); + $(event.currentTarget).data(dataKey, context); + } + + if (event) { + context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true; + } + + if ($(context.getTipElement()).hasClass(CLASS_NAME_SHOW$4) || context._hoverState === HOVER_STATE_SHOW) { + context._hoverState = HOVER_STATE_SHOW; + return; + } + + clearTimeout(context._timeout); + context._hoverState = HOVER_STATE_SHOW; + + if (!context.config.delay || !context.config.delay.show) { + context.show(); + return; + } + + context._timeout = setTimeout(function () { + if (context._hoverState === HOVER_STATE_SHOW) { + context.show(); + } + }, context.config.delay.show); + }; + + _proto._leave = function _leave(event, context) { + var dataKey = this.constructor.DATA_KEY; + context = context || $(event.currentTarget).data(dataKey); + + if (!context) { + context = new this.constructor(event.currentTarget, this._getDelegateConfig()); + $(event.currentTarget).data(dataKey, context); + } + + if (event) { + context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = false; + } + + if (context._isWithActiveTrigger()) { + return; + } + + clearTimeout(context._timeout); + context._hoverState = HOVER_STATE_OUT; + + if (!context.config.delay || !context.config.delay.hide) { + context.hide(); + return; + } + + context._timeout = setTimeout(function () { + if (context._hoverState === HOVER_STATE_OUT) { + context.hide(); + } + }, context.config.delay.hide); + }; + + _proto._isWithActiveTrigger = function _isWithActiveTrigger() { + for (var trigger in this._activeTrigger) { + if (this._activeTrigger[trigger]) { + return true; + } + } + + return false; + }; + + _proto._getConfig = function _getConfig(config) { + var dataAttributes = $(this.element).data(); + Object.keys(dataAttributes).forEach(function (dataAttr) { + if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) { + delete dataAttributes[dataAttr]; + } + }); + config = _objectSpread2(_objectSpread2(_objectSpread2({}, this.constructor.Default), dataAttributes), typeof config === 'object' && config ? config : {}); + + if (typeof config.delay === 'number') { + config.delay = { + show: config.delay, + hide: config.delay + }; + } + + if (typeof config.title === 'number') { + config.title = config.title.toString(); + } + + if (typeof config.content === 'number') { + config.content = config.content.toString(); + } + + Util.typeCheckConfig(NAME$6, config, this.constructor.DefaultType); + + if (config.sanitize) { + config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn); + } + + return config; + }; + + _proto._getDelegateConfig = function _getDelegateConfig() { + var config = {}; + + if (this.config) { + for (var key in this.config) { + if (this.constructor.Default[key] !== this.config[key]) { + config[key] = this.config[key]; + } + } + } + + return config; + }; + + _proto._cleanTipClass = function _cleanTipClass() { + var $tip = $(this.getTipElement()); + var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX); + + if (tabClass !== null && tabClass.length) { + $tip.removeClass(tabClass.join('')); + } + }; + + _proto._handlePopperPlacementChange = function _handlePopperPlacementChange(popperData) { + this.tip = popperData.instance.popper; + + this._cleanTipClass(); + + this.addAttachmentClass(this._getAttachment(popperData.placement)); + }; + + _proto._fixTransition = function _fixTransition() { + var tip = this.getTipElement(); + var initConfigAnimation = this.config.animation; + + if (tip.getAttribute('x-placement') !== null) { + return; + } + + $(tip).removeClass(CLASS_NAME_FADE$2); + this.config.animation = false; + this.hide(); + this.show(); + this.config.animation = initConfigAnimation; + } // Static + ; + + Tooltip._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$6); + + var _config = typeof config === 'object' && config; + + if (!data && /dispose|hide/.test(config)) { + return; + } + + if (!data) { + data = new Tooltip(this, _config); + $(this).data(DATA_KEY$6, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + _createClass(Tooltip, null, [{ + key: "VERSION", + get: function get() { + return VERSION$6; + } + }, { + key: "Default", + get: function get() { + return Default$4; + } + }, { + key: "NAME", + get: function get() { + return NAME$6; + } + }, { + key: "DATA_KEY", + get: function get() { + return DATA_KEY$6; + } + }, { + key: "Event", + get: function get() { + return Event; + } + }, { + key: "EVENT_KEY", + get: function get() { + return EVENT_KEY$6; + } + }, { + key: "DefaultType", + get: function get() { + return DefaultType$4; + } + }]); + + return Tooltip; + }(); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + + $.fn[NAME$6] = Tooltip._jQueryInterface; + $.fn[NAME$6].Constructor = Tooltip; + + $.fn[NAME$6].noConflict = function () { + $.fn[NAME$6] = JQUERY_NO_CONFLICT$6; + return Tooltip._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$7 = 'popover'; + var VERSION$7 = '4.5.0'; + var DATA_KEY$7 = 'bs.popover'; + var EVENT_KEY$7 = "." + DATA_KEY$7; + var JQUERY_NO_CONFLICT$7 = $.fn[NAME$7]; + var CLASS_PREFIX$1 = 'bs-popover'; + var BSCLS_PREFIX_REGEX$1 = new RegExp("(^|\\s)" + CLASS_PREFIX$1 + "\\S+", 'g'); + + var Default$5 = _objectSpread2(_objectSpread2({}, Tooltip.Default), {}, { + placement: 'right', + trigger: 'click', + content: '', + template: '' + }); + + var DefaultType$5 = _objectSpread2(_objectSpread2({}, Tooltip.DefaultType), {}, { + content: '(string|element|function)' + }); + + var CLASS_NAME_FADE$3 = 'fade'; + var CLASS_NAME_SHOW$5 = 'show'; + var SELECTOR_TITLE = '.popover-header'; + var SELECTOR_CONTENT = '.popover-body'; + var Event$1 = { + HIDE: "hide" + EVENT_KEY$7, + HIDDEN: "hidden" + EVENT_KEY$7, + SHOW: "show" + EVENT_KEY$7, + SHOWN: "shown" + EVENT_KEY$7, + INSERTED: "inserted" + EVENT_KEY$7, + CLICK: "click" + EVENT_KEY$7, + FOCUSIN: "focusin" + EVENT_KEY$7, + FOCUSOUT: "focusout" + EVENT_KEY$7, + MOUSEENTER: "mouseenter" + EVENT_KEY$7, + MOUSELEAVE: "mouseleave" + EVENT_KEY$7 + }; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var Popover = /*#__PURE__*/function (_Tooltip) { + _inheritsLoose(Popover, _Tooltip); + + function Popover() { + return _Tooltip.apply(this, arguments) || this; + } + + var _proto = Popover.prototype; + + // Overrides + _proto.isWithContent = function isWithContent() { + return this.getTitle() || this._getContent(); + }; + + _proto.addAttachmentClass = function addAttachmentClass(attachment) { + $(this.getTipElement()).addClass(CLASS_PREFIX$1 + "-" + attachment); + }; + + _proto.getTipElement = function getTipElement() { + this.tip = this.tip || $(this.config.template)[0]; + return this.tip; + }; + + _proto.setContent = function setContent() { + var $tip = $(this.getTipElement()); // We use append for html objects to maintain js events + + this.setElementContent($tip.find(SELECTOR_TITLE), this.getTitle()); + + var content = this._getContent(); + + if (typeof content === 'function') { + content = content.call(this.element); + } + + this.setElementContent($tip.find(SELECTOR_CONTENT), content); + $tip.removeClass(CLASS_NAME_FADE$3 + " " + CLASS_NAME_SHOW$5); + } // Private + ; + + _proto._getContent = function _getContent() { + return this.element.getAttribute('data-content') || this.config.content; + }; + + _proto._cleanTipClass = function _cleanTipClass() { + var $tip = $(this.getTipElement()); + var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX$1); + + if (tabClass !== null && tabClass.length > 0) { + $tip.removeClass(tabClass.join('')); + } + } // Static + ; + + Popover._jQueryInterface = function _jQueryInterface(config) { + return this.each(function () { + var data = $(this).data(DATA_KEY$7); + + var _config = typeof config === 'object' ? config : null; + + if (!data && /dispose|hide/.test(config)) { + return; + } + + if (!data) { + data = new Popover(this, _config); + $(this).data(DATA_KEY$7, data); + } + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError("No method named \"" + config + "\""); + } + + data[config](); + } + }); + }; + + _createClass(Popover, null, [{ + key: "VERSION", + // Getters + get: function get() { + return VERSION$7; + } + }, { + key: "Default", + get: function get() { + return Default$5; + } + }, { + key: "NAME", + get: function get() { + return NAME$7; + } + }, { + key: "DATA_KEY", + get: function get() { + return DATA_KEY$7; + } + }, { + key: "Event", + get: function get() { + return Event$1; + } + }, { + key: "EVENT_KEY", + get: function get() { + return EVENT_KEY$7; + } + }, { + key: "DefaultType", + get: function get() { + return DefaultType$5; + } + }]); + + return Popover; + }(Tooltip); + /** + * ------------------------------------------------------------------------ + * jQuery + * ------------------------------------------------------------------------ + */ + + + $.fn[NAME$7] = Popover._jQueryInterface; + $.fn[NAME$7].Constructor = Popover; + + $.fn[NAME$7].noConflict = function () { + $.fn[NAME$7] = JQUERY_NO_CONFLICT$7; + return Popover._jQueryInterface; + }; + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var NAME$8 = 'scrollspy'; + var VERSION$8 = '4.5.0'; + var DATA_KEY$8 = 'bs.scrollspy'; + var EVENT_KEY$8 = "." + DATA_KEY$8; + var DATA_API_KEY$6 = '.data-api'; + var JQUERY_NO_CONFLICT$8 = $.fn[NAME$8]; + var Default$6 = { + offset: 10, + method: 'auto', + target: '' + }; + var DefaultType$6 = { + offset: 'number', + method: 'string', + target: '(string|element)' + }; + var EVENT_ACTIVATE = "activate" + EVENT_KEY$8; + var EVENT_SCROLL = "scroll" + EVENT_KEY$8; + var EVENT_LOAD_DATA_API$2 = "load" + EVENT_KEY$8 + DATA_API_KEY$6; + var CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'; + var CLASS_NAME_ACTIVE$2 = 'active'; + var SELECTOR_DATA_SPY = '[data-spy="scroll"]'; + var SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'; + var SELECTOR_NAV_LINKS = '.nav-link'; + var SELECTOR_NAV_ITEMS = '.nav-item'; + var SELECTOR_LIST_ITEMS = '.list-group-item'; + var SELECTOR_DROPDOWN = '.dropdown'; + var SELECTOR_DROPDOWN_ITEMS = '.dropdown-item'; + var SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'; + var METHOD_OFFSET = 'offset'; + var METHOD_POSITION = 'position'; + /** + * ------------------------------------------------------------------------ + * Class Definition + * ------------------------------------------------------------------------ + */ + + var ScrollSpy = /*#__PURE__*/function () { + function ScrollSpy(element, config) { + var _this = this; + + this._element = element; + this._scrollElement = element.tagName === 'BODY' ? window : element; + this._config = this._getConfig(config); + this._selector = this._config.target + " " + SELECTOR_NAV_LINKS + "," + (this._config.target + " " + SELECTOR_LIST_ITEMS + ",") + (this._config.target + " " + SELECTOR_DROPDOWN_ITEMS); + this._offsets = []; + this._targets = []; + this._activeTarget = null; + this._scrollHeight = 0; + $(this._scrollElement).on(EVENT_SCROLL, function (event) { + return _this._process(event); + }); + this.refresh(); + + this._process(); + } // Getters + + + var _proto = ScrollSpy.prototype; + + // Public + _proto.refresh = function refresh() { + var _this2 = this; + + var autoMethod = this._scrollElement === this._scrollElement.window ? METHOD_OFFSET : METHOD_POSITION; + var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method; + var offsetBase = offsetMethod === METHOD_POSITION ? this._getScrollTop() : 0; + this._offsets = []; + this._targets = []; + this._scrollHeight = this._getScrollHeight(); + var targets = [].slice.call(document.querySelectorAll(this._selector)); + targets.map(function (element) { + var target; + var targetSelector = Util.getSelectorFromElement(element); + + if (targetSelector) { + target = document.querySelector(targetSelector); + } + + if (target) { + var targetBCR = target.getBoundingClientRect(); + + if (targetBCR.width || targetBCR.height) { + // TODO (fat): remove sketch reliance on jQuery position/offset + return [$(target)[offsetMethod]().top + offsetBase, targetSelector]; + } + } + + return null; + }).filter(function (item) { + return item; + }).sort(function (a, b) { + return a[0] - b[0]; + }).forEach(function (item) { + _this2._offsets.push(item[0]); + + _this2._targets.push(item[1]); + }); + }; + + _proto.dispose = function dispose() { + $.removeData(this._element, DATA_KEY$8); + $(this._scrollElement).off(EVENT_KEY$8); + this._element = null; + this._scrollElement = null; + this._config = null; + this._selector = null; + this._offsets = null; + this._targets = null; + this._activeTarget = null; + this._scrollHeight = null; + } // Private + ; + + _proto._getConfig = function _getConfig(config) { + config = _objectSpread2(_objectSpread2({}, Default$6), typeof config === 'object' && config ? config : {}); + + if (typeof config.target !== 'string' && Util.isElement(config.target)) { + var id = $(config.target).attr('id'); + + if (!id) { + id = Util.getUID(NAME$8); + $(config.target).attr('id', id); + } + + config.target = "#" + id; + } + + Util.typeCheckConfig(NAME$8, config, DefaultType$6); + return config; + }; + + _proto._getScrollTop = function _getScrollTop() { + return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop; + }; + + _proto._getScrollHeight = function _getScrollHeight() { + return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight); + }; + + _proto._getOffsetHeight = function _getOffsetHeight() { + return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height; + }; + + _proto._process = function _process() { + var scrollTop = this._getScrollTop() + this._config.offset; + + var scrollHeight = this._getScrollHeight(); + + var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight(); + + if (this._scrollHeight !== scrollHeight) { + this.refresh(); + } + + if (scrollTop >= maxScroll) { + var target = this._targets[this._targets.length - 1]; + + if (this._activeTarget !== target) { + this._activate(target); + } + + return; + } + + if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) { + this._activeTarget = null; + + this._clear(); + + return; + } + + for (var i = this._offsets.length; i--;) { + var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]); + + if (isActiveTarget) { + this._activate(this._targets[i]); + } + } + }; + + _proto._activate = function _activate(target) { + this._activeTarget = target; + + this._clear(); + + var queries = this._selector.split(',').map(function (selector) { + return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]"; + }); + + var $link = $([].slice.call(document.querySelectorAll(queries.join(',')))); + + if ($link.hasClass(CLASS_NAME_DROPDOWN_ITEM)) { + $link.closest(SELECTOR_DROPDOWN).find(SELECTOR_DROPDOWN_TOGGLE).addClass(CLASS_NAME_ACTIVE$2); + $link.addClass(CLASS_NAME_ACTIVE$2); + } else { + // Set triggered link as active + $link.addClass(CLASS_NAME_ACTIVE$2); // Set triggered links parents as active + // With both