Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ jobs:
build-type:
- 'release'
- 'debug'
- 'weval'
steps:
- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -173,6 +174,7 @@ jobs:
build-type:
- 'release'
- 'debug'
- 'weval'
steps:
- uses: actions/checkout@v4

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ examples/hello-world/guest/package-lock.json
examples/hello-world/guest/hello.component.wasm
/build-debug
/build-release
/build-release-weval
.vscode
/package-lock.json
.idea
Expand Down
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ set(EMBEDDING_DEP "starling-raw.wasm")
# Define output filenames based on build configuration
set(OUTPUT_NAME_RELEASE "starlingmonkey_embedding.wasm")
set(OUTPUT_NAME_DEBUG "starlingmonkey_embedding.debug.wasm")
set(OUTPUT_NAME_WEVAL "starlingmonkey_embedding_weval.wasm")

# Set the appropriate name based on current configuration
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(OUTPUT_FILENAME ${OUTPUT_NAME_DEBUG})
elseif(WEVAL)
set(OUTPUT_FILENAME ${OUTPUT_NAME_WEVAL})
set(EMBEDDING_DEP "starling-ics.wevalcache")
else()
set(OUTPUT_FILENAME ${OUTPUT_NAME_RELEASE})
endif()
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ exclude = ["StarlingMonkey/crates/rust-url"]
resolver = "2"

[workspace.package]
edition = "2021"
edition = "2024"
version = "0.1.0"

