mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-05-04 18:12:16 +00:00
Merge pull request #1057 from alexcrichton/start
Add a `#[wasm_bindgen(start)]` attribute
This commit is contained in:
commit
ed24a63aae
@ -46,6 +46,9 @@ pub struct Export {
|
||||
pub comments: Vec<String>,
|
||||
/// The name of the rust function/method on the rust side.
|
||||
pub rust_name: Ident,
|
||||
/// Whether or not this function should be flagged as the wasm start
|
||||
/// function.
|
||||
pub start: bool,
|
||||
}
|
||||
|
||||
/// The 3 types variations of `self`.
|
||||
|
@ -450,12 +450,21 @@ impl TryToTokens for ast::Export {
|
||||
let argtys = self.function.arguments.iter().map(|arg| &arg.ty);
|
||||
let attrs = &self.function.rust_attrs;
|
||||
|
||||
let start_check = if self.start {
|
||||
quote! {
|
||||
const _ASSERT: fn() = || #ret_ty { loop {} };
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
|
||||
(quote! {
|
||||
#(#attrs)*
|
||||
#[export_name = #export_name]
|
||||
#[allow(non_snake_case)]
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
||||
pub extern "C" fn #generated_name(#(#args),*) #ret_ty {
|
||||
#start_check
|
||||
// Scope all local variables to be destroyed after we call the
|
||||
// function to ensure that `#convert_ret`, if it panics, doesn't
|
||||
// leak anything.
|
||||
|
@ -82,6 +82,7 @@ fn shared_export<'a>(export: &'a ast::Export, intern: &'a Interner) -> Export<'a
|
||||
is_constructor: export.is_constructor,
|
||||
function: shared_function(&export.function, intern),
|
||||
comments: export.comments.iter().map(|s| &**s).collect(),
|
||||
start: export.start,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ use parity_wasm::elements::*;
|
||||
use descriptor::Descriptor;
|
||||
use js::js2rust::Js2Rust;
|
||||
use js::Context;
|
||||
use wasm_utils::Remap;
|
||||
|
||||
pub fn rewrite(input: &mut Context) -> Result<(), Error> {
|
||||
let info = ClosureDescriptors::new(input);
|
||||
@ -37,15 +38,21 @@ pub fn rewrite(input: &mut Context) -> Result<(), Error> {
|
||||
// function indices. We're going to be injecting a few imported functions
|
||||
// below which will shift the index space for all defined functions.
|
||||
input.parse_wasm_names();
|
||||
Remap {
|
||||
code_idx_to_descriptor: &info.code_idx_to_descriptor,
|
||||
old_num_imports: input
|
||||
.module
|
||||
.import_section()
|
||||
.map(|s| s.functions())
|
||||
.unwrap_or(0) as u32,
|
||||
}
|
||||
.remap_module(input.module);
|
||||
let old_num_imports = input
|
||||
.module
|
||||
.import_section()
|
||||
.map(|s| s.functions())
|
||||
.unwrap_or(0) as u32;
|
||||
Remap(|idx| {
|
||||
// If this was an imported function we didn't reorder those, so nothing
|
||||
// to do.
|
||||
if idx < old_num_imports {
|
||||
return idx
|
||||
}
|
||||
// ... otherwise we're injecting a number of new imports, so offset
|
||||
// everything.
|
||||
idx + info.code_idx_to_descriptor.len() as u32
|
||||
}).remap_module(input.module);
|
||||
|
||||
info.delete_function_table_entries(input);
|
||||
info.inject_imports(input)?;
|
||||
@ -298,119 +305,3 @@ impl ClosureDescriptors {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Remap<'a> {
|
||||
code_idx_to_descriptor: &'a BTreeMap<u32, DescribeInstruction>,
|
||||
old_num_imports: u32,
|
||||
}
|
||||
|
||||
impl<'a> Remap<'a> {
|
||||
fn remap_module(&self, module: &mut Module) {
|
||||
for section in module.sections_mut() {
|
||||
match section {
|
||||
Section::Export(e) => self.remap_export_section(e),
|
||||
Section::Element(e) => self.remap_element_section(e),
|
||||
Section::Code(e) => self.remap_code_section(e),
|
||||
Section::Start(i) => {
|
||||
self.remap_idx(i);
|
||||
}
|
||||
Section::Name(n) => self.remap_name_section(n),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_export_section(&self, section: &mut ExportSection) {
|
||||
for entry in section.entries_mut() {
|
||||
self.remap_export_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_export_entry(&self, entry: &mut ExportEntry) {
|
||||
match entry.internal_mut() {
|
||||
Internal::Function(i) => {
|
||||
self.remap_idx(i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_element_section(&self, section: &mut ElementSection) {
|
||||
for entry in section.entries_mut() {
|
||||
self.remap_element_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_element_entry(&self, entry: &mut ElementSegment) {
|
||||
for member in entry.members_mut() {
|
||||
self.remap_idx(member);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_code_section(&self, section: &mut CodeSection) {
|
||||
for body in section.bodies_mut() {
|
||||
self.remap_func_body(body);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_func_body(&self, body: &mut FuncBody) {
|
||||
self.remap_instructions(body.code_mut());
|
||||
}
|
||||
|
||||
fn remap_instructions(&self, code: &mut Instructions) {
|
||||
for instr in code.elements_mut() {
|
||||
self.remap_instruction(instr);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_instruction(&self, instr: &mut Instruction) {
|
||||
match instr {
|
||||
Instruction::Call(i) => {
|
||||
self.remap_idx(i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_name_section(&self, names: &mut NameSection) {
|
||||
match names {
|
||||
NameSection::Function(f) => self.remap_function_name_section(f),
|
||||
NameSection::Local(f) => self.remap_local_name_section(f),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_function_name_section(&self, names: &mut FunctionNameSection) {
|
||||
let map = names.names_mut();
|
||||
let new = IndexMap::with_capacity(map.len());
|
||||
for (mut idx, name) in mem::replace(map, new) {
|
||||
if !self.remap_idx(&mut idx) {
|
||||
map.insert(idx, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_local_name_section(&self, names: &mut LocalNameSection) {
|
||||
let map = names.local_names_mut();
|
||||
let new = IndexMap::with_capacity(map.len());
|
||||
for (mut idx, name) in mem::replace(map, new) {
|
||||
if !self.remap_idx(&mut idx) {
|
||||
map.insert(idx, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether `idx` pointed to a previously known descriptor function
|
||||
/// that we're switching to an import
|
||||
fn remap_idx(&self, idx: &mut u32) -> bool {
|
||||
// If this was an imported function we didn't reorder those, so nothing
|
||||
// to do.
|
||||
if *idx < self.old_num_imports {
|
||||
return false;
|
||||
}
|
||||
// ... otherwise we're injecting a number of new imports, so offset
|
||||
// everything.
|
||||
*idx += self.code_idx_to_descriptor.len() as u32;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ use shared;
|
||||
use super::Bindgen;
|
||||
use descriptor::{Descriptor, VectorKind};
|
||||
use wasm_interpreter::Interpreter;
|
||||
use wasm_utils::Remap;
|
||||
|
||||
mod js2rust;
|
||||
use self::js2rust::Js2Rust;
|
||||
@ -30,6 +31,7 @@ pub struct Context<'a> {
|
||||
pub imported_statics: HashSet<&'a str>,
|
||||
pub config: &'a Bindgen,
|
||||
pub module: &'a mut Module,
|
||||
pub start: Option<String>,
|
||||
|
||||
/// A map which maintains a list of what identifiers we've imported and what
|
||||
/// they're named locally.
|
||||
@ -163,7 +165,6 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
|
||||
pub fn finalize(&mut self, module_name: &str) -> Result<(String, String), Error> {
|
||||
self.parse_wasm_names();
|
||||
self.write_classes()?;
|
||||
|
||||
self.bind("__wbindgen_object_clone_ref", &|me| {
|
||||
@ -460,6 +461,37 @@ impl<'a> Context<'a> {
|
||||
|
||||
self.unexport_unused_internal_exports();
|
||||
closures::rewrite(self)?;
|
||||
|
||||
// Handle the `start` function, if one was specified. If we're in a
|
||||
// --test mode (such as wasm-bindgen-test-runner) then we skip this
|
||||
// entirely. Otherwise we want to first add a start function to the
|
||||
// `start` section if one is specified.
|
||||
//
|
||||
// Afterwards, we need to perform what's a bit of a hack. Right after we
|
||||
// added the start function, we remove it again because no current
|
||||
// strategy for bundlers and deployment works well enough with it. For
|
||||
// `--no-modules` output we need to be sure to call the start function
|
||||
// after our exports are wired up (or most imported functions won't
|
||||
// work).
|
||||
//
|
||||
// For ESM outputs bundlers like webpack also don't work because
|
||||
// currently they run the wasm initialization before the JS glue
|
||||
// initialization, meaning that if the wasm start function calls
|
||||
// imported functions the JS glue isn't ready to go just yet.
|
||||
//
|
||||
// To handle `--no-modules` we just unstart the start function and call
|
||||
// it manually. To handle the ESM use case we switch the start function
|
||||
// to calling an imported function which defers the start function via
|
||||
// `Promise.resolve().then(...)` to execute on the next microtask tick.
|
||||
let mut has_start_function = false;
|
||||
if self.config.emit_start {
|
||||
self.add_start_function()?;
|
||||
has_start_function = self.unstart_start_function();
|
||||
if has_start_function && !self.config.no_modules {
|
||||
self.inject_start_shim();
|
||||
}
|
||||
}
|
||||
|
||||
self.gc();
|
||||
|
||||
// Note that it's important `throw` comes last *after* we gc. The
|
||||
@ -537,6 +569,7 @@ impl<'a> Context<'a> {
|
||||
init.__wbindgen_wasm_instance = instance;
|
||||
init.__wbindgen_wasm_module = module;
|
||||
init.__wbindgen_wasm_memory = __exports.memory;
|
||||
{start}
|
||||
}});
|
||||
}};
|
||||
self.{global_name} = Object.assign(init, __exports);
|
||||
@ -550,6 +583,11 @@ impl<'a> Context<'a> {
|
||||
.map(|s| &**s)
|
||||
.unwrap_or("wasm_bindgen"),
|
||||
init_memory = memory,
|
||||
start = if has_start_function {
|
||||
"wasm.__wbindgen_start();"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
)
|
||||
} else if self.config.no_modules {
|
||||
format!(
|
||||
@ -578,7 +616,7 @@ impl<'a> Context<'a> {
|
||||
}}
|
||||
return instantiation.then(({{instance}}) => {{
|
||||
wasm = init.wasm = instance.exports;
|
||||
return;
|
||||
{start}
|
||||
}});
|
||||
}};
|
||||
self.{global_name} = Object.assign(init, __exports);
|
||||
@ -591,6 +629,11 @@ impl<'a> Context<'a> {
|
||||
.as_ref()
|
||||
.map(|s| &**s)
|
||||
.unwrap_or("wasm_bindgen"),
|
||||
start = if has_start_function {
|
||||
"wasm.__wbindgen_start();"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
)
|
||||
} else {
|
||||
let import_wasm = if self.globals.len() == 0 {
|
||||
@ -1774,7 +1817,7 @@ impl<'a> Context<'a> {
|
||||
.run(&mut self.module);
|
||||
}
|
||||
|
||||
fn parse_wasm_names(&mut self) {
|
||||
pub fn parse_wasm_names(&mut self) {
|
||||
let module = mem::replace(self.module, Module::default());
|
||||
let module = module.parse_names().unwrap_or_else(|p| p.1);
|
||||
*self.module = module;
|
||||
@ -2148,6 +2191,128 @@ impl<'a> Context<'a> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn add_start_function(&mut self) -> Result<(), Error> {
|
||||
let start = match &self.start {
|
||||
Some(name) => name.clone(),
|
||||
None => return Ok(()),
|
||||
};
|
||||
let idx = {
|
||||
let exports = self.module.export_section()
|
||||
.ok_or_else(|| format_err!("no export section found"))?;
|
||||
let entry = exports
|
||||
.entries()
|
||||
.iter()
|
||||
.find(|e| e.field() == start)
|
||||
.ok_or_else(|| format_err!("export `{}` not found", start))?;
|
||||
match entry.internal() {
|
||||
Internal::Function(i) => *i,
|
||||
_ => bail!("export `{}` wasn't a function", start),
|
||||
}
|
||||
};
|
||||
if let Some(prev_start) = self.module.start_section() {
|
||||
if let Some(NameSection::Function(n)) = self.module.names_section() {
|
||||
if let Some(prev) = n.names().get(prev_start) {
|
||||
bail!("cannot flag `{}` as start function as `{}` is \
|
||||
already the start function", start, prev);
|
||||
}
|
||||
}
|
||||
|
||||
bail!("cannot flag `{}` as start function as another \
|
||||
function is already the start function", start);
|
||||
}
|
||||
|
||||
self.set_start_section(idx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_start_section(&mut self, start: u32) {
|
||||
let mut pos = None;
|
||||
// See http://webassembly.github.io/spec/core/binary/modules.html#binary-module
|
||||
// for section ordering
|
||||
for (i, section) in self.module.sections().iter().enumerate() {
|
||||
match section {
|
||||
Section::Type(_) |
|
||||
Section::Import(_) |
|
||||
Section::Function(_) |
|
||||
Section::Table(_) |
|
||||
Section::Memory(_) |
|
||||
Section::Global(_) |
|
||||
Section::Export(_) => continue,
|
||||
_ => {
|
||||
pos = Some(i);
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
let pos = pos.unwrap_or(self.module.sections().len() - 1);
|
||||
self.module.sections_mut().insert(pos, Section::Start(start));
|
||||
}
|
||||
|
||||
/// If a start function is present, it removes it from the `start` section
|
||||
/// of the wasm module and then moves it to an exported function, named
|
||||
/// `__wbindgen_start`.
|
||||
fn unstart_start_function(&mut self) -> bool {
|
||||
let mut pos = None;
|
||||
let mut start = 0;
|
||||
for (i, section) in self.module.sections().iter().enumerate() {
|
||||
if let Section::Start(idx) = section {
|
||||
start = *idx;
|
||||
pos = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
match pos {
|
||||
Some(i) => {
|
||||
self.module.sections_mut().remove(i);
|
||||
let entry = ExportEntry::new(
|
||||
"__wbindgen_start".to_string(),
|
||||
Internal::Function(start),
|
||||
);
|
||||
self.module.export_section_mut().unwrap().entries_mut().push(entry);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Injects a `start` function into the wasm module. This start function
|
||||
/// calls a shim in the generated JS which defers the actual start function
|
||||
/// to the next microtask tick of the event queue.
|
||||
///
|
||||
/// See docs above at callsite for why this happens.
|
||||
fn inject_start_shim(&mut self) {
|
||||
let body = "function() {
|
||||
Promise.resolve().then(() => wasm.__wbindgen_start());
|
||||
}";
|
||||
self.export("__wbindgen_defer_start", body, None);
|
||||
|
||||
let imports = self.module.import_section()
|
||||
.map(|s| s.functions() as u32)
|
||||
.unwrap_or(0);
|
||||
Remap(|idx| {
|
||||
if idx < imports { idx } else { idx + 1 }
|
||||
}).remap_module(self.module);
|
||||
|
||||
let type_idx = {
|
||||
let types = self.module.type_section_mut().unwrap();
|
||||
let ty = Type::Function(FunctionType::new(Vec::new(), None));
|
||||
types.types_mut().push(ty);
|
||||
(types.types_mut().len() - 1) as u32
|
||||
};
|
||||
|
||||
let entry = ImportEntry::new(
|
||||
"__wbindgen_placeholder__".to_string(),
|
||||
"__wbindgen_defer_start".to_string(),
|
||||
External::Function(type_idx),
|
||||
);
|
||||
self.module
|
||||
.import_section_mut()
|
||||
.unwrap()
|
||||
.entries_mut()
|
||||
.push(entry);
|
||||
self.set_start_section(imports);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> SubContext<'a, 'b> {
|
||||
@ -2184,6 +2349,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
|
||||
fn generate_export(&mut self, export: &decode::Export<'b>) -> Result<(), Error> {
|
||||
if let Some(ref class) = export.class {
|
||||
assert!(!export.start);
|
||||
return self.generate_export_for_class(class, export);
|
||||
}
|
||||
|
||||
@ -2192,6 +2358,10 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
Some(d) => d,
|
||||
};
|
||||
|
||||
if export.start {
|
||||
self.set_start_function(export.function.name)?;
|
||||
}
|
||||
|
||||
let (js, ts, js_doc) = Js2Rust::new(&export.function.name, self.cx)
|
||||
.process(descriptor.unwrap_function())?
|
||||
.finish("function", &format!("wasm.{}", export.function.name));
|
||||
@ -2207,6 +2377,15 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_start_function(&mut self, start: &str) -> Result<(), Error> {
|
||||
if let Some(prev) = &self.cx.start {
|
||||
bail!("cannot flag `{}` as start function as `{}` is \
|
||||
already the start function", start, prev);
|
||||
}
|
||||
self.cx.start = Some(start.to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_export_for_class(
|
||||
&mut self,
|
||||
class_name: &'b str,
|
||||
|
@ -22,6 +22,7 @@ use parity_wasm::elements::*;
|
||||
mod decode;
|
||||
mod descriptor;
|
||||
mod js;
|
||||
mod wasm_utils;
|
||||
pub mod wasm2es6js;
|
||||
|
||||
pub struct Bindgen {
|
||||
@ -36,6 +37,7 @@ pub struct Bindgen {
|
||||
demangle: bool,
|
||||
keep_debug: bool,
|
||||
remove_name_section: bool,
|
||||
emit_start: bool,
|
||||
// Experimental support for `WeakRefGroup`, an upcoming ECMAScript feature.
|
||||
// Currently only enable-able through an env var.
|
||||
weak_refs: bool,
|
||||
@ -64,6 +66,7 @@ impl Bindgen {
|
||||
demangle: true,
|
||||
keep_debug: false,
|
||||
remove_name_section: false,
|
||||
emit_start: true,
|
||||
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
|
||||
threads: threads_config(),
|
||||
}
|
||||
@ -131,6 +134,11 @@ impl Bindgen {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn emit_start(&mut self, emit: bool) -> &mut Bindgen {
|
||||
self.emit_start = emit;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn generate<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
|
||||
self._generate(path.as_ref())
|
||||
}
|
||||
@ -195,7 +203,9 @@ impl Bindgen {
|
||||
imported_functions: Default::default(),
|
||||
imported_statics: Default::default(),
|
||||
direct_imports: Default::default(),
|
||||
start: None,
|
||||
};
|
||||
cx.parse_wasm_names();
|
||||
for program in programs.iter() {
|
||||
js::SubContext {
|
||||
program,
|
||||
|
104
crates/cli-support/src/wasm_utils.rs
Normal file
104
crates/cli-support/src/wasm_utils.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use std::mem;
|
||||
|
||||
use parity_wasm::elements::*;
|
||||
|
||||
pub struct Remap<F>(pub F);
|
||||
|
||||
impl<F> Remap<F> where F: FnMut(u32) -> u32 {
|
||||
pub fn remap_module(&mut self, module: &mut Module) {
|
||||
for section in module.sections_mut() {
|
||||
match section {
|
||||
Section::Export(e) => self.remap_export_section(e),
|
||||
Section::Element(e) => self.remap_element_section(e),
|
||||
Section::Code(e) => self.remap_code_section(e),
|
||||
Section::Start(i) => {
|
||||
self.remap_idx(i);
|
||||
}
|
||||
Section::Name(n) => self.remap_name_section(n),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_export_section(&mut self, section: &mut ExportSection) {
|
||||
for entry in section.entries_mut() {
|
||||
self.remap_export_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_export_entry(&mut self, entry: &mut ExportEntry) {
|
||||
match entry.internal_mut() {
|
||||
Internal::Function(i) => {
|
||||
self.remap_idx(i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_element_section(&mut self, section: &mut ElementSection) {
|
||||
for entry in section.entries_mut() {
|
||||
self.remap_element_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_element_entry(&mut self, entry: &mut ElementSegment) {
|
||||
for member in entry.members_mut() {
|
||||
self.remap_idx(member);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_code_section(&mut self, section: &mut CodeSection) {
|
||||
for body in section.bodies_mut() {
|
||||
self.remap_func_body(body);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_func_body(&mut self, body: &mut FuncBody) {
|
||||
self.remap_instructions(body.code_mut());
|
||||
}
|
||||
|
||||
fn remap_instructions(&mut self, code: &mut Instructions) {
|
||||
for instr in code.elements_mut() {
|
||||
self.remap_instruction(instr);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_instruction(&mut self, instr: &mut Instruction) {
|
||||
match instr {
|
||||
Instruction::Call(i) => {
|
||||
self.remap_idx(i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_name_section(&mut self, names: &mut NameSection) {
|
||||
match names {
|
||||
NameSection::Function(f) => self.remap_function_name_section(f),
|
||||
NameSection::Local(f) => self.remap_local_name_section(f),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_function_name_section(&mut self, names: &mut FunctionNameSection) {
|
||||
let map = names.names_mut();
|
||||
let new = IndexMap::with_capacity(map.len());
|
||||
for (mut idx, name) in mem::replace(map, new) {
|
||||
self.remap_idx(&mut idx);
|
||||
map.insert(idx, name);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_local_name_section(&mut self, names: &mut LocalNameSection) {
|
||||
let map = names.local_names_mut();
|
||||
let new = IndexMap::with_capacity(map.len());
|
||||
for (mut idx, name) in mem::replace(map, new) {
|
||||
self.remap_idx(&mut idx);
|
||||
map.insert(idx, name);
|
||||
}
|
||||
}
|
||||
|
||||
fn remap_idx(&mut self, idx: &mut u32) {
|
||||
*idx = (self.0)(*idx);
|
||||
}
|
||||
}
|
@ -132,6 +132,7 @@ fn rmain() -> Result<(), Error> {
|
||||
.nodejs(node)
|
||||
.input_module(module, wasm)
|
||||
.keep_debug(false)
|
||||
.emit_start(false)
|
||||
.generate(&tmpdir)
|
||||
.context("executing `wasm-bindgen` over the wasm file")?;
|
||||
shell.clear();
|
||||
|
@ -47,6 +47,7 @@ macro_rules! attrgen {
|
||||
(vendor_prefix, VendorPrefix(Span, Ident)),
|
||||
(variadic, Variadic(Span)),
|
||||
(typescript_custom_section, TypescriptCustomSection(Span)),
|
||||
(start, Start(Span)),
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -730,6 +731,20 @@ impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
|
||||
let comments = extract_doc_comments(&f.attrs);
|
||||
f.to_tokens(tokens);
|
||||
let opts = opts.unwrap_or_default();
|
||||
if opts.start().is_some() {
|
||||
if f.decl.generics.params.len() > 0 {
|
||||
bail_span!(
|
||||
&f.decl.generics,
|
||||
"the start function cannot have generics",
|
||||
);
|
||||
}
|
||||
if f.decl.inputs.len() > 0 {
|
||||
bail_span!(
|
||||
&f.decl.inputs,
|
||||
"the start function cannot have arguments",
|
||||
);
|
||||
}
|
||||
}
|
||||
program.exports.push(ast::Export {
|
||||
rust_class: None,
|
||||
js_class: None,
|
||||
@ -737,6 +752,7 @@ impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
|
||||
is_constructor: false,
|
||||
comments,
|
||||
rust_name: f.ident.clone(),
|
||||
start: opts.start().is_some(),
|
||||
function: f.convert(opts)?,
|
||||
});
|
||||
}
|
||||
@ -898,6 +914,7 @@ impl<'a, 'b> MacroParse<&'a BindgenAttrs> for (&'a Ident, &'b mut syn::ImplItem)
|
||||
is_constructor,
|
||||
function,
|
||||
comments,
|
||||
start: false,
|
||||
rust_name: method.sig.ident.clone(),
|
||||
});
|
||||
opts.check_used()?;
|
||||
|
12
crates/macro/ui-tests/start-function.rs
Normal file
12
crates/macro/ui-tests/start-function.rs
Normal file
@ -0,0 +1,12 @@
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn foo() {}
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn foo2(x: u32) {}
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn foo3<T>() {}
|
14
crates/macro/ui-tests/start-function.stderr
Normal file
14
crates/macro/ui-tests/start-function.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error: the start function cannot have arguments
|
||||
--> $DIR/start-function.rs:9:13
|
||||
|
|
||||
9 | pub fn foo2(x: u32) {}
|
||||
| ^^^^^^
|
||||
|
||||
error: the start function cannot have generics
|
||||
--> $DIR/start-function.rs:12:12
|
||||
|
|
||||
12 | pub fn foo3<T>() {}
|
||||
| ^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -84,6 +84,7 @@ macro_rules! shared_api {
|
||||
is_constructor: bool,
|
||||
function: Function<'a>,
|
||||
comments: Vec<&'a str>,
|
||||
start: bool,
|
||||
}
|
||||
|
||||
struct Enum<'a> {
|
||||
|
@ -1,5 +1,4 @@
|
||||
// For more comments about what's going on here, check out the `hello_world`
|
||||
// example.
|
||||
import('./canvas')
|
||||
.then(canvas => canvas.draw())
|
||||
.catch(console.error);
|
||||
|
@ -6,8 +6,8 @@ use std::f64;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn draw() {
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() {
|
||||
let document = web_sys::window().unwrap().document().unwrap();
|
||||
let canvas = document.get_element_by_id("canvas").unwrap();
|
||||
let canvas: web_sys::HtmlCanvasElement = canvas
|
||||
|
@ -1,6 +1,4 @@
|
||||
// For more comments about what's going on here, check out the `hello_world`
|
||||
// example
|
||||
const rust = import('./closures');
|
||||
rust
|
||||
.then(m => m.run())
|
||||
import('./closures')
|
||||
.catch(console.error);
|
||||
|
@ -7,7 +7,7 @@ use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{Document, Element, HtmlElement, Window};
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() -> Result<(), JsValue> {
|
||||
let window = web_sys::window().expect("should have a window in this context");
|
||||
let document = window.document().expect("window should have a document");
|
||||
|
@ -1,7 +1,4 @@
|
||||
// For more comments about what's going on here, check out the `hello_world`
|
||||
// example
|
||||
const rust = import('./console_log');
|
||||
|
||||
rust
|
||||
.then(m => m.run())
|
||||
import('./console_log')
|
||||
.catch(console.error);
|
||||
|
@ -3,7 +3,7 @@ extern crate web_sys;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
bare_bones();
|
||||
using_a_macro();
|
||||
|
@ -1,6 +1,4 @@
|
||||
// For more comments about what's going on here, check out the `hello_world`
|
||||
// example
|
||||
const rust = import('./dom');
|
||||
rust
|
||||
.then(m => m.run())
|
||||
import('./dom')
|
||||
.catch(console.error);
|
||||
|
@ -4,7 +4,7 @@ extern crate web_sys;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// Called by our JS entry point to run the example
|
||||
#[wasm_bindgen]
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() -> Result<(), JsValue> {
|
||||
// Use `web_sys`'s global `window` function to get a handle on the global
|
||||
// window object.
|
||||
|
@ -1,7 +1,4 @@
|
||||
// For more comments about what's going on here, check out the `hello_world`
|
||||
// example
|
||||
const rust = import('./import_js');
|
||||
|
||||
rust
|
||||
.then(m => m.run())
|
||||
import('./import_js')
|
||||
.catch(console.error);
|
||||
|
@ -26,7 +26,7 @@ extern "C" {
|
||||
fn log(s: &str);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
log(&format!("Hello, {}!", name()));
|
||||
|
||||
|
@ -27,7 +27,6 @@
|
||||
// initialization and return to us a promise when it's done
|
||||
// also, we can use 'await' on the returned promise
|
||||
await wasm_bindgen('./no_modules_bg.wasm');
|
||||
wasm_bindgen.run();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
@ -4,8 +4,8 @@ extern crate web_sys;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// Called by our JS entry point to run the example
|
||||
#[wasm_bindgen]
|
||||
pub fn run() -> Result<(), JsValue> {
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn main() -> Result<(), JsValue> {
|
||||
// Use `web_sys`'s global `window` function to get a handle on the global
|
||||
// window object.
|
||||
let window = web_sys::window().expect("no global `window` exists");
|
||||
|
@ -1,5 +1,4 @@
|
||||
// For more comments about what's going on here, check out the `hello_world`
|
||||
// example.
|
||||
import('./wasm_bindgen_paint')
|
||||
.then(paint => paint.main())
|
||||
.catch(console.error);
|
||||
|
@ -7,8 +7,8 @@ use std::rc::Rc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn main() -> Result<(), JsValue> {
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() -> Result<(), JsValue> {
|
||||
let document = web_sys::window().unwrap().document().unwrap();
|
||||
let canvas = document
|
||||
.create_element("canvas")?
|
||||
|
@ -1,6 +1,4 @@
|
||||
// For more comments about what's going on here, check out the `hello_world`
|
||||
// example
|
||||
const rust = import('./performance');
|
||||
rust
|
||||
.then(m => m.run())
|
||||
import('./performance')
|
||||
.catch(console.error);
|
||||
|
@ -17,8 +17,7 @@ macro_rules! console_log {
|
||||
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||||
}
|
||||
|
||||
// Called by our JS entry point to run the example
|
||||
#[wasm_bindgen]
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
let window = web_sys::window().expect("should have a window in this context");
|
||||
let performance = window
|
||||
|
@ -1,6 +1,4 @@
|
||||
// For more comments about what's going on here, check out the `hello_world`
|
||||
// example
|
||||
const rust = import('./wasm_in_wasm');
|
||||
rust
|
||||
.then(m => m.run())
|
||||
import('./wasm_in_wasm')
|
||||
.catch(console.error);
|
||||
|
@ -18,7 +18,7 @@ macro_rules! console_log {
|
||||
|
||||
const WASM: &[u8] = include_bytes!("add.wasm");
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() -> Result<(), JsValue> {
|
||||
console_log!("instantiating a new wasm module directly");
|
||||
let my_memory = wasm_bindgen::memory()
|
||||
|
@ -1,5 +1,4 @@
|
||||
// For more comments about what's going on here, check out the `hello_world`
|
||||
// example.
|
||||
import('./webgl')
|
||||
.then(webgl => webgl.draw())
|
||||
.catch(console.error);
|
||||
|
@ -7,8 +7,8 @@ use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{WebGlProgram, WebGlRenderingContext, WebGlShader};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn draw() -> Result<(), JsValue> {
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() -> Result<(), JsValue> {
|
||||
let document = web_sys::window().unwrap().document().unwrap();
|
||||
let canvas = document.get_element_by_id("canvas").unwrap();
|
||||
let canvas: web_sys::HtmlCanvasElement = canvas.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||||
|
@ -76,6 +76,7 @@
|
||||
- [`constructor`](./reference/attributes/on-rust-exports/constructor.md)
|
||||
- [`js_name = Blah`](./reference/attributes/on-rust-exports/js_name.md)
|
||||
- [`readonly`](./reference/attributes/on-rust-exports/readonly.md)
|
||||
- [`start`](./reference/attributes/on-rust-exports/start.md)
|
||||
- [`typescript_custom_section`](./reference/attributes/on-rust-exports/typescript_custom_section.md)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
31
guide/src/reference/attributes/on-rust-exports/start.md
Normal file
31
guide/src/reference/attributes/on-rust-exports/start.md
Normal file
@ -0,0 +1,31 @@
|
||||
# `start`
|
||||
|
||||
When attached to a `pub` function this attribute will configure the `start`
|
||||
section of the wasm executable to be emitted, executing the tagged function as
|
||||
soon as the wasm module is instantiated.
|
||||
|
||||
```rust
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn main() {
|
||||
// executed automatically ...
|
||||
}
|
||||
```
|
||||
|
||||
The `start` section of the wasm executable will be configured to execute the
|
||||
`main` function here as soon as it can. Note that due to various practical
|
||||
limitations today the start section of the executable may not literally point to
|
||||
`main`, but the `main` function here should be started up automatically when the
|
||||
wasm module is loaded.
|
||||
|
||||
There's a few caveats to be aware of when using the `start` attribute:
|
||||
|
||||
* The `start` function must take no arguments and must either return `()` or
|
||||
`Result<(), JsValue>`
|
||||
* Only one `start` function can be placed into a module, including its
|
||||
dependencies. If more than one is specified then `wasm-bindgen` will fail when
|
||||
the CLI is run. It's recommended that only applications use this attribute.
|
||||
* The `start` function will not be executed when testing.
|
||||
* If you're experimenting with WebAssembly threads, the `start` function is
|
||||
executed *once per thread*, not once globally!
|
||||
* Note that the `start` function is relatively new, so if you find any bugs with
|
||||
it, please feel free to report an issue!
|
@ -10,6 +10,8 @@ extern crate wasm_bindgen_test_crate_b;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
pub mod api;
|
||||
pub mod char;
|
||||
pub mod classes;
|
||||
@ -36,3 +38,9 @@ pub mod u64;
|
||||
pub mod validate_prt;
|
||||
pub mod variadic;
|
||||
pub mod vendor_prefix;
|
||||
|
||||
// should not be executed
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn start() {
|
||||
panic!();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user