Skip to content

Commit 9606594

Browse files
Disable variable lookup in presence of eval
The following code doesn't report an error now eval("var x = 42;") console.log(x);
1 parent 817e23c commit 9606594

File tree

3 files changed

+419
-43
lines changed

3 files changed

+419
-43
lines changed

src/lint.cpp

Lines changed: 73 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ void linter::visit_enter_function_scope() { this->scopes_.push(); }
230230
void linter::visit_enter_function_scope_body() {
231231
this->propagate_variable_uses_to_parent_scope(
232232
/*allow_variable_use_before_declaration=*/true,
233-
/*consume_arguments=*/true);
233+
/*consume_arguments=*/true,
234+
/*propagate_eval_use=*/true);
234235
}
235236

236237
void linter::visit_enter_named_function_scope(identifier function_name) {
@@ -244,7 +245,8 @@ void linter::visit_exit_block_scope() {
244245
QLJS_ASSERT(!this->scopes_.empty());
245246
this->propagate_variable_uses_to_parent_scope(
246247
/*allow_variable_use_before_declaration=*/false,
247-
/*consume_arguments=*/false);
248+
/*consume_arguments=*/false,
249+
/*propagate_eval_use=*/true);
248250
this->propagate_variable_declarations_to_parent_scope();
249251
this->scopes_.pop();
250252
}
@@ -260,7 +262,8 @@ void linter::visit_exit_class_scope() {
260262
QLJS_ASSERT(!this->scopes_.empty());
261263
this->propagate_variable_uses_to_parent_scope(
262264
/*allow_variable_use_before_declaration=*/false,
263-
/*consume_arguments=*/false);
265+
/*consume_arguments=*/false,
266+
/*propagate_eval_use=*/false);
264267

265268
// No variable declarations should be propagatable to the parent scope.
266269
for (const declared_variable &var :
@@ -275,7 +278,8 @@ void linter::visit_exit_for_scope() {
275278
QLJS_ASSERT(!this->scopes_.empty());
276279
this->propagate_variable_uses_to_parent_scope(
277280
/*allow_variable_use_before_declaration=*/false,
278-
/*consume_arguments=*/false);
281+
/*consume_arguments=*/false,
282+
/*propagate_eval_use=*/true);
279283
this->propagate_variable_declarations_to_parent_scope();
280284
this->scopes_.pop();
281285
}
@@ -284,7 +288,8 @@ void linter::visit_exit_function_scope() {
284288
QLJS_ASSERT(!this->scopes_.empty());
285289
this->propagate_variable_uses_to_parent_scope(
286290
/*allow_variable_use_before_declaration=*/true,
287-
/*consume_arguments=*/true);
291+
/*consume_arguments=*/true,
292+
/*propagate_eval_use=*/false);
288293
this->scopes_.pop();
289294
}
290295

@@ -416,7 +421,8 @@ void linter::visit_end_of_module() {
416421
this->propagate_variable_uses_to_parent_scope(
417422
/*parent_scope=*/global_scope,
418423
/*allow_variable_use_before_declaration=*/false,
419-
/*consume_arguments=*/false);
424+
/*consume_arguments=*/false,
425+
/*propagate_eval_use=*/false);
420426

421427
std::vector<identifier> typeof_variables;
422428
for (const used_variable &used_var : global_scope.variables_used) {
@@ -480,18 +486,21 @@ void linter::visit_end_of_module() {
480486
}
481487

482488
void linter::propagate_variable_uses_to_parent_scope(
483-
bool allow_variable_use_before_declaration, bool consume_arguments) {
489+
bool allow_variable_use_before_declaration, bool consume_arguments,
490+
bool propagate_eval_use) {
484491
this->propagate_variable_uses_to_parent_scope(
485492
/*parent_scope=*/this->parent_scope(),
486493
/*allow_variable_use_before_declaration=*/
487494
allow_variable_use_before_declaration,
488-
/*consume_arguments=*/consume_arguments);
495+
/*consume_arguments=*/
496+
consume_arguments,
497+
/*propagate_eval_use=*/propagate_eval_use);
489498
}
490499

