mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 17:02:45 +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
	
	 Linus Groh
						Linus Groh