Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions handlebars-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.4</version>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
Expand Down
68 changes: 68 additions & 0 deletions handlebars/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,23 @@
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>${nashorn.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
<version>${graaljs.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>js</artifactId>
<version>${graaljs.version}</version>
<type>pom</type>
<scope>runtime</scope>
<optional>true</optional>
</dependency>

<dependency>
Expand Down Expand Up @@ -240,6 +257,7 @@

<properties>
<nashorn.version>15.4</nashorn.version>
<graaljs.version>25.0.3</graaljs.version>
</properties>

<profiles>
Expand Down Expand Up @@ -300,5 +318,55 @@
</plugins>
</build>
</profile>

<profile>
<id>graaljs-jit</id>
<dependencies>
<dependency>
<groupId>org.graalvm.compiler</groupId>
<artifactId>compiler</artifactId>
<version>${graaljs.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>graal-compiler-path</id>
<phase>initialize</phase>
<goals>
<goal>properties</goal>
</goals>
</execution>
<execution>
<id>copy-graaljs-modules</id>
<phase>generate-test-resources</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/graaljs-modules</outputDirectory>
<includeGroupIds>org.graalvm.polyglot,org.graalvm.js,org.graalvm.truffle,org.graalvm.sdk,org.graalvm.regex,org.graalvm.shadowed</includeGroupIds>
<excludeArtifactIds>compiler</excludeArtifactIds>
<excludeTypes>pom</excludeTypes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<useModulePath>false</useModulePath>
<argLine>-Duser.language=en -Duser.country=US -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI --module-path=${project.build.directory}/graaljs-modules --add-modules=org.graalvm.polyglot --upgrade-module-path=${org.graalvm.compiler:compiler:jar} --add-exports=org.graalvm.truffle.compiler/com.oracle.truffle.compiler=jdk.graal.compiler --add-exports=org.graalvm.truffle.compiler/com.oracle.truffle.compiler.hotspot=jdk.graal.compiler --add-exports=org.graalvm.truffle.compiler/com.oracle.truffle.compiler.hotspot.libgraal=jdk.graal.compiler --add-exports=org.graalvm.word/org.graalvm.word.impl=jdk.graal.compiler</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.slf4j.Logger;

Expand All @@ -38,6 +37,7 @@
import com.github.jknack.handlebars.internal.Files;
import com.github.jknack.handlebars.internal.FormatterChain;
import com.github.jknack.handlebars.internal.HbsParserFactory;
import com.github.jknack.handlebars.internal.ScriptEngineFactory;
import com.github.jknack.handlebars.internal.Throwing;
import com.github.jknack.handlebars.io.ClassPathTemplateLoader;
import com.github.jknack.handlebars.io.CompositeTemplateLoader;
Expand Down Expand Up @@ -1442,19 +1442,21 @@ private static EscapingStrategy newEscapeChain(final EscapingStrategy[] chain) {
}

/**
* @return Nashorn engine.
* @return A JavaScript engine (GraalJS or Nashorn, whichever is available).
*/
private ScriptEngine engine() {
synchronized (this) {
if (this.engine == null) {

this.engine = new ScriptEngineManager().getEngineByName("nashorn");
this.engine = ScriptEngineFactory.create();

Throwing.run(() -> {
//polyfill globalThis as it is used in handlebars 4.7.9 and is not supported by nashorn
engine.eval("var globalThis = this;");
engine.eval(Files.read(this.handlebarsJsFile, charset));
});
Throwing.run(
() -> {
if (ScriptEngineFactory.isNashorn(engine)) {
engine.eval("var globalThis = this;");
}
engine.eval(Files.read(this.handlebarsJsFile, charset));
});
}
return this.engine;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import java.util.regex.Pattern;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -37,6 +36,7 @@
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.HelperRegistry;
import com.github.jknack.handlebars.internal.Files;
import com.github.jknack.handlebars.internal.ScriptEngineFactory;
import com.github.jknack.handlebars.internal.Throwing;

/**
Expand Down Expand Up @@ -175,7 +175,9 @@ public HelperRegistry registerHelpers(final String filename, final String source
notNull(filename, "The filename is required.");
notEmpty(source, "The source is required.");
ScriptEngine engine = engine();
Throwing.run(() -> engine.eval(adaptES6Literals(source)));
String adaptedSource =
ScriptEngineFactory.isNashorn(engine) ? adaptES6Literals(source) : source;
Throwing.run(() -> engine.eval(adaptedSource));
return this;
}

Expand Down Expand Up @@ -276,12 +278,12 @@ public DefaultHelperRegistry setCharset(final Charset charset) {
}

/**
* @return Nashorn engine.
* @return A JavaScript engine (GraalJS or Nashorn, whichever is available).
*/
private ScriptEngine engine() {
synchronized (this) {
if (this.engine == null) {
this.engine = new ScriptEngineManager().getEngineByName("nashorn");
this.engine = ScriptEngineFactory.create();

this.engine.put("Handlebars_java", this);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Handlebars.java: https://github.com/jknack/handlebars.java
* Apache License Version 2.0 http://www.apache.org/licenses/LICENSE-2.0
*/
package com.github.jknack.handlebars.internal;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

/**
* Factory for creating a JavaScript {@link ScriptEngine}. By default tries Nashorn first, then
* falls back to GraalJS. The engine can be forced via the {@code hbs.js_engine} system property.
*/
public final class ScriptEngineFactory {

private ScriptEngineFactory() {}

/**
* Create a JavaScript engine. If the system property {@code hbs.js_engine} is set, only that
* engine is tried. Otherwise tries Nashorn first, then GraalJS.
*
* @return a JavaScript ScriptEngine.
* @throws IllegalStateException if no JavaScript engine is available or the requested engine is
* not found.
*/
public static ScriptEngine create() {
String requested = System.getProperty("hbs.js_engine");
if (requested != null) {
return createRequested(requested);
}
return createDefault();
}

private static ScriptEngine createDefault() {
ScriptEngineManager manager = new ScriptEngineManager();

ScriptEngine engine = manager.getEngineByName("nashorn");
if (engine != null) {
return engine;
}

engine = manager.getEngineByName("graal.js");
if (engine != null) {
configureGraalJS(engine);
return engine;
}

throw new IllegalStateException(
"No JavaScript engine found. Add either GraalJS or Nashorn to the classpath.");
}

private static ScriptEngine createRequested(String name) {
ScriptEngineManager manager = new ScriptEngineManager();

switch (name) {
case "nashorn":
ScriptEngine nashorn = manager.getEngineByName("nashorn");
if (nashorn == null) {
throw new IllegalStateException(
"JavaScript engine 'nashorn' requested via hbs.js_engine but is not available."
+ " Add org.openjdk.nashorn:nashorn-core to the classpath.");
}
return nashorn;

case "graaljs":
ScriptEngine graaljs = manager.getEngineByName("graal.js");
if (graaljs == null) {
throw new IllegalStateException(
"JavaScript engine 'graaljs' requested via hbs.js_engine but is not available."
+ " Add org.graalvm.js:js-scriptengine to the classpath.");
}
configureGraalJS(graaljs);
return graaljs;

default:
throw new IllegalStateException(
"Unknown hbs.js_engine value: '"
+ name
+ "'. Supported values are 'nashorn' and 'graaljs'.");
}
}

private static void configureGraalJS(ScriptEngine engine) {
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("polyglot.js.allowAllAccess", true);
bindings.put("polyglot.js.nashorn-compat", true);
bindings.put("polyglot.js.ecmascript-version", "2022");
}

/**
* Check whether the given engine is Nashorn.
*
* @param engine the script engine.
* @return true if the engine is Nashorn.
*/
public static boolean isNashorn(ScriptEngine engine) {
return engine.getFactory().getEngineName().toLowerCase().contains("nashorn");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
import java.util.Map;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleBindings;

import org.junit.jupiter.api.Test;

import com.github.jknack.handlebars.internal.ScriptEngineFactory;
import com.github.jknack.handlebars.js.JavaScriptHelperTest;

public class NashornTest {
Expand Down Expand Up @@ -45,13 +45,13 @@ public String getName() {
@Test
public void bootstrap() throws Exception {
Handlebars hbs = new Handlebars();
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");
ScriptEngine engine = ScriptEngineFactory.create();
SimpleBindings bindings = new SimpleBindings();
bindings.put("Handlebars_java", hbs);
nashorn.eval(
engine.eval(
new FileReader(Paths.get("src/main/resources/helpers.nashorn.js").toFile()), bindings);

nashorn.eval(
engine.eval(
new FileReader(
Paths.get("src/test/resources/com/github/jknack/handlebars/js/helpers.js").toFile()),
bindings);
Expand Down