Skip to content

Implement rustwasm/rfcs#5, implement Deref for imports and structural by default #1019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Nov 12, 2018
22 changes: 18 additions & 4 deletions crates/backend/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ impl ToTokens for ast::ImportType {
use wasm_bindgen::convert::RefFromWasmAbi;
use wasm_bindgen::describe::WasmDescribe;
use wasm_bindgen::{JsValue, JsCast};
use wasm_bindgen::__rt::core::mem::ManuallyDrop;
use wasm_bindgen::__rt::core;

impl WasmDescribe for #rust_name {
fn describe() {
Expand Down Expand Up @@ -589,13 +589,13 @@ impl ToTokens for ast::ImportType {

impl RefFromWasmAbi for #rust_name {
type Abi = <JsValue as RefFromWasmAbi>::Abi;
type Anchor = ManuallyDrop<#rust_name>;
type Anchor = core::mem::ManuallyDrop<#rust_name>;

#[inline]
unsafe fn ref_from_abi(js: Self::Abi, extra: &mut Stack) -> Self::Anchor {
let tmp = <JsValue as RefFromWasmAbi>::ref_from_abi(js, extra);
ManuallyDrop::new(#rust_name {
obj: ManuallyDrop::into_inner(tmp),
core::mem::ManuallyDrop::new(#rust_name {
obj: core::mem::ManuallyDrop::into_inner(tmp),
})
}
}
Expand Down Expand Up @@ -657,6 +657,20 @@ impl ToTokens for ast::ImportType {
};
}).to_tokens(tokens);

let deref_target = match self.extends.first() {
Some(target) => quote! { #target },
None => quote! { JsValue },
};
(quote! {
impl core::ops::Deref for #rust_name {
type Target = #deref_target;

#[inline]
fn deref(&self) -> &#deref_target {
self.as_ref()
}
}
}).to_tokens(tokens);
for superclass in self.extends.iter() {
(quote! {
impl From<#rust_name> for #superclass {
Expand Down
2 changes: 1 addition & 1 deletion crates/cli-support/src/js/js2rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ impl<'a, 'b> Js2Rust<'a, 'b> {
name = name
));
}
self.rust_arguments.push(format!("{} ? 1 : 0", name));
self.rust_arguments.push(format!("{}", name));
}
Descriptor::Char => {
self.js_arguments.push((name.clone(), "string".to_string()));
Expand Down
187 changes: 82 additions & 105 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use std::mem;

use decode;
Expand Down Expand Up @@ -65,6 +64,18 @@ pub struct SubContext<'a, 'b: 'a> {
pub vendor_prefixes: HashMap<&'b str, Vec<&'b str>>,
}

pub enum ImportTarget {
Function(String),
Method(String),
Constructor(String),
StructuralMethod(String),
StructuralGetter(Option<String>, String),
StructuralSetter(Option<String>, String),
StructuralIndexingGetter(Option<String>),
StructuralIndexingSetter(Option<String>),
StructuralIndexingDeleter(Option<String>),
}

const INITIAL_SLAB_VALUES: &[&str] = &["undefined", "null", "true", "false"];

