Skip to content

Commit b2de563

Browse files
committed
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().
1 parent 12b283f commit b2de563

File tree

6 files changed

+37
-22
lines changed

6 files changed

+37
-22
lines changed

Userland/Libraries/LibJS/AST.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ Value FunctionExpression::instantiate_ordinary_function_expression(Interpreter&
195195
scope->create_immutable_binding(global_object, name(), false);
196196
}
197197

198-
auto closure = ECMAScriptFunctionObject::create(global_object, used_name, body(), parameters(), function_length(), scope, kind(), is_strict_mode(), might_need_arguments_object(), is_arrow_function());
198+
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());
199199

200200
// FIXME: 6. Perform SetFunctionName(closure, name).
201201
// FIXME: 7. Perform MakeConstructor(closure).
@@ -1734,6 +1734,10 @@ void FunctionNode::dump(int indent, String const& class_name) const
17341734
{
17351735
print_indent(indent);
17361736
outln("{}{} '{}'", class_name, m_kind == FunctionKind::Generator ? "*" : "", name());
1737+
if (m_contains_direct_call_to_eval) {
1738+
print_indent(indent + 1);
1739+
outln("\033[31;1m(direct eval)\033[0m");
1740+
}
17371741
if (!m_parameters.is_empty()) {
17381742
print_indent(indent + 1);
17391743
outln("(Parameters)");
@@ -3210,7 +3214,7 @@ void ScopeNode::block_declaration_instantiation(GlobalObject& global_object, Env
32103214

32113215
if (is<FunctionDeclaration>(declaration)) {
32123216
auto& function_declaration = static_cast<FunctionDeclaration const&>(declaration);
3213-
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());
3217+
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());
32143218
VERIFY(is<DeclarativeEnvironment>(*environment));
32153219
static_cast<DeclarativeEnvironment&>(*environment).initialize_or_set_mutable_binding({}, global_object, function_declaration.name(), function);
32163220
}
@@ -3354,7 +3358,7 @@ ThrowCompletionOr<void> Program::global_declaration_instantiation(Interpreter& i
33543358
});
33553359

33563360
for (auto& declaration : functions_to_initialize) {
3357-
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());
3361+
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());
33583362
global_environment.create_global_function_binding(declaration.name(), function, false);
33593363
if (auto* exception = interpreter.exception())
33603364
return throw_completion(exception->value());

Userland/Libraries/LibJS/AST.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -441,18 +441,20 @@ class FunctionNode {
441441
i32 function_length() const { return m_function_length; }
442442
bool is_strict_mode() const { return m_is_strict_mode; }
443443
bool might_need_arguments_object() const { return m_might_need_arguments_object; }
444+
bool contains_direct_call_to_eval() const { return m_contains_direct_call_to_eval; }
444445
bool is_arrow_function() const { return m_is_arrow_function; }
445446
FunctionKind kind() const { return m_kind; }
446447

447448
protected:
448-
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)
449+
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)
449450
: m_name(move(name))
450451
, m_body(move(body))
451452
, m_parameters(move(parameters))
452453
, m_function_length(function_length)
453454
, m_kind(kind)
454455
, m_is_strict_mode(is_strict_mode)
455456
, m_might_need_arguments_object(might_need_arguments_object)
457+
, m_contains_direct_call_to_eval(contains_direct_call_to_eval)
456458
, m_is_arrow_function(is_arrow_function)
457459
{
458460
if (m_is_arrow_function)
@@ -476,6 +478,7 @@ class FunctionNode {
476478
FunctionKind m_kind;
477479
bool m_is_strict_mode { false };
478480
bool m_might_need_arguments_object { false };
481+
bool m_contains_direct_call_to_eval { false };
479482
bool m_is_arrow_function { false };
480483
};
481484

@@ -485,9 +488,9 @@ class FunctionDeclaration final
485488
public:
486489
static bool must_have_name() { return true; }
487490

488-
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)
491+
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)
489492
: Declaration(source_range)
490-
, FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, false)
493+
, FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, false)
491494
{
492495
}
493496

