1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 08:58:11 +00:00

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().
This commit is contained in:
Andreas Kling 2021-10-08 12:43:38 +02:00
parent 12b283f32f
commit b2de563166
6 changed files with 37 additions and 22 deletions

View file

@ -195,7 +195,7 @@ Value FunctionExpression::instantiate_ordinary_function_expression(Interpreter&
scope->create_immutable_binding(global_object, name(), false); 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: 6. Perform SetFunctionName(closure, name).
// FIXME: 7. Perform MakeConstructor(closure). // FIXME: 7. Perform MakeConstructor(closure).
@ -1734,6 +1734,10 @@ void FunctionNode::dump(int indent, String const& class_name) const
{ {
print_indent(indent); print_indent(indent);
outln("{}{} '{}'", class_name, m_kind == FunctionKind::Generator ? "*" : "", name()); 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()) { if (!m_parameters.is_empty()) {
print_indent(indent + 1); print_indent(indent + 1);
outln("(Parameters)"); outln("(Parameters)");
@ -3210,7 +3214,7 @@ void ScopeNode::block_declaration_instantiation(GlobalObject& global_object, Env
if (is<FunctionDeclaration>(declaration)) { if (is<FunctionDeclaration>(declaration)) {
auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration); auto& function_declaration = static_cast<FunctionDeclaration const&>(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<DeclarativeEnvironment>(*environment)); VERIFY(is<DeclarativeEnvironment>(*environment));
static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, global_object, function_declaration.name(), function); static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, global_object, function_declaration.name(), function);
} }
@ -3354,7 +3358,7 @@ ThrowCompletionOr<void> Program::global_declaration_instantiation(Interpreter& i
}); });
for (auto& declaration : functions_to_initialize) { 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); global_environment.create_global_function_binding(declaration.name(), function, false);
if (auto* exception = interpreter.exception()) if (auto* exception = interpreter.exception())
return throw_completion(exception->value()); return throw_completion(exception->value());

View file

@ -441,11 +441,12 @@ public:
i32 function_length() const { return m_function_length; } i32 function_length() const { return m_function_length; }
bool is_strict_mode() const { return m_is_strict_mode; } bool is_strict_mode() const { return m_is_strict_mode; }
bool might_need_arguments_object() const { return m_might_need_arguments_object; } 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; } bool is_arrow_function() const { return m_is_arrow_function; }
FunctionKind kind() const { return m_kind; } FunctionKind kind() const { return m_kind; }
protected: protected:
FunctionNode(FlyString name, NonnullRefPtr<Statement> body, Vector<Parameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool is_arrow_function) FunctionNode(FlyString name, NonnullRefPtr<Statement> body, Vector<Parameter> 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_name(move(name))
, m_body(move(body)) , m_body(move(body))
, m_parameters(move(parameters)) , m_parameters(move(parameters))
@ -453,6 +454,7 @@ protected:
, m_kind(kind) , m_kind(kind)
, m_is_strict_mode(is_strict_mode) , m_is_strict_mode(is_strict_mode)
, m_might_need_arguments_object(might_need_arguments_object) , 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) , m_is_arrow_function(is_arrow_function)
{ {
if (m_is_arrow_function) if (m_is_arrow_function)
@ -476,6 +478,7 @@ private:
FunctionKind m_kind; FunctionKind m_kind;
bool m_is_strict_mode { false }; bool m_is_strict_mode { false };
bool m_might_need_arguments_object { false }; bool m_might_need_arguments_object { false };
bool m_contains_direct_call_to_eval { false };
bool m_is_arrow_function { false }; bool m_is_arrow_function { false };
}; };
@ -485,9 +488,9 @@ class FunctionDeclaration final
public: public:
static bool must_have_name() { return true; } static bool must_have_name() { return true; }
FunctionDeclaration(SourceRange source_range, FlyString const& name, NonnullRefPtr<Statement> body, Vector<Parameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object) FunctionDeclaration(SourceRange source_range, FlyString const& name, NonnullRefPtr<Statement> body, Vector<Parameter> parameters, i32 function_length, FunctionKind kind, bool is_strict_mode, bool might_need_arguments_object, bool contains_direct_call_to_eval)
: Declaration(source_range) : 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: public:
static bool must_have_name() { return false; } static bool must_have_name() { return false; }
FunctionExpression(SourceRange source_range, FlyString const& name, NonnullRefPtr<Statement> body, Vector<Parameter> 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<Statement> body, Vector<Parameter> 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) : 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)
{ {
} }