491500
template <class Scope>
492501
void linter::propagate_variable_uses_to_parent_scope(
493502
Scope &parent_scope, bool allow_variable_use_before_declaration,
494-
bool consume_arguments) {
503+
bool consume_arguments, bool propagate_eval_use) {
495504
scope &current_scope = this->current_scope();
496505

497506
auto is_current_scope_function_name = [&](const used_variable &var) {
@@ -500,44 +509,67 @@ void linter::propagate_variable_uses_to_parent_scope(
500509
var.name.normalized_name();
501510
};
502511

503-
for (const used_variable &used_var : current_scope.variables_used) {
504-
QLJS_ASSERT(!current_scope.declared_variables.find(used_var.name));
505-
const declared_variable *var =
506-
parent_scope.declared_variables.find(used_var.name);
507-
if (var) {
508-
// This variable was declared in the parent scope. Don't propagate.
509-
if (used_var.kind == used_variable_kind::assignment) {
510-
this->report_error_if_assignment_is_illegal(
511-
var, used_var.name, /*is_assigned_before_declaration=*/false);
512+
auto has_eval = [&](const std::vector<used_variable> &variables_used) {
513+
for (const used_variable &used_var : variables_used) {
514+
if (used_var.name.normalized_name() == u8"eval") {
515+
if (propagate_eval_use) {
516+
(allow_variable_use_before_declaration
517+
? parent_scope.variables_used_in_descendant_scope
518+
: parent_scope.variables_used)
519+
.emplace_back(used_var);
520+
}
521+
return true;
522+
}
523+
}
524+
return false;
525+
};
526+
527+
bool has_eval_in_current_scope = has_eval(current_scope.variables_used);
528+
if (!has_eval_in_current_scope) {
529+
for (const used_variable &used_var : current_scope.variables_used) {
530+
QLJS_ASSERT(!current_scope.declared_variables.find(used_var.name));
531+
const declared_variable *var =
532+
parent_scope.declared_variables.find(used_var.name);
533+
if (var) {
534+
// This variable was declared in the parent scope. Don't propagate.
535+
if (used_var.kind == used_variable_kind::assignment) {
536+
this->report_error_if_assignment_is_illegal(
537+
var, used_var.name, /*is_assigned_before_declaration=*/false);
538+
}
539+
} else if (consume_arguments &&
540+
used_var.name.normalized_name() == u8"arguments") {
541+
// Treat this variable as declared in the current scope.
542+
} else if (is_current_scope_function_name(used_var)) {
543+
// Treat this variable as declared in the current scope.
544+
} else {
545+
(allow_variable_use_before_declaration
546+
? parent_scope.variables_used_in_descendant_scope
547+
: parent_scope.variables_used)
548+
.emplace_back(used_var);
512549
}
513-
} else if (consume_arguments &&
514-
used_var.name.normalized_name() == u8"arguments") {
515-
// Treat this variable as declared in the current scope.
516-
} else if (is_current_scope_function_name(used_var)) {
517-
// Treat this variable as declared in the current scope.
518-
} else {
519-
(allow_variable_use_before_declaration
520-
? parent_scope.variables_used_in_descendant_scope
521-
: parent_scope.variables_used)
522-
.emplace_back(used_var);
523550
}
524551
}
525552
current_scope.variables_used.clear();
526553

527-
for (const used_variable &used_var :
528-
current_scope.variables_used_in_descendant_scope) {
529-
const declared_variable *var =
530-
parent_scope.declared_variables.find(used_var.name);
531-
if (var) {
532-
// This variable was declared in the parent scope. Don't propagate.
533-
if (used_var.kind == used_variable_kind::assignment) {
534-
this->report_error_if_assignment_is_illegal(
535-
var, used_var.name, /*is_assigned_before_declaration=*/false);
554+
bool has_eval_in_descendant_scope =
555+
has_eval_in_current_scope ||
556+
has_eval(current_scope.variables_used_in_descendant_scope);
557+
if (!has_eval_in_descendant_scope) {
558+
for (const used_variable &used_var :
559+
current_scope.variables_used_in_descendant_scope) {
560+
const declared_variable *var =
561+
parent_scope.declared_variables.find(used_var.name);
562+
if (var) {
563+
// This variable was declared in the parent scope. Don't propagate.
564+
if (used_var.kind == used_variable_kind::assignment) {
565+
this->report_error_if_assignment_is_illegal(
566+
var, used_var.name, /*is_assigned_before_declaration=*/false);
567+
}
568+
} else if (is_current_scope_function_name(used_var)) {
569+
// Treat this variable as declared in the current scope.
570+
} else {
571+
parent_scope.variables_used_in_descendant_scope.emplace_back(used_var);
536572
}
537-
} else if (is_current_scope_function_name(used_var)) {
538-
// Treat this variable as declared in the current scope.
539-
} else {
540-
parent_scope.variables_used_in_descendant_scope.emplace_back(used_var);
541573
}
542574
}
543575
current_scope.variables_used_in_descendant_scope.clear();

src/quick-lint-js/lint.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,12 @@ class linter {
210210
void visit_variable_use(identifier name, used_variable_kind);
211211

212212
void propagate_variable_uses_to_parent_scope(
213-
bool allow_variable_use_before_declaration, bool consume_arguments);
213+
bool allow_variable_use_before_declaration, bool consume_arguments,
214+
bool propagate_eval_use);
214215
template <class Scope>
215216
void propagate_variable_uses_to_parent_scope(
216217
Scope &parent_scope, bool allow_variable_use_before_declaration,
217-
bool consume_arguments);
218+
bool consume_arguments, bool propagate_eval_use);
218219

219220
void propagate_variable_declarations_to_parent_scope();
220221

0 commit comments

Comments
 (0)