Skip to content

Support AOT dynamic extension linking #425

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 21, 2024
Merged
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: 1 addition & 1 deletion .github/workflows/build-gems.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
id: fetch
with:
supported-ruby-platforms: |
exclude: [arm-linux]
exclude: [arm-linux, x64-mingw32]
stable-ruby-versions: |
exclude: [head]

Expand Down
97 changes: 91 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ gemspec
group :development do
gem "rake"
gem "rake-compiler"
gem "rb_sys", "0.9.85"
gem "rb_sys", "0.9.97"
end

group :check do
Expand Down
4 changes: 3 additions & 1 deletion ext/ruby_wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ publish = false
crate-type = ["cdylib"]

[dependencies]
magnus = "0.6.2"
magnus = { version = "0.6.2", features = ["bytes"] }
bytes = "1"
wizer = "4.0.0"
wasi-vfs-cli = { git = "https://github.com/kateinoigakukun/wasi-vfs/", tag = "0.5.2" }
structopt = "0.3.26"
wit-component = "0.203.0"
97 changes: 93 additions & 4 deletions ext/ruby_wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ use magnus::{
value::{self, InnerValue},
wrap, Error, ExceptionClass, RModule, Ruby,
};
use wizer::Wizer;
use structopt::StructOpt;
use wizer::Wizer;

static RUBY_WASM: value::Lazy<RModule> =
value::Lazy::new(|ruby| ruby.define_module("RubyWasmExt").unwrap());

fn preinit(core_module: Vec<u8>) -> Result<Vec<u8>, Error> {
fn preinit(core_module: bytes::Bytes) -> Result<bytes::Bytes, Error> {
let rbwasm_error = eval("RubyWasmExt::Error")?;
let rbwasm_error = ExceptionClass::from_value(rbwasm_error).unwrap();
let mut wizer = Wizer::new();
Expand All @@ -26,6 +26,7 @@ fn preinit(core_module: Vec<u8>) -> Result<Vec<u8>, Error> {
wizer
.run(&core_module)
.map_err(|e| Error::new(rbwasm_error, format!("failed to run wizer: {}", e)))
.map(|output| output.into())
}

struct WasiVfsInner {
Expand Down Expand Up @@ -53,14 +54,91 @@ impl WasiVfs {
self.0.borrow_mut().map_dirs.push((guest_dir.into(), host_dir.into()));
}

fn pack(&self, wasm_bytes: Vec<u8>) -> Result<Vec<u8>, Error> {
fn pack(&self, wasm_bytes: bytes::Bytes) -> Result<bytes::Bytes, Error> {
let output_bytes = wasi_vfs_cli::pack(&wasm_bytes, self.0.borrow().map_dirs.clone()).map_err(|e| {
Error::new(
exception::standard_error(),
format!("failed to pack wasi vfs: {}", e),
)
})?;
Ok(output_bytes)
Ok(output_bytes.into())
}
}

#[wrap(class = "RubyWasmExt::ComponentLink")]
struct ComponentLink(std::cell::RefCell<Option<wit_component::Linker>>);

impl ComponentLink {
fn new() -> Self {
Self(std::cell::RefCell::new(Some(wit_component::Linker::default())))
}
fn linker(&self, body: impl FnOnce(wit_component::Linker) -> Result<wit_component::Linker, Error>) -> Result<(), Error> {
let mut linker = self.0.take().ok_or_else(|| {
Error::new(
exception::standard_error(),
"linker is already consumed".to_string(),
)
})?;
linker = body(linker)?;
self.0.replace(Some(linker));
Ok(())
}

fn library(&self, name: String, module: bytes::Bytes, dl_openable: bool) -> Result<(), Error> {
self.linker(|linker| {
linker.library(&name, &module, dl_openable).map_err(|e| {
Error::new(
exception::standard_error(),
format!("failed to link library: {}", e),
)
})
})
}
fn adapter(&self, name: String, module: bytes::Bytes) -> Result<(), Error> {
self.linker(|linker| {
linker.adapter(&name, &module).map_err(|e| {
Error::new(
exception::standard_error(),
format!("failed to link adapter: {}", e),
)
})
})
}
fn validate(&self, validate: bool) -> Result<(), Error> {
self.linker(|linker| {
Ok(linker.validate(validate))
})
}
fn stack_size(&self, size: u32) -> Result<(), Error> {
self.linker(|linker| {
Ok(linker.stack_size(size))
})
}
fn stub_missing_functions(&self, stub: bool) -> Result<(), Error> {
self.linker(|linker| {
Ok(linker.stub_missing_functions(stub))
})
}
fn use_built_in_libdl(&self, use_libdl: bool) -> Result<(), Error> {
self.linker(|linker| {
Ok(linker.use_built_in_libdl(use_libdl))
})
}
fn encode(&self) -> Result<bytes::Bytes, Error> {
// Take the linker out of the cell and consume it
let linker = self.0.borrow_mut().take().ok_or_else(|| {
Error::new(
exception::standard_error(),
"linker is already consumed".to_string(),
)
})?;
let encoded = linker.encode().map_err(|e| {
Error::new(
exception::standard_error(),
format!("failed to encode linker: {}", e),
)
})?;
Ok(encoded.into())
}
}

Expand All @@ -76,5 +154,16 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
wasi_vfs.define_singleton_method("run_cli", function!(WasiVfs::run_cli, 1))?;
wasi_vfs.define_method("map_dir", method!(WasiVfs::map_dir, 2))?;
wasi_vfs.define_method("pack", method!(WasiVfs::pack, 1))?;

let component_link = module.define_class("ComponentLink", ruby.class_object())?;
component_link.define_singleton_method("new", function!(ComponentLink::new, 0))?;
component_link.define_method("library", method!(ComponentLink::library, 3))?;
component_link.define_method("adapter", method!(ComponentLink::adapter, 2))?;
component_link.define_method("validate", method!(ComponentLink::validate, 1))?;
component_link.define_method("stack_size", method!(ComponentLink::stack_size, 1))?;
component_link.define_method("stub_missing_functions", method!(ComponentLink::stub_missing_functions, 1))?;
component_link.define_method("use_built_in_libdl", method!(ComponentLink::use_built_in_libdl, 1))?;
component_link.define_method("encode", method!(ComponentLink::encode, 0))?;

Ok(())
}
1 change: 1 addition & 0 deletions lib/ruby_wasm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require_relative "ruby_wasm/util"
require_relative "ruby_wasm/build"
require_relative "ruby_wasm/packager"
require_relative "ruby_wasm/packager/component_adapter"
require_relative "ruby_wasm/packager/file_system"
require_relative "ruby_wasm/packager/core"

Expand Down
Loading