@@ -511,9 +514,9 @@ class FunctionExpression final
511514
public:
512515
static bool must_have_name() { return false; }
513516

514-
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)
517+
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)
515518
: Expression(source_range)
516-
, FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, is_arrow_function)
519+
, FunctionNode(name, move(body), move(parameters), function_length, kind, is_strict_mode, might_need_arguments_object, contains_direct_call_to_eval, is_arrow_function)
517520
{
518521
}
519522

Userland/Libraries/LibJS/Parser.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -670,11 +670,13 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
670670
m_state.labels_in_scope = move(old_labels_in_scope);
671671
});
672672

673+
bool contains_direct_call_to_eval = false;
674+
673675
auto function_body_result = [&]() -> RefPtr<FunctionBody> {
674676
TemporaryChange change(m_state.in_arrow_function_context, true);
675677
if (match(TokenType::CurlyOpen)) {
676678
// Parse a function body with statements
677-
return parse_function_body(parameters, FunctionKind::Regular);
679+
return parse_function_body(parameters, FunctionKind::Regular, contains_direct_call_to_eval);
678680
}
679681
if (match_expression()) {
680682
// Parse a function body which returns a single expression
@@ -689,6 +691,7 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
689691
return_block->append<ReturnStatement>({ m_filename, rule_start.position(), position() }, move(return_expression));
690692
if (m_state.strict_mode)
691693
return_block->set_strict_mode();
694+
contains_direct_call_to_eval = function_scope.contains_direct_call_to_eval();
692695
return return_block;
693696
}
694697
// Invalid arrow function body
@@ -715,7 +718,7 @@ RefPtr<FunctionExpression> Parser::try_parse_arrow_function_expression(bool expe
715718
return create_ast_node<FunctionExpression>(
716719
{ m_state.current_token.filename(), rule_start.position(), position() }, "", move(body),
717720
move(parameters), function_length, FunctionKind::Regular, body->in_strict_mode(),
718-
/* might_need_arguments_object */ false, /* is_arrow_function */ true);
721+
/* might_need_arguments_object */ false, contains_direct_call_to_eval, /* is_arrow_function */ true);
719722
}
720723

721724
RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction allow_function)
@@ -1034,12 +1037,12 @@ NonnullRefPtr<ClassExpression> Parser::parse_class_expression(bool expect_class_
10341037
constructor = create_ast_node<FunctionExpression>(
10351038
{ m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body),
10361039
Vector { FunctionNode::Parameter { FlyString { "args" }, nullptr, true } }, 0, FunctionKind::Regular,
1037-
/* is_strict_mode */ true, /* might_need_arguments_object */ false);
1040+
/* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false);
10381041
} else {
10391042
constructor = create_ast_node<FunctionExpression>(
10401043
{ m_state.current_token.filename(), rule_start.position(), position() }, class_name, move(constructor_body),
10411044
Vector<FunctionNode::Parameter> {}, 0, FunctionKind::Regular,
1042-
/* is_strict_mode */ true, /* might_need_arguments_object */ false);
1045+
/* is_strict_mode */ true, /* might_need_arguments_object */ false, /* contains_direct_call_to_eval */ false);
10431046
}
10441047
}
10451048

@@ -1996,7 +1999,7 @@ void Parser::parse_statement_list(ScopeNode& output_node, AllowLabelledFunction
19961999
}
19972000

19982001
// FunctionBody, https://tc39.es/ecma262/#prod-FunctionBody
1999-
NonnullRefPtr<FunctionBody> Parser::parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind)
2002+
NonnullRefPtr<FunctionBody> Parser::parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval)
20002003
{
20012004
auto rule_start = push_start();
20022005
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
20522055

20532056
consume(TokenType::CurlyClose);
20542057
m_state.strict_mode = previous_strict_mode;
2058+
contains_direct_call_to_eval = function_scope.contains_direct_call_to_eval();
20552059
return function_body;
20562060
}
20572061

@@ -2117,7 +2121,8 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options)
21172121
m_state.labels_in_scope = move(old_labels_in_scope);
21182122
});
21192123

