Skip to content
Closed
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
4 changes: 4 additions & 0 deletions demo/caller/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ publish = false

[dependencies]
wa-demo = { path = "../wa" }

[features]
feat_a = ["wa-demo/feat_a"]
feat_b = ["wa-demo/feat_b"]
9 changes: 8 additions & 1 deletion demo/impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ pub extern "C" fn demo(input: TokenStream) -> TokenStream {
};

let ident = input.ident;
let message = format!("Hello from WASM! My name is {}.", ident);
let mut message = format!("Hello from WASM! My name is {}.", ident);

if proc_macro2::features::check("feat_a") {
message.push_str(" feat_a is enabled.");
}
if proc_macro2::features::check("feat_b") {
message.push_str(" feat_b is enabled.");
}

quote! {
impl #ident {
Expand Down
4 changes: 4 additions & 0 deletions demo/wa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ proc-macro = true

[dependencies]
watt = "0.3"

[features]
feat_a = []
feat_b = []
2 changes: 1 addition & 1 deletion demo/wa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ extern crate proc_macro;
use proc_macro::TokenStream;
use watt::WasmMacro;

static MACRO: WasmMacro = WasmMacro::new(WASM);
static MACRO: WasmMacro = watt::wasm_macro_features!(WASM; "feat_a", "feat_b");
static WASM: &[u8] = include_bytes! {
"../../impl/target/wasm32-unknown-unknown/release/watt_demo.wasm"
};
Expand Down
30 changes: 30 additions & 0 deletions proc-macro/src/features.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
static mut ENABLED_FEATURES: Vec<u32> = Vec::new();

#[no_mangle]
#[doc(hidden)]
// FIXME: This is horribly unsafe (static mut) but should be ok because unthreaded wasm
/// SAFETY: only call this when no other code in the wasm module is running
pub unsafe extern "C" fn __watt_publish_feature(feature: u32) {
if let Err(i) = ENABLED_FEATURES.binary_search(&feature) {
ENABLED_FEATURES.insert(i, feature);
}
}

pub fn check(feature: &'static str) -> bool {
/// A simple 32 bit FNV hash function. Must be the same one as used on the proc-macro side.
///
/// <https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function>
/// <http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-1a>
fn fnv1a(bytes: &[u8]) -> u32 {
const FNV_OFFSET: u32 = 0x811C_9DC5;
const FNV_PRIME: u32 = 0x100_0193;
let mut hash = FNV_OFFSET;
for &byte in bytes {
hash ^= byte as u32;
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}
let feature_hash = fnv1a(feature.as_bytes());
unsafe { ENABLED_FEATURES.binary_search(&feature_hash).is_ok() }
}
3 changes: 3 additions & 0 deletions proc-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ mod encode;
mod ffi;
mod rc;

// FIXME: this is just hacked onto watt proc-macro shim because it's necessarily already in the wasm
pub mod features;

use crate::rc::Rc;
use std::char;
use std::cmp::Ordering;
Expand Down
11 changes: 11 additions & 0 deletions src/features.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#[macro_export]
macro_rules! wasm_macro_features {
($wasm:expr; $($feature:expr),* $(,)?) => {
$crate::WasmMacro::new_with_features(
$wasm,
&[$(
#[cfg(feature = $feature)] $feature,
)*]
)
};
}
63 changes: 52 additions & 11 deletions src/interpret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ std::thread_local! {
impl ThreadState {
pub fn instance(&mut self, instance: &WasmMacro) -> &ModuleInst {
let id = instance.id();
let entry = match self.instances.entry(id) {
let ThreadState { store, instances } = self;
let entry = match instances.entry(id) {
Entry::Occupied(e) => return e.into_mut(),
Entry::Vacant(v) => v,
};
Expand All @@ -37,10 +38,43 @@ impl ThreadState {
let module = decode_module(cursor).unwrap();
#[cfg(watt_debug)]
print_module(&module);
let extern_vals = extern_vals(&module, &mut self.store);
let module_instance = instantiate_module(&mut self.store, module, &extern_vals).unwrap();
entry.insert(module_instance)
let extern_vals = extern_vals(&module, store);
let module_instance = instantiate_module(store, module, &extern_vals).unwrap();
let module = entry.insert(module_instance);

for feature in instance.features {
publish_feature(feature, store, module)
}

module
}
}

fn publish_feature(feature: &str, store: &mut Store, instance: &ModuleInst) {
/// A simple 32 bit FNV hash function. Must be the same one as used on the wasm side.
///
/// <https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function>
/// <http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-1a>
fn fnv1a(bytes: &[u8]) -> u32 {
const FNV_OFFSET: u32 = 0x811C_9DC5;
const FNV_PRIME: u32 = 0x100_0193;
let mut hash = FNV_OFFSET;
for &byte in bytes {
hash ^= byte as u32;
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}

// FIXME: guard against collisions
let feature_hash = fnv1a(feature.as_bytes());
let fn_publish_feature = match get_export(instance, "__watt_publish_feature") {
Ok(ExternVal::Func(f)) => f,
_ => unimplemented!("__watt_publish_feature not found"),
};
let arg = Value::I32(feature_hash);

call(store, fn_publish_feature, vec![arg]);
}

pub fn proc_macro(fun: &str, inputs: Vec<TokenStream>, instance: &WasmMacro) -> TokenStream {
Expand All @@ -59,10 +93,14 @@ pub fn proc_macro(fun: &str, inputs: Vec<TokenStream>, instance: &WasmMacro) ->

let args: Vec<Value> = raws
.into_iter()
.map(|raw| call(state, exports.raw_to_token_stream, vec![raw]))
.map(|raw| call(&mut state.store, exports.raw_to_token_stream, vec![raw]).unwrap())
.collect();
let output = call(state, exports.main, args);
let raw = call(state, exports.token_stream_into_raw, vec![output]);
let output = call(&mut state.store, exports.main, args).unwrap();
let raw = call(
&mut state.store,
exports.token_stream_into_raw,
vec![output],
).unwrap();
let handle = match raw {
Value::I32(handle) => handle,
_ => unimplemented!("unexpected macro return type"),
Expand Down Expand Up @@ -99,11 +137,14 @@ impl Exports {
}
}

fn call(state: &mut ThreadState, func: FuncAddr, args: Vec<Value>) -> Value {
match invoke_func(&mut state.store, func, args) {
fn call(store: &mut Store, func: FuncAddr, args: Vec<Value>) -> Option<Value> {
match invoke_func(store, func, args) {
Ok(ret) => {
assert_eq!(ret.len(), 1);
ret.into_iter().next().unwrap()
match ret.len() {
0 => return None,
1 => ret.into_iter().next(),
_ => panic!("wasm returned more values than expected")
}
}
Err(err) => panic!("{:?}", err),
}
Expand Down
12 changes: 12 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ mod runtime;
mod data;
mod decode;
mod encode;
mod features;
mod import;
mod sym;

Expand All @@ -218,6 +219,7 @@ use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
pub struct WasmMacro {
wasm: &'static [u8],
id: AtomicUsize,
features: &'static [&'static str],
}

impl WasmMacro {
Expand All @@ -232,8 +234,18 @@ impl WasmMacro {
/// # };
/// ```
pub const fn new(wasm: &'static [u8]) -> WasmMacro {
const EMPTY: &[&str] = &[];
WasmMacro::new_with_features(wasm, EMPTY)
}

#[doc(hidden)]
pub const fn new_with_features(
wasm: &'static [u8],
features: &'static [&'static str],
) -> WasmMacro {
WasmMacro {
wasm,
features,
id: AtomicUsize::new(0),
}
}
Expand Down