GraalVM Polyglot Programming
Embedding JavaScript, Python, and Ruby with GraalVM Polyglot APIs.
# CLAUDE.md — GraalVM Polyglot Programming
## What polyglot is
- GraalVM can run JavaScript, Python, Ruby, R, and LLVM-based languages from inside a Java program.
- Languages share a single VM, GC, and compiler. Calls between languages are zero-copy where possible.
- Use cases: embedding a scripting language, running user-supplied snippets, mixing ML libraries with a Java service.
## Setup
- Add the polyglot SDK and the language you want:
```xml
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>polyglot</artifactId>
<version>23.1.0</version>
</dependency>
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>js</artifactId>
<version>23.1.0</version>
<type>pom</type>
</dependency>
```
- For Python, use `python-community`. Each language is a separate dependency.
## Basic API
```java
try (Context ctx = Context.newBuilder("js")
.allowAllAccess(true)
.build()) {
Value result = ctx.eval("js", "1 + 2 * 3");
System.out.println(result.asInt()); // 7
}
```
- `Context` is the runtime. Reuse it for many evaluations — creation is expensive.
- `Value` is the polyglot representation of any JS/Python/Ruby value. Convert with `asInt`, `asString`, `as(T.class)`.
- `Source.create("js", code)` lets you name the source for stack traces.
## Calling Java from a guest language
```java
ctx.getBindings("js").putMember("repo", userRepository);
ctx.eval("js", "repo.findById('123').getEmail()");
```
- `allowHostAccess(HostAccess.ALL)` to expose Java methods. Use `HostAccess.EXPLICIT` and `@HostAccess.Export` to allow only annotated methods — safer for user-supplied code.
## Calling guest from Java
```java
Value func = ctx.eval("js", "(x) => x * 2");
int result = func.execute(21).asInt();
```
- Treat `Value` as a function with `.execute(...)`.
- For complex types, define a Java interface and `Value.as(MyInterface.class)` to bridge.
## Sandboxing
- For executing **untrusted** code (user scripts, plugins):
```java
Context ctx = Context.newBuilder("js")
.allowHostAccess(HostAccess.NONE)
.allowIO(false)
.allowCreateThread(false)
.resourceLimits(ResourceLimits.newBuilder().statementLimit(10_000).build())
.build();
```
- Set CPU and statement limits — otherwise an infinite loop hangs the host.
- Don't allow `allowAllAccess(true)` for untrusted input.
## Performance
- First execution warms up the compiler. Reuse `Context` and pre-parsed `Source` objects.
- For hot loops, write the kernel in Java and call it from the guest.
- Polyglot has overhead — don't use it for things you could do in a single language.
## Native image
- Polyglot **does** work with native image, but each language adds significantly to image size.
- Build with `--language:js` (and similar) flags.
- Test in native mode early — some language features may not be supported in native.
## Don't
- Don't reach for polyglot to "use a Python library" without measuring. JNI to a native lib or shelling out is sometimes simpler.
- Don't eval user input without sandboxing. JS can do everything Java can if you give it `HostAccess.ALL`.
- Don't create a new `Context` per request. The boot cost is real.
- Don't ship polyglot to production without resource limits — one runaway script will hang the JVM.
Other Java templates
Modern Java Rules
Java 21+ defaults: records, sealed types, pattern matching, virtual threads, var.
Java Testing with JUnit 5
JUnit 5 + AssertJ + Mockito + Testcontainers — opinionated test conventions.
Java Virtual Threads & Concurrency
Project Loom virtual threads, structured concurrency, and modern concurrent patterns.
Java Build (Maven & Gradle)
Build conventions, dependency management, multi-module projects, and CI patterns.
Java Streams & Collections
Streams API, immutable collections, Collectors, and idiomatic data manipulation.