Skip to content

Commit

Permalink
Guarantee static_method_of uses the right this (#1795)
Browse files Browse the repository at this point in the history
This came up during #1760 where `Promise.resolve` must be invoked with
`this` as the `Promise` object, but we were erroneously importing it in
such a way that it didn't have a shim and `this` was `undefined`.
  • Loading branch information
alexcrichton committed Sep 26, 2019
1 parent 025b1d8 commit 0b1a764
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 2 deletions.
5 changes: 5 additions & 0 deletions crates/cli-support/src/js/mod.rs
Expand Up @@ -2269,6 +2269,11 @@ impl<'a> Context<'a> {
}
},

AuxImport::ValueWithThis(class, name) => {
let class = self.import_name(class)?;
Ok(format!("{}.{}({})", class, name, variadic_args(&args)?))
}

AuxImport::Instanceof(js) => {
assert!(webidl_ty.kind == ast::WebidlFunctionKind::Static);
assert!(!variadic);
Expand Down
7 changes: 5 additions & 2 deletions crates/cli-support/src/webidl/mod.rs
Expand Up @@ -278,6 +278,10 @@ pub enum AuxImport {
/// imported
Value(AuxValue),

/// A static method on a class is being imported, and the `this` of the
/// function call is expected to always be the class.
ValueWithThis(JsImport, String),

/// This import is expected to be a function that takes an `anyref` and
/// returns a `bool`. It's expected that it tests if the argument is an
/// instance of (using `instanceof`) the name specified.
Expand Down Expand Up @@ -950,8 +954,7 @@ impl<'a> Context<'a> {
match op.kind {
decode::OperationKind::Regular => {
if op.is_static {
class.fields.push(function.name.to_string());
Ok((AuxImport::Value(AuxValue::Bare(class)), false))
Ok((AuxImport::ValueWithThis(class, function.name.to_string()), false))
} else if structural {
Ok((
AuxImport::StructuralMethod(function.name.to_string()),
Expand Down
3 changes: 3 additions & 0 deletions crates/cli-support/src/webidl/standard.rs
Expand Up @@ -574,6 +574,9 @@ fn check_standard_import(import: &AuxImport) -> Result<(), Error> {
| AuxImport::Value(AuxValue::ClassSetter(js, name)) => {
format!("field access of `{}` for {}", name, desc_js(js))
}
AuxImport::ValueWithThis(js, method) => {
format!("method `{}.{}`", desc_js(js), method)
}
AuxImport::Instanceof(js) => format!("instance of check of {}", desc_js(js)),
AuxImport::Static(js) => format!("static js value {}", desc_js(js)),
AuxImport::StructuralMethod(name) => format!("structural method `{}`", name),
Expand Down
Expand Up @@ -23,3 +23,6 @@ let instant = Date::now();

This is similar to the `js_namespace` attribute, but the usage from within Rust
is different since the method also becomes a static method of the imported type.
Additionally this attribute also specifies that the `this` parameter when
invoking the method is expected to be the JS class, e.g. always invoked as
`Date.now()` instead of `const x = Date.now; x()`.
8 changes: 8 additions & 0 deletions tests/wasm/imports.js
Expand Up @@ -107,3 +107,11 @@ exports.import_inside_private_module = function() {};
exports.should_call_undefined_functions = () => false;

exports.STATIC_STRING = 'x';

class StaticMethodCheck {
static static_method_of_right_this() {
assert.ok(this === StaticMethodCheck);
}
}

exports.StaticMethodCheck = StaticMethodCheck;
8 changes: 8 additions & 0 deletions tests/wasm/imports.rs
Expand Up @@ -52,6 +52,9 @@ extern "C" {
fn assert_dead_import_not_generated();
fn should_call_undefined_functions() -> bool;

type StaticMethodCheck;
#[wasm_bindgen(static_method_of = StaticMethodCheck)]
fn static_method_of_right_this();

static STATIC_STRING: String;
}
Expand Down Expand Up @@ -240,3 +243,8 @@ fn undefined_function_is_ok() {
fn static_string_ok() {
assert_eq!(*STATIC_STRING, "x");
}

#[wasm_bindgen_test]
fn static_method_of_has_right_this() {
StaticMethodCheck::static_method_of_right_this();
}

0 comments on commit 0b1a764

Please sign in to comment.