mirror of
https://github.com/RGBCube/serenity
synced 2025-07-10 04:27:35 +00:00
LibJS: Throw TypeError when calling class constructor without 'new'
This commit is contained in:
parent
b07c7f589f
commit
1b0c862f3a
5 changed files with 21 additions and 5 deletions
|
@ -715,6 +715,7 @@ Value ClassExpression::execute(Interpreter& interpreter, GlobalObject& global_ob
|
||||||
|
|
||||||
ASSERT(class_constructor_value.is_function() && class_constructor_value.as_function().is_script_function());
|
ASSERT(class_constructor_value.is_function() && class_constructor_value.as_function().is_script_function());
|
||||||
ScriptFunction* class_constructor = static_cast<ScriptFunction*>(&class_constructor_value.as_function());
|
ScriptFunction* class_constructor = static_cast<ScriptFunction*>(&class_constructor_value.as_function());
|
||||||
|
class_constructor->set_is_class_constructor();
|
||||||
Value super_constructor = js_undefined();
|
Value super_constructor = js_undefined();
|
||||||
if (!m_super_class.is_null()) {
|
if (!m_super_class.is_null()) {
|
||||||
super_constructor = m_super_class->execute(interpreter, global_object);
|
super_constructor = m_super_class->execute(interpreter, global_object);
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
M(BigIntBadOperatorOtherType, "Cannot use {} operator with BigInt and other type") \
|
M(BigIntBadOperatorOtherType, "Cannot use {} operator with BigInt and other type") \
|
||||||
M(BigIntIntArgument, "BigInt argument must be an integer") \
|
M(BigIntIntArgument, "BigInt argument must be an integer") \
|
||||||
M(BigIntInvalidValue, "Invalid value for BigInt: {}") \
|
M(BigIntInvalidValue, "Invalid value for BigInt: {}") \
|
||||||
|
M(ClassConstructorWithoutNew, "Class constructor {} must be called with 'new'") \
|
||||||
M(ClassDoesNotExtendAConstructorOrNull, "Class extends value {} is not a constructor or null") \
|
M(ClassDoesNotExtendAConstructorOrNull, "Class extends value {} is not a constructor or null") \
|
||||||
M(Convert, "Cannot convert {} to {}") \
|
M(Convert, "Cannot convert {} to {}") \
|
||||||
M(ConvertUndefinedToObject, "Cannot convert undefined to object") \
|
M(ConvertUndefinedToObject, "Cannot convert undefined to object") \
|
||||||
|
|
|
@ -110,7 +110,7 @@ LexicalEnvironment* ScriptFunction::create_environment()
|
||||||
return environment;
|
return environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value ScriptFunction::call()
|
Value ScriptFunction::execute_function_body()
|
||||||
{
|
{
|
||||||
auto& vm = this->vm();
|
auto& vm = this->vm();
|
||||||
|
|
||||||
|
@ -150,13 +150,22 @@ Value ScriptFunction::call()
|
||||||
return interpreter->execute_statement(global_object(), m_body, move(arguments), ScopeType::Function);
|
return interpreter->execute_statement(global_object(), m_body, move(arguments), ScopeType::Function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value ScriptFunction::call()
|
||||||
|
{
|
||||||
|
if (m_is_class_constructor) {
|
||||||
|
vm().throw_exception<TypeError>(global_object(), ErrorType::ClassConstructorWithoutNew, m_name);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return execute_function_body();
|
||||||
|
}
|
||||||
|
|
||||||
Value ScriptFunction::construct(Function&)
|
Value ScriptFunction::construct(Function&)
|
||||||
{
|
{
|
||||||
if (m_is_arrow_function) {
|
if (m_is_arrow_function) {
|
||||||
vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, m_name);
|
vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, m_name);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return call();
|
return execute_function_body();
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_DEFINE_NATIVE_GETTER(ScriptFunction::length_getter)
|
JS_DEFINE_NATIVE_GETTER(ScriptFunction::length_getter)
|
||||||
|
|
|
@ -50,6 +50,8 @@ public:
|
||||||
virtual const FlyString& name() const override { return m_name; };
|
virtual const FlyString& name() const override { return m_name; };
|
||||||
void set_name(const FlyString& name) { m_name = name; };
|
void set_name(const FlyString& name) { m_name = name; };
|
||||||
|
|
||||||
|
void set_is_class_constructor() { m_is_class_constructor = true; };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool is_strict_mode() const final { return m_is_strict; }
|
virtual bool is_strict_mode() const final { return m_is_strict; }
|
||||||
|
|
||||||
|
@ -58,6 +60,8 @@ private:
|
||||||
virtual LexicalEnvironment* create_environment() override;
|
virtual LexicalEnvironment* create_environment() override;
|
||||||
virtual void visit_children(Visitor&) override;
|
virtual void visit_children(Visitor&) override;
|
||||||
|
|
||||||
|
Value execute_function_body();
|
||||||
|
|
||||||
JS_DECLARE_NATIVE_GETTER(length_getter);
|
JS_DECLARE_NATIVE_GETTER(length_getter);
|
||||||
JS_DECLARE_NATIVE_GETTER(name_getter);
|
JS_DECLARE_NATIVE_GETTER(name_getter);
|
||||||
|
|
||||||
|
@ -68,6 +72,7 @@ private:
|
||||||
i32 m_function_length { 0 };
|
i32 m_function_length { 0 };
|
||||||
bool m_is_strict { false };
|
bool m_is_strict { false };
|
||||||
bool m_is_arrow_function { false };
|
bool m_is_arrow_function { false };
|
||||||
|
bool m_is_class_constructor { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,18 +46,18 @@ test("constructor length affects class length", () => {
|
||||||
expect(B).toHaveLength(2);
|
expect(B).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip("must be invoked with 'new'", () => {
|
test("must be invoked with 'new'", () => {
|
||||||
class A {
|
class A {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
A();
|
A();
|
||||||
}).toThrow(TypeError); // FIXME: Add message when this test works
|
}).toThrowWithMessage(TypeError, "Class constructor A must be called with 'new'");
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
A.prototype.constructor();
|
A.prototype.constructor();
|
||||||
}).toThrow(TypeError); // FIXME: Add message when this test works
|
}).toThrowWithMessage(TypeError, "Class constructor A must be called with 'new'");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("implicit constructor", () => {
|
test("implicit constructor", () => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue