Browse Source

fix computed property methods can call super methods (#2274)

<!---
Thank you for contributing to Boa! Please fill out the template below, and remove or add any
information as you feel neccesary.
--->

This Pull Request fixes: 79e3bc5176/test/language/computed-property-names/object/method/super.js

This PR solves the bug of using the `super` keyword in the method attribute of object. When the environment is `None`, the `vm` gets the top element  of `vm.stack` as `this`, such as:
```js
var a = {
    f() {
        return super.m();
    }
};

var b = {
    m() {
        retrun "super";
    }
};

Object.setPrototypeOf(a, b);

a.f(); // the top of stack is `a`
let f = a.f;
f(); // the top of stack is `global_this`, so `super` cannot be used
```

### Can be improved

What I think is that when I use `object.method()`, the engine should bind `this_object`  to the `environment`, instead of using `vm.stack.last()...`.

### TODOS
1. `super` need to look for properties all the way to the end.

Co-authored-by: creampnx_x <2270436024@qq.com>
pull/2278/head
creampnx_x 2 years ago
parent
commit
c43539a428
  1. 12
      boa_engine/src/vm/mod.rs
  2. 45
      boa_engine/src/vm/tests.rs

12
boa_engine/src/vm/mod.rs

@ -1521,7 +1521,17 @@ impl Context {
let function = function_object
.as_function()
.expect("must be function object");
function.get_home_object().cloned()
let mut home_object = function.get_home_object().cloned();
if home_object.is_none() {
home_object = env
.get_this_binding()
.expect("can not get `this` object")
.as_object()
.cloned();
}
home_object
} else {
return self.throw_range_error("Must call super constructor in derived class before accessing 'this' or returning from derived constructor");
};

45
boa_engine/src/vm/tests.rs

@ -137,3 +137,48 @@ fn finally_block_binding_env() {
Ok(JsValue::from("Hey hey people"))
);
}
#[test]
fn run_super_method_in_object() {
let source = r#"
let proto = {
m() { return "super"; }
};
let obj = {
v() { return super.m(); }
};
Object.setPrototypeOf(obj, proto);
obj.v();
"#;
assert_eq!(
Context::default().eval(source.as_bytes()),
Ok(JsValue::from("super"))
)
}
#[test]
fn get_reference_by_super() {
let source = r#"
var fromA, fromB;
var A = { fromA: 'a', fromB: 'a' };
var B = { fromB: 'b' };
Object.setPrototypeOf(B, A);
var obj = {
fromA: 'c',
fromB: 'c',
method() {
fromA = (() => { return super.fromA; })();
fromB = (() => { return super.fromB; })();
}
};
Object.setPrototypeOf(obj, B);
obj.method();
fromA + fromB
"#;
assert_eq!(
Context::default().eval(source.as_bytes()),
Ok(JsValue::from("ab"))
)
}

Loading…
Cancel
Save