From 3c9be900d8eb083f69395290559705d1cfcf23e1 Mon Sep 17 00:00:00 2001 From: SpencerPark Date: Fri, 29 Jun 2018 20:29:07 -0400 Subject: [PATCH] Support eval --- README.md | 8 ++------ docs/img/eval.png | Bin 0 -> 2593 bytes .../java/io/github/spencerpark/ijava/IJava.java | 1 + .../io/github/spencerpark/ijava/JavaKernel.java | 11 ++++++++--- .../ijava/execution/IJavaExecutionControl.java | 16 ++++++++-------- .../execution/IncompleteSourceException.java | 1 + src/main/resources/ijava-eval-init.jshell | 9 +++++++++ 7 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 docs/img/eval.png create mode 100644 src/main/resources/ijava-eval-init.jshell diff --git a/README.md b/README.md index 2cf54fd..f5981f9 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ For Maven dependency resolution, the kernel is using [ShrinkWrap resolvers](http * [Try online](#try-online) * [Features](#features) - * [TODO](#todo) * [Requirements](#requirements) * [Installing](#installing) * [Configuring](#configuring) @@ -45,14 +44,11 @@ Currently the kernel supports ![maven-pom-dep](docs/img/maven-pom-dep.png) * Display rich output (See also [display.md](docs/display.md) and [maven magic](docs/magics.md#addmavendependencies)). Chart library in the demo photo is [XChart](https://github.com/knowm/XChart) with the sample code taken from their README. ![display-img](docs/img/display-img.png) +* `eval` function. **Note: the signature is `Object eval(String) throws Exception`.** This evaluates the expression (a cell) in the user scope and returns the actual evaluation result instead of a serialized one. + ![eval](docs/img/eval.png) * Configurable evaluation timeout ![timeout](docs/img/timeout.png) -#### TODO - -- [ ] Support magics for making queries about the current environment. -- [ ] Compile javadocs when displaying introspection requests as html. - ### Requirements 1. [Java JDK >=9](http://www.oracle.com/technetwork/java/javase/downloads/index.html). **Not the JRE** diff --git a/docs/img/eval.png b/docs/img/eval.png new file mode 100644 index 0000000000000000000000000000000000000000..1c20712ce439fbcb588e928259becd531c88a689 GIT binary patch literal 2593 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 literal 0 HcmV?d00001 diff --git a/src/main/java/io/github/spencerpark/ijava/IJava.java b/src/main/java/io/github/spencerpark/ijava/IJava.java index 8e0f7cc..0482689 100644 --- a/src/main/java/io/github/spencerpark/ijava/IJava.java +++ b/src/main/java/io/github/spencerpark/ijava/IJava.java @@ -48,6 +48,7 @@ public class IJava { public static final String DEFAULT_SHELL_INIT_RESOURCE_PATH = "ijava-jshell-init.jshell"; public static final String MAGICS_INIT_RESOURCE_PATH = "ijava-magics-init.jshell"; public static final String DISPLAY_INIT_RESOURCE_PATH = "ijava-display-init.jshell"; + public static final String EVAL_INIT_RESOURCE_PATH = "ijava-eval-init.jshell"; public static final String VERSION; diff --git a/src/main/java/io/github/spencerpark/ijava/JavaKernel.java b/src/main/java/io/github/spencerpark/ijava/JavaKernel.java index 22e4b07..d315be1 100644 --- a/src/main/java/io/github/spencerpark/ijava/JavaKernel.java +++ b/src/main/java/io/github/spencerpark/ijava/JavaKernel.java @@ -75,6 +75,7 @@ public JavaKernel() { .startupScript(IJava.resource(IJava.DEFAULT_SHELL_INIT_RESOURCE_PATH)) .startupScript(IJava.resource(IJava.MAGICS_INIT_RESOURCE_PATH)) .startupScript(IJava.resource(IJava.DISPLAY_INIT_RESOURCE_PATH)) + .startupScript(IJava.resource(IJava.EVAL_INIT_RESOURCE_PATH)) .startupScriptFiles(System.getenv(IJava.STARTUP_SCRIPTS_KEY)) .startupScript(System.getenv(IJava.STARTUP_SCRIPT_KEY)) .timeoutFromString(System.getenv(IJava.TIMEOUT_DURATION_KEY)) @@ -244,11 +245,15 @@ private List formatEvaluationTimeoutException(EvaluationTimeoutException return fmt; } - @Override - public DisplayData eval(String expr) throws Exception { + public Object evalRaw(String expr) throws Exception { expr = this.magicsTransformer.transformMagics(expr); - Object result = this.evaluator.eval(expr); + return this.evaluator.eval(expr); + } + + @Override + public DisplayData eval(String expr) throws Exception { + Object result = this.evalRaw(expr); if (result != null) return result instanceof DisplayData diff --git a/src/main/java/io/github/spencerpark/ijava/execution/IJavaExecutionControl.java b/src/main/java/io/github/spencerpark/ijava/execution/IJavaExecutionControl.java index 4e91a5e..26377fc 100644 --- a/src/main/java/io/github/spencerpark/ijava/execution/IJavaExecutionControl.java +++ b/src/main/java/io/github/spencerpark/ijava/execution/IJavaExecutionControl.java @@ -32,6 +32,7 @@ import java.util.Objects; import java.util.UUID; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; /** * An ExecutionControl very similar to {@link jdk.jshell.execution.LocalExecutionControl} but which @@ -46,13 +47,13 @@ public class IJavaExecutionControl extends DirectExecutionControl { private static final Object NULL = new Object(); + private static final AtomicInteger EXECUTOR_THREAD_ID = new AtomicInteger(0); + private final ExecutorService executor; private final long timeoutTime; private final TimeUnit timeoutUnit; - private volatile Future runningTask; - private final Map results = new ConcurrentHashMap<>(); public IJavaExecutionControl() { @@ -62,7 +63,7 @@ public IJavaExecutionControl() { public IJavaExecutionControl(long timeoutTime, TimeUnit timeoutUnit) { this.timeoutTime = timeoutTime; this.timeoutUnit = timeoutTime > 0 ? Objects.requireNonNull(timeoutUnit) : TimeUnit.MILLISECONDS; - this.executor = Executors.newSingleThreadExecutor((r) -> new Thread(r, "IJava-executor")); + this.executor = Executors.newCachedThreadPool(r -> new Thread(r, "IJava-executor-" + EXECUTOR_THREAD_ID.getAndIncrement())); } public long getTimeoutDuration() { @@ -81,12 +82,12 @@ public Object takeResult(String key) { } private Object execute(Method doitMethod) throws TimeoutException, Exception { - this.runningTask = this.executor.submit(() -> doitMethod.invoke(null)); + Future runningTask = this.executor.submit(() -> doitMethod.invoke(null)); try { if (this.timeoutTime > 0) - return this.runningTask.get(this.timeoutTime, this.timeoutUnit); - return this.runningTask.get(); + return runningTask.get(this.timeoutTime, this.timeoutUnit); + return runningTask.get(); } catch (CancellationException e) { // If canceled this means that stop() was invoked in which case the protocol is to // throw an ExecutionControl.StoppedException. @@ -132,8 +133,7 @@ protected String invoke(Method doitMethod) throws Exception { @Override public void stop() throws EngineTerminationException, InternalException { - if (this.runningTask != null) - this.runningTask.cancel(true); + this.executor.shutdownNow(); } @Override diff --git a/src/main/java/io/github/spencerpark/ijava/execution/IncompleteSourceException.java b/src/main/java/io/github/spencerpark/ijava/execution/IncompleteSourceException.java index 2dc3496..263b7b0 100644 --- a/src/main/java/io/github/spencerpark/ijava/execution/IncompleteSourceException.java +++ b/src/main/java/io/github/spencerpark/ijava/execution/IncompleteSourceException.java @@ -27,6 +27,7 @@ public class IncompleteSourceException extends Exception { private final String source; public IncompleteSourceException(String source) { + super(source); this.source = source; } diff --git a/src/main/resources/ijava-eval-init.jshell b/src/main/resources/ijava-eval-init.jshell new file mode 100644 index 0000000..e830cf1 --- /dev/null +++ b/src/main/resources/ijava-eval-init.jshell @@ -0,0 +1,9 @@ +Object eval(String expr) throws Exception { + io.github.spencerpark.ijava.JavaKernel kernel = io.github.spencerpark.ijava.IJava.getKernelInstance(); + + if (kernel != null) { + return kernel.evalRaw(expr); + } else { + throw new RuntimeException("No IJava kernel running"); + } +} \ No newline at end of file