Test for var declarations in eval which conflict with let
Previously, name conflicts between var and let declarations were only made into exceptions if they were visible at parse-time. This patch adds runtime checks so that sloppy-mode direct eval can't introduce conflicting var declarations. The change is implemented by traversing the scope chain when a direct eval introduces a var declaration to look for conflicting let declarations, up to the function boundary. BUG=v8:4454 R=adamk LOG=Y Review URL: https://codereview.chromium.org/1382513003 Cr-Commit-Position: refs/heads/master@{#31211}
This commit is contained in:
parent
9feb530594
commit
d515e5138d
@ -264,7 +264,8 @@ Handle<Object> Context::Lookup(Handle<String> name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. Check global objects, subjects of with, and extension objects.
|
// 1. Check global objects, subjects of with, and extension objects.
|
||||||
if ((context->IsNativeContext() || context->IsWithContext() ||
|
if ((context->IsNativeContext() ||
|
||||||
|
(context->IsWithContext() && ((flags & SKIP_WITH_CONTEXT) == 0)) ||
|
||||||
context->IsFunctionContext() || context->IsBlockContext()) &&
|
context->IsFunctionContext() || context->IsBlockContext()) &&
|
||||||
context->extension_receiver() != nullptr) {
|
context->extension_receiver() != nullptr) {
|
||||||
Handle<JSReceiver> object(context->extension_receiver());
|
Handle<JSReceiver> object(context->extension_receiver());
|
||||||
@ -384,7 +385,9 @@ Handle<Object> Context::Lookup(Handle<String> name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. Prepare to continue with the previous (next outermost) context.
|
// 3. Prepare to continue with the previous (next outermost) context.
|
||||||
if (context->IsNativeContext()) {
|
if (context->IsNativeContext() ||
|
||||||
|
((flags & STOP_AT_DECLARATION_SCOPE) != 0 &&
|
||||||
|
context->is_declaration_context())) {
|
||||||
follow_context_chain = false;
|
follow_context_chain = false;
|
||||||
} else {
|
} else {
|
||||||
context = Handle<Context>(context->previous(), isolate);
|
context = Handle<Context>(context->previous(), isolate);
|
||||||
|
@ -13,11 +13,15 @@ namespace internal {
|
|||||||
|
|
||||||
|
|
||||||
enum ContextLookupFlags {
|
enum ContextLookupFlags {
|
||||||
FOLLOW_CONTEXT_CHAIN = 1,
|
FOLLOW_CONTEXT_CHAIN = 1 << 0,
|
||||||
FOLLOW_PROTOTYPE_CHAIN = 2,
|
FOLLOW_PROTOTYPE_CHAIN = 1 << 1,
|
||||||
|
STOP_AT_DECLARATION_SCOPE = 1 << 2,
|
||||||
|
SKIP_WITH_CONTEXT = 1 << 3,
|
||||||
|
|
||||||
DONT_FOLLOW_CHAINS = 0,
|
DONT_FOLLOW_CHAINS = 0,
|
||||||
FOLLOW_CHAINS = FOLLOW_CONTEXT_CHAIN | FOLLOW_PROTOTYPE_CHAIN
|
FOLLOW_CHAINS = FOLLOW_CONTEXT_CHAIN | FOLLOW_PROTOTYPE_CHAIN,
|
||||||
|
LEXICAL_TEST =
|
||||||
|
FOLLOW_CONTEXT_CHAIN | STOP_AT_DECLARATION_SCOPE | SKIP_WITH_CONTEXT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -847,10 +847,8 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
|||||||
__ mov(r0, Operand(Smi::FromInt(0))); // Indicates no initial value.
|
__ mov(r0, Operand(Smi::FromInt(0))); // Indicates no initial value.
|
||||||
}
|
}
|
||||||
__ Push(r2, r0);
|
__ Push(r2, r0);
|
||||||
__ CallRuntime(IsImmutableVariableMode(mode)
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
? Runtime::kDeclareReadOnlyLookupSlot
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
: Runtime::kDeclareLookupSlot,
|
|
||||||
2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -906,7 +904,8 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
|||||||
__ Push(r2);
|
__ Push(r2);
|
||||||
// Push initial value for function declaration.
|
// Push initial value for function declaration.
|
||||||
VisitForStackValue(declaration->fun());
|
VisitForStackValue(declaration->fun());
|
||||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 2);
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -851,10 +851,8 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
|||||||
// Pushing 0 (xzr) indicates no initial value.
|
// Pushing 0 (xzr) indicates no initial value.
|
||||||
__ Push(x2, xzr);
|
__ Push(x2, xzr);
|
||||||
}
|
}
|
||||||
__ CallRuntime(IsImmutableVariableMode(mode)
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
? Runtime::kDeclareReadOnlyLookupSlot
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
: Runtime::kDeclareLookupSlot,
|
|
||||||
2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -910,7 +908,8 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
|||||||
__ Push(x2);
|
__ Push(x2);
|
||||||
// Push initial value for function declaration.
|
// Push initial value for function declaration.
|
||||||
VisitForStackValue(declaration->fun());
|
VisitForStackValue(declaration->fun());
|
||||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 2);
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -796,10 +796,9 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
|||||||
} else {
|
} else {
|
||||||
__ push(Immediate(Smi::FromInt(0))); // Indicates no initial value.
|
__ push(Immediate(Smi::FromInt(0))); // Indicates no initial value.
|
||||||
}
|
}
|
||||||
__ CallRuntime(IsImmutableVariableMode(mode)
|
__ push(
|
||||||
? Runtime::kDeclareReadOnlyLookupSlot
|
Immediate(Smi::FromInt(variable->DeclarationPropertyAttributes())));
|
||||||
: Runtime::kDeclareLookupSlot,
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -851,7 +850,9 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
|||||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||||
__ push(Immediate(variable->name()));
|
__ push(Immediate(variable->name()));
|
||||||
VisitForStackValue(declaration->fun());
|
VisitForStackValue(declaration->fun());
|
||||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 2);
|
__ push(
|
||||||
|
Immediate(Smi::FromInt(variable->DeclarationPropertyAttributes())));
|
||||||
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -851,10 +851,8 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
|||||||
__ mov(a0, zero_reg); // Smi::FromInt(0) indicates no initial value.
|
__ mov(a0, zero_reg); // Smi::FromInt(0) indicates no initial value.
|
||||||
}
|
}
|
||||||
__ Push(a2, a0);
|
__ Push(a2, a0);
|
||||||
__ CallRuntime(IsImmutableVariableMode(mode)
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
? Runtime::kDeclareReadOnlyLookupSlot
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
: Runtime::kDeclareLookupSlot,
|
|
||||||
2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -910,7 +908,8 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
|||||||
__ Push(a2);
|
__ Push(a2);
|
||||||
// Push initial value for function declaration.
|
// Push initial value for function declaration.
|
||||||
VisitForStackValue(declaration->fun());
|
VisitForStackValue(declaration->fun());
|
||||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 2);
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -849,10 +849,8 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
|||||||
__ mov(a0, zero_reg); // Smi::FromInt(0) indicates no initial value.
|
__ mov(a0, zero_reg); // Smi::FromInt(0) indicates no initial value.
|
||||||
}
|
}
|
||||||
__ Push(a2, a0);
|
__ Push(a2, a0);
|
||||||
__ CallRuntime(IsImmutableVariableMode(mode)
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
? Runtime::kDeclareReadOnlyLookupSlot
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
: Runtime::kDeclareLookupSlot,
|
|
||||||
2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -908,7 +906,8 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
|||||||
__ Push(a2);
|
__ Push(a2);
|
||||||
// Push initial value for function declaration.
|
// Push initial value for function declaration.
|
||||||
VisitForStackValue(declaration->fun());
|
VisitForStackValue(declaration->fun());
|
||||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 2);
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -817,10 +817,10 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
|||||||
__ LoadSmiLiteral(r3, Smi::FromInt(0)); // Indicates no initial value.
|
__ LoadSmiLiteral(r3, Smi::FromInt(0)); // Indicates no initial value.
|
||||||
__ Push(r5, r3);
|
__ Push(r5, r3);
|
||||||
}
|
}
|
||||||
__ CallRuntime(IsImmutableVariableMode(mode)
|
__ LoadSmiLiteral(
|
||||||
? Runtime::kDeclareReadOnlyLookupSlot
|
r3, Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
: Runtime::kDeclareLookupSlot,
|
__ Push(r3);
|
||||||
2);
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -871,7 +871,10 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
|||||||
__ Push(r5);
|
__ Push(r5);
|
||||||
// Push initial value for function declaration.
|
// Push initial value for function declaration.
|
||||||
VisitForStackValue(declaration->fun());
|
VisitForStackValue(declaration->fun());
|
||||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 2);
|
__ LoadSmiLiteral(
|
||||||
|
r5, Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
|
__ Push(r5);
|
||||||
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -809,10 +809,8 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
|||||||
} else {
|
} else {
|
||||||
__ Push(Smi::FromInt(0)); // Indicates no initial value.
|
__ Push(Smi::FromInt(0)); // Indicates no initial value.
|
||||||
}
|
}
|
||||||
__ CallRuntime(IsImmutableVariableMode(mode)
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
? Runtime::kDeclareReadOnlyLookupSlot
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
: Runtime::kDeclareLookupSlot,
|
|
||||||
2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -865,7 +863,8 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
|||||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||||
__ Push(variable->name());
|
__ Push(variable->name());
|
||||||
VisitForStackValue(declaration->fun());
|
VisitForStackValue(declaration->fun());
|
||||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 2);
|
__ Push(Smi::FromInt(variable->DeclarationPropertyAttributes()));
|
||||||
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -793,10 +793,8 @@ void FullCodeGenerator::VisitVariableDeclaration(
|
|||||||
} else {
|
} else {
|
||||||
__ push(Immediate(Smi::FromInt(0))); // Indicates no initial value.
|
__ push(Immediate(Smi::FromInt(0))); // Indicates no initial value.
|
||||||
}
|
}
|
||||||
__ CallRuntime(IsImmutableVariableMode(mode)
|
__ push(Immediate(variable->DeclarationPropertyAttributes()));
|
||||||
? Runtime::kDeclareReadOnlyLookupSlot
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
: Runtime::kDeclareLookupSlot,
|
|
||||||
2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -844,7 +842,8 @@ void FullCodeGenerator::VisitFunctionDeclaration(
|
|||||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||||
__ push(Immediate(variable->name()));
|
__ push(Immediate(variable->name()));
|
||||||
VisitForStackValue(declaration->fun());
|
VisitForStackValue(declaration->fun());
|
||||||
__ CallRuntime(Runtime::kDeclareLookupSlot, 2);
|
__ push(Immediate(variable->DeclarationPropertyAttributes()));
|
||||||
|
__ CallRuntime(Runtime::kDeclareLookupSlot, 3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2114,6 +2114,7 @@ Variable* Parser::Declare(Declaration* declaration,
|
|||||||
var = new (zone()) Variable(declaration_scope, name, mode, kind,
|
var = new (zone()) Variable(declaration_scope, name, mode, kind,
|
||||||
declaration->initialization(), kNotAssigned);
|
declaration->initialization(), kNotAssigned);
|
||||||
var->AllocateTo(VariableLocation::LOOKUP, -1);
|
var->AllocateTo(VariableLocation::LOOKUP, -1);
|
||||||
|
var->SetFromEval();
|
||||||
resolve = true;
|
resolve = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,23 +11,28 @@
|
|||||||
|
|
||||||
// Ecma-262 3rd 8.6.1
|
// Ecma-262 3rd 8.6.1
|
||||||
enum PropertyAttributes {
|
enum PropertyAttributes {
|
||||||
NONE = v8::None,
|
NONE = v8::None,
|
||||||
READ_ONLY = v8::ReadOnly,
|
READ_ONLY = v8::ReadOnly,
|
||||||
DONT_ENUM = v8::DontEnum,
|
DONT_ENUM = v8::DontEnum,
|
||||||
DONT_DELETE = v8::DontDelete,
|
DONT_DELETE = v8::DontDelete,
|
||||||
|
|
||||||
SEALED = DONT_DELETE,
|
SEALED = DONT_DELETE,
|
||||||
FROZEN = SEALED | READ_ONLY,
|
FROZEN = SEALED | READ_ONLY,
|
||||||
|
|
||||||
STRING = 8, // Used to filter symbols and string names
|
STRING = 8, // Used to filter symbols and string names
|
||||||
SYMBOLIC = 16,
|
SYMBOLIC = 16,
|
||||||
PRIVATE_SYMBOL = 32,
|
PRIVATE_SYMBOL = 32,
|
||||||
|
|
||||||
DONT_SHOW = DONT_ENUM | SYMBOLIC | PRIVATE_SYMBOL,
|
DONT_SHOW = DONT_ENUM | SYMBOLIC | PRIVATE_SYMBOL,
|
||||||
ABSENT = 64 // Used in runtime to indicate a property is absent.
|
ABSENT = 64, // Used in runtime to indicate a property is absent.
|
||||||
// ABSENT can never be stored in or returned from a descriptor's attributes
|
// ABSENT can never be stored in or returned from a descriptor's attributes
|
||||||
// bitfield. It is only used as a return value meaning the attributes of
|
// bitfield. It is only used as a return value meaning the attributes of
|
||||||
// a non-existent property.
|
// a non-existent property.
|
||||||
|
|
||||||
|
// When creating a property, EVAL_DECLARED used to indicate that the property
|
||||||
|
// came from a sloppy-mode direct eval, and certain checks need to be done.
|
||||||
|
// Cannot be stored in or returned from a descriptor's attributes bitfield.
|
||||||
|
EVAL_DECLARED = 128
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -223,10 +223,22 @@ Object* DeclareLookupSlot(Isolate* isolate, Handle<String> name,
|
|||||||
|
|
||||||
int index;
|
int index;
|
||||||
PropertyAttributes attributes;
|
PropertyAttributes attributes;
|
||||||
ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
|
|
||||||
BindingFlags binding_flags;
|
BindingFlags binding_flags;
|
||||||
Handle<Object> holder =
|
|
||||||
context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
if ((attr & EVAL_DECLARED) != 0) {
|
||||||
|
// Check for a conflict with a lexically scoped variable
|
||||||
|
context_arg->Lookup(name, LEXICAL_TEST, &index, &attributes,
|
||||||
|
&binding_flags);
|
||||||
|
if (attributes != ABSENT &&
|
||||||
|
(binding_flags == MUTABLE_CHECK_INITIALIZED ||
|
||||||
|
binding_flags == IMMUTABLE_CHECK_INITIALIZED)) {
|
||||||
|
return ThrowRedeclarationError(isolate, name);
|
||||||
|
}
|
||||||
|
attr = static_cast<PropertyAttributes>(attr & ~EVAL_DECLARED);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Object> holder = context->Lookup(name, DONT_FOLLOW_CHAINS, &index,
|
||||||
|
&attributes, &binding_flags);
|
||||||
if (holder.is_null()) {
|
if (holder.is_null()) {
|
||||||
// In case of JSProxy, an exception might have been thrown.
|
// In case of JSProxy, an exception might have been thrown.
|
||||||
if (isolate->has_pending_exception()) return isolate->heap()->exception();
|
if (isolate->has_pending_exception()) return isolate->heap()->exception();
|
||||||
@ -307,21 +319,14 @@ Object* DeclareLookupSlot(Isolate* isolate, Handle<String> name,
|
|||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_DeclareLookupSlot) {
|
RUNTIME_FUNCTION(Runtime_DeclareLookupSlot) {
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
DCHECK_EQ(2, args.length());
|
DCHECK_EQ(3, args.length());
|
||||||
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
||||||
CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 1);
|
CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 1);
|
||||||
|
CONVERT_ARG_HANDLE_CHECKED(Smi, property_attributes, 2);
|
||||||
|
|
||||||
return DeclareLookupSlot(isolate, name, initial_value, NONE);
|
PropertyAttributes attributes =
|
||||||
}
|
static_cast<PropertyAttributes>(property_attributes->value());
|
||||||
|
return DeclareLookupSlot(isolate, name, initial_value, attributes);
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_DeclareReadOnlyLookupSlot) {
|
|
||||||
HandleScope scope(isolate);
|
|
||||||
DCHECK_EQ(2, args.length());
|
|
||||||
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
|
||||||
CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 1);
|
|
||||||
|
|
||||||
return DeclareLookupSlot(isolate, name, initial_value, READ_ONLY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -566,8 +566,7 @@ namespace internal {
|
|||||||
F(DeclareGlobals, 2, 1) \
|
F(DeclareGlobals, 2, 1) \
|
||||||
F(InitializeVarGlobal, 3, 1) \
|
F(InitializeVarGlobal, 3, 1) \
|
||||||
F(InitializeConstGlobal, 2, 1) \
|
F(InitializeConstGlobal, 2, 1) \
|
||||||
F(DeclareLookupSlot, 2, 1) \
|
F(DeclareLookupSlot, 3, 1) \
|
||||||
F(DeclareReadOnlyLookupSlot, 2, 1) \
|
|
||||||
F(InitializeLegacyConstLookupSlot, 3, 1) \
|
F(InitializeLegacyConstLookupSlot, 3, 1) \
|
||||||
F(NewSloppyArguments_Generic, 1, 1) \
|
F(NewSloppyArguments_Generic, 1, 1) \
|
||||||
F(NewStrictArguments_Generic, 1, 1) \
|
F(NewStrictArguments_Generic, 1, 1) \
|
||||||
|
@ -44,6 +44,7 @@ Variable::Variable(Scope* scope, const AstRawString* name, VariableMode mode,
|
|||||||
strong_mode_reference_start_position_(RelocInfo::kNoPosition),
|
strong_mode_reference_start_position_(RelocInfo::kNoPosition),
|
||||||
strong_mode_reference_end_position_(RelocInfo::kNoPosition),
|
strong_mode_reference_end_position_(RelocInfo::kNoPosition),
|
||||||
local_if_not_shadowed_(NULL),
|
local_if_not_shadowed_(NULL),
|
||||||
|
is_from_eval_(false),
|
||||||
force_context_allocation_(false),
|
force_context_allocation_(false),
|
||||||
is_used_(false),
|
is_used_(false),
|
||||||
initialization_flag_(initialization_flag),
|
initialization_flag_(initialization_flag),
|
||||||
|
@ -124,6 +124,8 @@ class Variable: public ZoneObject {
|
|||||||
index_ = index;
|
index_ = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetFromEval() { is_from_eval_ = true; }
|
||||||
|
|
||||||
static int CompareIndex(Variable* const* v, Variable* const* w);
|
static int CompareIndex(Variable* const* v, Variable* const* w);
|
||||||
|
|
||||||
void RecordStrongModeReference(int start_position, int end_position) {
|
void RecordStrongModeReference(int start_position, int end_position) {
|
||||||
@ -144,6 +146,16 @@ class Variable: public ZoneObject {
|
|||||||
int strong_mode_reference_end_position() const {
|
int strong_mode_reference_end_position() const {
|
||||||
return strong_mode_reference_end_position_;
|
return strong_mode_reference_end_position_;
|
||||||
}
|
}
|
||||||
|
PropertyAttributes DeclarationPropertyAttributes() const {
|
||||||
|
int property_attributes = NONE;
|
||||||
|
if (IsImmutableVariableMode(mode_)) {
|
||||||
|
property_attributes |= READ_ONLY;
|
||||||
|
}
|
||||||
|
if (is_from_eval_) {
|
||||||
|
property_attributes |= EVAL_DECLARED;
|
||||||
|
}
|
||||||
|
return static_cast<PropertyAttributes>(property_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Scope* scope_;
|
Scope* scope_;
|
||||||
@ -165,6 +177,9 @@ class Variable: public ZoneObject {
|
|||||||
// binding scope (exclusive).
|
// binding scope (exclusive).
|
||||||
Variable* local_if_not_shadowed_;
|
Variable* local_if_not_shadowed_;
|
||||||
|
|
||||||
|
// True if this variable is introduced by a sloppy eval
|
||||||
|
bool is_from_eval_;
|
||||||
|
|
||||||
// Usage info.
|
// Usage info.
|
||||||
bool force_context_allocation_; // set by variable resolver
|
bool force_context_allocation_; // set by variable resolver
|
||||||
bool is_used_;
|
bool is_used_;
|
||||||
|
109
test/mjsunit/harmony/block-eval-var-over-legacy-const.js
Normal file
109
test/mjsunit/harmony/block-eval-var-over-legacy-const.js
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2015 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: --harmony-sloppy --harmony-sloppy-let --harmony-sloppy-function
|
||||||
|
|
||||||
|
// Var-let conflict in a function throws, even if the var is in an eval
|
||||||
|
|
||||||
|
let caught = false;
|
||||||
|
|
||||||
|
// Throws at the top level of a function
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
let x = 1;
|
||||||
|
eval('const x = 2');
|
||||||
|
})()
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// If the eval is in its own block scope, throws
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
let y = 1;
|
||||||
|
{ eval('const y = 2'); }
|
||||||
|
})()
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// If the let is in its own block scope, with the eval, throws
|
||||||
|
caught = false
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
{
|
||||||
|
let x = 1;
|
||||||
|
eval('const x = 2');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// Legal if the let is no longer visible
|
||||||
|
caught = false
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
{
|
||||||
|
let x = 1;
|
||||||
|
}
|
||||||
|
eval('const x = 2');
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertFalse(caught);
|
||||||
|
|
||||||
|
// In global scope
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
let z = 1;
|
||||||
|
eval('const z = 2');
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// Let declarations beyond a function boundary don't conflict
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
let a = 1;
|
||||||
|
(function() {
|
||||||
|
eval('const a');
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertFalse(caught);
|
||||||
|
|
||||||
|
// var across with doesn't conflict
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
with ({x: 1}) {
|
||||||
|
eval("const x = 2;");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertFalse(caught);
|
||||||
|
|
||||||
|
// var can still conflict with let across a with
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
let x;
|
||||||
|
with ({x: 1}) {
|
||||||
|
eval("const x = 2;");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
191
test/mjsunit/harmony/block-eval-var-over-let.js
Normal file
191
test/mjsunit/harmony/block-eval-var-over-let.js
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
// Copyright 2015 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: --harmony-sloppy --harmony-sloppy-let --harmony-sloppy-function --no-legacy-const
|
||||||
|
|
||||||
|
// Var-let conflict in a function throws, even if the var is in an eval
|
||||||
|
|
||||||
|
let caught = false;
|
||||||
|
|
||||||
|
// Throws at the top level of a function
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
let x = 1;
|
||||||
|
eval('var x = 2');
|
||||||
|
})()
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// If the eval is in its own block scope, throws
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
let y = 1;
|
||||||
|
{ eval('var y = 2'); }
|
||||||
|
})()
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// If the let is in its own block scope, with the eval, throws
|
||||||
|
caught = false
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
{
|
||||||
|
let x = 1;
|
||||||
|
eval('var x = 2');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// Legal if the let is no longer visible
|
||||||
|
caught = false
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
{
|
||||||
|
let x = 1;
|
||||||
|
}
|
||||||
|
eval('var x = 2');
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertFalse(caught);
|
||||||
|
|
||||||
|
// All the same works for const:
|
||||||
|
// Throws at the top level of a function
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
const x = 1;
|
||||||
|
eval('var x = 2');
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// If the eval is in its own block scope, throws
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
const y = 1;
|
||||||
|
{ eval('var y = 2'); }
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// If the const is in its own block scope, with the eval, throws
|
||||||
|
caught = false
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
{
|
||||||
|
const x = 1;
|
||||||
|
eval('var x = 2');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// Legal if the const is no longer visible
|
||||||
|
caught = false
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
{
|
||||||
|
const x = 1;
|
||||||
|
}
|
||||||
|
eval('var x = 2');
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertFalse(caught);
|
||||||
|
|
||||||
|
// In global scope
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
let z = 1;
|
||||||
|
eval('var z = 2');
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// Let declarations beyond a function boundary don't conflict
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
let a = 1;
|
||||||
|
(function() {
|
||||||
|
eval('var a');
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertFalse(caught);
|
||||||
|
|
||||||
|
// var across with doesn't conflict
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
with ({x: 1}) {
|
||||||
|
eval("var x = 2;");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertFalse(caught);
|
||||||
|
|
||||||
|
// var can still conflict with let across a with
|
||||||
|
caught = false;
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
let x;
|
||||||
|
with ({x: 1}) {
|
||||||
|
eval("var x = 2;");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// Functions declared in eval also conflict
|
||||||
|
caught = false
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
{
|
||||||
|
let x = 1;
|
||||||
|
eval('function x() {}');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertTrue(caught);
|
||||||
|
|
||||||
|
// TODO(littledan): Hoisting x out of the block should be
|
||||||
|
// prevented in this case BUG(v8:4479)
|
||||||
|
caught = false
|
||||||
|
try {
|
||||||
|
(function() {
|
||||||
|
{
|
||||||
|
let x = 1;
|
||||||
|
eval('{ function x() {} }');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
// TODO(littledan): switch to assertTrue when bug is fixed
|
||||||
|
assertTrue(caught);
|
Loading…
Reference in New Issue
Block a user