LibJS: Make super() in catch block work

The TryStatement handler execution creates a new LexicalEnvironment
without a current function set, which we were not accounting for when
trying to get the super constructor while executing a SuperExpression.
This makes it work but isn't pretty - this needs some refactoring to be
close to the spec for that to happen.

Fixes #7045.
This commit is contained in:
Linus Groh 2021-05-11 23:31:30 +01:00
parent d85b9fd5a0
commit 0a329d2d70
Notes: sideshowbarker 2024-07-18 18:19:20 +09:00
2 changed files with 30 additions and 2 deletions

View file

@ -220,7 +220,14 @@ Value CallExpression::execute(Interpreter& interpreter, GlobalObject& global_obj
if (result.is_object())
new_object = &result.as_object();
} else if (is<SuperExpression>(*m_callee)) {
auto* super_constructor = interpreter.current_environment()->current_function()->prototype();
// FIXME: This is merely a band-aid to make super() inside catch {} work (which constructs
// a new LexicalEnvironment without current function). Implement GetSuperConstructor()
// and subsequently GetThisEnvironment() instead.
auto* function_environment = interpreter.current_environment();
if (!function_environment->current_function())
function_environment = static_cast<LexicalEnvironment*>(function_environment->parent());
auto* super_constructor = function_environment->current_function()->prototype();
// FIXME: Functions should track their constructor kind.
if (!super_constructor || !super_constructor->is_function()) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotAConstructor, "Super constructor");
@ -230,7 +237,7 @@ Value CallExpression::execute(Interpreter& interpreter, GlobalObject& global_obj
if (vm.exception())
return {};
interpreter.current_environment()->bind_this_value(global_object, result);
function_environment->bind_this_value(global_object, result);
} else {
result = vm.call(function, this_value, move(arguments));
}

View file

@ -131,3 +131,24 @@ test("super constructor call from child class with argument", () => {
expect(p.x).toBe(3);
expect(c.x).toBe(10);
});
test("issue #7045, super constructor call from child class in catch {}", () => {
class Parent {
constructor(x) {
this.x = x;
}
}
class Child extends Parent {
constructor() {
try {
throw new Error("Error in Child constructor");
} catch (e) {
super(e.message);
}
}
}
const c = new Child();
expect(c.x).toBe("Error in Child constructor");
});