mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-31 17:31:06 +00:00
Implement static
imports
This allows importing static objects like `document`, `window`, or an arbitrary JS object from a module
This commit is contained in:
parent
eebe23649a
commit
8e894fcfc5
crates
wasm-bindgen-cli-support/src
wasm-bindgen-macro/src
wasm-bindgen-shared/src
src
tests
@ -1044,9 +1044,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
for f in self.program.exports.iter() {
|
for f in self.program.exports.iter() {
|
||||||
self.generate_export(f);
|
self.generate_export(f);
|
||||||
}
|
}
|
||||||
for f in self.program.imports.iter() {
|
for f in self.program.imported_functions.iter() {
|
||||||
self.generate_import(f);
|
self.generate_import(f);
|
||||||
}
|
}
|
||||||
|
for f in self.program.imported_fields.iter() {
|
||||||
|
self.generate_import_field(f);
|
||||||
|
}
|
||||||
for e in self.program.enums.iter() {
|
for e in self.program.enums.iter() {
|
||||||
self.generate_enum(e);
|
self.generate_enum(e);
|
||||||
}
|
}
|
||||||
@ -1319,6 +1322,24 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
|||||||
(format!("{} {}", prefix, dst), format!("{} {}", prefix, dst_ts))
|
(format!("{} {}", prefix, dst), format!("{} {}", prefix, dst_ts))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn generate_import_field(&mut self, import: &shared::ImportField) {
|
||||||
|
let name = import.shim_name();
|
||||||
|
self.cx.imports_to_rewrite.insert(name.clone());
|
||||||
|
|
||||||
|
if let Some(ref module) = import.module {
|
||||||
|
self.cx.imports.push_str(&format!("
|
||||||
|
import {{ {} }} from '{}';
|
||||||
|
", import.name, module));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cx.expose_add_heap_object();
|
||||||
|
self.cx.globals.push_str(&format!("
|
||||||
|
export function {}() {{
|
||||||
|
return addHeapObject({});
|
||||||
|
}}
|
||||||
|
", name, import.name));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate_import(&mut self, import: &shared::Import) {
|
pub fn generate_import(&mut self, import: &shared::Import) {
|
||||||
if let Some(ref module) = import.module {
|
if let Some(ref module) = import.module {
|
||||||
let name_to_import = import.class.as_ref().unwrap_or(&import.function.name);
|
let name_to_import = import.class.as_ref().unwrap_or(&import.function.name);
|
||||||
|
@ -11,6 +11,7 @@ pub struct Program {
|
|||||||
pub enums: Vec<Enum>,
|
pub enums: Vec<Enum>,
|
||||||
pub imported_types: Vec<ImportedType>,
|
pub imported_types: Vec<ImportedType>,
|
||||||
pub structs: Vec<Struct>,
|
pub structs: Vec<Struct>,
|
||||||
|
pub imported_fields: Vec<ImportField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Export {
|
pub struct Export {
|
||||||
@ -62,6 +63,13 @@ pub struct ImportedType {
|
|||||||
pub name: syn::Ident,
|
pub name: syn::Ident,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ImportField {
|
||||||
|
pub vis: syn::Visibility,
|
||||||
|
pub ty: syn::Type,
|
||||||
|
pub module: Option<String>,
|
||||||
|
pub name: syn::Ident,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Type {
|
pub enum Type {
|
||||||
// special
|
// special
|
||||||
Vector(VectorType, bool),
|
Vector(VectorType, bool),
|
||||||
@ -208,6 +216,7 @@ impl Program {
|
|||||||
match item {
|
match item {
|
||||||
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, &opts),
|
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, &opts),
|
||||||
syn::ForeignItem::Type(t) => self.push_foreign_ty(t, &opts),
|
syn::ForeignItem::Type(t) => self.push_foreign_ty(t, &opts),
|
||||||
|
syn::ForeignItem::Static(s) => self.push_foreign_static(s, &opts),
|
||||||
_ => panic!("only foreign functions/types allowed for now"),
|
_ => panic!("only foreign functions/types allowed for now"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,6 +350,20 @@ impl Program {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_foreign_static(&mut self,
|
||||||
|
f: syn::ForeignItemStatic,
|
||||||
|
module_opts: &BindgenAttrs) {
|
||||||
|
if f.mutability.is_some() {
|
||||||
|
panic!("cannot import mutable globals yet")
|
||||||
|
}
|
||||||
|
self.imported_fields.push(ImportField {
|
||||||
|
module: module_opts.module().map(|s| s.to_string()),
|
||||||
|
ty: *f.ty,
|
||||||
|
vis: f.vis,
|
||||||
|
name: f.ident
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn literal(&self, dst: &mut Tokens) -> usize {
|
pub fn literal(&self, dst: &mut Tokens) -> usize {
|
||||||
let mut tmp = Tokens::new();
|
let mut tmp = Tokens::new();
|
||||||
let cnt = {
|
let cnt = {
|
||||||
@ -792,3 +815,12 @@ impl ToTokens for VectorType {
|
|||||||
me.to_tokens(tokens);
|
me.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImportField {
|
||||||
|
pub fn shared(&self) -> shared::ImportField {
|
||||||
|
shared::ImportField {
|
||||||
|
module: self.module.clone(),
|
||||||
|
name: self.name.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -78,6 +78,9 @@ impl ToTokens for ast::Program {
|
|||||||
for it in self.imported_types.iter() {
|
for it in self.imported_types.iter() {
|
||||||
it.to_tokens(tokens);
|
it.to_tokens(tokens);
|
||||||
}
|
}
|
||||||
|
for it in self.imported_fields.iter() {
|
||||||
|
it.to_tokens(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
// Generate a static which will eventually be what lives in a custom section
|
// Generate a static which will eventually be what lives in a custom section
|
||||||
// of the wasm executable. For now it's just a plain old static, but we'll
|
// of the wasm executable. For now it's just a plain old static, but we'll
|
||||||
@ -591,3 +594,28 @@ impl ToTokens for ast::Enum {
|
|||||||
}).to_tokens(into);
|
}).to_tokens(into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToTokens for ast::ImportField {
|
||||||
|
fn to_tokens(&self, into: &mut Tokens) {
|
||||||
|
let name = self.name;
|
||||||
|
let ty = &self.ty;
|
||||||
|
let shim_name = syn::Ident::from(self.shared().shim_name());
|
||||||
|
let vis = &self.vis;
|
||||||
|
(my_quote! {
|
||||||
|
#vis static #name: ::wasm_bindgen::JsStatic<#ty> = {
|
||||||
|
fn init() -> #ty {
|
||||||
|
extern {
|
||||||
|
fn #shim_name() -> u32;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
::wasm_bindgen::convert::WasmBoundary::from_js(#shim_name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
::wasm_bindgen::JsStatic {
|
||||||
|
__inner: ::std::cell::UnsafeCell::new(None),
|
||||||
|
__init: init,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}).to_tokens(into);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -107,7 +107,8 @@ impl Literal for ast::Program {
|
|||||||
fn literal(&self, a: &mut LiteralBuilder) {
|
fn literal(&self, a: &mut LiteralBuilder) {
|
||||||
a.fields(&[
|
a.fields(&[
|
||||||
("exports", &|a| a.list_of(&self.exports)),
|
("exports", &|a| a.list_of(&self.exports)),
|
||||||
("imports", &|a| a.list_of(&self.imports)),
|
("imported_functions", &|a| a.list_of(&self.imports)),
|
||||||
|
("imported_fields", &|a| a.list_of(&self.imported_fields)),
|
||||||
("enums", &|a| a.list_of(&self.enums)),
|
("enums", &|a| a.list_of(&self.enums)),
|
||||||
("custom_type_names", &|a| {
|
("custom_type_names", &|a| {
|
||||||
let names = self.exports
|
let names = self.exports
|
||||||
@ -271,3 +272,15 @@ impl Literal for ast::Variant {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Literal for ast::ImportField {
|
||||||
|
fn literal(&self, a: &mut LiteralBuilder) {
|
||||||
|
a.fields(&[
|
||||||
|
("name", &|a| a.str(self.name.as_ref())),
|
||||||
|
("module", &|a| match self.module {
|
||||||
|
Some(ref s) => a.str(s),
|
||||||
|
None => a.append("null"),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,7 +11,8 @@ pub const SCHEMA_VERSION: &str = "0";
|
|||||||
pub struct Program {
|
pub struct Program {
|
||||||
pub exports: Vec<Export>,
|
pub exports: Vec<Export>,
|
||||||
pub enums: Vec<Enum>,
|
pub enums: Vec<Enum>,
|
||||||
pub imports: Vec<Import>,
|
pub imported_functions: Vec<Import>,
|
||||||
|
pub imported_fields: Vec<ImportField>,
|
||||||
pub custom_type_names: Vec<CustomTypeName>,
|
pub custom_type_names: Vec<CustomTypeName>,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
pub schema_version: String,
|
pub schema_version: String,
|
||||||
@ -30,6 +31,12 @@ pub struct Import {
|
|||||||
pub function: Function,
|
pub function: Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ImportField {
|
||||||
|
pub module: Option<String>,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Export {
|
pub struct Export {
|
||||||
pub class: Option<String>,
|
pub class: Option<String>,
|
||||||
@ -148,3 +155,9 @@ pub fn version() -> String {
|
|||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImportField {
|
||||||
|
pub fn shim_name(&self) -> String {
|
||||||
|
format!("__wbg_field_import_shim_{}", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
47
src/lib.rs
47
src/lib.rs
@ -9,8 +9,12 @@
|
|||||||
|
|
||||||
extern crate wasm_bindgen_macro;
|
extern crate wasm_bindgen_macro;
|
||||||
|
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
|
||||||
|
use convert::WasmBoundary;
|
||||||
|
|
||||||
/// A module which is typically glob imported from:
|
/// A module which is typically glob imported from:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -243,6 +247,49 @@ impl Drop for JsValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrapper type for imported statics.
|
||||||
|
///
|
||||||
|
/// This type is used whenever a `static` is imported from a JS module, for
|
||||||
|
/// example this import:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// #[wasm_bindgen]
|
||||||
|
/// extern {
|
||||||
|
/// static console: JsValue;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// will generate in Rust a value that looks like:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// static console: JsStatic<JsValue> = ...;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This type implements `Deref` to the inner type so it's typically used as if
|
||||||
|
/// it were `&T`.
|
||||||
|
pub struct JsStatic<T> {
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub __inner: UnsafeCell<Option<T>>,
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub __init: fn() -> T,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: Sync> Sync for JsStatic<T> {}
|
||||||
|
unsafe impl<T: Send> Send for JsStatic<T> {}
|
||||||
|
|
||||||
|
impl<T: WasmBoundary> Deref for JsStatic<T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
unsafe {
|
||||||
|
(*self.__inner.get()).get_or_insert_with(|| {
|
||||||
|
assert!(T::DESCRIPTOR == JsValue::DESCRIPTOR,
|
||||||
|
"only JS values can be imported as statics for now");
|
||||||
|
(self.__init)()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Throws a JS exception.
|
/// Throws a JS exception.
|
||||||
///
|
///
|
||||||
/// This function will throw a JS exception with the message provided. The
|
/// This function will throw a JS exception with the message provided. The
|
||||||
|
@ -278,3 +278,35 @@ fn free_imports() {
|
|||||||
"#)
|
"#)
|
||||||
.test();
|
.test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_a_field() {
|
||||||
|
test_support::project()
|
||||||
|
.file("src/lib.rs", r#"
|
||||||
|
#![feature(proc_macro)]
|
||||||
|
|
||||||
|
extern crate wasm_bindgen;
|
||||||
|
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen(module = "./test")]
|
||||||
|
extern {
|
||||||
|
static IMPORT: JsValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn run() {
|
||||||
|
assert_eq!(IMPORT.as_f64(), Some(1.0));
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.file("test.ts", r#"
|
||||||
|
import { run } from "./out";
|
||||||
|
|
||||||
|
export const IMPORT = 1.0;
|
||||||
|
|
||||||
|
export function test() {
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
"#)
|
||||||
|
.test();
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user