Commit 63efda35 authored by adamk's avatar adamk Committed by Commit bot

Remove strong mode support from Scope and Variable

This frees up one bit in FunctionKind, which I plan to make slightly
more syntactic info about functions available in SharedFunctionInfo
(needed for ES2015 Function.name support).

BUG=v8:3956, v8:4760
LOG=n

Review URL: https://codereview.chromium.org/1704223002

Cr-Commit-Position: refs/heads/master@{#34125}
parent 5a92a170
......@@ -1604,32 +1604,7 @@ Local<Script> UnboundScript::BindToCurrentContext() {
function_info(i::SharedFunctionInfo::cast(*obj), obj->GetIsolate());
i::Isolate* isolate = obj->GetIsolate();
i::ScopeInfo* scope_info = function_info->scope_info();
i::Handle<i::JSReceiver> global(isolate->native_context()->global_object());
for (int i = 0; i < scope_info->StrongModeFreeVariableCount(); ++i) {
i::Handle<i::String> name_string(scope_info->StrongModeFreeVariableName(i));
i::ScriptContextTable::LookupResult result;
i::Handle<i::ScriptContextTable> script_context_table(
isolate->native_context()->script_context_table());
if (!i::ScriptContextTable::Lookup(script_context_table, name_string,
&result)) {
i::Handle<i::Name> name(scope_info->StrongModeFreeVariableName(i));
Maybe<bool> has = i::JSReceiver::HasProperty(global, name);
if (has.IsJust() && !has.FromJust()) {
i::PendingCompilationErrorHandler pending_error_handler_;
pending_error_handler_.ReportMessageAt(
scope_info->StrongModeFreeVariableStartPosition(i),
scope_info->StrongModeFreeVariableEndPosition(i),
i::MessageTemplate::kStrongUnboundGlobal, name_string,
i::kReferenceError);
i::Handle<i::Script> script(i::Script::cast(function_info->script()));
pending_error_handler_.ThrowPendingError(isolate, script);
isolate->ReportPendingMessages();
isolate->OptionalRescheduleException(true);
return Local<Script>();
}
}
}
i::Handle<i::JSFunction> function =
obj->GetIsolate()->factory()->NewFunctionFromSharedFunctionInfo(
function_info, isolate->native_context());
......
......@@ -549,24 +549,10 @@ class VariableDeclaration final : public Declaration {
return mode() == VAR ? kCreatedInitialized : kNeedsInitialization;
}
bool is_class_declaration() const { return is_class_declaration_; }
// VariableDeclarations can be grouped into consecutive declaration
// groups. Each VariableDeclaration is associated with the start position of
// the group it belongs to. The positions are used for strong mode scope
// checks for classes and functions.
int declaration_group_start() const { return declaration_group_start_; }
protected:
VariableDeclaration(Zone* zone, VariableProxy* proxy, VariableMode mode,
Scope* scope, int pos, bool is_class_declaration = false,
int declaration_group_start = -1)
: Declaration(zone, proxy, mode, scope, pos),
is_class_declaration_(is_class_declaration),
declaration_group_start_(declaration_group_start) {}
bool is_class_declaration_;
int declaration_group_start_;
Scope* scope, int pos)
: Declaration(zone, proxy, mode, scope, pos) {}
};
......@@ -3074,12 +3060,11 @@ class AstNodeFactory final BASE_EMBEDDED {
AstValueFactory* ast_value_factory() const { return ast_value_factory_; }
VariableDeclaration* NewVariableDeclaration(
VariableProxy* proxy, VariableMode mode, Scope* scope, int pos,
bool is_class_declaration = false, int declaration_group_start = -1) {
VariableDeclaration* NewVariableDeclaration(VariableProxy* proxy,
VariableMode mode, Scope* scope,
int pos) {
return new (parser_zone_)
VariableDeclaration(parser_zone_, proxy, mode, scope, pos,
is_class_declaration, declaration_group_start);
VariableDeclaration(parser_zone_, proxy, mode, scope, pos);
}
FunctionDeclaration* NewFunctionDeclaration(VariableProxy* proxy,
......
......@@ -19,16 +19,12 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
ZoneList<Variable*> stack_locals(scope->StackLocalCount(), zone);
ZoneList<Variable*> context_locals(scope->ContextLocalCount(), zone);
ZoneList<Variable*> context_globals(scope->ContextGlobalCount(), zone);
ZoneList<Variable*> strong_mode_free_variables(0, zone);
scope->CollectStackAndContextLocals(&stack_locals, &context_locals,
&context_globals,
&strong_mode_free_variables);
&context_globals);
const int stack_local_count = stack_locals.length();
const int context_local_count = context_locals.length();
const int context_global_count = context_globals.length();
const int strong_mode_free_variable_count =
strong_mode_free_variables.length();
// Make sure we allocate the correct amount.
DCHECK_EQ(scope->ContextLocalCount(), context_local_count);
DCHECK_EQ(scope->ContextGlobalCount(), context_global_count);
......@@ -77,7 +73,6 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
const int length = kVariablePartIndex + parameter_count +
(1 + stack_local_count) + 2 * context_local_count +
2 * context_global_count +
3 * strong_mode_free_variable_count +
(has_receiver ? 1 : 0) + (has_function_name ? 2 : 0);
Factory* factory = isolate->factory();
......@@ -104,7 +99,6 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
scope_info->SetStackLocalCount(stack_local_count);
scope_info->SetContextLocalCount(context_local_count);
scope_info->SetContextGlobalCount(context_global_count);
scope_info->SetStrongModeFreeVariableCount(strong_mode_free_variable_count);
int index = kVariablePartIndex;
// Add parameters.
......@@ -173,25 +167,6 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
scope_info->set(index++, Smi::FromInt(value));
}
DCHECK(index == scope_info->StrongModeFreeVariableNameEntriesIndex());
for (int i = 0; i < strong_mode_free_variable_count; ++i) {
scope_info->set(index++, *strong_mode_free_variables[i]->name());
}
DCHECK(index == scope_info->StrongModeFreeVariablePositionEntriesIndex());
for (int i = 0; i < strong_mode_free_variable_count; ++i) {
// Unfortunately, the source code positions are stored as int even though
// int32_t would be enough (given the maximum source code length).
Handle<Object> start_position = factory->NewNumberFromInt(
static_cast<int32_t>(strong_mode_free_variables[i]
->strong_mode_reference_start_position()));
scope_info->set(index++, *start_position);
Handle<Object> end_position = factory->NewNumberFromInt(
static_cast<int32_t>(strong_mode_free_variables[i]
->strong_mode_reference_end_position()));
scope_info->set(index++, *end_position);
}
// If the receiver is allocated, add its index.
DCHECK(index == scope_info->ReceiverEntryIndex());
if (has_receiver) {
......@@ -226,7 +201,6 @@ Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) {
const int stack_local_count = 0;
const int context_local_count = 1;
const int context_global_count = 0;
const int strong_mode_free_variable_count = 0;
const bool has_simple_parameters = true;
const VariableAllocationInfo receiver_info = CONTEXT;
const VariableAllocationInfo function_name_info = NONE;
......@@ -237,7 +211,6 @@ Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) {
const int length = kVariablePartIndex + parameter_count +
(1 + stack_local_count) + 2 * context_local_count +
2 * context_global_count +
3 * strong_mode_free_variable_count +
(has_receiver ? 1 : 0) + (has_function_name ? 2 : 0);
Factory* factory = isolate->factory();
......@@ -259,7 +232,6 @@ Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) {
scope_info->SetStackLocalCount(stack_local_count);
scope_info->SetContextLocalCount(context_local_count);
scope_info->SetContextGlobalCount(context_global_count);
scope_info->SetStrongModeFreeVariableCount(strong_mode_free_variable_count);
int index = kVariablePartIndex;
const int first_slot_index = 0;
......@@ -276,9 +248,6 @@ Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) {
ContextLocalMaybeAssignedFlag::encode(kNotAssigned);
scope_info->set(index++, Smi::FromInt(value));
DCHECK(index == scope_info->StrongModeFreeVariableNameEntriesIndex());
DCHECK(index == scope_info->StrongModeFreeVariablePositionEntriesIndex());
// And here we record that this scopeinfo binds a receiver.
DCHECK(index == scope_info->ReceiverEntryIndex());
const int receiver_index = Context::MIN_CONTEXT_SLOTS + 0;
......@@ -482,35 +451,6 @@ bool ScopeInfo::LocalIsSynthetic(int var) {
}
String* ScopeInfo::StrongModeFreeVariableName(int var) {
DCHECK(0 <= var && var < StrongModeFreeVariableCount());
int info_index = StrongModeFreeVariableNameEntriesIndex() + var;
return String::cast(get(info_index));
}
int ScopeInfo::StrongModeFreeVariableStartPosition(int var) {
DCHECK(0 <= var && var < StrongModeFreeVariableCount());
int info_index = StrongModeFreeVariablePositionEntriesIndex() + var * 2;
int32_t value = 0;
bool ok = get(info_index)->ToInt32(&value);
USE(ok);
DCHECK(ok);
return value;
}
int ScopeInfo::StrongModeFreeVariableEndPosition(int var) {
DCHECK(0 <= var && var < StrongModeFreeVariableCount());
int info_index = StrongModeFreeVariablePositionEntriesIndex() + var * 2 + 1;
int32_t value = 0;
bool ok = get(info_index)->ToInt32(&value);
USE(ok);
DCHECK(ok);
return value;
}
int ScopeInfo::StackSlotIndex(String* name) {
DCHECK(name->IsInternalizedString());
if (length() > 0) {
......@@ -691,20 +631,8 @@ int ScopeInfo::ContextGlobalInfoEntriesIndex() {
}
int ScopeInfo::StrongModeFreeVariableNameEntriesIndex() {
return ContextGlobalInfoEntriesIndex() + ContextGlobalCount();
}
int ScopeInfo::StrongModeFreeVariablePositionEntriesIndex() {
return StrongModeFreeVariableNameEntriesIndex() +
StrongModeFreeVariableCount();
}
int ScopeInfo::ReceiverEntryIndex() {
return StrongModeFreeVariablePositionEntriesIndex() +
2 * StrongModeFreeVariableCount();
return ContextGlobalInfoEntriesIndex() + ContextGlobalCount();
}
......
This diff is collapsed.
......@@ -24,8 +24,7 @@ class VariableMap: public ZoneHashMap {
Variable* Declare(Scope* scope, const AstRawString* name, VariableMode mode,
Variable::Kind kind, InitializationFlag initialization_flag,
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned,
int declaration_group_start = -1);
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned);
Variable* Lookup(const AstRawString* name);
......@@ -163,8 +162,7 @@ class Scope: public ZoneObject {
// declared before, the previously declared variable is returned.
Variable* DeclareLocal(const AstRawString* name, VariableMode mode,
InitializationFlag init_flag, Variable::Kind kind,
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned,
int declaration_group_start = -1);
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned);
// Declare an implicit global variable in this scope which must be a
// script scope. The variable was introduced (possibly from an inner
......@@ -377,12 +375,6 @@ class Scope: public ZoneObject {
IsClassConstructor(function_kind())));
}
const Scope* NearestOuterEvalScope() const {
if (is_eval_scope()) return this;
if (outer_scope() == nullptr) return nullptr;
return outer_scope()->NearestOuterEvalScope();
}
// ---------------------------------------------------------------------------
// Accessors.
......@@ -503,25 +495,15 @@ class Scope: public ZoneObject {
// The ModuleDescriptor for this scope; only for module scopes.
ModuleDescriptor* module() const { return module_descriptor_; }
void set_class_declaration_group_start(int position) {
class_declaration_group_start_ = position;
}
int class_declaration_group_start() const {
return class_declaration_group_start_;
}
// ---------------------------------------------------------------------------
// Variable allocation.
// Collect stack and context allocated local variables in this scope. Note
// that the function variable - if present - is not collected and should be
// handled separately.
void CollectStackAndContextLocals(
ZoneList<Variable*>* stack_locals, ZoneList<Variable*>* context_locals,
ZoneList<Variable*>* context_globals,
ZoneList<Variable*>* strong_mode_free_variables = nullptr);
void CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
ZoneList<Variable*>* context_locals,
ZoneList<Variable*>* context_globals);
// Current number of var or const locals.
int num_var_or_const() { return num_var_or_const_; }
......@@ -784,12 +766,6 @@ class Scope: public ZoneObject {
MUST_USE_RESULT
bool ResolveVariablesRecursively(ParseInfo* info, AstNodeFactory* factory);
bool CheckStrongModeDeclaration(VariableProxy* proxy, Variable* var);
// If this scope is a method scope of a class, return the corresponding
// class variable, otherwise nullptr.
ClassVariable* ClassVariableForMethod() const;
// Scope analysis.
void PropagateScopeInfo(bool outer_scope_calls_sloppy_eval);
bool HasTrivialContext() const;
......@@ -854,10 +830,6 @@ class Scope: public ZoneObject {
Zone* zone_;
PendingCompilationErrorHandler pending_error_handler_;
// For tracking which classes are declared consecutively. Needed for strong
// mode.
int class_declaration_group_start_;
};
} // namespace internal
......
......@@ -40,9 +40,6 @@ Variable::Variable(Scope* scope, const AstRawString* name, VariableMode mode,
location_(VariableLocation::UNALLOCATED),
index_(-1),
initializer_position_(RelocInfo::kNoPosition),
has_strong_mode_reference_(false),
strong_mode_reference_start_position_(RelocInfo::kNoPosition),
strong_mode_reference_end_position_(RelocInfo::kNoPosition),
local_if_not_shadowed_(NULL),
is_from_eval_(false),
force_context_allocation_(false),
......
......@@ -15,12 +15,9 @@ namespace internal {
// variables. Variables themselves are never directly referred to from the AST,
// they are maintained by scopes, and referred to from VariableProxies and Slots
// after binding and variable allocation.
class ClassVariable;
class Variable: public ZoneObject {
public:
enum Kind { NORMAL, FUNCTION, CLASS, THIS, ARGUMENTS };
enum Kind { NORMAL, FUNCTION, THIS, ARGUMENTS };
Variable(Scope* scope, const AstRawString* name, VariableMode mode, Kind kind,
InitializationFlag initialization_flag,
......@@ -84,7 +81,6 @@ class Variable: public ZoneObject {
}
bool is_function() const { return kind_ == FUNCTION; }
bool is_class() const { return kind_ == CLASS; }
bool is_this() const { return kind_ == THIS; }
bool is_arguments() const { return kind_ == ARGUMENTS; }
......@@ -98,11 +94,6 @@ class Variable: public ZoneObject {
return is_this() || *name() == *isolate->factory()->this_string();
}
ClassVariable* AsClassVariable() {
DCHECK(is_class());
return reinterpret_cast<ClassVariable*>(this);
}
// True if the variable is named eval and not known to be shadowed.
bool is_possibly_eval(Isolate* isolate) const {
return IsVariable(isolate->factory()->eval_string());
......@@ -132,24 +123,6 @@ class Variable: public ZoneObject {
static int CompareIndex(Variable* const* v, Variable* const* w);
void RecordStrongModeReference(int start_position, int end_position) {
// Record the earliest reference to the variable. Used in error messages for
// strong mode references to undeclared variables.
if (has_strong_mode_reference_ &&
strong_mode_reference_start_position_ < start_position)
return;
has_strong_mode_reference_ = true;
strong_mode_reference_start_position_ = start_position;
strong_mode_reference_end_position_ = end_position;
}
bool has_strong_mode_reference() const { return has_strong_mode_reference_; }
int strong_mode_reference_start_position() const {
return strong_mode_reference_start_position_;
}
int strong_mode_reference_end_position() const {
return strong_mode_reference_end_position_;
}
PropertyAttributes DeclarationPropertyAttributes() const {
int property_attributes = NONE;
if (IsImmutableVariableMode(mode_)) {
......@@ -169,11 +142,6 @@ class Variable: public ZoneObject {
VariableLocation location_;
int index_;
int initializer_position_;
// Tracks whether the variable is bound to a VariableProxy which is in strong
// mode, and if yes, the source location of the reference.
bool has_strong_mode_reference_;
int strong_mode_reference_start_position_;
int strong_mode_reference_end_position_;
// If this field is set, this variable references the stored locally bound
// variable, but it might be shadowed by variable bindings introduced by
......@@ -190,28 +158,6 @@ class Variable: public ZoneObject {
InitializationFlag initialization_flag_;
MaybeAssignedFlag maybe_assigned_;
};
class ClassVariable : public Variable {
public:
ClassVariable(Scope* scope, const AstRawString* name, VariableMode mode,
InitializationFlag initialization_flag,
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned,
int declaration_group_start = -1)
: Variable(scope, name, mode, Variable::CLASS, initialization_flag,
maybe_assigned_flag),
declaration_group_start_(declaration_group_start) {}
int declaration_group_start() const { return declaration_group_start_; }
void set_declaration_group_start(int declaration_group_start) {
declaration_group_start_ = declaration_group_start;
}
private:
// For classes we keep track of consecutive groups of delcarations. They are
// needed for strong mode scoping checks. TODO(marja, rossberg): Implement
// checks for functions too.
int declaration_group_start_;
};
} // namespace internal
} // namespace v8
......
......@@ -984,15 +984,10 @@ enum FunctionKind {
kDefaultConstructor = 1 << 4,
kSubclassConstructor = 1 << 5,
kBaseConstructor = 1 << 6,
kInObjectLiteral = 1 << 7,
kDefaultBaseConstructor = kDefaultConstructor | kBaseConstructor,
kDefaultSubclassConstructor = kDefaultConstructor | kSubclassConstructor,
kClassConstructor =
kBaseConstructor | kSubclassConstructor | kDefaultConstructor,
kConciseMethodInObjectLiteral = kConciseMethod | kInObjectLiteral,
kConciseGeneratorMethodInObjectLiteral =
kConciseGeneratorMethod | kInObjectLiteral,
kAccessorFunctionInObjectLiteral = kAccessorFunction | kInObjectLiteral,
};
......@@ -1006,10 +1001,7 @@ inline bool IsValidFunctionKind(FunctionKind kind) {
kind == FunctionKind::kDefaultBaseConstructor ||
kind == FunctionKind::kDefaultSubclassConstructor ||
kind == FunctionKind::kBaseConstructor ||
kind == FunctionKind::kSubclassConstructor ||
kind == FunctionKind::kConciseMethodInObjectLiteral ||
kind == FunctionKind::kConciseGeneratorMethodInObjectLiteral ||
kind == FunctionKind::kAccessorFunctionInObjectLiteral;
kind == FunctionKind::kSubclassConstructor;
}
......@@ -1071,18 +1063,6 @@ inline bool IsConstructable(FunctionKind kind, LanguageMode mode) {
}
inline bool IsInObjectLiteral(FunctionKind kind) {
DCHECK(IsValidFunctionKind(kind));
return kind & FunctionKind::kInObjectLiteral;
}
inline FunctionKind WithObjectLiteralBit(FunctionKind kind) {
kind = static_cast<FunctionKind>(kind | FunctionKind::kInObjectLiteral);
DCHECK(IsValidFunctionKind(kind));
return kind;
}
inline uint32_t ObjectHash(Address address) {
// All objects are at least pointer aligned, so we can remove the trailing
// zeros.
......
......@@ -336,8 +336,6 @@ class CallSite {
T(StrongSuperCallMissing, \
"In strong mode, invoking the super constructor in a subclass is " \
"required") \
T(StrongUnboundGlobal, \
"In strong mode, using an undeclared global variable '%' is not allowed") \
T(UnsupportedSuper, "Unsupported reference to 'super'") \
/* RangeError */ \
T(DateRange, "Provided date is not in valid range.") \
......@@ -489,8 +487,6 @@ class CallSite {
"with 'break', 'continue', 'return' or 'throw'") \
T(StrongUndefined, \
"In strong mode, binding or assigning to 'undefined' is deprecated") \
T(StrongUseBeforeDeclaration, \
"In strong mode, declaring variable '%' before its use is required") \
T(StrongVar, \
"In strong mode, 'var' is deprecated, use 'let' or 'const' instead") \
T(TemplateOctalLiteral, \
......
......@@ -4170,10 +4170,6 @@ class ScopeInfo : public FixedArray {
// exposed to the user in a debugger.
bool LocalIsSynthetic(int var);
String* StrongModeFreeVariableName(int var);
int StrongModeFreeVariableStartPosition(int var);
int StrongModeFreeVariableEndPosition(int var);
// Lookup support for serialized scope info. Returns the
// the stack slot index for a given slot name if the slot is
// present; otherwise returns a value < 0. The name must be an internalized
......@@ -4243,8 +4239,7 @@ class ScopeInfo : public FixedArray {
V(ParameterCount) \
V(StackLocalCount) \
V(ContextLocalCount) \
V(ContextGlobalCount) \
V(StrongModeFreeVariableCount)
V(ContextGlobalCount)
#define FIELD_ACCESSORS(name) \
inline void Set##name(int value); \
......@@ -4286,15 +4281,10 @@ class ScopeInfo : public FixedArray {
// the context locals in ContextLocalNameEntries. One slot is used per
// context local, so in total this part occupies ContextLocalCount()
// slots in the array.
// 6. StrongModeFreeVariableNameEntries:
// Stores the names of strong mode free variables.
// 7. StrongModeFreeVariablePositionEntries:
// Stores the locations (start and end position) of strong mode free
// variables.
// 8. RecieverEntryIndex:
// 6. RecieverEntryIndex:
// If the scope binds a "this" value, one slot is reserved to hold the
// context or stack slot index for the variable.
// 9. FunctionNameEntryIndex:
// 7. FunctionNameEntryIndex:
// If the scope belongs to a named function expression this part contains
// information about the function variable. It always occupies two array
// slots: a. The name of the function variable.
......@@ -4306,8 +4296,6 @@ class ScopeInfo : public FixedArray {
int ContextGlobalNameEntriesIndex();
int ContextLocalInfoEntriesIndex();
int ContextGlobalInfoEntriesIndex();
int StrongModeFreeVariableNameEntriesIndex();
int StrongModeFreeVariablePositionEntriesIndex();
int ReceiverEntryIndex();
int FunctionNameEntryIndex();
......@@ -7159,7 +7147,6 @@ class SharedFunctionInfo: public HeapObject {
kIsDefaultConstructor,
kIsSubclassConstructor,
kIsBaseConstructor,
kIsInObjectLiteral,
// byte 3
kDeserialized,
kNeverCompiled,
......@@ -7180,10 +7167,9 @@ class SharedFunctionInfo: public HeapObject {
ASSERT_FUNCTION_KIND_ORDER(kDefaultConstructor, kIsDefaultConstructor);
ASSERT_FUNCTION_KIND_ORDER(kSubclassConstructor, kIsSubclassConstructor);
ASSERT_FUNCTION_KIND_ORDER(kBaseConstructor, kIsBaseConstructor);
ASSERT_FUNCTION_KIND_ORDER(kInObjectLiteral, kIsInObjectLiteral);
#undef ASSERT_FUNCTION_KIND_ORDER
class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 8> {};
class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 7> {};
class DeoptCountBits : public BitField<int, 0, 4> {};
class OptReenableTriesBits : public BitField<int, 4, 18> {};
......
......@@ -1735,8 +1735,6 @@ ParserBase<Traits>::ParsePropertyDefinition(
: FunctionKind::kBaseConstructor;
}
if (!in_class) kind = WithObjectLiteralBit(kind);
value = this->ParseFunctionLiteral(
*name, scanner()->location(), kSkipFunctionNameCheck, kind,
RelocInfo::kNoPosition, FunctionLiteral::kAnonymousExpression,
......@@ -1778,11 +1776,10 @@ ParserBase<Traits>::ParsePropertyDefinition(
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
}
FunctionKind kind = FunctionKind::kAccessorFunction;
if (!in_class) kind = WithObjectLiteralBit(kind);
typename Traits::Type::FunctionLiteral value = this->ParseFunctionLiteral(
*name, scanner()->location(), kSkipFunctionNameCheck, kind,
RelocInfo::kNoPosition, FunctionLiteral::kAnonymousExpression,
*name, scanner()->location(), kSkipFunctionNameCheck,
FunctionKind::kAccessorFunction, RelocInfo::kNoPosition,
FunctionLiteral::kAnonymousExpression,
is_get ? FunctionLiteral::kGetterArity : FunctionLiteral::kSetterArity,
language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
......
......@@ -1270,20 +1270,10 @@ Statement* Parser::ParseStatementListItem(bool* ok) {
// Statement
// Declaration
if (peek() != Token::CLASS) {
// No more classes follow; reset the start position for the consecutive
// class declaration group.
scope_->set_class_declaration_group_start(-1);
}
switch (peek()) {
case Token::FUNCTION:
return ParseFunctionDeclaration(NULL, ok);
case Token::CLASS:
if (scope_->class_declaration_group_start() < 0) {
scope_->set_class_declaration_group_start(
scanner()->peek_location().beg_pos);
}
Consume(Token::CLASS);
return ParseClassDeclaration(NULL, ok);
case Token::CONST:
......@@ -1961,18 +1951,11 @@ Variable* Parser::Declare(Declaration* declaration,
if (var == NULL) {
// Declare the name.
Variable::Kind kind = Variable::NORMAL;
int declaration_group_start = -1;
if (is_function_declaration) {
kind = Variable::FUNCTION;
} else if (declaration->IsVariableDeclaration() &&
declaration->AsVariableDeclaration()->is_class_declaration()) {
kind = Variable::CLASS;
declaration_group_start =
declaration->AsVariableDeclaration()->declaration_group_start();
}
var = declaration_scope->DeclareLocal(
name, mode, declaration->initialization(), kind, kNotAssigned,
declaration_group_start);
name, mode, declaration->initialization(), kind, kNotAssigned);
} else if ((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) &&
!declaration_scope->is_script_scope()) {
// Duplicate legacy const definitions throw at runtime.
......@@ -2232,30 +2215,10 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names,
VariableMode mode = is_strong(language_mode()) ? CONST : LET;
VariableProxy* proxy = NewUnresolved(name, mode);
const bool is_class_declaration = true;
Declaration* declaration = factory()->NewVariableDeclaration(
proxy, mode, scope_, pos, is_class_declaration,
scope_->class_declaration_group_start());
Variable* outer_class_variable =
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
Declaration* declaration =
factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
proxy->var()->set_initializer_position(position());
// This is needed because a class ("class Name { }") creates two bindings (one
// in the outer scope, and one in the class scope). The method is a function
// scope inside the inner scope (class scope). The consecutive class
// declarations are in the outer scope.
if (value->class_variable_proxy() && value->class_variable_proxy()->var() &&
outer_class_variable->is_class()) {
// In some cases, the outer variable is not detected as a class variable;
// this happens e.g., for lazy methods. They are excluded from strong mode
// checks for now. TODO(marja, rossberg): re-create variables with the
// correct Kind and remove this hack.
value->class_variable_proxy()
->var()
->AsClassVariable()
->set_declaration_group_start(
outer_class_variable->AsClassVariable()->declaration_group_start());
}
Assignment* assignment =
factory()->NewAssignment(Token::INIT, proxy, value, pos);
Statement* assignment_statement =
......@@ -4912,10 +4875,8 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
VariableProxy* proxy = NULL;
if (name != NULL) {
proxy = NewUnresolved(name, CONST);
const bool is_class_declaration = true;
Declaration* declaration = factory()->NewVariableDeclaration(
proxy, CONST, block_scope, pos, is_class_declaration,
scope_->class_declaration_group_start());
Declaration* declaration =
factory()->NewVariableDeclaration(proxy, CONST, block_scope, pos);
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
}
......
......@@ -6658,61 +6658,6 @@ TEST(StrongModeFreeVariablesDeclaredInGlobalPrototype) {
}
TEST(StrongModeFreeVariablesNotDeclared) {
i::FLAG_strong_mode = true;
v8::V8::Initialize();
v8::HandleScope scope(CcTest::isolate());
v8::Context::Scope context_scope(v8::Context::New(CcTest::isolate()));
v8::TryCatch try_catch(CcTest::isolate());
// Test that referencing unintroduced variables in sloppy mode is ok.
const char* script1 =
"if (false) { \n"
" not_there1; \n"
"} \n";
CompileRun(v8_str(script1));
CHECK(!try_catch.HasCaught());
// But not in strong mode.
{
const char* script2 =
"\"use strong\"; \n"
"if (false) { \n"
" not_there2; \n"
"} \n";
v8::TryCatch try_catch2(CcTest::isolate());
v8_compile(v8_str(script2));
CHECK(try_catch2.HasCaught());
v8::String::Utf8Value exception(try_catch2.Exception());
CHECK_EQ(0,
strcmp(
"ReferenceError: In strong mode, using an undeclared global "
"variable 'not_there2' is not allowed",
*exception));
}
// Check that the variable reference is detected inside a strong function too,
// even if the script scope is not strong.
{
const char* script3 =
"(function not_lazy() { \n"
" \"use strong\"; \n"
" if (false) { \n"
" not_there3; \n"
" } \n"
"})(); \n";
v8::TryCatch try_catch2(CcTest::isolate());
v8_compile(v8_str(script3));
CHECK(try_catch2.HasCaught());
v8::String::Utf8Value exception(try_catch2.Exception());
CHECK_EQ(0,
strcmp(
"ReferenceError: In strong mode, using an undeclared global "
"variable 'not_there3' is not allowed",
*exception));
}
}
static const ParserFlag kAllDestructuringFlags[] = {
kAllowHarmonyDestructuring, kAllowHarmonyDestructuringAssignment,
kAllowHarmonyDefaultParameters};
......
// 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: --strong-mode
// Note that it's essential for these tests that the reference is inside dead
// code (because we already produce ReferenceErrors for run-time unresolved
// variables and don't want to confuse those with strong mode errors). But the
// errors should *not* be inside lazy, unexecuted functions, since lazy parsing
// doesn't produce strong mode scoping errors).
// In addition, assertThrows will call eval and that changes variable binding
// types (see e.g., UNBOUND_EVAL_SHADOWED). We can avoid unwanted side effects
// by wrapping the code to be tested inside an outer function.
function assertThrowsHelper(code) {
"use strict";
let prologue = "(function outer() { if (false) { ";
let epilogue = " } })();";
assertThrows("'use strong'; " + prologue + code + epilogue, ReferenceError);
// Make sure the error happens only in strong mode (note that we need strict
// mode here because of let).
assertDoesNotThrow("'use strict'; " + prologue + code + epilogue);
}
(function DeclarationAfterUse() {
// Note that these tests only test cases where the declaration is found but is
// after the use. In particular, we cannot yet detect cases where the use can
// possibly bind to a global variable.
assertThrowsHelper("x; let x = 0;");
assertThrowsHelper("function f() { x; let x = 0; }");
assertThrowsHelper("function f() { x; } let x = 0;");
assertThrowsHelper("x; const x = 0;");
assertThrowsHelper("function f() { x; const x = 0; }");
assertThrowsHelper("function f() { x; } const x = 0;");
// These tests needs to be done a bit more manually, since var is not allowed
// in strong mode:
assertThrows(
`(function outer() {
function f() { 'use strong'; if (false) { x; } } var x = 0; f();
})()`,
ReferenceError);
assertDoesNotThrow(
"(function outer() {\n" +
" function f() { if (false) { x; } } var x = 0; f(); \n" +
"})()");
assertThrows(
"(function outer() {\n" +
" function f() { 'use strong'; if (false) { x; } } var x; f(); \n" +
"})()",
ReferenceError);
assertDoesNotThrow(
"(function outer() {\n" +
" function f() { if (false) { x; } } var x; f(); \n" +
"})()");
// Use occurring in the initializer of the declaration:
assertThrowsHelper("let x = x + 1;");
assertThrowsHelper("let x = x;");
assertThrowsHelper("let x = y, y = 4;");
assertThrowsHelper("let x = function() { x; }");
assertThrowsHelper("let x = a => { x; }");
assertThrowsHelper("function f(x) { return x; }; let x = f(x);");
assertThrowsHelper("const x = x;");
assertThrowsHelper("const x = function() { x; }");
assertThrowsHelper("const x = a => { x; }");
assertThrowsHelper("function f(x) {return x}; const x = f(x);");
assertThrowsHelper("for (let x = x; ; ) { }");
assertThrowsHelper("for (const x = x; ; ) { }");
assertThrowsHelper("for (let x = y, y; ; ) { }");
assertThrowsHelper("for (const x = y, y = 0; ; ) { }");
// Computed property names
assertThrowsHelper("let o = { 'a': 'b', [o.a]: 'c'};");
})();
(function DeclarationAfterUseInClasses() {
// Referring to a variable declared later
assertThrowsHelper("class C { m() { x; } } let x = 0;");
assertThrowsHelper("class C { static m() { x; } } let x = 0;");
assertThrowsHelper("class C { [x]() { } } let x = 0;");
assertThrowsHelper("class C { m() { x; } } const x = 0;");
assertThrowsHelper("class C { static m() { x; } } const x = 0;");
assertThrowsHelper("class C { [x]() { } } const x = 0;");
// Referring to the class name.
assertThrowsHelper("class C extends C { }");
assertThrowsHelper("let C = class C2 extends C { }");
assertThrowsHelper("let C = class C2 extends C2 { }");
assertThrowsHelper("let C = class C2 { constructor() { C; } }");
assertThrowsHelper("let C = class C2 { method() { C; } }");
assertThrowsHelper("let C = class C2 { *generator_method() { C; } }");
assertThrowsHelper(
`let C = class C2 {
static a() { return 'A'; }
[C.a()]() { return 'B'; }
};`);
assertThrowsHelper(
`let C = class C2 {
static a() { return 'A'; }
[C2.a()]() { return 'B'; }
};`);
assertThrowsHelper(
`let C = class C2 {
[(function() { C; return 'A';})()]() { return 'B'; }
};`);
// The reference to C or C2 is inside a function, but not a method.
assertThrowsHelper(
`let C = class C2 {
[(function() { C2; return 'A';})()]() { return 'B'; }
};`);
assertThrowsHelper(
`let C = class C2 {
[(function() { C; return 'A';})()]() { return 'B'; }
};`);
// The reference to C or C2 is inside a method, but it's not a method of the
// relevant class (C2).
assertThrowsHelper(
`let C = class C2 {
[(new (class D { m() { C2; return 'A'; } })).m()]() {
return 'B';
}
}`);
assertThrowsHelper(
`let C = class C2 {
[(new (class D { m() { C; return 'A'; } })).m()]() {
return 'B';
}
}`);
assertThrowsHelper(
`let C = class C2 {
[({m() { C2; return 'A'; }}).m()]() { return 'B'; }
}`);
assertThrowsHelper(
`let C = class C2 {
[({m() { C; return 'A'; }}).m()]() { return 'B'; }
}`);
assertThrowsHelper(
`class COuter {
m() {
class CInner {
[({ m() { CInner; return 'A'; } }).m()]() {
return 'B';
}
}
}
}`);
})();
(function UsesWhichAreFine() {
"use strong";
let var1 = 0;
var1;
let var2a = 0, var2b = var2a + 1, var2c = 2 + var2b;
for (let var3 = 0; var3 < 1; var3++) {
var3;
}
for (let var4a = 0, var4b = var4a; var4a + var4b < 4; var4a++, var4b++) {
var4a;
var4b;
}
let var5 = 5;
for (; var5 < 10; ++var5) { }
let arr = [1, 2];
for (let i of arr) {
i;
}
try {
throw "error";
} catch (e) {
e;
}
function func1() { func1; this; }
func1();
func1;
function * func2() { func2; this; }
func2();
func2;
function func4(p, ...rest) { p; rest; this; func2; }
// TODO(arv): The arity checking is not correct with rest parameters.
func4(1, 2);
let func5 = (p1, p2) => { p1; p2; };
func5(1, 2);
let func5b = p1 => p1;
func5b(1);
function func6() {
var1, var2a, var2b, var2c;
}
class C1 { constructor() { C1; } }; new C1();
let C2 = class C3 { constructor() { C3; } }; new C2();
class C4 { method() { C4; } *generator_method() { C4; } }; new C4();
let C5 = class C6 { method() { C6; } *generator_method() { C6; } }; new C5();
class C7 { static method() { C7; } }; new C7();
let C8 = class C9 { static method() { C9; } }; new C8();
class C10 { get x() { C10; } }; new C10();
let C11 = class C12 { get x() { C12; } }; new C11();
// Regression test for unnamed classes.
let C13 = class { m() { var1; } };
class COuter {
m() {
class CInner {
// Here we can refer to COuter but not to CInner (see corresponding
// assertion test):
[({ m() { COuter; return 'A'; } }).m()]() { return 'B'; }
// And here we can refer to both:
n() { COuter; CInner; }
}
return new CInner();
}
}
(new COuter()).m().n();
// Making sure the check which is supposed to prevent "object literal inside
// computed property name references the class name" is not too generic:
class C14 { m() { let obj = { n() { C14 } }; obj.n(); } }; (new C14()).m();
})();
// 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: --strong-mode
"use strict"
let prologue_dead = "(function outer() { if (false) { ";
let epilogue_dead = " } })();";
let prologue_live = "(function outer() { ";
let epilogue_live = "})();";
// For code which already throws a run-time error in non-strong mode; we assert
// that we now get the error already compilation time.
function assertLateErrorsBecomeEarly(code) {
assertThrows("'use strong'; " + prologue_dead + code + epilogue_dead,
ReferenceError);
// Make sure the error happens only in strong mode (note that we need strict
// mode here because of let).
assertDoesNotThrow("'use strict'; " + prologue_dead + code + epilogue_dead);
// But if we don't put the references inside a dead code, it throws a run-time
// error (also in strict mode).
assertThrows("'use strong'; " + prologue_live + code + epilogue_live,
ReferenceError);
assertThrows("'use strict'; " + prologue_live + code + epilogue_live,
ReferenceError);
}
// For code which doesn't throw an error at all in non-strong mode.
function assertNonErrorsBecomeEarly(code) {
assertThrows("'use strong'; " + prologue_dead + code + epilogue_dead,
ReferenceError);
assertDoesNotThrow("'use strict'; " + prologue_dead + code + epilogue_dead);
assertThrows("'use strong'; " + prologue_live + code + epilogue_live,
ReferenceError);
assertDoesNotThrow("'use strict'; " + prologue_live + code + epilogue_live,
ReferenceError);
}
(function InitTimeReferenceForward() {
// It's never OK to have an init time reference to a class which hasn't been
// declared.
assertLateErrorsBecomeEarly(
`class A extends B { }
class B {}`);
assertLateErrorsBecomeEarly(
`class A {
[B.sm()]() { }
}
class B {
static sm() { return 0; }
}`);
})();
(function InitTimeReferenceBackward() {
// Backwards is of course fine.
"use strong";
class A {
static sm() { return 0; }
}
let i = "making these classes non-consecutive";
class B extends A {};
"by inserting statements and declarations in between";
class C {
[A.sm()]() { }
};
})();
(function BasicMutualRecursion() {
"use strong";
class A {
m() { B; }
static sm() { B; }
}
// No statements or declarations between the classes.
class B {
m() { A; }
static sm() { A; }
}
})();
(function MutualRecursionWithMoreClasses() {
"use strong";
class A {
m() { B; C; }
static sm() { B; C; }
}
class B {
m() { A; C; }
static sm() { A; C; }
}
class C {
m() { A; B; }
static sm() { A; B; }
}
})();
(function ReferringForwardInDeeperScopes() {
"use strong";
function foo() {
class A1 {
m() { B1; }
}
class B1 { }
}
class Outer {
m() {
class A2 {
m() { B2; }
}
class B2 { }
}
}
for (let i = 0; i < 1; ++i) {
class A3 {
m() { B3; }
}
class B3 { }
}
(a, b) => {
class A4 {
m() { B4; }
}
class B4 { }
}
})();
(function ReferringForwardButClassesNotConsecutive() {
assertNonErrorsBecomeEarly(
`class A {
m() { B; }
}
;
class B {}`);
assertNonErrorsBecomeEarly(
`let A = class {
m() { B; }
}
class B {}`);
assertNonErrorsBecomeEarly(
`class A {
m() { B1; } // Just a normal use-before-declaration.
}
let B1 = class B2 {}`);
assertNonErrorsBecomeEarly(
`class A {
m() { B; }
}
let i = 0;
class B {}`);
assertNonErrorsBecomeEarly(
`class A {
m() { B; }
}
function foo() {}
class B {}`);
assertNonErrorsBecomeEarly(
`function foo() {
class A {
m() { B; }
}
}
class B {}`);
assertNonErrorsBecomeEarly(
`class A extends class B { m() { C; } } {
}
class C { }`);
assertLateErrorsBecomeEarly(
`class A extends class B { [C.sm()]() { } } {
}
class C { static sm() { return 'a';} }`);
assertLateErrorsBecomeEarly(
`class A extends class B extends C { } {
}
class C { }`);
})();
(function RegressionForClassResolution() {
assertNonErrorsBecomeEarly(
`let A = class B {
m() { C; }
}
;;;;
class C {}
class B {}`);
})();
(function TestMultipleMethodScopes() {
"use strong";
// Test cases where the reference is inside multiple method scopes.
class A1 {
m() {
class C1 {
m() { B1; }
}
}
}
class B1 { }
;
class A2 {
m() {
class C2 extends B2 {
}
}
}
class B2 { }
})();
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