Skip to content

Support name alias on type directly for methods #2124

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/backend/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ pub struct ImportFunction {
pub enum ImportFunctionKind {
Method {
class: String,
rust_class_str: String,
aliased_by_js_class: bool,
ty: syn::Type,
kind: MethodKind,
},
Expand Down Expand Up @@ -185,6 +187,8 @@ pub struct ImportStatic {
pub struct ImportType {
pub vis: syn::Visibility,
pub rust_name: Ident,
pub rust_name_str: String,
pub aliased_by_js_name: bool,
pub js_name: String,
pub attrs: Vec<syn::Attribute>,
pub typescript_type: Option<String>,
Expand Down
17 changes: 15 additions & 2 deletions crates/backend/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,20 @@ fn shared_import_function<'a>(
intern: &'a Interner,
) -> Result<ImportFunction<'a>, Diagnostic> {
let method = match &i.kind {
ast::ImportFunctionKind::Method { class, kind, .. } => {
ast::ImportFunctionKind::Method {
class,
rust_class_str,
kind,
aliased_by_js_class,
..
} => {
let kind = from_ast_method_kind(&i.function, intern, kind)?;
Some(MethodData { class, kind })
Some(MethodData {
class,
kind,
rust_class_str,
aliased_by_js_class: *aliased_by_js_class,
})
}
ast::ImportFunctionKind::Normal => None,
};
Expand All @@ -291,6 +302,8 @@ fn shared_import_static<'a>(i: &'a ast::ImportStatic, intern: &'a Interner) -> I
fn shared_import_type<'a>(i: &'a ast::ImportType, intern: &'a Interner) -> ImportType<'a> {
ImportType {
name: &i.js_name,
rust_name_str: &i.rust_name_str,
aliased_by_js_name: i.aliased_by_js_name,
instanceof_shim: &i.instanceof_shim,
vendor_prefixes: i.vendor_prefixes.iter().map(|x| intern.intern(x)).collect(),
}
Expand Down
43 changes: 42 additions & 1 deletion crates/cli-support/src/wit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct Context<'a> {
anyref_enabled: bool,
wasm_interface_types: bool,
support_start: bool,
import_alias_name_map: HashMap<String, String>,
}

struct InstructionBuilder<'a, 'b> {
Expand Down Expand Up @@ -64,9 +65,39 @@ pub fn process(
anyref_enabled,
wasm_interface_types,
support_start,
import_alias_name_map: Default::default(),
};
cx.init()?;

// collect name alias from js_name of imported types.
for program in &programs {
let decode::Program { imports, .. } = &program;
for import in imports {
match &import.kind {
decode::ImportKind::Type(t) => {
// we don't insert new items into HashMap if imported type is not aliased
if t.aliased_by_js_name {
cx.import_alias_name_map
.entry(t.rust_name_str.to_string())
.or_insert(t.name.to_string());
}
}
decode::ImportKind::Function(f) => {
let decode::ImportFunction { method, .. } = f;
if let Some(data) = method {
// same as above, not insert if class_name is not aliased
if data.aliased_by_js_class {
cx.import_alias_name_map
.entry(data.rust_class_str.to_string())
.or_insert(data.class.to_string());
}
}
}
_ => {}
}
}
}

for program in programs {
cx.program(program)?;
}
Expand Down Expand Up @@ -530,7 +561,17 @@ impl<'a> Context<'a> {
// to the WebAssembly instance.
let (id, import) = match method {
Some(data) => {
let class = self.determine_import(import, &data.class)?;
// if js_class is set explicitly, ignoire other aliases.
// if js_namespace is set, imports would be determined by js_namespace
let class_name = if data.aliased_by_js_class || (&import.js_namespace).is_some() {
data.class.to_string()
} else {
self.import_alias_name_map
.get(&data.rust_class_str.to_string())
.unwrap_or(&data.class.to_string())
.clone()
};
let class = self.determine_import(import, class_name.as_str())?;
match &data.kind {
// NB: `structural` is ignored for constructors since the
// js type isn't expected to change anyway.
Expand Down
49 changes: 32 additions & 17 deletions crates/macro-support/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,10 +433,11 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a ast::ImportModule)> for syn::ForeignIte
_ => bail_span!(class, "first argument of method must be a path"),
};
let class_name = extract_path_ident(class_name)?;
let class_name = opts
.js_class()
.map(|p| p.0.into())
.unwrap_or_else(|| class_name.to_string());
let rust_class_str = class_name.to_string();
let (class_name, aliased_by_js_class) = match opts.js_class().map(|p| p.0.into()) {
Some(p) => (p, true),
_ => (rust_class_str.clone(), false),
};

let kind = ast::MethodKind::Operation(ast::Operation {
is_static: false,
Expand All @@ -445,22 +446,31 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a ast::ImportModule)> for syn::ForeignIte

ast::ImportFunctionKind::Method {
class: class_name,
rust_class_str,
aliased_by_js_class,
ty: class.clone(),
kind,
}
} else if let Some(cls) = opts.static_method_of() {
let class = opts
.js_class()
.map(|p| p.0.into())
.unwrap_or_else(|| cls.to_string());
let rust_class_str = cls.to_string();
let (class, aliased_by_js_class) = match opts.js_class().map(|p| p.0.into()) {
Some(p) => (p, true),
_ => (rust_class_str.clone(), false),
};
let ty = ident_ty(cls.clone());

let kind = ast::MethodKind::Operation(ast::Operation {
is_static: true,
kind: operation_kind,
});

ast::ImportFunctionKind::Method { class, ty, kind }
ast::ImportFunctionKind::Method {
class,
rust_class_str,
aliased_by_js_class,
ty,
kind,
}
} else if opts.constructor().is_some() {
let class = match js_ret {
Some(ref ty) => ty,
Expand All @@ -474,13 +484,16 @@ impl<'a> ConvertToAst<(BindgenAttrs, &'a ast::ImportModule)> for syn::ForeignIte
_ => bail_span!(self, "return value of constructor must be a bare path"),
};
let class_name = extract_path_ident(class_name)?;
let class_name = opts
.js_class()
.map(|p| p.0.into())
.unwrap_or_else(|| class_name.to_string());
let rust_class_str = class_name.to_string();
let (class_name, aliased_by_js_class) = match opts.js_class().map(|p| p.0.into()) {
Some(p) => (p, true),
_ => (rust_class_str.clone(), false),
};

ast::ImportFunctionKind::Method {
class: class_name.to_string(),
rust_class_str,
aliased_by_js_class,
ty: class.clone(),
kind: ast::MethodKind::Constructor,
}
Expand Down Expand Up @@ -533,10 +546,10 @@ impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {

fn convert(self, attrs: BindgenAttrs) -> Result<Self::Target, Diagnostic> {
assert_not_variadic(&attrs)?;
let js_name = attrs
.js_name()
.map(|s| s.0)
.map_or_else(|| self.ident.to_string(), |s| s.to_string());
let (js_name, aliased_by_js_name) = match attrs.js_name().map(|s| (s.0)) {
Some(s) => (s.to_string(), true),
_ => (self.ident.to_string(), false),
};
let typescript_type = attrs.typescript_type().map(|s| s.0.to_string());
let is_type_of = attrs.is_type_of().cloned();
let shim = format!("__wbg_instanceof_{}_{}", self.ident, ShortHash(&self.ident));
Expand All @@ -562,7 +575,9 @@ impl ConvertToAst<BindgenAttrs> for syn::ForeignItemType {
doc_comment: None,
instanceof_shim: shim,
is_type_of,
rust_name_str: self.ident.to_string(),
rust_name: self.ident,
aliased_by_js_name,
typescript_type,
js_name,
extends,
Expand Down
4 changes: 4 additions & 0 deletions crates/shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ macro_rules! shared_api {

struct MethodData<'a> {
class: &'a str,
rust_class_str: &'a str,
aliased_by_js_class: bool,
kind: MethodKind<'a>,
}

Expand Down Expand Up @@ -81,6 +83,8 @@ macro_rules! shared_api {

struct ImportType<'a> {
name: &'a str,
rust_name_str: &'a str,
aliased_by_js_name: bool,
instanceof_shim: &'a str,
vendor_prefixes: Vec<&'a str>,
}
Expand Down
14 changes: 14 additions & 0 deletions tests/wasm/imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,23 @@ class StaticMethodCheck {
static static_method_of_right_this() {
assert.ok(this === StaticMethodCheck);
}
static static_method_for_type_alias(val) {
return val * 6;
}
}

class StaticMethodCheck2 {
constructor(val) {
this.inner_value = val * 7;
}

get getter_for_type_alias() {
return this.inner_value;
}
}

exports.StaticMethodCheck = StaticMethodCheck;
exports.StaticMethodCheck2 = StaticMethodCheck2;

exports.receive_undefined = val => {
assert.strictEqual(val, undefined);
Expand Down
29 changes: 29 additions & 0 deletions tests/wasm/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,21 @@ extern "C" {

#[wasm_bindgen(js_namespace = same_js_namespace_from_module)]
fn func_from_module_1_same_js_namespace(s: i32) -> i32;

#[wasm_bindgen(js_name = "StaticMethodCheck")]
type StaticClassAliasedFromType;

#[wasm_bindgen(static_method_of = StaticClassAliasedFromType)]
fn static_method_for_type_alias(s: i32) -> i32;

#[wasm_bindgen(js_name = "StaticMethodCheck2")]
type StaticClassAliasedFromType2;

#[wasm_bindgen(constructor)]
fn new_for_type_alias(s: i32) -> StaticClassAliasedFromType2;

#[wasm_bindgen(method, getter)]
fn getter_for_type_alias(this: &StaticClassAliasedFromType2) -> i32;
}

#[wasm_bindgen(module = "tests/wasm/imports_2.js")]
Expand Down Expand Up @@ -324,3 +339,17 @@ fn func_from_two_modules_same_js_namespace() {
assert_eq!(func_from_module_1_same_js_namespace(2), 10);
assert_eq!(func_from_module_2_same_js_namespace(2), 12);
}

#[wasm_bindgen_test]
fn func_test_for_static_method_for_type_alias() {
assert_eq!(
StaticClassAliasedFromType::static_method_for_type_alias(3),
18
);
}

#[wasm_bindgen_test]
fn func_test_for_new_and_method_for_type_alias() {
let instance = StaticClassAliasedFromType2::new_for_type_alias(4);
assert_eq!(instance.getter_for_type_alias(), 28);
}