Merge pull request #1057 from alexcrichton/start

Add a `#[wasm_bindgen(start)]` attribute
This commit is contained in:
Alex Crichton 2018-11-29 00:11:43 -06:00 committed by GitHub
commit ed24a63aae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 430 additions and 167 deletions

View File

@ -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`.

View File

@ -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.

View File

@ -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,
}
}

View File

@ -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
}
}

View File

@ -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,

View File

@ -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,

View 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);
}
}

View File

@ -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();

View File

@ -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()?;

View 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>() {}

View 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

View File

@ -84,6 +84,7 @@ macro_rules! shared_api {
is_constructor: bool,
function: Function<'a>,
comments: Vec<&'a str>,
start: bool,
}
struct Enum<'a> {

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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");

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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.

View File

@ -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);

View File

@ -26,7 +26,7 @@ extern "C" {
fn log(s: &str);
}
#[wasm_bindgen]
#[wasm_bindgen(start)]
pub fn run() {
log(&format!("Hello, {}!", name()));

View File

@ -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>

View File

@ -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");

View File

@ -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);

View File

@ -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")?

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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()

View File

@ -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);

View File

@ -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>()?;

View File

@ -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)
--------------------------------------------------------------------------------

View 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!

View File

@ -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!();
}