mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-11-28 05:52:21 +03: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
@ -1044,9 +1044,12 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
for f in self.program.exports.iter() {
|
||||
self.generate_export(f);
|
||||
}
|
||||
for f in self.program.imports.iter() {
|
||||
for f in self.program.imported_functions.iter() {
|
||||
self.generate_import(f);
|
||||
}
|
||||
for f in self.program.imported_fields.iter() {
|
||||
self.generate_import_field(f);
|
||||
}
|
||||
for e in self.program.enums.iter() {
|
||||
self.generate_enum(e);
|
||||
}
|
||||
@ -1319,6 +1322,24 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
(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) {
|
||||
if let Some(ref module) = import.module {
|
||||
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 imported_types: Vec<ImportedType>,
|
||||
pub structs: Vec<Struct>,
|
||||
pub imported_fields: Vec<ImportField>,
|
||||
}
|
||||
|
||||
pub struct Export {
|
||||
@ -62,6 +63,13 @@ pub struct ImportedType {
|
||||
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 {
|
||||
// special
|
||||
Vector(VectorType, bool),
|
||||
@ -208,6 +216,7 @@ impl Program {
|
||||
match item {
|
||||
syn::ForeignItem::Fn(f) => self.push_foreign_fn(f, &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"),
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
let mut tmp = Tokens::new();
|
||||
let cnt = {
|
||||
@ -792,3 +815,12 @@ impl ToTokens for VectorType {
|
||||
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() {
|
||||
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
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
a.fields(&[
|
||||
("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)),
|
||||
("custom_type_names", &|a| {
|
||||
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 exports: Vec<Export>,
|
||||
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 version: String,
|
||||
pub schema_version: String,
|
||||
@ -30,6 +31,12 @@ pub struct Import {
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ImportField {
|
||||
pub module: Option<String>,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Export {
|
||||
pub class: Option<String>,
|
||||
@ -148,3 +155,9 @@ pub fn version() -> String {
|
||||
}
|
||||
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;
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ops::Deref;
|
||||
use std::ptr;
|
||||
|
||||
use convert::WasmBoundary;
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// This function will throw a JS exception with the message provided. The
|
||||
|
@ -278,3 +278,35 @@ fn free_imports() {
|
||||
"#)
|
||||
.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…
Reference in New Issue
Block a user