From cb9b59ae01dc6792ade7b829c0795aa49f200716 Mon Sep 17 00:00:00 2001 From: Auxilor Date: Mon, 25 Apr 2022 11:32:47 +0100 Subject: [PATCH] Added ScriptUtils --- build.gradle.kts | 5 +- .../core/integrations/shop/ShopWrapper.java | 4 +- .../java/com/willfp/eco/util/ScriptUtils.java | 115 ++++++++++++++++++ eco-api/src/test/java/ScriptUtilsTest.java | 40 ++++++ 4 files changed, 162 insertions(+), 2 deletions(-) create mode 100644 eco-api/src/main/java/com/willfp/eco/util/ScriptUtils.java create mode 100644 eco-api/src/test/java/ScriptUtilsTest.java diff --git a/build.gradle.kts b/build.gradle.kts index ebc57776..0ae34b47 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -82,6 +82,10 @@ allprojects { } dependencies { + // Kotlin + implementation(kotlin("stdlib", version = "1.6.21")) + implementation(kotlin("scripting-jsr223", version = "1.6.21")) + // Included in spigot jar, no need to move to implementation compileOnly("org.jetbrains:annotations:23.0.0") compileOnly("com.google.guava:guava:31.1-jre") @@ -100,7 +104,6 @@ allprojects { // Other implementation("com.github.ben-manes.caffeine:caffeine:3.0.6") implementation("org.apache.maven:maven-artifact:3.8.4") - implementation(kotlin("stdlib", version = "1.6.21")) } tasks.withType { diff --git a/eco-api/src/main/java/com/willfp/eco/core/integrations/shop/ShopWrapper.java b/eco-api/src/main/java/com/willfp/eco/core/integrations/shop/ShopWrapper.java index 9acdb719..aebd3de7 100644 --- a/eco-api/src/main/java/com/willfp/eco/core/integrations/shop/ShopWrapper.java +++ b/eco-api/src/main/java/com/willfp/eco/core/integrations/shop/ShopWrapper.java @@ -16,7 +16,9 @@ public interface ShopWrapper extends Integration { } /** - * Register sell event adapters. + * Get the sell event adapter. + * + * @return The listener. */ @Nullable default Listener getSellEventAdapter() { diff --git a/eco-api/src/main/java/com/willfp/eco/util/ScriptUtils.java b/eco-api/src/main/java/com/willfp/eco/util/ScriptUtils.java new file mode 100644 index 00000000..5e398cbf --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/util/ScriptUtils.java @@ -0,0 +1,115 @@ +package com.willfp.eco.util; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Utilities / API methods for kotlin scripts. + */ +public final class ScriptUtils { + /** + * The cache of compiled scripts. + */ + private static final Cache COMPILED_SCRIPTS = Caffeine.newBuilder() + .build(); + + /** + * Empty (dummy) compiled script. + */ + private static final CompiledScript EMPTY_SCRIPT = new CompiledScript() { + @Override + public Object eval(@NotNull final ScriptContext context) { + return null; + } + + @Override + public ScriptEngine getEngine() { + return ScriptUtils.getEngine(); + } + }; + + /** + * Evaluate a script. + * + * @param script The script. + * @return The return value of the script. + */ + @Nullable + public static Object eval(@NotNull final String script) { + return eval(script, (x) -> { + // Do nothing. + }); + } + + /** + * Evaluate a script. + * + * @param script The script. + * @param bindingsFactory The consumer to create bindings. + * @return The return value of the script. + */ + @Nullable + public static Object eval(@NotNull final String script, + @NotNull final Consumer bindingsFactory) { + Bindings toHash = getEngine().createBindings(); + bindingsFactory.accept(toHash); + Map bindingsMap = new HashMap<>(toHash); + + try { + return COMPILED_SCRIPTS.get(new ScriptToCompile(script, bindingsMap), (it) -> { + try { + var engine = getEngine(); + Bindings bindings = engine.createBindings(); + bindingsFactory.accept(bindings); + engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE); + return engine.compile(it.script); + } catch (ScriptException e) { + e.printStackTrace(); + return EMPTY_SCRIPT; + } + }).eval(); + } catch (ScriptException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Get new engine instance. + * + * @param Intersection type. + * @return The engine. + */ + @SuppressWarnings("unchecked") + private static T getEngine() { + return (T) new ScriptEngineManager().getEngineByExtension("kts"); + } + + /** + * Metadata to compile a script. + * + * @param script The script. + * @param bindings The bindings. + */ + private record ScriptToCompile(@NotNull String script, + @NotNull Map bindings) { + + } + + private ScriptUtils() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } +} diff --git a/eco-api/src/test/java/ScriptUtilsTest.java b/eco-api/src/test/java/ScriptUtilsTest.java new file mode 100644 index 00000000..6370e5a2 --- /dev/null +++ b/eco-api/src/test/java/ScriptUtilsTest.java @@ -0,0 +1,40 @@ +import com.willfp.eco.util.ScriptUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ScriptUtilsTest { + @Test + public void testScriptUtils() { + Assertions.assertEquals( + "Test", + ScriptUtils.eval("\"Test\"") + ); + Assertions.assertNull(ScriptUtils.eval("empasd___a !&&b1923")); + Assertions.assertEquals( + 10, + ScriptUtils.eval("8 + 2") + ); + Assertions.assertEquals( + "XVIII", + ScriptUtils.eval("com.willfp.eco.util.NumberUtils.toNumeral(18)") + ); + } + + @Test + public void testBindings() { + Assertions.assertEquals( + 10, + ScriptUtils.eval("x + y", (bindings) -> { + bindings.put("x", 2); + bindings.put("y", 8); + }) + ); + Assertions.assertEquals( + 12, + ScriptUtils.eval("x + y", (bindings) -> { + bindings.put("x", 3); + bindings.put("y", 9); + }) + ); + } +}