j2s/README.md

78 lines
4.0 KiB
Markdown
Raw Normal View History

2025-01-09 22:34:29 +00:00
# J2S (Java-JavaScript or Java to JavaScript)
2023-08-05 15:37:18 +00:00
2023-12-24 12:31:50 +00:00
**WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
2023-08-05 15:37:18 +00:00
2025-01-10 02:53:11 +00:00
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.
2023-08-05 15:37:18 +00:00
2025-01-10 02:53:11 +00:00
## How to use?
2023-08-05 15:37:18 +00:00
2025-01-10 02:53:11 +00:00
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.
2023-08-05 15:37:18 +00:00
```java
2024-01-10 09:25:29 +00:00
var engine = new Engine();
2025-01-10 02:53:11 +00:00
var thread = engine.start();
```
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.
2025-01-10 02:53:11 +00:00
### Registering the compiler
2023-12-24 12:31:50 +00:00
2025-01-10 02:53:11 +00:00
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.all()) {
DebugContext.get(env).onFunctionLoad(el.body(), el.map(mapper));
2025-01-10 02:53:11 +00:00
}
// 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));
2023-08-05 15:37:18 +00:00
```
2025-01-10 02:53:11 +00:00
If all goes well, we will get "11.666..." as a result.