mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-12-25 11:02:11 +03:00
webidl: add support for static attributes
This commit is contained in:
parent
fe5cde8636
commit
0938858aa8
@ -701,24 +701,6 @@ impl ImportFunction {
|
||||
}
|
||||
|
||||
fn shared(&self) -> shared::ImportFunction {
|
||||
let mut method = false;
|
||||
let mut js_new = false;
|
||||
let mut class_name = None;
|
||||
match self.kind {
|
||||
ImportFunctionKind::Method {
|
||||
ref class,
|
||||
ref kind,
|
||||
..
|
||||
} => {
|
||||
match kind {
|
||||
MethodKind::Normal => method = true,
|
||||
MethodKind::Constructor => js_new = true,
|
||||
MethodKind::Static => {}
|
||||
}
|
||||
class_name = Some(class);
|
||||
}
|
||||
ImportFunctionKind::Normal => {}
|
||||
}
|
||||
let mut getter = None;
|
||||
let mut setter = None;
|
||||
|
||||
@ -730,15 +712,34 @@ impl ImportFunction {
|
||||
let s = s.map(|s| s.to_string());
|
||||
setter = Some(s.unwrap_or_else(|| self.infer_setter_property()));
|
||||
}
|
||||
|
||||
let mut method = None;
|
||||
match self.kind {
|
||||
ImportFunctionKind::Method {
|
||||
ref class,
|
||||
ref kind,
|
||||
..
|
||||
} => {
|
||||
let kind = match kind {
|
||||
MethodKind::Normal => shared::MethodKind::Normal,
|
||||
MethodKind::Constructor => shared::MethodKind::Constructor,
|
||||
MethodKind::Static => shared::MethodKind::Static,
|
||||
};
|
||||
method = Some(shared::MethodData {
|
||||
class: class.clone(),
|
||||
kind,
|
||||
getter,
|
||||
setter,
|
||||
});
|
||||
}
|
||||
ImportFunctionKind::Normal => {}
|
||||
}
|
||||
|
||||
shared::ImportFunction {
|
||||
shim: self.shim.to_string(),
|
||||
catch: self.function.opts.catch(),
|
||||
method,
|
||||
js_new,
|
||||
structural: self.function.opts.structural(),
|
||||
getter,
|
||||
setter,
|
||||
class: class_name.cloned(),
|
||||
function: self.function.shared(),
|
||||
}
|
||||
}
|
||||
|
@ -1605,76 +1605,78 @@ impl<'a, 'b> SubContext<'a, 'b> {
|
||||
{
|
||||
let descriptor = self.cx.describe(&import.shim);
|
||||
|
||||
let target = match import.class {
|
||||
Some(ref class) if import.js_new => {
|
||||
format!("new {}", self.import_name(info, class)?)
|
||||
}
|
||||
Some(ref class) if import.method => {
|
||||
let target = match &import.method {
|
||||
Some(shared::MethodData { class, kind, getter, setter }) => {
|
||||
let class = self.import_name(info, class)?;
|
||||
let target = if let Some(ref g) = import.getter {
|
||||
if import.structural {
|
||||
format!("function() {{
|
||||
return this.{};
|
||||
}}", g)
|
||||
} else {
|
||||
self.cx.expose_get_inherited_descriptor();
|
||||
format!(
|
||||
"GetOwnOrInheritedPropertyDescriptor\
|
||||
({}.prototype, '{}').get",
|
||||
class,
|
||||
g,
|
||||
)
|
||||
}
|
||||
} else if let Some(ref s) = import.setter {
|
||||
if import.structural {
|
||||
format!("function(y) {{
|
||||
this.{} = y;
|
||||
}}", s)
|
||||
} else {
|
||||
self.cx.expose_get_inherited_descriptor();
|
||||
format!(
|
||||
"GetOwnOrInheritedPropertyDescriptor\
|
||||
({}.prototype, '{}').set",
|
||||
class,
|
||||
s,
|
||||
)
|
||||
}
|
||||
if let shared::MethodKind::Constructor = kind {
|
||||
format!("new {}", class)
|
||||
} else {
|
||||
if import.structural {
|
||||
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));
|
||||
}
|
||||
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
|
||||
let is_static = if let shared::MethodKind::Static = kind {
|
||||
true
|
||||
} else {
|
||||
format!("{}.prototype.{}", class, import.function.name)
|
||||
}
|
||||
};
|
||||
self.cx.global(&format!("
|
||||
const {}_target = {};
|
||||
", import.shim, target));
|
||||
format!("{}_target.call", import.shim)
|
||||
}
|
||||
Some(ref class) => {
|
||||
let class = self.import_name(info, class)?;
|
||||
self.cx.global(&format!("
|
||||
const {}_target = {}.{};
|
||||
", import.shim, class, import.function.name));
|
||||
format!("{}_target", import.shim)
|
||||
false
|
||||
};
|
||||
|
||||
let target = if let Some(g) = getter {
|
||||
if import.structural {
|
||||
format!("function(y) {{
|
||||
return this.{};
|
||||
}}", g)
|
||||
} else {
|
||||
self.cx.expose_get_inherited_descriptor();
|
||||
format!(
|
||||
"GetOwnOrInheritedPropertyDescriptor\
|
||||
({}{}, '{}').get",
|
||||
class,
|
||||
if is_static { "" } else { ".prototype "},
|
||||
g,
|
||||
)
|
||||
}
|
||||
} else if let Some(s) = setter {
|
||||
if import.structural {
|
||||
format!("function(y) {{
|
||||
this.{} = y;
|
||||
}}", s)
|
||||
} else {
|
||||
self.cx.expose_get_inherited_descriptor();
|
||||
format!(
|
||||
"GetOwnOrInheritedPropertyDescriptor\
|
||||
({}{}, '{}').set",
|
||||
class,
|
||||
if is_static { "" } else { ".prototype "},
|
||||
s,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if import.structural {
|
||||
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));
|
||||
}
|
||||
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
|
||||
} else {
|
||||
format!("{}{}.{}", class, if is_static { "" } else { ".prototype" }, import.function.name)
|
||||
}
|
||||
};
|
||||
self.cx.global(&format!("
|
||||
const {}_target = {};
|
||||
", import.shim, target));
|
||||
format!("{}_target{}", import.shim, if is_static { "" } else { ".call" })
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let name = self.import_name(info, &import.function.name)?;
|
||||
|
@ -39,13 +39,24 @@ pub enum ImportKind {
|
||||
pub struct ImportFunction {
|
||||
pub shim: String,
|
||||
pub catch: bool,
|
||||
pub method: bool,
|
||||
pub js_new: bool,
|
||||
pub method: Option<MethodData>,
|
||||
pub structural: bool,
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct MethodData {
|
||||
pub class: String,
|
||||
pub kind: MethodKind,
|
||||
pub getter: Option<String>,
|
||||
pub setter: Option<String>,
|
||||
pub class: Option<String>,
|
||||
pub function: Function,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub enum MethodKind {
|
||||
Normal,
|
||||
Constructor,
|
||||
Static,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
@ -55,8 +66,7 @@ pub struct ImportStatic {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct ImportType {
|
||||
}
|
||||
pub struct ImportType {}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Export {
|
||||
@ -77,7 +87,7 @@ pub struct Enum {
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct EnumVariant {
|
||||
pub name: String,
|
||||
pub value: u32
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
@ -101,20 +111,16 @@ pub struct StructField {
|
||||
|
||||
pub fn new_function(struct_name: &str) -> String {
|
||||
let mut name = format!("__wbg_");
|
||||
name.extend(struct_name
|
||||
.chars()
|
||||
.flat_map(|s| s.to_lowercase()));
|
||||
name.extend(struct_name.chars().flat_map(|s| s.to_lowercase()));
|
||||
name.push_str("_new");
|
||||
return name
|
||||
return name;
|
||||
}
|
||||
|
||||
pub fn free_function(struct_name: &str) -> String {
|
||||
let mut name = format!("__wbg_");
|
||||
name.extend(struct_name
|
||||
.chars()
|
||||
.flat_map(|s| s.to_lowercase()));
|
||||
name.extend(struct_name.chars().flat_map(|s| s.to_lowercase()));
|
||||
name.push_str("_free");
|
||||
return name
|
||||
return name;
|
||||
}
|
||||
|
||||
pub fn free_function_export_name(function_name: &str) -> String {
|
||||
@ -128,27 +134,23 @@ pub fn struct_function_export_name(struct_: &str, f: &str) -> String {
|
||||
.collect::<String>();
|
||||
name.push_str("_");
|
||||
name.push_str(f);
|
||||
return name
|
||||
return name;
|
||||
}
|
||||
|
||||
pub fn struct_field_get(struct_: &str, f: &str) -> String {
|
||||
let mut name = String::from("__wbg_get_");
|
||||
name.extend(struct_
|
||||
.chars()
|
||||
.flat_map(|s| s.to_lowercase()));
|
||||
name.extend(struct_.chars().flat_map(|s| s.to_lowercase()));
|
||||
name.push_str("_");
|
||||
name.push_str(f);
|
||||
return name
|
||||
return name;
|
||||
}
|
||||
|
||||
pub fn struct_field_set(struct_: &str, f: &str) -> String {
|
||||
let mut name = String::from("__wbg_set_");
|
||||
name.extend(struct_
|
||||
.chars()
|
||||
.flat_map(|s| s.to_lowercase()));
|
||||
name.extend(struct_.chars().flat_map(|s| s.to_lowercase()));
|
||||
name.push_str("_");
|
||||
name.push_str(f);
|
||||
return name
|
||||
return name;
|
||||
}
|
||||
|
||||
pub fn version() -> String {
|
||||
@ -158,5 +160,5 @@ pub fn version() -> String {
|
||||
v.push_str(s);
|
||||
v.push_str(")");
|
||||
}
|
||||
return v
|
||||
return v;
|
||||
}
|
||||
|
@ -29,8 +29,8 @@ use quote::ToTokens;
|
||||
mod util;
|
||||
|
||||
use util::{
|
||||
create_basic_method, create_function, ident_ty, raw_ident, rust_ident, webidl_ty_to_syn_ty,
|
||||
wrap_import_function, TypePosition,
|
||||
create_basic_method, create_function, create_getter, create_setter, ident_ty, rust_ident,
|
||||
webidl_ty_to_syn_ty, wrap_import_function, TypePosition,
|
||||
};
|
||||
|
||||
/// Either `Ok(t)` or `Err(failure::Error)`.
|
||||
@ -265,10 +265,11 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::InterfaceMember {
|
||||
|
||||
impl<'a> WebidlParse<&'a str> for webidl::ast::Attribute {
|
||||
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
|
||||
match *self {
|
||||
webidl::ast::Attribute::Regular(ref attr) => attr.webidl_parse(program, self_name),
|
||||
match self {
|
||||
webidl::ast::Attribute::Regular(attr) => attr.webidl_parse(program, self_name),
|
||||
webidl::ast::Attribute::Static(attr) => attr.webidl_parse(program, self_name),
|
||||
// TODO
|
||||
webidl::ast::Attribute::Static(_) | webidl::ast::Attribute::Stringifier(_) => {
|
||||
webidl::ast::Attribute::Stringifier(_) => {
|
||||
warn!("Unsupported WebIDL attribute: {:?}", self);
|
||||
Ok(())
|
||||
}
|
||||
@ -292,61 +293,46 @@ impl<'a> WebidlParse<&'a str> for webidl::ast::Operation {
|
||||
|
||||
impl<'a> WebidlParse<&'a str> for webidl::ast::RegularAttribute {
|
||||
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
|
||||
fn create_getter(
|
||||
this: &webidl::ast::RegularAttribute,
|
||||
self_name: &str,
|
||||
) -> Option<backend::ast::Import> {
|
||||
let ret = match webidl_ty_to_syn_ty(&this.type_, TypePosition::Return) {
|
||||
None => {
|
||||
warn!("Attribute's type does not yet support reading: {:?}. Skipping getter binding for {:?}",
|
||||
this.type_, this);
|
||||
return None;
|
||||
}
|
||||
Some(ty) => Some(ty),
|
||||
};
|
||||
|
||||
let kind = backend::ast::ImportFunctionKind::Method {
|
||||
class: self_name.to_string(),
|
||||
ty: ident_ty(rust_ident(self_name)),
|
||||
kind: backend::ast::MethodKind::Normal,
|
||||
};
|
||||
|
||||
create_function(
|
||||
&this.name,
|
||||
iter::empty(),
|
||||
kind,
|
||||
ret,
|
||||
vec![backend::ast::BindgenAttr::Getter(Some(raw_ident(
|
||||
&this.name,
|
||||
)))],
|
||||
).map(wrap_import_function)
|
||||
}
|
||||
|
||||
fn create_setter(
|
||||
this: &webidl::ast::RegularAttribute,
|
||||
self_name: &str,
|
||||
) -> Option<backend::ast::Import> {
|
||||
let kind = backend::ast::ImportFunctionKind::Method {
|
||||
class: self_name.to_string(),
|
||||
ty: ident_ty(rust_ident(self_name)),
|
||||
kind: backend::ast::MethodKind::Normal,
|
||||
};
|
||||
|
||||
create_function(
|
||||
&format!("set_{}", this.name),
|
||||
iter::once((&*this.name, &*this.type_, false)),
|
||||
kind,
|
||||
None,
|
||||
vec![backend::ast::BindgenAttr::Setter(Some(raw_ident(
|
||||
&this.name,
|
||||
)))],
|
||||
).map(wrap_import_function)
|
||||
}
|
||||
|
||||
create_getter(self, self_name).map(|import| program.imports.push(import));
|
||||
create_getter(
|
||||
&self.name,
|
||||
&self.type_,
|
||||
self_name,
|
||||
backend::ast::MethodKind::Normal,
|
||||
).map(wrap_import_function)
|
||||
.map(|import| program.imports.push(import));
|
||||
|
||||
if !self.read_only {
|
||||
create_setter(self, self_name).map(|import| program.imports.push(import));
|
||||
create_setter(
|
||||
&self.name,
|
||||
&self.type_,
|
||||
self_name,
|
||||
backend::ast::MethodKind::Normal,
|
||||
).map(wrap_import_function)
|
||||
.map(|import| program.imports.push(import));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WebidlParse<&'a str> for webidl::ast::StaticAttribute {
|
||||
fn webidl_parse(&self, program: &mut backend::ast::Program, self_name: &'a str) -> Result<()> {
|
||||
create_getter(
|
||||
&self.name,
|
||||
&self.type_,
|
||||
self_name,
|
||||
backend::ast::MethodKind::Static,
|
||||
).map(wrap_import_function)
|
||||
.map(|import| program.imports.push(import));
|
||||
|
||||
if !self.read_only {
|
||||
create_setter(
|
||||
&self.name,
|
||||
&self.type_,
|
||||
self_name,
|
||||
backend::ast::MethodKind::Static,
|
||||
).map(wrap_import_function)
|
||||
.map(|import| program.imports.push(import));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::iter::FromIterator;
|
||||
use std::iter::{self, FromIterator};
|
||||
|
||||
use backend;
|
||||
use heck::SnakeCase;
|
||||
@ -30,7 +30,7 @@ pub fn rust_ident(name: &str) -> Ident {
|
||||
|
||||
// Create an `Ident` without checking to see if it conflicts with a Rust
|
||||
// keyword.
|
||||
pub fn raw_ident(name: &str) -> Ident {
|
||||
fn raw_ident(name: &str) -> Ident {
|
||||
Ident::new(name, proc_macro2::Span::call_site())
|
||||
}
|
||||
|
||||
@ -288,6 +288,56 @@ pub fn create_basic_method(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_getter(
|
||||
name: &str,
|
||||
ty: &webidl::ast::Type,
|
||||
self_name: &str,
|
||||
kind: backend::ast::MethodKind,
|
||||
) -> Option<backend::ast::ImportFunction> {
|
||||
let ret = match webidl_ty_to_syn_ty(ty, TypePosition::Return) {
|
||||
None => {
|
||||
warn!("Attribute's type does not yet support reading: {:?}", ty);
|
||||
return None;
|
||||
}
|
||||
Some(ty) => Some(ty),
|
||||
};
|
||||
|
||||
let kind = backend::ast::ImportFunctionKind::Method {
|
||||
class: self_name.to_string(),
|
||||
ty: ident_ty(rust_ident(self_name)),
|
||||
kind,
|
||||
};
|
||||
|
||||
create_function(
|
||||
name,
|
||||
iter::empty(),
|
||||
kind,
|
||||
ret,
|
||||
vec![backend::ast::BindgenAttr::Getter(Some(raw_ident(name)))],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_setter(
|
||||
name: &str,
|
||||
ty: &webidl::ast::Type,
|
||||
self_name: &str,
|
||||
kind: backend::ast::MethodKind,
|
||||
) -> Option<backend::ast::ImportFunction> {
|
||||
let kind = backend::ast::ImportFunctionKind::Method {
|
||||
class: self_name.to_string(),
|
||||
ty: ident_ty(rust_ident(self_name)),
|
||||
kind,
|
||||
};
|
||||
|
||||
create_function(
|
||||
&format!("set_{}", name),
|
||||
iter::once((name, ty, false)),
|
||||
kind,
|
||||
None,
|
||||
vec![backend::ast::BindgenAttr::Setter(Some(raw_ident(name)))],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn wrap_import_function(function: backend::ast::ImportFunction) -> backend::ast::Import {
|
||||
backend::ast::Import {
|
||||
module: None,
|
||||
|
@ -184,7 +184,6 @@ fn static_method() {
|
||||
"foo.webidl",
|
||||
r#"
|
||||
interface Foo {
|
||||
[Pure]
|
||||
static double swap(double value);
|
||||
};
|
||||
"#,
|
||||
@ -232,3 +231,63 @@ fn static_method() {
|
||||
)
|
||||
.test();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_property() {
|
||||
project()
|
||||
.file(
|
||||
"foo.webidl",
|
||||
r#"
|
||||
interface Foo {
|
||||
static attribute double value;
|
||||
};
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"foo.ts",
|
||||
r#"
|
||||
export class Foo {
|
||||
private static _value: number = 0;
|
||||
|
||||
static get value(): number {
|
||||
return Foo._value;
|
||||
}
|
||||
|
||||
static set value(_value: number) {
|
||||
Foo._value = _value;
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.file(
|
||||
"src/lib.rs",
|
||||
r#"
|
||||
#![feature(proc_macro, wasm_custom_section, wasm_import_module)]
|
||||
|
||||
extern crate wasm_bindgen;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
pub mod foo;
|
||||
|
||||
use foo::Foo;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn test() {
|
||||
let tmp = Foo::value() == 0.;
|
||||
assert!(tmp);
|
||||
Foo::set_value(3.14159);
|
||||
let tmp = Foo::value() == 3.14159;
|
||||
assert!(tmp);
|
||||
let tmp = Foo::value() != 2.71828;
|
||||
assert!(tmp);
|
||||
Foo::set_value(2.71828);
|
||||
let tmp = Foo::value() == 2.71828;
|
||||
assert!(tmp);
|
||||
let tmp = Foo::value() != 3.14159;
|
||||
assert!(tmp);
|
||||
}
|
||||
"#,
|
||||
)
|
||||
.test();
|
||||
}
|
Loading…
Reference in New Issue
Block a user