Skip to content

Commit 2a46db3

Browse files
authored
Update docs for plugins (#832)
* Update docs for plugins * Use headings for the plugin API * Delete accidentally added Wasm file
1 parent bfd6176 commit 2a46db3

File tree

3 files changed

+155
-47
lines changed

3 files changed

+155
-47
lines changed

docs/docs-contributing-architecture.md

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ This document is intended to provide an overview of the architecture of `javy`.
88
flowchart TD
99
javy-cli --> wasm
1010
subgraph wasm[plugin.wasm]
11-
javy-plugin --> javy
11+
javy-plugin --> javy-plugin-api
12+
javy-plugin-api --> javy
1213
javy --> rquickjs
1314
end
1415
```
@@ -30,7 +31,7 @@ QuickJS that would be helpful, this is the place to add it.
3031
#### Example
3132

3233
This is a contrived example of how to make a change. If I want to add
33-
a configuuration to set a global variable called `javy_rocks` to `true`, I would
34+
a configuration to set a global variable called `javy_rocks` to `true`, I would
3435
do the following:
3536

3637
In `crates/javy/src/config.rs`:
@@ -39,41 +40,56 @@ In `crates/javy/src/config.rs`:
3940
/// A configuration for [`Runtime`](crate::Runtime).
4041
#[derive(Debug)]
4142
pub struct Config {
42-
+ pub(crate) set_javy_rocks: bool,
43+
+ pub(crate) javy_rocks: bool,
4344
}
4445

4546
impl Default for Config {
4647
/// Creates a [`Config`] with default values.
4748
fn default() -> Self {
4849
Self {
49-
+ set_javy_rocks: false,
50+
+ javy_rocks: false,
5051
}
5152
}
5253
}
5354

5455
impl Config {
5556
+ /// Sets `globalThis.javy_rocks` to `true`.
56-
+ pub fn set_javy_rocks(&mut self) -> &mut Self {
57-
+ self.set_javy_rocks = true;
57+
+ pub fn javy_rocks(&mut self) -> &mut Self {
58+
+ self.javy_rocks = true;
5859
+ self
5960
+ }
6061
}
6162
```
6263

64+
In `crates/javy/src/apis/javy_rocks.rs`:
65+
66+
```rust
67+
pub struct JavyRocks;
68+
69+
impl Instrinsic for JavyRocks {
70+
unsafe fn add_intrinsic(ctx: NonNull<qjs::JSContext>) {
71+
register(Ctx::from_raw(ctx)).expect("registering Javy Rocks to succeed")
72+
}
73+
}
74+
75+
fn register<'js>(this: Ctx<'js>) -> Result<()> {
76+
let globals = this.globals();
77+
globals.set("javy_rocks", true);
78+
}
79+
```
80+
6381
In `crates/javy/src/runtime.rs`:
6482

6583
```diff
66-
+ context.with(|cx| {
67-
+ if cfg.set_javy_rocks {
68-
+ let globals = cx.globals();
69-
+ globals.set("javy_rocks", true);
84+
+ if cfg.javy_rocks {
85+
+ unsafe {
86+
+ JavyRocks::add_intrinsic(ctx.as_raw())
7087
+ }
71-
+ });
72-
+
88+
+ }
7389
```
7490

75-
76-
Read the `config` and call the appropriate methods on `context` to apply the
91+
Define a new property on the `Config`, in `runtime`'s `build_from_config` add
92+
the intrinsic if the config property is enabled, and use `context` to define the
7793
configuration.
7894

7995
#### When to add a `cargo` feature
@@ -110,9 +126,10 @@ You should gate your feature with a cargo feature if your feature/change:
110126
### `javy-plugin`
111127

112128
Gets compiled to `plugin.wasm` for use by the CLI and in environments for
113-
running dynamically linked modules. This isn't intended to be used as a code
114-
library by third parties. Contains logic for driving the `javy` crate for Wasm
115-
modules generated by `javy-cli`.
129+
running dynamically linked modules. This is the default plugin for Javy.
130+
This isn't intended to be used as a code library by third parties. Defines a
131+
an `initialize_runtime` function that uses a configuration structure to
132+
allow the CLI to set various JS runtime configuration options.
116133

117134
#### When to add a `cargo` feature
118135

@@ -122,6 +139,13 @@ this for the event loop because the current implementation has not been
122139
thoroughly vetted. We also need a build of Javy with event loop support to run
123140
a number of web platform tests for text encoding.
124141

142+
### `javy-plugin-api`
143+
144+
Used by Javy plugins to provide common implementations for exports and custom
145+
sections the plugin is expected to expose. This drives the APIs exposed by the
146+
`javy` crate. For example, this crate adds a Wasm function export for `invoke`
147+
to the plugin which is used for actually running the JavaScript.
148+
125149
## `npm` packages
126150

127151
### `javy`

docs/docs-using-dynamic-linking.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,30 @@ linked modules do not and will not execute in WebAssembly runtimes that do not
1010
meet these requirements.
1111

1212
To successfully instantiate and run a dynamically linked Javy module, the
13-
execution environment must provide a `javy_quickjs_provider_v<version>` namespace for
14-
importing that links to the exports provided by the `plugin.wasm`
15-
module. Dynamically linked modules **cannot** be instantiated in environments
16-
that do not provide this import.
13+
execution environment must provide a collection of imports that match the
14+
exports from a Javy plugin Wasm module (for example,
15+
`canonical_abi_realloc` and `invoke`). The namespace for these imports must
16+
match the import namespace specified by the Javy plugin Wasm module used to
17+
generate the dynamically linked Wasm module (this is, if the plugin's import
18+
namespace was `my_plugin_v1`, then the imports must be made available under the
19+
module name `my_plugin_v1`). This value is available from the `import_namespace`
20+
custom section in the Javy plugin module. You can also statically inspect the
21+
imports of the dynamically linked Wasm module to determine the import
22+
namespace. Dynamically linked modules **cannot** be instantiated in
23+
environments that do not provide the required imports.
1724

1825
Dynamically linked Javy modules are tied to QuickJS since they use QuickJS's
1926
bytecode representation.
2027

28+
#### Obtaining the default plugin module
2129

22-
#### Obtaining the plugin module
30+
The `plugin.wasm` module is available as an asset on the Javy release you are
31+
using.
2332

24-
The `plugin.wasm` module is available as an asset on the Javy
25-
release you are using.
33+
It can also be obtained by running `javy emit-plugin -o <path>` to write the
34+
module into `<path>`.
2635

27-
It can also be obtained by running `javy emit-plugin -o
28-
<path>` to write the module into `<path>`.
29-
30-
#### Creating and running a dynamically linked module througy the CLI
36+
#### Creating and running a dynamically linked module through the CLI
3137

3238
Run:
3339

docs/docs-using-extending.md

Lines changed: 97 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,106 @@
11
# Extending
22

33
If you want to use Javy for your own project, you may find that the existing
4-
code is not sufficient since you may want to offer custom APIs or use different
5-
branding for the CLI. The approach we'd recommend taking is to create your own
6-
version of the `javy-cli` and `javy-plugin` crates (you could fork these if you
7-
would like) and depend on the upstream version of the `javy` crate. You can add
8-
your own implementations of custom JS APIs in your fork of the `javy-plugin` crate
9-
or in a different crate that you depend on in your `javy-plugin` fork. If you find
10-
that something is missing in the `javy` crate that you require to implement
11-
something in your fork, we would appreciate it if you would open a GitHub issue
12-
and consider making the change upstream instead of in your fork so all users of
13-
the `javy` crate can benefit.
4+
code is not sufficient since you may want to offer custom JavaScript APIs. The
5+
approach we recommend taking is to create your own Javy plugin Wasm module.
6+
This plugin module can then be specified when using the Javy CLI when building
7+
Javy Wasm modules as a `-C plugin` flag when using `javy build`.
8+
9+
To create your own Javy plugin Wasm module, create a new Rust project that
10+
will compile to a library (that is, `cargo init --lib`).
11+
12+
Your `Cargo.toml` should look like:
13+
14+
```toml
15+
[package]
16+
name = "my-plugin-name"
17+
version = "0.1.0"
18+
19+
[lib]
20+
name = "my_plugin_name"
21+
crate-type = ["cdylib"]
22+
23+
[dependencies]
24+
javy-plugin-api = "1.0.0"
25+
```
26+
27+
And `src/lib.rs` should look like:
28+
29+
```rust
30+
use javy_plugin_api::import_namespace;
31+
use javy_plugin_api::javy::quickjs::prelude::Func;
32+
use javy_plugin_api::javy::Config;
33+
34+
import_namespace!("my_plugin_name");
35+
36+
#[export_name = "initialize_runtime"]
37+
pub extern "C" fn initialize_runtime() {
38+
let mut config = Config::default();
39+
config
40+
.text_encoding(true)
41+
.javy_stream_io(true);
42+
43+
javy_plugin_api::initialize_runtime(config, |runtime| {
44+
runtime
45+
.context()
46+
.with(|ctx| {
47+
ctx.globals()
48+
.set("isThisAPlugin", Func::from(|| "yes it is"))
49+
})
50+
.unwrap();
51+
runtime
52+
})
53+
.unwrap();
54+
}
55+
```
56+
57+
You can then run `cargo build --target=wasm32-wasip1 --release` to create a
58+
Wasm module. Then you need to run
59+
60+
```
61+
javy init-plugin <path_to_plugin> -o <path_to_initialized_module>`
62+
```
63+
64+
which will validate and initialize the Javy runtime. This `javy init-plugin`
65+
step is required for the plugin to be useable by the Javy CLI.
1466

1567
See our documentation on [using complex data types in Wasm
1668
functions](./contributing-complex-data-types.md) for how to support Wasm
1769
functions that need to use byte arrays, strings, or structured data.
1870

19-
For a visual representation of how we expect forks to consume our crates:
71+
## The full plugin API
2072

21-
```mermaid
22-
flowchart TD
23-
your-cli --> wasm
24-
subgraph wasm[your_plugin.wasm]
25-
your-plugin --> javy[upstream javy]
26-
javy --> rquickjs
27-
end
28-
```
73+
This is the Wasm API the Javy CLI expects Javy plugins to expose. The
74+
`javy-plugin-api` crate will export implementations of all required exported
75+
functions except `initialize_runtime`. `import_namespace!` will define the
76+
`import_namespace` custom section.
77+
78+
### Exported Wasm functions
79+
80+
#### `initialize_runtime() -> ()`
81+
82+
This will initialize a mutable global containing the Javy runtime for use by
83+
`compile_src` and `invoke`.
84+
85+
#### `canonical_abi_realloc(orig_ptr: i32, orig_len: i32, new_ptr: i32, new_len: i32) -> ptr: i32`
86+
87+
This is used to allocate memory in the plugin module.
88+
89+
#### `compile_src(src_ptr: i32, src_len: i32) -> bytecode_wide_ptr: i32`
90+
91+
This is used to compile JavaScript source code to QuickJS bytecode. The return
92+
pointer points to a tuple of `(bytecode_ptr: i32, bytecode_len: i32)` in the
93+
plugin instance's linear memory.
94+
95+
#### `invoke(bytecode_ptr: i32, bytecode_len: i32, fn_name_ptr: i32, fn_name_len: i32) -> ()`
96+
97+
This is used to evaluate the JavaScript code and optionally to call an exported
98+
JS function if `fn_name_ptr` is not `0`.
99+
100+
### Custom sections
101+
102+
#### `import_namespace`
103+
104+
Contains a UTF-8 encoded string. This is used to determine the namespace that
105+
will be used for the Wasm imports in dynamically linked modules built with this
106+
plugin.

0 commit comments

Comments
 (0)