Skip to content

Commit

Permalink
Merge pull request #1019 from alexcrichton/rfc-5
Browse files Browse the repository at this point in the history
Implement rustwasm/rfcs#5, implement `Deref` for imports and `structural` by default
  • Loading branch information
alexcrichton authored Nov 12, 2018
2 parents f3b9c69 + cb246e3 commit dc4e785
Show file tree
Hide file tree
Showing 27 changed files with 618 additions and 246 deletions.
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 @@ -413,7 +413,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 @@ -1944,7 +1955,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 @@ -1959,137 +1970,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

0 comments on commit dc4e785

Please sign in to comment.