2120-
auto body = parse_function_body(parameters, function_kind);
2124+
bool contains_direct_call_to_eval = false;
2125+
auto body = parse_function_body(parameters, function_kind, contains_direct_call_to_eval);
21212126

21222127
auto has_strict_directive = body->in_strict_mode();
21232128

@@ -2127,7 +2132,8 @@ NonnullRefPtr<FunctionNodeType> Parser::parse_function_node(u8 parse_options)
21272132
return create_ast_node<FunctionNodeType>(
21282133
{ m_state.current_token.filename(), rule_start.position(), position() },
21292134
name, move(body), move(parameters), function_length,
2130-
function_kind, has_strict_directive, m_state.function_might_need_arguments_object);
2135+
function_kind, has_strict_directive, m_state.function_might_need_arguments_object,
2136+
contains_direct_call_to_eval);
21312137
}
21322138

21332139
Vector<FunctionNode::Parameter> Parser::parse_formal_parameters(int& function_length, u8 parse_options)

Userland/Libraries/LibJS/Parser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class Parser {
7272

7373
NonnullRefPtr<Statement> parse_statement(AllowLabelledFunction allow_labelled_function = AllowLabelledFunction::No);
7474
NonnullRefPtr<BlockStatement> parse_block_statement();
75-
NonnullRefPtr<FunctionBody> parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind);
75+
NonnullRefPtr<FunctionBody> parse_function_body(Vector<FunctionDeclaration::Parameter> const& parameters, FunctionKind function_kind, bool& contains_direct_call_to_eval);
7676
NonnullRefPtr<ReturnStatement> parse_return_statement();
7777
NonnullRefPtr<VariableDeclaration> parse_variable_declaration(bool for_loop_variable_declaration = false);
7878
NonnullRefPtr<Statement> parse_for_statement();

Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
namespace JS {
2626

27-
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)
27+
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)
2828
{
2929
Object* prototype = nullptr;
3030
switch (kind) {
@@ -35,10 +35,10 @@ ECMAScriptFunctionObject* ECMAScriptFunctionObject::create(GlobalObject& global_
3535
prototype = global_object.generator_function_prototype();
3636
break;
3737
}
38-
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);
38+
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);
3939
}
4040

41-
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)
41+
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)
4242
: FunctionObject(prototype)
4343
, m_environment(parent_scope)
4444
, m_formal_parameters(move(formal_parameters))
@@ -49,6 +49,7 @@ ECMAScriptFunctionObject::ECMAScriptFunctionObject(FlyString name, Statement con
4949
, m_function_length(function_length)
5050
, m_kind(kind)
5151
, m_might_need_arguments_object(might_need_arguments_object)
52+
, m_contains_direct_call_to_eval(contains_direct_call_to_eval)
5253
, m_is_arrow_function(is_arrow_function)
5354
{
5455
// NOTE: This logic is from OrdinaryFunctionCreate, https://tc39.es/ecma262/#sec-ordinaryfunctioncreate
@@ -367,7 +368,7 @@ ThrowCompletionOr<void> ECMAScriptFunctionObject::function_declaration_instantia
367368
VERIFY(!vm.exception());
368369

369370
for (auto& declaration : functions_to_initialize) {
370-
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());
371+
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());
371372
var_environment->set_mutable_binding(global_object(), declaration.name(), function, false);
372373
}
373374

Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ class ECMAScriptFunctionObject final : public FunctionObject {
2828
Global,
2929
};
3030

31-
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);
31+
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);
3232

33-
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);
33+
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);
3434
virtual void initialize(GlobalObject&) override;
3535
virtual ~ECMAScriptFunctionObject();
3636

@@ -101,6 +101,7 @@ class ECMAScriptFunctionObject final : public FunctionObject {
101101
i32 m_function_length { 0 };
102102
FunctionKind m_kind { FunctionKind::Regular };
103103
bool m_might_need_arguments_object { true };
104+
bool m_contains_direct_call_to_eval { true };
104105
bool m_is_arrow_function { false };
105106
bool m_has_simple_parameter_list { false };
106107
};

0 commit comments

Comments
 (0)