mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 15:12:45 +00:00 
			
		
		
		
	 4fa5748093
			
		
	
	
		4fa5748093
		
	
	
	
	
		
			
			This gives FunctionNode a "might need arguments object" boolean flag and
sets it based on the simplest possible heuristic for this: if we
encounter an identifier called "arguments" or "eval" up to the next
(nested) function declaration or expression, we won't need an arguments
object. Otherwise, we *might* need one - the final decision is made in
the FunctionDeclarationInstantiation AO.
Now, this is obviously not perfect. Even if you avoid eval, something
like `foo.arguments` will still trigger a false positive - but it's a
start and already massively cuts down on needlessly allocated objects,
especially in real-world code that is often minified, and so a full
"arguments" identifier will be an actual arguments object more often
than not.
To illustrate the actual impact of this change, here's the number of
allocated arguments objects during a full test-js run:
Before:
- Unmapped arguments objects: 78765
- Mapped arguments objects: 2455
After:
- Unmapped arguments objects: 18
- Mapped arguments objects: 37
This results in a ~5% speedup of test-js on my Linux host machine, and
about 3.5% on i686 Serenity in QEMU (warm runs, average of 5).
The following microbenchmark (calling an empty function 1M times) runs
25% faster on Linux and 45% on Serenity:
    function foo() {}
    for (var i = 0; i < 1_000_000; ++i)
        foo();
test262 reports no changes in either direction, apart from a speedup :^)
		
	
			
		
			
				
	
	
		
			108 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			108 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <LibJS/AST.h>
 | |
| #include <LibJS/Bytecode/Generator.h>
 | |
| #include <LibJS/Runtime/FunctionObject.h>
 | |
| 
 | |
| namespace JS {
 | |
| 
 | |
| // 10.2 ECMAScript Function Objects, https://tc39.es/ecma262/#sec-ecmascript-function-objects
 | |
| class ECMAScriptFunctionObject final : public FunctionObject {
 | |
|     JS_OBJECT(ECMAScriptFunctionObject, FunctionObject);
 | |
| 
 | |
| public:
 | |
|     enum class ConstructorKind : u8 {
 | |
|         Base,
 | |
|         Derived,
 | |
|     };
 | |
| 
 | |
|     enum class ThisMode : u8 {
 | |
|         Lexical,
 | |
|         Strict,
 | |
|         Global,
 | |
|     };
 | |
| 
 | |
|     static ECMAScriptFunctionObject* create(GlobalObject&, FlyString name, Statement const& ecmascript_code, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, Environment* parent_scope, FunctionKind, bool is_strict, bool might_need_arguments_object = true, bool is_arrow_function = false);
 | |
| 
 | |
|     ECMAScriptFunctionObject(FlyString name, Statement const& ecmascript_code, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, Environment* parent_scope, Object& prototype, FunctionKind, bool is_strict, bool might_need_arguments_object, bool is_arrow_function);
 | |
|     virtual void initialize(GlobalObject&) override;
 | |
|     virtual ~ECMAScriptFunctionObject();
 | |
| 
 | |
|     Statement const& ecmascript_code() const { return m_ecmascript_code; }
 | |
|     Vector<FunctionNode::Parameter> const& formal_parameters() const { return m_formal_parameters; };
 | |
| 
 | |
|     virtual Value call() override;
 | |
|     virtual Value construct(FunctionObject& new_target) override;
 | |
| 
 | |
|     virtual const FlyString& name() const override { return m_name; };
 | |
|     void set_name(const FlyString& name);
 | |
| 
 | |
|     void set_is_class_constructor() { m_is_class_constructor = true; };
 | |
| 
 | |
|     auto& bytecode_executable() const { return m_bytecode_executable; }
 | |
| 
 | |
|     Environment* environment() { return m_environment; }
 | |
|     virtual Realm* realm() const override { return m_realm; }
 | |
| 
 | |
|     ConstructorKind constructor_kind() const { return m_constructor_kind; };
 | |
|     void set_constructor_kind(ConstructorKind constructor_kind) { m_constructor_kind = constructor_kind; }
 | |
| 
 | |
|     ThisMode this_mode() const { return m_this_mode; }
 | |
| 
 | |
|     Object* home_object() const { return m_home_object; }
 | |
|     void set_home_object(Object* home_object) { m_home_object = home_object; }
 | |
| 
 | |
|     struct InstanceField {
 | |
|         StringOrSymbol name;
 | |
|         ECMAScriptFunctionObject* initializer { nullptr };
 | |
| 
 | |
|         void define_field(VM& vm, Object& receiver) const;
 | |
|     };
 | |
| 
 | |
|     Vector<InstanceField> const& fields() const { return m_fields; }
 | |
|     void add_field(StringOrSymbol property_key, ECMAScriptFunctionObject* initializer) { m_fields.empend(property_key, initializer); }
 | |
| 
 | |
|     // This is for IsSimpleParameterList (static semantics)
 | |
|     bool has_simple_parameter_list() const { return m_has_simple_parameter_list; }
 | |
| 
 | |
|     virtual bool has_constructor() const override { return true; }
 | |
| 
 | |
| protected:
 | |
|     virtual bool is_strict_mode() const final { return m_strict; }
 | |
| 
 | |
| private:
 | |
|     virtual bool is_ecmascript_function_object() const override { return true; }
 | |
|     virtual FunctionEnvironment* new_function_environment(Object* new_target) override;
 | |
|     virtual void visit_edges(Visitor&) override;
 | |
| 
 | |
|     ThrowCompletionOr<void> function_declaration_instantiation(Interpreter*);
 | |
|     Value execute_function_body();
 | |
| 
 | |
|     // Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects
 | |
|     Environment* m_environment { nullptr };                       // [[Environment]]
 | |
|     Vector<FunctionNode::Parameter> const m_formal_parameters;    // [[FormalParameters]]
 | |
|     NonnullRefPtr<Statement> m_ecmascript_code;                   // [[ECMAScriptCode]]
 | |
|     ConstructorKind m_constructor_kind { ConstructorKind::Base }; // [[ConstructorKind]]
 | |
|     Realm* m_realm { nullptr };                                   // [[Realm]]
 | |
|     ThisMode m_this_mode { ThisMode::Global };                    // [[ThisMode]]
 | |
|     bool m_strict { false };                                      // [[Strict]]
 | |
|     Object* m_home_object { nullptr };                            // [[HomeObject]]
 | |
|     Vector<InstanceField> m_fields;                               // [[Fields]]
 | |
|     bool m_is_class_constructor { false };                        // [[IsClassConstructor]]
 | |
| 
 | |
|     FlyString m_name;
 | |
|     Optional<Bytecode::Executable> m_bytecode_executable;
 | |
|     i32 m_function_length { 0 };
 | |
|     FunctionKind m_kind { FunctionKind::Regular };
 | |
|     bool m_might_need_arguments_object { true };
 | |
|     bool m_is_arrow_function { false };
 | |
|     bool m_has_simple_parameter_list { false };
 | |
| };
 | |
| 
 | |
| }
 |