View file

@ -670,11 +670,13 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
m_state.labels_in_scope = move(old_labels_in_scope); m_state.labels_in_scope = move(old_labels_in_scope);
}); });
bool contains_direct_call_to_eval = false;
auto function_body_result = [&]() -> RefPtr<FunctionBody> { auto function_body_result = [&]() -> RefPtr<FunctionBody> {
TemporaryChange change(m_state.in_arrow_function_context, true); TemporaryChange change(m_state.in_arrow_function_context, true);
if (match(TokenType::CurlyOpen)) { if (match(TokenType::CurlyOpen)) {
// Parse a function body with statements // 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()) { if (match_expression()) {
// Parse a function body which returns a single expression // Parse a function body which returns a single expression
@ -689,6 +691,7 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
return_block->append<ReturnStatement>({ m_filename, rule_start.position(), position() }, move(return_expression)); return_block->append<ReturnStatement>({ m_filename, rule_start.position(), position() }, move(return_expression));
if (m_state.strict_mode) if (m_state.strict_mode)
return_block->set_strict_mode(); return_block->set_strict_mode();
contains_direct_call_to_eval = function_scope.contains_direct_call_to_eval();
return return_block; return return_block;
} }
// Invalid arrow function body // Invalid arrow function body
@ -715,7 +718,7 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
return create_ast_node<FunctionExpression>( return create_ast_node<FunctionExpression>(
{ m_state.current_token.filename(), rule_start.position(), position() }, "", move(body), { m_state.current_token.filename(), rule_start.position(), position() }, "", move(body),
move(parameters), function_length, FunctionKind::Regular, body->in_strict_mode(), 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<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction allow_function) RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction allow_function)
@ -1034,12 +1037,12 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
constructor = create_ast_node<FunctionExpression>( constructor = create_ast_node<FunctionExpression>(
{ m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body), { m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body),
Vector { FunctionNode::Parameter { FlyString { "args" }, nullptr, true } }, 0, FunctionKind::Regular, 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 { } else {
constructor = create_ast_node<FunctionExpression>( constructor = create_ast_node<FunctionExpression>(
{ m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body), { m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body),
Vector<FunctionNode::Parameter> {}, 0, FunctionKind::Regular, Vector<FunctionNode::Parameter> {}, 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 // FunctionBody, https://tc39.es/ecma262/#prod-FunctionBody
NonnullRefPtr<FunctionBody> Parser::parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind) NonnullRefPtr<FunctionBody> Parser::parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval)
{ {
auto rule_start = push_start(); auto rule_start = push_start();
auto function_body = create_ast_node<FunctionBody>({ m_state.current_token.filename(), rule_start.position(), position() }); auto function_body = create_ast_node<FunctionBody>({ m_state.current_token.filename(), rule_start.position(), position() });
@ -2052,6 +2055,7 @@ NonnullRefPtr<FunctionBody> Parser::parse_function_body(Vector<FunctionDeclarati
consume(TokenType::CurlyClose); consume(TokenType::CurlyClose);
m_state.strict_mode = previous_strict_mode; m_state.strict_mode = previous_strict_mode;
contains_direct_call_to_eval = function_scope.contains_direct_call_to_eval();
return function_body; return function_body;
} }
@ -2117,7 +2121,8 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options)
m_state.labels_in_scope = move(old_labels_in_scope); 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(); auto has_strict_directive = body->in_strict_mode();
@ -2127,7 +2132,8 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options)
return create_ast_node<FunctionNodeType>( return create_ast_node<FunctionNodeType>(
{ m_state.current_token.filename(), rule_start.position(), position() }, { m_state.current_token.filename(), rule_start.position(), position() },
name, move(body), move(parameters), function_length, 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<FunctionNode::Parameter> Parser::parse_formal_parameters(int& function_length, u8 parse_options) Vector<FunctionNode::Parameter> Parser::parse_formal_parameters(int& function_length, u8 parse_options)

View file

@ -72,7 +72,7 @@ public:
NonnullRefPtr<Statement> parse_statement(AllowLabelledFunction allow_labelled_function = AllowLabelledFunction::No); NonnullRefPtr<Statement> parse_statement(AllowLabelledFunction allow_labelled_function = AllowLabelledFunction::No);
NonnullRefPtr<BlockStatement> parse_block_statement(); NonnullRefPtr<BlockStatement> parse_block_statement();
NonnullRefPtr<FunctionBody> parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind); NonnullRefPtr<FunctionBody> parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval);
NonnullRefPtr<ReturnStatement> parse_return_statement(); NonnullRefPtr<ReturnStatement> parse_return_statement();
NonnullRefPtr<VariableDeclaration> parse_variable_declaration(bool for_loop_variable_declaration = false); NonnullRefPtr<VariableDeclaration> parse_variable_declaration(bool for_loop_variable_declaration = false);
NonnullRefPtr<Statement> parse_for_statement(); NonnullRefPtr<Statement> parse_for_statement();

View file

@ -24,7 +24,7 @@
namespace JS { namespace JS {
ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_object, FlyString name, Statement const& ecmascript_code, Vector<FunctionNode::Parameter> 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<FunctionNode::Parameter> 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; Object* prototype = nullptr;
switch (kind) { switch (kind) {
@ -35,10 +35,10 @@ ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_
prototype = global_object.generator_function_prototype(); prototype = global_object.generator_function_prototype();
break; break;
} }
return global_object.heap().allocate<ECMAScriptFunctionObject>(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<ECMAScriptFunctionObject>(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<FunctionNode::Parameter> 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<FunctionNode::Parameter> 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) : FunctionObject(prototype)
, m_environment(parent_scope) , m_environment(parent_scope)
, m_formal_parameters(move(formal_parameters)) , m_formal_parameters(move(formal_parameters))
@ -49,6 +49,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, Statement con
, m_function_length(function_length) , m_function_length(function_length)
, m_kind(kind) , m_kind(kind)
, m_might_need_arguments_object(might_need_arguments_object) , 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) , m_is_arrow_function(is_arrow_function)
{ {
// NOTE: This logic is from OrdinaryFunctionCreate, https://tc39.es/ecma262/#sec-ordinaryfunctioncreate // NOTE: This logic is from OrdinaryFunctionCreate, https://tc39.es/ecma262/#sec-ordinaryfunctioncreate
@ -367,7 +368,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
VERIFY(!vm.exception()); VERIFY(!vm.exception());
for (auto& declaration : functions_to_initialize) { 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); var_environment->set_mutable_binding(global_object(), declaration.name(), function, false);
} }

View file

@ -28,9 +28,9 @@ public:
Global, 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); 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 contains_direct_call_to_eval = 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); 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 contains_direct_call_to_eval, bool is_arrow_function);
virtual void initialize(GlobalObject&) override; virtual void initialize(GlobalObject&) override;
virtual ~ECMAScriptFunctionObject(); virtual ~ECMAScriptFunctionObject();
@ -101,6 +101,7 @@ private:
i32 m_function_length { 0 }; i32 m_function_length { 0 };
FunctionKind m_kind { FunctionKind::Regular }; FunctionKind m_kind { FunctionKind::Regular };
bool m_might_need_arguments_object { true }; bool m_might_need_arguments_object { true };
bool m_contains_direct_call_to_eval { true };
bool m_is_arrow_function { false }; bool m_is_arrow_function { false };
bool m_has_simple_parameter_list { false }; bool m_has_simple_parameter_list { false };
}; };