impl<'a> Context<'a> {
Expand Down Expand Up @@ -1933,7 +1944,7 @@ impl<'a, 'b> SubContext<'a, 'b> {
Some(d) => d,
};

let target = self.generated_import_target(info, import, &descriptor)?;
let target = self.generated_import_target(info, import)?;

let js = Rust2Js::new(self.cx)
.catch(import.catch)
Expand All @@ -1948,137 +1959,103 @@ impl<'a, 'b> SubContext<'a, 'b> {
&mut self,
info: &decode::Import<'b>,
import: &decode::ImportFunction,
descriptor: &Descriptor,
) -> Result<String, Error> {
) -> Result<ImportTarget, Error> {
let method_data = match &import.method {
Some(data) => data,
None => {
let name = self.import_name(info, &import.function.name)?;
return Ok(if name.contains(".") {
self.cx.global(&format!(
"
const {}_target = {};
",
import.shim, name
));
format!("{}_target", import.shim)
} else {
name
});
if import.structural || !name.contains(".") {
return Ok(ImportTarget::Function(name))
}
self.cx.global(&format!("const {}_target = {};", import.shim, name));
let target = format!("{}_target", import.shim);
return Ok(ImportTarget::Function(target))
}
};

let class = self.import_name(info, &method_data.class)?;
let op = match &method_data.kind {
decode::MethodKind::Constructor => return Ok(format!("new {}", class)),
decode::MethodKind::Constructor => {
return Ok(ImportTarget::Constructor(class.to_string()))
}
decode::MethodKind::Operation(op) => op,
};
let target = if import.structural {
let location = if op.is_static { &class } else { "this" };
if import.structural {
let class = if op.is_static { Some(class.clone()) } else { None };

match &op.kind {
return Ok(match &op.kind {
decode::OperationKind::Regular => {
let nargs = descriptor.unwrap_function().arguments.len();
let mut s = format!("function(");
for i in 0..nargs - 1 {
if i > 0 {
drop(write!(s, ", "));
}
drop(write!(s, "x{}", i));
let name = import.function.name.to_string();
match class {
Some(c) => ImportTarget::Function(format!("{}.{}", c, name)),
None => ImportTarget::StructuralMethod(name),
}
s.push_str(") { \nreturn this.");
s.push_str(&import.function.name);
s.push_str("(");
for i in 0..nargs - 1 {
if i > 0 {
drop(write!(s, ", "));
}
drop(write!(s, "x{}", i));
}
s.push_str(");\n}");
s
}
decode::OperationKind::Getter(g) => format!(
"function() {{
return {}.{};
}}",
location, g
),
decode::OperationKind::Setter(s) => format!(
"function(y) {{
{}.{} = y;
}}",
location, s
),
decode::OperationKind::IndexingGetter => format!(
"function(y) {{
return {}[y];
}}",
location
),
decode::OperationKind::IndexingSetter => format!(
"function(y, z) {{
{}[y] = z;
}}",
location
),
decode::OperationKind::IndexingDeleter => format!(
"function(y) {{
delete {}[y];
}}",
location
),
}
} else {
let target = format!("typeof {0} === 'undefined' ? null : {}{}",
class,
if op.is_static { "" } else { ".prototype" });
let (mut target, name) = match &op.kind {
decode::OperationKind::Regular => {
(format!("{}.{}", target, import.function.name), &import.function.name)
}
decode::OperationKind::Getter(g) => {
self.cx.expose_get_inherited_descriptor();
(format!(
"GetOwnOrInheritedPropertyDescriptor({}, '{}').get",
target, g,
), g)
ImportTarget::StructuralGetter(class, g.to_string())
}
decode::OperationKind::Setter(s) => {
self.cx.expose_get_inherited_descriptor();
(format!(
"GetOwnOrInheritedPropertyDescriptor({}, '{}').set",
target, s,
), s)
ImportTarget::StructuralSetter(class, s.to_string())
}
decode::OperationKind::IndexingGetter => {
panic!("indexing getter should be structural")
ImportTarget::StructuralIndexingGetter(class)
}
decode::OperationKind::IndexingSetter => {
panic!("indexing setter should be structural")
ImportTarget::StructuralIndexingSetter(class)
}
decode::OperationKind::IndexingDeleter => {
panic!("indexing deleter should be structural")
ImportTarget::StructuralIndexingDeleter(class)
}
};
target.push_str(&format!(" || function() {{
throw new Error(`wasm-bindgen: {}.{} does not exist`);
}}", class, name));
if op.is_static {
target.insert(0, '(');
target.push_str(").bind(");
target.push_str(&class);
target.push_str(")");
}
target
})
}

let target = format!("typeof {0} === 'undefined' ? null : {}{}",
class,
if op.is_static { "" } else { ".prototype" });
let (mut target, name) = match &op.kind {
decode::OperationKind::Regular => {
(format!("{}.{}", target, import.function.name), &import.function.name)
}
decode::OperationKind::Getter(g) => {
self.cx.expose_get_inherited_descriptor();
(format!(
"GetOwnOrInheritedPropertyDescriptor({}, '{}').get",
target, g,
), g)
}
decode::OperationKind::Setter(s) => {
self.cx.expose_get_inherited_descriptor();
(format!(
"GetOwnOrInheritedPropertyDescriptor({}, '{}').set",
target, s,
), s)
}
decode::OperationKind::IndexingGetter => {
panic!("indexing getter should be structural")
}
decode::OperationKind::IndexingSetter => {
panic!("indexing setter should be structural")
}
decode::OperationKind::IndexingDeleter => {
panic!("indexing deleter should be structural")
}
};
target.push_str(&format!(" || function() {{
throw new Error(`wasm-bindgen: {}.{} does not exist`);
}}", class, name));
if op.is_static {
target.insert(0, '(');
target.push_str(").bind(");
target.push_str(&class);
target.push_str(")");
}

self.cx.global(&format!("const {}_target = {};", import.shim, target));
Ok(format!(
"{}_target{}",
import.shim,
if op.is_static { "" } else { ".call" }
))
Ok(if op.is_static {
ImportTarget::Function(format!("{}_target", import.shim))
} else {
ImportTarget::Method(format!("{}_target", import.shim))
})
}

fn generate_import_type(
Expand Down
Loading