[workspace.lints.clippy]
Expand Down
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ STARLINGMONKEY_DEPS = $(STARLINGMONKEY_SRC)/cmake/* embedding/* $(STARLINGMONKEY
all: release
debug: lib/starlingmonkey_embedding.debug.wasm lib/spidermonkey-embedding-splicer.js
release: lib/starlingmonkey_embedding.wasm lib/spidermonkey-embedding-splicer.js
release-weval: lib/starlingmonkey_ics.wevalcache lib/spidermonkey-embedding-splicer.js

lib/spidermonkey-embedding-splicer.js: target/wasm32-wasip1/release/splicer_component.wasm crates/spidermonkey-embedding-splicer/wit/spidermonkey-embedding-splicer.wit | obj lib
@$(JCO) new target/wasm32-wasip1/release/splicer_component.wasm -o obj/spidermonkey-embedding-splicer.wasm --wasi-reactor
Expand All @@ -27,6 +28,13 @@ lib/starlingmonkey_embedding.wasm: $(STARLINGMONKEY_DEPS) | lib
cmake -B build-release -DCMAKE_BUILD_TYPE=Release
make -j16 -C build-release starlingmonkey_embedding

lib/starlingmonkey_embedding_weval.wasm: $(STARLINGMONKEY_DEPS) | lib
cmake -B build-release-weval -DCMAKE_BUILD_TYPE=Release -DUSE_WASM_OPT=OFF -DWEVAL=ON
make -j16 -C build-release-weval starlingmonkey_embedding

lib/starlingmonkey_ics.wevalcache: lib/starlingmonkey_embedding_weval.wasm
@cp build-release-weval/starling-raw.wasm/starling-ics.wevalcache $@


lib/starlingmonkey_embedding.debug.wasm: $(STARLINGMONKEY_DEPS) | lib
cmake -B build-debug -DCMAKE_BUILD_TYPE=RelWithDebInfo
Expand Down
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,21 @@ Establishing this initial prototype as a singular flexible engine foundation tha

### Weval AOT Compilation

Note: unfortunately Weval AOT Compilation is disabled for the time being, due to incompatibilities with newer versions of the LLVM toolchain used to compile StarlingMonkey. See [this](https://bytecodealliance.zulipchat.com/#narrow/channel/459697-StarlingMonkey/topic/Updating.20Gecko.20version/near/527089464) and the following messages for details.
[Weval][weval] ahead-of-time (AOT) compilation can be enabled to improve runtime performance by pre-compiling JavaScript inline caches.

To enable AOT compilation, set the `enableAot: true` option or use the `--aot` CLI flag.

AOT compilation can also be configured with the following options:

| Option | Type | Example | Description |
|------------------------|-------------------------------------|-----------------|--------------------------------------------------------------------------|
| `aotMinStackSizeBytes` | `number | Number | bigint | BigInt` | `2_007_846_092` | The minimum stack size (via `RUST_MIN_STACK`) to set when running `weval` |

[weval]: https://github.com/bytecodealliance/weval

#### Custom `weval` binary for AOT

To use a custom (pre-downloaded) [`weval`][weval] binary, set the `wevalBin` option to the path to your desired weval binary.

## Platform APIs

Expand Down Expand Up @@ -215,6 +229,18 @@ export function componentize(opts: {
* Path to custom ComponentizeJS engine build to use
*/
engine?: string;
/**
* Path to custom weval cache to use
*/
aotCache?: string;
/**
* Enable AoT using weval
*/
enableAot?: boolean;
/**
* Use a pre-existing path to the `weval` binary, if present
*/
wevalBin?: string;
/**
* Use a pre-existing path to the `wizer` binary, if present
*/
Expand All @@ -240,7 +266,7 @@ export function componentize(opts: {
*/
enableFeatures?: [];
/**
* Pass environment variables to the spawned Wizer Process
* Pass environment variables to the spawned Wizer or Weval Process
* If set to true, all host environment variables are passed
* To pass only a subset, provide an object with the desired variables
*/
Expand Down Expand Up @@ -319,6 +345,14 @@ npm install
npm run build
```

Before being able to use `componentize-js` with AOT support (ex. via `npm link`, from `jco`), you'll need to run:

```console
npm run build:weval
```

This will produce `lib/starlingmonkey_embedding_weval.wasm` and `lib/starlingmonkey_ics.wevalcache`.

To clean up a local installation (i.e. remove the installation of StarlingMonkey):

```console
Expand All @@ -333,6 +367,12 @@ To run all tests:
npm run test
```

To run tests with AOT (weval) enabled:

```console
npm run test:weval
```

### Running a specific test

To run a specific test suite, you can pass an argument to [`vitest`][vitest]:
Expand Down
2 changes: 1 addition & 1 deletion StarlingMonkey
Submodule StarlingMonkey updated 116 files
31 changes: 17 additions & 14 deletions crates/spidermonkey-embedding-splicer/src/bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use heck::*;
use js_component_bindgen::function_bindgen::{
ErrHandling, FunctionBindgen, ResourceData, ResourceMap, ResourceTable,
};
use js_component_bindgen::intrinsics::{render_intrinsics, Intrinsic};
use js_component_bindgen::intrinsics::{Intrinsic, render_intrinsics};
use js_component_bindgen::names::LocalNames;
use js_component_bindgen::source::Source;
use wit_bindgen_core::abi::{self, LiftLower};
Expand Down Expand Up @@ -359,8 +359,6 @@ pub fn componentize_bindgen(
let repCnt = 1;
let repTable = new Map();

contentGlobal.Symbol.dispose = Symbol.dispose = Symbol.for('dispose');

let [$memory, $realloc{}] = $bindings;
delete globalThis.$bindings;

Expand Down Expand Up @@ -1119,12 +1117,11 @@ impl EsmBindgen {
} else {
expt_name
};
if let Some(alias) = interface_name_from_string(expt_name_sans_version) {
if !self.exports.contains_key(&alias)
&& !self.export_aliases.values().any(|_alias| &alias == _alias)
{
self.export_aliases.insert(expt_name.to_string(), alias);
}
if let Some(alias) = interface_name_from_string(expt_name_sans_version)
&& !self.exports.contains_key(&alias)
&& !self.export_aliases.values().any(|_alias| &alias == _alias)
{
self.export_aliases.insert(expt_name.to_string(), alias);
}
}
}
Expand Down Expand Up @@ -1240,9 +1237,15 @@ impl EsmBindgen {
"verifyInterfaceFn"
};
if let Some(alias) = self.export_aliases.get(export_name) {
uwriteln!(bind_exports, "{verify_name}({local_name}, '{export_name}', '{external_name}', '{alias}');");
uwriteln!(
bind_exports,
"{verify_name}({local_name}, '{export_name}', '{external_name}', '{alias}');"
);
} else {
uwriteln!(bind_exports, "{verify_name}({local_name}, '{export_name}', '{external_name}', null);");
uwriteln!(
bind_exports,
"{verify_name}({local_name}, '{export_name}', '{external_name}', null);"
);
};
}
}
Expand Down Expand Up @@ -1288,12 +1291,12 @@ fn interface_name_from_string(name: &str) -> Option<String> {
let name = &name[path_idx + 1..];
let at_idx = name.rfind('@');
let alias = name[..at_idx.unwrap_or(name.len())].to_lower_camel_case();
let iface_name = Some(if let Some(at_idx) = at_idx {

Some(if let Some(at_idx) = at_idx {
format!("{alias}_{}", name[at_idx + 1..].replace(['.', '-'], "_"))
} else {
alias
});
iface_name
})
}

fn binding_name(func_name: &str, iface_name: &Option<String>) -> String {
Expand Down
2 changes: 1 addition & 1 deletion crates/spidermonkey-embedding-splicer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::path::Path;

use anyhow::{bail, Context, Result};
use anyhow::{Context, Result, bail};
use wit_parser::{PackageId, Resolve};

pub mod bindgen;
Expand Down
52 changes: 43 additions & 9 deletions crates/spidermonkey-embedding-splicer/src/splice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ use wasmparser::ExternalKind;
use wasmparser::MemArg;
use wasmparser::Operator;
use wirm::ir::function::{FunctionBuilder, FunctionModifier};
use wirm::ir::id::{ExportsID, FunctionID, LocalID};
use wirm::ir::id::{ExportsID, FunctionID, GlobalID, LocalID};
use wirm::ir::module::Module;
use wirm::ir::types::{BlockType, ElementItems, InstrumentationMode};
use wirm::ir::module::module_globals::GlobalKind;
use wirm::ir::types::{BlockType, ElementItems, InitInstr, InstrumentationMode, Value};
use wirm::module_builder::AddLocal;
use wirm::opcode::{Inject, InjectAt};
use wirm::{DataType, Opcode};
use wit_component::metadata::{decode, Bindgen};
use wit_component::StringEncoding;
use wit_component::metadata::{Bindgen, decode};
use wit_parser::Resolve;

use crate::bindgen::BindingItem;
Expand Down Expand Up @@ -562,11 +563,11 @@ fn synthesize_import_functions(

// stack the return arg now as it chains with the
// args we're about to add to the stack
if impt_sig.ret.is_some() {
if let Some(ret) = impt_sig.ret {
func.local_get(vp_arg);

// if an i64 return, then we need to stack the extra BigInt constructor arg for that now
if matches!(impt_sig.ret.unwrap(), CoreTy::I64) {
if matches!(ret, CoreTy::I64) {
func.local_get(ctx_arg);
}
}
Expand Down Expand Up @@ -725,7 +726,7 @@ fn synthesize_import_functions(

// create imported function table
let els = module.elements.iter_mut().next().unwrap();
if let ElementItems::Functions(ref mut funcs) = &mut els.items {
if let ElementItems::Functions(funcs) = &mut els.items {
for fid in import_fnids {
funcs.push(fid);
}
Expand Down Expand Up @@ -757,7 +758,27 @@ fn synthesize_import_functions(
.get_fn_modifier(coreabi_get_import_fid)
.unwrap();

// Find the I32Const base index and compute the delta to new base
// Save the idx parameter to local at the start of the function,
// so that orignal code does not oeverwrit it.
let idx_local = builder.add_local(DataType::I32);
builder.inject_at(
0,
InstrumentationMode::Before,
Operator::LocalGet {
local_index: *arg_idx,
},
);
builder.inject_at(
0,
InstrumentationMode::Before,
Operator::LocalSet {
local_index: *idx_local,
},
);

// Find the I32Const base index and compute the delta to new base.
// The codegen may split the base index into:
// `global.get N` + `i32.const X` where the global is a constant.
let mut table_instr_idx = 0usize;
let mut delta: i32 = 0;
{
Expand All @@ -769,7 +790,20 @@ fn synthesize_import_functions(
if *value < 1000 || *value > 5000 {
continue;
}
delta = import_fn_table_start_idx - *value;
// Check if instruction just before is a global.get with a
// constant value and if so, include the global's init value
// in the base computation.
let mut base = *value;
if idx > 0
&& let Operator::GlobalGet { global_index } = &ops_ro[idx - 1]
&& let GlobalKind::Local(local_global) =
module.globals.get_kind(GlobalID(*global_index))
&& let [InitInstr::Value(Value::I32(v))] =
local_global.init_expr.instructions()
{
base += v;
}
delta = import_fn_table_start_idx - base;
table_instr_idx = idx;
break;
}
Expand All @@ -780,7 +814,7 @@ fn synthesize_import_functions(
table_instr_idx,
InstrumentationMode::Before,
Operator::LocalGet {
local_index: *arg_idx,
local_index: *idx_local,
},
);
builder.inject_at(
Expand Down
4 changes: 2 additions & 2 deletions crates/spidermonkey-embedding-splicer/src/stub_wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::collections::HashSet;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};

use anyhow::{bail, Result};
use anyhow::{Result, bail};
use wasmparser::{MemArg, TypeRef};
use wirm::ir::function::FunctionBuilder;
use wirm::ir::id::{FunctionID, LocalID};
Expand All @@ -15,7 +15,7 @@ use wit_parser::Resolve;
use crate::parse_wit;
use crate::wit::exports::local::spidermonkey_embedding_splicer::splicer::Feature;

const WASI_VERSIONS: [&str; 4] = ["0.2.0", "0.2.1", "0.2.2", "0.2.3"];
const WASI_VERSIONS: [&str; 5] = ["0.2.0", "0.2.1", "0.2.2", "0.2.3", "0.2.10"];

fn stub_wasi_imports<StubFn>(
module: &mut Module,
Expand Down
2 changes: 1 addition & 1 deletion crates/spidermonkey-embedding-splicer/src/wit.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::{bail, Result};
use anyhow::{Result, bail};

wit_bindgen::generate!({
world: "spidermonkey-embedding-splicer",
Expand Down
4 changes: 4 additions & 0 deletions examples/hello-world/guest/componentize.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { resolve } from 'node:path';

import { componentize } from '@bytecodealliance/componentize-js';

// AoT compilation makes use of weval (https://github.com/bytecodealliance/weval)
const enableAot = process.env.ENABLE_AOT == '1';

const jsSource = await readFile('hello.js', 'utf8');

const { component } = await componentize(jsSource, {
witPath: resolve('hello.wit'),
enableAot,
});

await writeFile('hello.component.wasm', component);
Loading