Commit f6057ff1 authored by Leszek Swirski's avatar Leszek Swirski Committed by Commit Bot

[scopes] Push sloppy eval check through eval scopes

Sloppy eval extends the outer declaration scope's context. This is also
true for sloppy eval inside of other sloppy evals -- the outer declaration
scope's context is extended rather than the outer sloppy eval's
declaration scope. However, we consider eval scopes to also be declaration
scopes, for the purposes of strict eval and caching lookup variables. So,
we need to make sure that we skip through sloppy eval scopes when marking
a scope as calls_sloppy_eval.

In fact, we implement this rather as never marking sloppy eval scopes as
calls_sloppy_eval, under the assumption that the parent scope will already
have been marked calls_sloppy_eval by the outer eval.

As a drive-by, fix a TODO to move this logic from calls_sloppy_eval() to
RecordEvalCall(), rename the variable to something more meaningful, and
make Snapshotting to use a new calls_eval bit on Scope.

Bug: chromium:996751
Change-Id: I27ccc7ef429a7ce60b3bb02bf64a3820ae4a2c36
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1773247
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63455}
parent de2654df
......@@ -168,7 +168,6 @@ Scope::Scope(Zone* zone, ScopeType scope_type, Handle<ScopeInfo> scope_info)
#ifdef DEBUG
already_resolved_ = true;
#endif
if (scope_info->CallsSloppyEval()) scope_calls_eval_ = true;
set_language_mode(scope_info->language_mode());
num_heap_slots_ = scope_info->ContextLength();
DCHECK_LE(Context::MIN_CONTEXT_SLOTS, num_heap_slots_);
......@@ -184,6 +183,10 @@ DeclarationScope::DeclarationScope(Zone* zone, ScopeType scope_type,
params_(0, zone) {
DCHECK_NE(scope_type, SCRIPT_SCOPE);
SetDefaults();
if (scope_info->SloppyEvalCanExtendVars()) {
DCHECK(!is_eval_scope());
sloppy_eval_can_extend_vars_ = true;
}
}
Scope::Scope(Zone* zone, const AstRawString* catch_variable_name,
......@@ -256,7 +259,8 @@ void Scope::SetDefaults() {
set_language_mode(LanguageMode::kSloppy);
scope_calls_eval_ = false;
calls_eval_ = false;
sloppy_eval_can_extend_vars_ = false;
scope_nonlinear_ = false;
is_hidden_ = false;
is_debug_evaluate_scope_ = false;
......@@ -621,7 +625,7 @@ Variable* DeclarationScope::DeclareFunctionVar(const AstRawString* name,
: NORMAL_VARIABLE;
function_ = new (zone())
Variable(this, name, VariableMode::kConst, kind, kCreatedInitialized);
if (calls_sloppy_eval()) {
if (sloppy_eval_can_extend_vars()) {
cache->NonLocal(name, VariableMode::kDynamic);
} else {
cache->variables_.Add(zone(), function_);
......@@ -647,7 +651,8 @@ Scope* Scope::FinalizeBlockScope() {
#endif
if (variables_.occupancy() > 0 ||
(is_declaration_scope() && AsDeclarationScope()->calls_sloppy_eval())) {
(is_declaration_scope() &&
AsDeclarationScope()->sloppy_eval_can_extend_vars())) {
return this;
}
......@@ -677,10 +682,10 @@ Scope* Scope::FinalizeBlockScope() {
if (inner_scope_calls_eval_) outer_scope()->inner_scope_calls_eval_ = true;
// No need to propagate scope_calls_eval_, since if it was relevant to
// this scope we would have had to bail out at the top.
DCHECK(!scope_calls_eval_ || !is_declaration_scope() ||
!is_sloppy(language_mode()));
// No need to propagate sloppy_eval_can_extend_vars_, since if it was relevant
// to this scope we would have had to bail out at the top.
DCHECK(!is_declaration_scope() ||
!AsDeclarationScope()->sloppy_eval_can_extend_vars());
// This block does not need a context.
num_heap_slots_ = 0;
......@@ -745,8 +750,8 @@ void Scope::Snapshot::Reparent(DeclarationScope* new_parent) {
outer_closure->locals_.Rewind(top_local_);
// Move eval calls since Snapshot's creation into new_parent.
if (outer_scope_and_calls_eval_->scope_calls_eval_) {
new_parent->scope_calls_eval_ = true;
if (outer_scope_and_calls_eval_->calls_eval_) {
new_parent->RecordDeclarationScopeEvalCall();
new_parent->inner_scope_calls_eval_ = true;
}
......@@ -1241,7 +1246,7 @@ int Scope::ContextChainLengthUntilOutermostSloppyEval() const {
if (!s->NeedsContext()) continue;
length++;
if (s->is_declaration_scope() &&
s->AsDeclarationScope()->calls_sloppy_eval()) {
s->AsDeclarationScope()->sloppy_eval_can_extend_vars()) {
result = length;
}
}
......@@ -1670,7 +1675,8 @@ void Scope::Print(int n) {
Indent(n1, "// strict mode scope\n");
}
if (IsAsmModule()) Indent(n1, "// scope is an asm module\n");
if (is_declaration_scope() && AsDeclarationScope()->calls_sloppy_eval()) {
if (is_declaration_scope() &&
AsDeclarationScope()->sloppy_eval_can_extend_vars()) {
Indent(n1, "// scope calls sloppy 'eval'\n");
}
if (is_declaration_scope() && AsDeclarationScope()->NeedsHomeObject()) {
......@@ -1834,8 +1840,9 @@ Variable* Scope::Lookup(VariableProxy* proxy, Scope* scope,
return LookupWith(proxy, scope, outer_scope_end, entry_point,
force_context_allocation);
}
if (V8_UNLIKELY(scope->is_declaration_scope() &&
scope->AsDeclarationScope()->calls_sloppy_eval())) {
if (V8_UNLIKELY(
scope->is_declaration_scope() &&
scope->AsDeclarationScope()->sloppy_eval_can_extend_vars())) {
return LookupSloppyEval(proxy, scope, outer_scope_end, entry_point,
force_context_allocation);
}
......@@ -1906,7 +1913,7 @@ Variable* Scope::LookupSloppyEval(VariableProxy* proxy, Scope* scope,
Scope* outer_scope_end, Scope* entry_point,
bool force_context_allocation) {
DCHECK(scope->is_declaration_scope() &&
scope->AsDeclarationScope()->calls_sloppy_eval());
scope->AsDeclarationScope()->sloppy_eval_can_extend_vars());
// If we're compiling eval, it's possible that the outer scope is the first
// ScopeInfo-backed scope.
......@@ -2256,9 +2263,9 @@ void Scope::AllocateVariablesRecursively() {
scope->is_with_scope() || scope->is_module_scope() ||
scope->IsAsmModule() || scope->ForceContextForLanguageMode() ||
(scope->is_function_scope() &&
scope->AsDeclarationScope()->calls_sloppy_eval()) ||
scope->AsDeclarationScope()->sloppy_eval_can_extend_vars()) ||
(scope->is_block_scope() && scope->is_declaration_scope() &&
scope->AsDeclarationScope()->calls_sloppy_eval());
scope->AsDeclarationScope()->sloppy_eval_can_extend_vars());
// If we didn't allocate any locals in the local context, then we only
// need the minimal number of slots if we must have a context.
......
......@@ -5,6 +5,7 @@
#ifndef V8_AST_SCOPES_H_
#define V8_AST_SCOPES_H_
#include <numeric>
#include "src/ast/ast.h"
#include "src/base/compiler-specific.h"
#include "src/base/hashmap.h"
......@@ -13,6 +14,7 @@
#include "src/objects/function-kind.h"
#include "src/objects/objects.h"
#include "src/utils/pointer-with-payload.h"
#include "src/utils/utils.h"
#include "src/zone/zone.h"
namespace v8 {
......@@ -110,8 +112,10 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
}
void RestoreEvalFlag() {
outer_scope_and_calls_eval_->scope_calls_eval_ =
outer_scope_and_calls_eval_.GetPayload();
if (outer_scope_and_calls_eval_.GetPayload()) {
// This recreates both calls_eval and sloppy_eval_can_extend_vars.
outer_scope_and_calls_eval_.GetPointer()->RecordEvalCall();
}
}
void Reparent(DeclarationScope* new_parent);
......@@ -264,9 +268,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// Inform the scope and outer scopes that the corresponding code contains an
// eval call.
void RecordEvalCall() {
scope_calls_eval_ = true;
}
inline void RecordEvalCall();
void RecordInnerScopeEvalCall() {
inner_scope_calls_eval_ = true;
......@@ -459,7 +461,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
int ContextChainLength(Scope* scope) const;
// The number of contexts between this and the outermost context that has a
// sloppy eval call. One if this->calls_sloppy_eval().
// sloppy eval call. One if this->sloppy_eval_can_extend_vars().
int ContextChainLengthUntilOutermostSloppyEval() const;
// Find the closest class scope in the current scope and outer scopes. If no
......@@ -702,9 +704,11 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// The language mode of this scope.
STATIC_ASSERT(LanguageModeSize == 2);
bool is_strict_ : 1;
// This scope or a nested catch scope or with scope contain an 'eval' call. At
// the 'eval' call site this scope is the declaration scope.
bool scope_calls_eval_ : 1;
// This scope contains an 'eval' call.
bool calls_eval_ : 1;
// The context associated with this scope can be extended by a sloppy eval
// called inside of it.
bool sloppy_eval_can_extend_vars_ : 1;
// This scope's declarations might not be executed in order (e.g., switch).
bool scope_nonlinear_ : 1;
bool is_hidden_ : 1;
......@@ -752,11 +756,50 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
IsClassConstructor(function_kind())));
}
bool calls_sloppy_eval() const {
// TODO(delphick): Calculate this when setting and change the name of
// scope_calls_eval_.
return !is_script_scope() && scope_calls_eval_ &&
is_sloppy(language_mode());
// Inform the scope and outer scopes that the corresponding code contains an
// eval call.
void RecordDeclarationScopeEvalCall() {
calls_eval_ = true;
// If this isn't a sloppy eval, we don't care about it.
if (language_mode() != LanguageMode::kSloppy) return;
// Sloppy eval in script scopes can only introduce global variables anyway,
// so we don't care that it calls sloppy eval.
if (is_script_scope()) return;
// Sloppy eval in a eval scope can only introduce variables into the outer
// (non-eval) declaration scope, not into this eval scope.
if (is_eval_scope()) {
#ifdef DEBUG
// One of three things must be true:
// 1. The outer non-eval declaration scope should already be marked as
// being extendable by sloppy eval, by the current sloppy eval rather
// than the inner one,
// 2. The outer non-eval declaration scope is a script scope and thus
// isn't extendable anyway, or
// 3. This is a debug evaluate and all bets are off.
DeclarationScope* outer_decl_scope = outer_scope()->GetDeclarationScope();
while (outer_decl_scope->is_eval_scope()) {
outer_decl_scope = outer_decl_scope->GetDeclarationScope();
}
if (outer_decl_scope->is_debug_evaluate_scope()) {
// Don't check anything.
// TODO(9662): Figure out where variables declared by an eval inside a
// debug-evaluate actually go.
} else if (!outer_decl_scope->is_script_scope()) {
DCHECK(outer_decl_scope->sloppy_eval_can_extend_vars_);
}
#endif
return;
}
sloppy_eval_can_extend_vars_ = true;
}
bool sloppy_eval_can_extend_vars() const {
return sloppy_eval_can_extend_vars_;
}
bool was_lazily_parsed() const { return was_lazily_parsed_; }
......@@ -1137,13 +1180,21 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
RareData* rare_data_ = nullptr;
};
void Scope::RecordEvalCall() {
calls_eval_ = true;
GetDeclarationScope()->RecordDeclarationScopeEvalCall();
RecordInnerScopeEvalCall();
}
Scope::Snapshot::Snapshot(Scope* scope)
: outer_scope_and_calls_eval_(scope, scope->scope_calls_eval_),
: outer_scope_and_calls_eval_(scope, scope->calls_eval_),
top_inner_scope_(scope->inner_scope_),
top_unresolved_(scope->unresolved_list_.end()),
top_local_(scope->GetClosureScope()->locals_.end()) {
// Reset in order to record eval calls during this Snapshot's lifetime.
outer_scope_and_calls_eval_.GetPointer()->scope_calls_eval_ = false;
outer_scope_and_calls_eval_.GetPointer()->calls_eval_ = false;
outer_scope_and_calls_eval_.GetPointer()->sloppy_eval_can_extend_vars_ =
false;
}
class ModuleScope final : public DeclarationScope {
......
......@@ -774,7 +774,7 @@ void ScopeIterator::VisitLocalScope(const Visitor& visitor, Mode mode) const {
DCHECK(!context_->IsScriptContext());
DCHECK(!context_->IsNativeContext());
DCHECK(!context_->IsWithContext());
if (!context_->scope_info().CallsSloppyEval()) return;
if (!context_->scope_info().SloppyEvalCanExtendVars()) return;
if (context_->extension_object().is_null()) return;
Handle<JSObject> extension(context_->extension_object(), isolate_);
Handle<FixedArray> keys =
......
......@@ -2275,7 +2275,7 @@ void ScopeInfo::ScopeInfoPrint(std::ostream& os) { // NOLINT
os << "\n - context locals : " << ContextLocalCount();
os << "\n - scope type: " << scope_type();
if (CallsSloppyEval()) os << "\n - sloppy eval";
if (SloppyEvalCanExtendVars()) os << "\n - sloppy eval";
os << "\n - language mode: " << language_mode();
if (is_declaration_scope()) os << "\n - declaration scope";
if (HasReceiver()) {
......
......@@ -166,7 +166,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
bool has_simple_parameters = false;
bool is_asm_module = false;
bool calls_sloppy_eval = false;
bool sloppy_eval_can_extend_vars = false;
if (scope->is_function_scope()) {
DeclarationScope* function_scope = scope->AsDeclarationScope();
has_simple_parameters = function_scope->has_simple_parameters();
......@@ -175,13 +175,14 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
FunctionKind function_kind = kNormalFunction;
if (scope->is_declaration_scope()) {
function_kind = scope->AsDeclarationScope()->function_kind();
calls_sloppy_eval = scope->AsDeclarationScope()->calls_sloppy_eval();
sloppy_eval_can_extend_vars =
scope->AsDeclarationScope()->sloppy_eval_can_extend_vars();
}
// Encode the flags.
int flags =
ScopeTypeField::encode(scope->scope_type()) |
CallsSloppyEvalField::encode(calls_sloppy_eval) |
SloppyEvalCanExtendVarsField::encode(sloppy_eval_can_extend_vars) |
LanguageModeField::encode(scope->language_mode()) |
DeclarationScopeField::encode(scope->is_declaration_scope()) |
ReceiverVariableField::encode(receiver_info) |
......@@ -356,7 +357,8 @@ Handle<ScopeInfo> ScopeInfo::CreateForWithScope(
// Encode the flags.
int flags =
ScopeTypeField::encode(WITH_SCOPE) | CallsSloppyEvalField::encode(false) |
ScopeTypeField::encode(WITH_SCOPE) |
SloppyEvalCanExtendVarsField::encode(false) |
LanguageModeField::encode(LanguageMode::kSloppy) |
DeclarationScopeField::encode(false) |
ReceiverVariableField::encode(NONE) | HasClassBrandField::encode(false) |
......@@ -418,7 +420,8 @@ Handle<ScopeInfo> ScopeInfo::CreateForBootstrapping(Isolate* isolate,
// Encode the flags.
int flags =
ScopeTypeField::encode(type) | CallsSloppyEvalField::encode(false) |
ScopeTypeField::encode(type) |
SloppyEvalCanExtendVarsField::encode(false) |
LanguageModeField::encode(LanguageMode::kSloppy) |
DeclarationScopeField::encode(true) |
ReceiverVariableField::encode(is_empty_function ? UNUSED : CONTEXT) |
......@@ -491,12 +494,12 @@ ScopeType ScopeInfo::scope_type() const {
return ScopeTypeField::decode(Flags());
}
bool ScopeInfo::CallsSloppyEval() const {
bool calls_sloppy_eval =
length() > 0 && CallsSloppyEvalField::decode(Flags());
DCHECK_IMPLIES(calls_sloppy_eval, is_sloppy(language_mode()));
DCHECK_IMPLIES(calls_sloppy_eval, is_declaration_scope());
return calls_sloppy_eval;
bool ScopeInfo::SloppyEvalCanExtendVars() const {
bool sloppy_eval_can_extend_vars =
length() > 0 && SloppyEvalCanExtendVarsField::decode(Flags());
DCHECK_IMPLIES(sloppy_eval_can_extend_vars, is_sloppy(language_mode()));
DCHECK_IMPLIES(sloppy_eval_can_extend_vars, is_declaration_scope());
return sloppy_eval_can_extend_vars;
}
LanguageMode ScopeInfo::language_mode() const {
......@@ -517,9 +520,9 @@ int ScopeInfo::ContextLength() const {
bool has_context =
context_locals > 0 || force_context || function_name_context_slot ||
scope_type() == WITH_SCOPE || scope_type() == CLASS_SCOPE ||
(scope_type() == BLOCK_SCOPE && CallsSloppyEval() &&
(scope_type() == BLOCK_SCOPE && SloppyEvalCanExtendVars() &&
is_declaration_scope()) ||
(scope_type() == FUNCTION_SCOPE && CallsSloppyEval()) ||
(scope_type() == FUNCTION_SCOPE && SloppyEvalCanExtendVars()) ||
(scope_type() == FUNCTION_SCOPE && IsAsmModule()) ||
scope_type() == MODULE_SCOPE;
......
......@@ -51,7 +51,7 @@ class ScopeInfo : public FixedArray {
bool is_class_scope() const;
// Does this scope make a sloppy eval call?
bool CallsSloppyEval() const;
bool SloppyEvalCanExtendVars() const;
// Return the number of context slots for code if a context is allocated. This
// number consists of three parts:
......@@ -221,9 +221,9 @@ class ScopeInfo : public FixedArray {
// Properties of scopes.
using ScopeTypeField = BitField<ScopeType, 0, 4>;
using CallsSloppyEvalField = ScopeTypeField::Next<bool, 1>;
using SloppyEvalCanExtendVarsField = ScopeTypeField::Next<bool, 1>;
STATIC_ASSERT(LanguageModeSize == 2);
using LanguageModeField = CallsSloppyEvalField::Next<LanguageMode, 1>;
using LanguageModeField = SloppyEvalCanExtendVarsField::Next<LanguageMode, 1>;
using DeclarationScopeField = LanguageModeField::Next<bool, 1>;
using ReceiverVariableField =
DeclarationScopeField::Next<VariableAllocationInfo, 2>;
......
......@@ -105,7 +105,7 @@ void ReparentExpressionScope(uintptr_t stack_limit, Expression* expr,
// sloppy eval.
DCHECK(scope->is_block_scope());
DCHECK(scope->is_declaration_scope());
DCHECK(scope->AsDeclarationScope()->calls_sloppy_eval());
DCHECK(scope->AsDeclarationScope()->sloppy_eval_can_extend_vars());
DCHECK(scope->outer_scope()->is_function_scope());
Reparenter r(stack_limit, expr, scope);
......
......@@ -1293,18 +1293,7 @@ class ParserBase {
Scope* scope) {
if (impl()->IsIdentifier(expression) &&
impl()->IsEval(impl()->AsIdentifier(expression))) {
scope->RecordInnerScopeEvalCall();
function_state_->RecordFunctionOrEvalCall();
if (is_sloppy(scope->language_mode())) {
// For sloppy scopes we also have to record the call at function level,
// in case it includes declarations that will be hoisted.
scope->GetDeclarationScope()->RecordEvalCall();
}
// This call is only necessary to track evals that may be
// inside arrow function parameter lists. In that case,
// Scope::Snapshot::Reparent will move this bit down into
// the arrow function's scope.
scope->RecordEvalCall();
return Call::IS_POSSIBLY_EVAL;
......
......@@ -2574,12 +2574,11 @@ Block* Parser::BuildParameterInitializationBlock(
initial_value, kNoSourcePosition);
}
Scope* param_scope = scope();
DeclarationScope* param_scope = scope()->AsDeclarationScope();
ScopedPtrList<Statement>* param_init_statements = &init_statements;
base::Optional<ScopedPtrList<Statement>> non_simple_param_init_statements;
if (!parameter->is_simple() &&
scope()->AsDeclarationScope()->calls_sloppy_eval()) {
if (!parameter->is_simple() && param_scope->sloppy_eval_can_extend_vars()) {
param_scope = NewVarblockScope();
param_scope->set_start_position(parameter->pattern->position());
param_scope->set_end_position(parameter->initializer_end_position);
......@@ -2604,7 +2603,7 @@ Block* Parser::BuildParameterInitializationBlock(
factory()->NewBlock(true, *non_simple_param_init_statements);
non_simple_param_init_statements.reset();
param_block->set_scope(param_scope);
param_scope = param_scope->FinalizeBlockScope();
param_scope = param_scope->FinalizeBlockScope()->AsDeclarationScope();
init_statements.Add(param_block);
}
++index;
......
......@@ -21,8 +21,9 @@ namespace internal {
namespace {
using ScopeCallsSloppyEvalField = BitField8<bool, 0, 1>;
using InnerScopeCallsEvalField = ScopeCallsSloppyEvalField::Next<bool, 1>;
using ScopeSloppyEvalCanExtendVarsField = BitField8<bool, 0, 1>;
using InnerScopeCallsEvalField =
ScopeSloppyEvalCanExtendVarsField::Next<bool, 1>;
using VariableMaybeAssignedField = BitField8<bool, 0, 1>;
using VariableContextAllocatedField = VariableMaybeAssignedField::Next<bool, 1>;
......@@ -352,9 +353,9 @@ void PreparseDataBuilder::SaveDataForScope(Scope* scope) {
#endif
uint8_t eval =
ScopeCallsSloppyEvalField::encode(
ScopeSloppyEvalCanExtendVarsField::encode(
scope->is_declaration_scope() &&
scope->AsDeclarationScope()->calls_sloppy_eval()) |
scope->AsDeclarationScope()->sloppy_eval_can_extend_vars()) |
InnerScopeCallsEvalField::encode(scope->inner_scope_calls_eval());
byte_data_.Reserve(kUint8Size);
byte_data_.WriteUint8(eval);
......@@ -599,7 +600,7 @@ void BaseConsumedPreparseData<Data>::RestoreDataForScope(Scope* scope) {
CHECK(scope_data_->HasRemainingBytes(ByteData::kUint8Size));
uint32_t eval = scope_data_->ReadUint8();
if (ScopeCallsSloppyEvalField::decode(eval)) scope->RecordEvalCall();
if (ScopeSloppyEvalCanExtendVarsField::decode(eval)) scope->RecordEvalCall();
if (InnerScopeCallsEvalField::decode(eval)) scope->RecordInnerScopeEvalCall();
if (scope->is_function_scope()) {
......
......@@ -386,7 +386,7 @@ PreParserBlock PreParser::BuildParameterInitializationBlock(
const PreParserFormalParameters& parameters) {
DCHECK(!parameters.is_simple);
DCHECK(scope()->is_function_scope());
if (scope()->AsDeclarationScope()->calls_sloppy_eval() &&
if (scope()->AsDeclarationScope()->sloppy_eval_can_extend_vars() &&
preparse_data_builder_ != nullptr) {
// We cannot replicate the Scope structure constructed by the Parser,
// because we've lost information whether each individual parameter was
......
......@@ -1098,7 +1098,7 @@ TEST(ScopeUsesArgumentsSuperThis) {
}
if (is_sloppy(scope->language_mode())) {
CHECK_EQ((source_data[i].expected & EVAL) != 0,
scope->AsDeclarationScope()->calls_sloppy_eval());
scope->AsDeclarationScope()->sloppy_eval_can_extend_vars());
}
}
}
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --stress-lazy-source-positions
eval(`
eval("");
(function f() {
// This undefined should always be known to be the global undefined value,
// even though there is a sloppy eval call inside the top eval scope.
return undefined;
})();
`);
// The above logic should work through multiple layers of eval nesting.
eval(`
eval(\`
eval(\\\`
eval("");
(function f() {
return undefined;
})();
\\\`);
\`);
`);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment