From b2de563166d8cebcba4cde017108c71a4ce326f7 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 8 Oct 2021 12:43:38 +0200 Subject: [PATCH] LibJS: Propagate "contains direct call to eval()" flag from parser We now propagate this flag to FunctionDeclaration, and then also into ECMAScriptFunctionObject. This will be used to disable optimizations that aren't safe in the presence of direct eval(). --- Userland/Libraries/LibJS/AST.cpp | 10 +++++++--- Userland/Libraries/LibJS/AST.h | 13 +++++++----- Userland/Libraries/LibJS/Parser.cpp | 20 ++++++++++++------- Userland/Libraries/LibJS/Parser.h | 2 +- .../Runtime/ECMAScriptFunctionObject.cpp | 9 +++++---- .../LibJS/Runtime/ECMAScriptFunctionObject.h | 5 +++-- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index df17066a04..8077a11eb5 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -195,7 +195,7 @@ Value FunctionExpression::instantiate_ordinary_function_expression(Interpreter& scope->create_immutable_binding(global_object, name(), false); } - auto closure = ECMAScriptFunctionObject::create(global_object, used_name, body(), parameters(), function_length(), scope, kind(), is_strict_mode(), might_need_arguments_object(), is_arrow_function()); + auto closure = ECMAScriptFunctionObject::create(global_object, used_name, body(), parameters(), function_length(), scope, kind(), is_strict_mode(), might_need_arguments_object(), contains_direct_call_to_eval(), is_arrow_function()); // FIXME: 6. Perform SetFunctionName(closure, name). // FIXME: 7. Perform MakeConstructor(closure). @@ -1734,6 +1734,10 @@ void FunctionNode::dump(int indent, String const& class_name) const { print_indent(indent); outln("{}{} '{}'", class_name, m_kind == FunctionKind::Generator ? "*" : "", name()); + if (m_contains_direct_call_to_eval) { + print_indent(indent + 1); + outln("\033[31;1m(direct eval)\033[0m"); + } if (!m_parameters.is_empty()) { print_indent(indent + 1); outln("(Parameters)"); @@ -3210,7 +3214,7 @@ void ScopeNode::block_declaration_instantiation(GlobalObject& global_object, Env if (is(declaration)) { auto& function_declaration = static_cast(declaration); - auto* function = ECMAScriptFunctionObject::create(global_object, function_declaration.name(), function_declaration.body(), function_declaration.parameters(), function_declaration.function_length(), environment, function_declaration.kind(), function_declaration.is_strict_mode(), function_declaration.might_need_arguments_object()); + auto* function = ECMAScriptFunctionObject::create(global_object, function_declaration.name(), function_declaration.body(), function_declaration.parameters(), function_declaration.function_length(), environment, function_declaration.kind(), function_declaration.is_strict_mode(), function_declaration.might_need_arguments_object(), function_declaration.contains_direct_call_to_eval()); VERIFY(is(*environment)); static_cast(*environment).initialize_or_set_mutable_binding({}, global_object, function_declaration.name(), function); } @@ -3354,7 +3358,7 @@ ThrowCompletionOr Program::global_declaration_instantiation(Interpreter& i }); for (auto& declaration : functions_to_initialize) { - auto* function = ECMAScriptFunctionObject::create(global_object, declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), &global_environment, declaration.kind(), declaration.is_strict_mode(), declaration.might_need_arguments_object()); + auto* function = ECMAScriptFunctionObject::create(global_object, declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), &global_environment, declaration.kind(), declaration.is_strict_mode(), declaration.might_need_arguments_object(), declaration.contains_direct_call_to_eval()); global_environment.create_global_function_binding(declaration.name(), function, false); if (auto* exception = interpreter.exception()) return throw_completion(exception->value()); diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 27a1797871..d2cdb8bbaa 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -441,11 +441,12 @@ public: i32 function_length() const { return m_function_length; } bool is_strict_mode() const { return m_is_strict_mode; } bool might_need_arguments_object() const { return m_might_need_arguments_object; } + bool contains_direct_call_to_eval() const { return m_contains_direct_call_to_eval; } bool is_arrow_function() const { return m_is_arrow_function; } FunctionKind kind() const { return m_kind; } protected: - FunctionNode(FlyString name, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool is_arrow_function) + FunctionNode(FlyString name, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function) : m_name(move(name)) , m_body(move(body)) , m_parameters(move(parameters)) @@ -453,6 +454,7 @@ protected: , m_kind(kind) , m_is_strict_mode(is_strict_mode) , m_might_need_arguments_object(might_need_arguments_object) + , m_contains_direct_call_to_eval(contains_direct_call_to_eval) , m_is_arrow_function(is_arrow_function) { if (m_is_arrow_function) @@ -476,6 +478,7 @@ private: FunctionKind m_kind; bool m_is_strict_mode { false }; bool m_might_need_arguments_object { false }; + bool m_contains_direct_call_to_eval { false }; bool m_is_arrow_function { false }; }; @@ -485,9 +488,9 @@ class FunctionDeclaration final public: static bool must_have_name() { return true; } - FunctionDeclaration(SourceRange source_range, FlyString const& name, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object) + FunctionDeclaration(SourceRange source_range, FlyString const& name, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval) : Declaration(source_range) - , FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, false) + , FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, false) { } @@ -511,9 +514,9 @@ class FunctionExpression final public: static bool must_have_name() { return false; } - FunctionExpression(SourceRange source_range, FlyString const& name, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool is_arrow_function = false) + FunctionExpression(SourceRange source_range, FlyString const& name, NonnullRefPtr body, Vector parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function = false) : Expression(source_range) - , FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, is_arrow_function) + , FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function) { } diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index bf33bfcb52..875c520ac9 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -670,11 +670,13 @@ RefPtr Parser::try_parse_arrow_function_expression(bool expe m_state.labels_in_scope = move(old_labels_in_scope); }); + bool contains_direct_call_to_eval = false; + auto function_body_result = [&]() -> RefPtr { TemporaryChange change(m_state.in_arrow_function_context, true); if (match(TokenType::CurlyOpen)) { // Parse a function body with statements - return parse_function_body(parameters, FunctionKind::Regular); + return parse_function_body(parameters, FunctionKind::Regular, contains_direct_call_to_eval); } if (match_expression()) { // Parse a function body which returns a single expression @@ -689,6 +691,7 @@ RefPtr Parser::try_parse_arrow_function_expression(bool expe return_block->append({ m_filename, rule_start.position(), position() }, move(return_expression)); if (m_state.strict_mode) return_block->set_strict_mode(); + contains_direct_call_to_eval = function_scope.contains_direct_call_to_eval(); return return_block; } // Invalid arrow function body @@ -715,7 +718,7 @@ RefPtr Parser::try_parse_arrow_function_expression(bool expe return create_ast_node( { m_state.current_token.filename(), rule_start.position(), position() }, "", move(body), move(parameters), function_length, FunctionKind::Regular, body->in_strict_mode(), - /* might_need_arguments_object */ false, /* is_arrow_function */ true); + /* might_need_arguments_object */ false, contains_direct_call_to_eval, /* is_arrow_function */ true); } RefPtr Parser::try_parse_labelled_statement(AllowLabelledFunction allow_function) @@ -1034,12 +1037,12 @@ NonnullRefPtr Parser::parse_class_expression(bool expect_class_ constructor = create_ast_node( { m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body), Vector { FunctionNode::Parameter { FlyString { "args" }, nullptr, true } }, 0, FunctionKind::Regular, - /* is_strict_mode */ true, /* might_need_arguments_object */ false); + /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false); } else { constructor = create_ast_node( { m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body), Vector {}, 0, FunctionKind::Regular, - /* is_strict_mode */ true, /* might_need_arguments_object */ false); + /* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false); } } @@ -1996,7 +1999,7 @@ void Parser::parse_statement_list(ScopeNode& output_node, AllowLabelledFunction } // FunctionBody, https://tc39.es/ecma262/#prod-FunctionBody -NonnullRefPtr Parser::parse_function_body(Vector const& parameters, FunctionKind function_kind) +NonnullRefPtr Parser::parse_function_body(Vector const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval) { auto rule_start = push_start(); auto function_body = create_ast_node({ m_state.current_token.filename(), rule_start.position(), position() }); @@ -2052,6 +2055,7 @@ NonnullRefPtr Parser::parse_function_body(Vector Parser::parse_function_node(u8 parse_options) m_state.labels_in_scope = move(old_labels_in_scope); }); - auto body = parse_function_body(parameters, function_kind); + bool contains_direct_call_to_eval = false; + auto body = parse_function_body(parameters, function_kind, contains_direct_call_to_eval); auto has_strict_directive = body->in_strict_mode(); @@ -2127,7 +2132,8 @@ NonnullRefPtr Parser::parse_function_node(u8 parse_options) return create_ast_node( { m_state.current_token.filename(), rule_start.position(), position() }, name, move(body), move(parameters), function_length, - function_kind, has_strict_directive, m_state.function_might_need_arguments_object); + function_kind, has_strict_directive, m_state.function_might_need_arguments_object, + contains_direct_call_to_eval); } Vector Parser::parse_formal_parameters(int& function_length, u8 parse_options) diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index 4613aaf8fd..684adb86b3 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -72,7 +72,7 @@ public: NonnullRefPtr parse_statement(AllowLabelledFunction allow_labelled_function = AllowLabelledFunction::No); NonnullRefPtr parse_block_statement(); - NonnullRefPtr parse_function_body(Vector const& parameters, FunctionKind function_kind); + NonnullRefPtr parse_function_body(Vector const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval); NonnullRefPtr parse_return_statement(); NonnullRefPtr parse_variable_declaration(bool for_loop_variable_declaration = false); NonnullRefPtr parse_for_statement(); diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index e998236294..ba8fb91805 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -24,7 +24,7 @@ namespace JS { -ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_object, FlyString name, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Environment* parent_scope, FunctionKind kind, bool is_strict, bool might_need_arguments_object, bool is_arrow_function) +ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_object, FlyString name, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Environment* parent_scope, FunctionKind kind, bool is_strict, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function) { Object* prototype = nullptr; switch (kind) { @@ -35,10 +35,10 @@ ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_ prototype = global_object.generator_function_prototype(); break; } - return global_object.heap().allocate(global_object, move(name), ecmascript_code, move(parameters), m_function_length, parent_scope, *prototype, kind, is_strict, might_need_arguments_object, is_arrow_function); + return global_object.heap().allocate(global_object, move(name), ecmascript_code, move(parameters), m_function_length, parent_scope, *prototype, kind, is_strict, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function); } -ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, Statement const& ecmascript_code, Vector formal_parameters, i32 function_length, Environment* parent_scope, Object& prototype, FunctionKind kind, bool strict, bool might_need_arguments_object, bool is_arrow_function) +ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, Statement const& ecmascript_code, Vector formal_parameters, i32 function_length, Environment* parent_scope, Object& prototype, FunctionKind kind, bool strict, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function) : FunctionObject(prototype) , m_environment(parent_scope) , m_formal_parameters(move(formal_parameters)) @@ -49,6 +49,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, Statement con , m_function_length(function_length) , m_kind(kind) , m_might_need_arguments_object(might_need_arguments_object) + , m_contains_direct_call_to_eval(contains_direct_call_to_eval) , m_is_arrow_function(is_arrow_function) { // NOTE: This logic is from OrdinaryFunctionCreate, https://tc39.es/ecma262/#sec-ordinaryfunctioncreate @@ -367,7 +368,7 @@ ThrowCompletionOr ECMAScriptFunctionObject::function_declaration_instantia VERIFY(!vm.exception()); for (auto& declaration : functions_to_initialize) { - auto* function = ECMAScriptFunctionObject::create(global_object(), declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), lex_environment, declaration.kind(), declaration.is_strict_mode(), declaration.might_need_arguments_object()); + auto* function = ECMAScriptFunctionObject::create(global_object(), declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), lex_environment, declaration.kind(), declaration.is_strict_mode(), declaration.might_need_arguments_object(), declaration.contains_direct_call_to_eval()); var_environment->set_mutable_binding(global_object(), declaration.name(), function, false); } diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h index b936997988..f7f4121869 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h @@ -28,9 +28,9 @@ public: Global, }; - static ECMAScriptFunctionObject* create(GlobalObject&, FlyString name, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Environment* parent_scope, FunctionKind, bool is_strict, bool might_need_arguments_object = true, bool is_arrow_function = false); + static ECMAScriptFunctionObject* create(GlobalObject&, FlyString name, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Environment* parent_scope, FunctionKind, bool is_strict, bool might_need_arguments_object = true, bool contains_direct_call_to_eval = true, bool is_arrow_function = false); - ECMAScriptFunctionObject(FlyString name, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Environment* parent_scope, Object& prototype, FunctionKind, bool is_strict, bool might_need_arguments_object, bool is_arrow_function); + ECMAScriptFunctionObject(FlyString name, Statement const& ecmascript_code, Vector parameters, i32 m_function_length, Environment* parent_scope, Object& prototype, FunctionKind, bool is_strict, bool might_need_arguments_object, bool contains_direct_call_to_eval, bool is_arrow_function); virtual void initialize(GlobalObject&) override; virtual ~ECMAScriptFunctionObject(); @@ -101,6 +101,7 @@ private: i32 m_function_length { 0 }; FunctionKind m_kind { FunctionKind::Regular }; bool m_might_need_arguments_object { true }; + bool m_contains_direct_call_to_eval { true }; bool m_is_arrow_function { false }; bool m_has_simple_parameter_list { false }; };