clearing up README
This commit is contained in:
parent
d563fc4919
commit
c8a89849ee
80
README.md
80
README.md
@ -2,22 +2,76 @@
|
|||||||
|
|
||||||
**WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
|
**WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
|
||||||
|
|
||||||
J2S is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
|
J2S is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. A small REPL (`me.topchetoeu.j2s.repl.SimpleRepl`) library with an included simple debugger (`me.topchetoeu.j2s.repl.debug.SimpleDebugger`). These are more or less reference implementations. In the future, most of the primordials logic of `SimpleRepl` will be moved in the "lib" project, but for now, it will stay there.
|
||||||
|
|
||||||
## Example
|
## How to use?
|
||||||
|
|
||||||
The following is going to execute a simple javascript statement:
|
Since this is mostly targeted for integration into other applications, here, examples for invoking JS code from Java will be shown. In the future, a more comprehensive wiki will be made.
|
||||||
|
|
||||||
|
### Setting up an event loop
|
||||||
|
|
||||||
|
First of all, you will want to create an event loop. While not required, 99% of the times you will want to have one.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
var engine = new Engine();
|
var engine = new Engine();
|
||||||
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
|
var thread = engine.start();
|
||||||
var env = Internals.apply(new Environment());
|
|
||||||
|
|
||||||
// Queue code to load internal libraries and start engine
|
|
||||||
var awaitable = engine.pushMsg(false, env, new Filename("tmp", "eval"), "10 + Math.sqrt(5 / 3)", null);
|
|
||||||
// Run the engine on the same thread, until the event loop runs empty
|
|
||||||
engine.run(true);
|
|
||||||
|
|
||||||
// Get our result
|
|
||||||
System.out.println(awaitable.await());
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Hooray! Now you have an event loop. The thread that was automatically created is a daemon thread, so it will harakiri when the rest of the application ends. If you don't want to use the built-in thread, you can instead run it with `engine.run(untilEmpty)`. If you pass true (which you most likely need), the event loop will be run blocking-ly until it is empty. Otherwise, it will be run forever.
|
||||||
|
|
||||||
|
### Creating the execution environment
|
||||||
|
|
||||||
|
This is one of the other crucial parts of J2S's architecture - the environment. It contains the global scope, a reference to the event loop, the global scope, the debugger, source mappings and a lot more. To run JS code, you must create an environment:
|
||||||
|
|
||||||
|
```java
|
||||||
|
var env = Environment.empty();
|
||||||
|
env.add(EventLoop.KEY, engine); // Gives
|
||||||
|
env.add(DebugContext.KEY, new DebugContext()); // For source mappings
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, the environment is nothing more than a simple map of objects that may be of interest to the JS code. Although you can do much more with the environment, we will leave it at that.
|
||||||
|
|
||||||
|
### Registering the compiler
|
||||||
|
|
||||||
|
Since the compiler is a part of the runtime, you need to register it in the environment. You can use the following boilerplate, although a nicer API will be exposed later on:
|
||||||
|
|
||||||
|
```java
|
||||||
|
env.add(Compiler.KEY, (_env, filename, raw, mapper) -> {
|
||||||
|
try {
|
||||||
|
// Invokes the compiler. Will return a CompilerResult, which, along other things,
|
||||||
|
// gives us all the compiled function bodies (aka function scaffoldings, that can be used to construct a function value)
|
||||||
|
var res = JavaScript.compile(env, filename, raw, true);
|
||||||
|
var body = res.body();
|
||||||
|
|
||||||
|
// We'll register the source and function source mappings for debugging
|
||||||
|
DebugContext.get(env).onSource(filename, raw);
|
||||||
|
for (var el : res.bodies()) {
|
||||||
|
DebugContext.get(env).onFunctionLoad(el, res.map(mapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, we will construct the function
|
||||||
|
// Few things to note: we need to pass the environment, the name of the function (the filename),
|
||||||
|
// and the last thing: the captures. Since we are compiling the main function, we don't have
|
||||||
|
// any captures, so we pass an empty array
|
||||||
|
return new CodeFunction(env, filename.toString(), body, new Value[0][]);
|
||||||
|
}
|
||||||
|
catch (SyntaxException e) {
|
||||||
|
// Convert the exception to an engine exception
|
||||||
|
var res = EngineException.ofSyntax(e.msg);
|
||||||
|
// Add the location of the error to its stack trace
|
||||||
|
res.add(env, e.loc.filename() + "", e.loc);
|
||||||
|
throw res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Evaluating a piece of code on the event loop
|
||||||
|
|
||||||
|
This is what you really want to do: run code! You can do that in the following way:
|
||||||
|
|
||||||
|
```java
|
||||||
|
var result = engine.pushMsg(false, env, Filename.parse("my-app://test.js"), "return 10 + 5 / 3;", Value.UNDEFINED).get();
|
||||||
|
System.out.println(result.toReadable(env));
|
||||||
|
```
|
||||||
|
|
||||||
|
If all goes well, we will get "11.666..." as a result.
|
||||||
|
@ -2,6 +2,7 @@ package me.topchetoeu.j2s.compilation;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Stack;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import me.topchetoeu.j2s.common.FunctionBody;
|
import me.topchetoeu.j2s.common.FunctionBody;
|
||||||
@ -18,6 +19,7 @@ import me.topchetoeu.j2s.compilation.scope.Variable;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
public final class CompileResult {
|
public final class CompileResult {
|
||||||
@ -64,9 +66,31 @@ public final class CompileResult {
|
|||||||
public void setLocation(Location type) {
|
public void setLocation(Location type) {
|
||||||
setLocation(instructions.size() - 1, type);
|
setLocation(instructions.size() - 1, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocationAndDebug(Location loc, BreakpointType type) {
|
public void setLocationAndDebug(Location loc, BreakpointType type) {
|
||||||
setLocationAndDebug(instructions.size() - 1, loc, type);
|
setLocationAndDebug(instructions.size() - 1, loc, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterable<FunctionBody> bodies() {
|
||||||
|
var stack = new Stack<FunctionBody>();
|
||||||
|
stack.push(body());
|
||||||
|
|
||||||
|
return () -> new Iterator<FunctionBody>() {
|
||||||
|
@Override public FunctionBody next() {
|
||||||
|
if (stack.empty()) return null;
|
||||||
|
else {
|
||||||
|
var res = stack.pop();
|
||||||
|
for (var el : res.children) {
|
||||||
|
stack.push(el);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override public boolean hasNext() {
|
||||||
|
return !stack.empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public CompileResult addChild(FunctionNode node, CompileResult res) {
|
public CompileResult addChild(FunctionNode node, CompileResult res) {
|
||||||
this.children.add(res);
|
this.children.add(res);
|
||||||
|
@ -13,11 +13,9 @@ import java.util.Optional;
|
|||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.regex.PatternSyntaxException;
|
import java.util.regex.PatternSyntaxException;
|
||||||
|
|
||||||
import me.topchetoeu.j2s.common.FunctionBody;
|
|
||||||
import me.topchetoeu.j2s.common.Metadata;
|
import me.topchetoeu.j2s.common.Metadata;
|
||||||
import me.topchetoeu.j2s.common.Reading;
|
import me.topchetoeu.j2s.common.Reading;
|
||||||
import me.topchetoeu.j2s.common.SyntaxException;
|
import me.topchetoeu.j2s.common.SyntaxException;
|
||||||
@ -25,8 +23,6 @@ import me.topchetoeu.j2s.common.environment.Environment;
|
|||||||
import me.topchetoeu.j2s.common.environment.Key;
|
import me.topchetoeu.j2s.common.environment.Key;
|
||||||
import me.topchetoeu.j2s.common.json.JSON;
|
import me.topchetoeu.j2s.common.json.JSON;
|
||||||
import me.topchetoeu.j2s.common.parsing.Filename;
|
import me.topchetoeu.j2s.common.parsing.Filename;
|
||||||
import me.topchetoeu.j2s.common.parsing.Location;
|
|
||||||
import me.topchetoeu.j2s.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.j2s.compilation.JavaScript;
|
import me.topchetoeu.j2s.compilation.JavaScript;
|
||||||
import me.topchetoeu.j2s.repl.debug.DebugServer;
|
import me.topchetoeu.j2s.repl.debug.DebugServer;
|
||||||
import me.topchetoeu.j2s.repl.debug.Debugger;
|
import me.topchetoeu.j2s.repl.debug.Debugger;
|
||||||
@ -63,8 +59,12 @@ public class SimpleRepl {
|
|||||||
try {
|
try {
|
||||||
var res = JavaScript.compile(env, filename, raw, true);
|
var res = JavaScript.compile(env, filename, raw, true);
|
||||||
var body = res.body();
|
var body = res.body();
|
||||||
|
|
||||||
DebugContext.get(env).onSource(filename, raw);
|
DebugContext.get(env).onSource(filename, raw);
|
||||||
registerFunc(env, body, res, mapper);
|
for (var el : res.bodies()) {
|
||||||
|
DebugContext.get(env).onFunctionLoad(el, res.map(mapper));
|
||||||
|
}
|
||||||
|
|
||||||
return new CodeFunction(env, filename.toString(), body, new Value[0][]);
|
return new CodeFunction(env, filename.toString(), body, new Value[0][]);
|
||||||
}
|
}
|
||||||
catch (SyntaxException e) {
|
catch (SyntaxException e) {
|
||||||
@ -901,15 +901,7 @@ public class SimpleRepl {
|
|||||||
var tsCompiler = wrap(Compiler.get(environment), tsEnvironment, environment, tsCompilerFactory[0]);
|
var tsCompiler = wrap(Compiler.get(environment), tsEnvironment, environment, tsCompilerFactory[0]);
|
||||||
environment.add(Compiler.KEY, tsCompiler);
|
environment.add(Compiler.KEY, tsCompiler);
|
||||||
}
|
}
|
||||||
private static void registerFunc(Environment env, FunctionBody body, CompileResult res, Function<Location, Location> mapper) {
|
|
||||||
var map = res.map(mapper);
|
|
||||||
|
|
||||||
DebugContext.get(env).onFunctionLoad(body, map);
|
|
||||||
|
|
||||||
for (var i = 0; i < body.children.length; i++) {
|
|
||||||
registerFunc(env, body.children[i], res.children.get(i), mapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String args[]) throws InterruptedException {
|
public static void main(String args[]) throws InterruptedException {
|
||||||
SimpleRepl.args = args;
|
SimpleRepl.args = args;
|
||||||
|
Loading…
Reference in New Issue
Block a user