diff --git a/src/ast.cc b/src/ast.cc index 9d18f6929e..adbad418a5 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -85,8 +85,8 @@ VariableProxy::VariableProxy(Isolate* isolate, Variable* var) VariableProxy::VariableProxy(Isolate* isolate, Handle name, bool is_this, - int position, - Interface* interface) + Interface* interface, + int position) : Expression(isolate), name_(name), var_(NULL), diff --git a/src/ast.h b/src/ast.h index 156b879d08..e38def8dc8 100644 --- a/src/ast.h +++ b/src/ast.h @@ -1496,8 +1496,8 @@ class VariableProxy: public Expression { VariableProxy(Isolate* isolate, Handle name, bool is_this, - int position, - Interface* interface); + Interface* interface, + int position); Handle name_; Variable* var_; // resolved variable, or NULL @@ -2846,11 +2846,10 @@ class AstNodeFactory BASE_EMBEDDED { VariableProxy* NewVariableProxy(Handle name, bool is_this, - int position = RelocInfo::kNoPosition, - Interface* interface = - Interface::NewValue()) { + Interface* interface = Interface::NewValue(), + int position = RelocInfo::kNoPosition) { VariableProxy* proxy = - new(zone_) VariableProxy(isolate_, name, is_this, position, interface); + new(zone_) VariableProxy(isolate_, name, is_this, interface, position); VISIT_AND_RETURN(VariableProxy, proxy) } diff --git a/src/interface.cc b/src/interface.cc index 86bb9d0bf4..336be82c60 100644 --- a/src/interface.cc +++ b/src/interface.cc @@ -124,8 +124,16 @@ void Interface::Unify(Interface* that, Zone* zone, bool* ok) { *ok = true; if (this == that) return; - if (this->IsValue()) return that->MakeValue(ok); - if (that->IsValue()) return this->MakeValue(ok); + if (this->IsValue()) { + that->MakeValue(ok); + if (*ok && this->IsConst()) that->MakeConst(ok); + return; + } + if (that->IsValue()) { + this->MakeValue(ok); + if (*ok && that->IsConst()) this->MakeConst(ok); + return; + } #ifdef DEBUG if (FLAG_print_interface_details) { @@ -214,6 +222,8 @@ void Interface::Print(int n) { if (IsUnknown()) { PrintF("unknown\n"); + } else if (IsConst()) { + PrintF("const\n"); } else if (IsValue()) { PrintF("value\n"); } else if (IsModule()) { diff --git a/src/interface.h b/src/interface.h index 2670e7428d..94ef11ba5c 100644 --- a/src/interface.h +++ b/src/interface.h @@ -36,25 +36,41 @@ namespace internal { // This class implements the following abstract grammar of interfaces // (i.e. module types): -// interface ::= UNDETERMINED | VALUE | MODULE(exports) +// interface ::= UNDETERMINED | VALUE | CONST | MODULE(exports) // exports ::= {name : interface, ...} -// A frozen module type is one that is fully determined. Unification does not -// allow adding additional exports to frozen interfaces. -// Otherwise, unifying modules merges their exports. +// A frozen type is one that is fully determined. Unification does not +// allow to turn non-const values into const, or adding additional exports to +// frozen interfaces. Otherwise, unifying modules merges their exports. // Undetermined types are unification variables that can be unified freely. +// There is a natural subsort lattice that reflects the increase of knowledge: +// +// undetermined +// // | \\ . +// value (frozen) module +// // \\ / \ // +// const fr.value fr.module +// \\ / +// fr.const +// +// where the bold lines are the only transitions allowed. class Interface : public ZoneObject { public: // --------------------------------------------------------------------------- // Factory methods. + static Interface* NewUnknown(Zone* zone) { + return new(zone) Interface(NONE); + } + static Interface* NewValue() { static Interface value_interface(VALUE + FROZEN); // Cached. return &value_interface; } - static Interface* NewUnknown(Zone* zone) { - return new(zone) Interface(NONE); + static Interface* NewConst() { + static Interface value_interface(VALUE + CONST + FROZEN); // Cached. + return &value_interface; } static Interface* NewModule(Zone* zone) { @@ -80,6 +96,12 @@ class Interface : public ZoneObject { if (*ok) Chase()->flags_ |= VALUE; } + // Determine this interface to be an immutable interface. + void MakeConst(bool* ok) { + *ok = !IsModule() && (IsConst() || !IsFrozen()); + if (*ok) Chase()->flags_ |= VALUE + CONST; + } + // Determine this interface to be a module interface. void MakeModule(bool* ok) { *ok = !IsValue(); @@ -107,6 +129,9 @@ class Interface : public ZoneObject { // Check whether this is a value type. bool IsValue() { return Chase()->flags_ & VALUE; } + // Check whether this is a constant type. + bool IsConst() { return Chase()->flags_ & CONST; } + // Check whether this is a module type. bool IsModule() { return Chase()->flags_ & MODULE; } @@ -161,8 +186,9 @@ class Interface : public ZoneObject { enum Flags { // All flags are monotonic NONE = 0, VALUE = 1, // This type describes a value - MODULE = 2, // This type describes a module - FROZEN = 4 // This type is fully determined + CONST = 2, // This type describes a constant + MODULE = 4, // This type describes a module + FROZEN = 8 // This type is fully determined }; int flags_; diff --git a/src/parser.cc b/src/parser.cc index 2dbfeccdd6..2a7d7805d1 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1408,8 +1408,8 @@ Module* Parser::ParseModuleVariable(bool* ok) { PrintF("# Module variable %s ", name->ToAsciiArray()); #endif VariableProxy* proxy = top_scope_->NewUnresolved( - factory(), name, scanner().location().beg_pos, - Interface::NewModule(zone())); + factory(), name, Interface::NewModule(zone()), + scanner().location().beg_pos); return factory()->NewModuleVariable(proxy); } @@ -1499,7 +1499,6 @@ Block* Parser::ParseImportDeclaration(bool* ok) { Declaration* declaration = factory()->NewImportDeclaration(proxy, module, top_scope_); Declare(declaration, true, CHECK_OK); - // TODO(rossberg): Add initialization statement to block. } return block; @@ -1740,7 +1739,7 @@ VariableProxy* Parser::NewUnresolved( // Let/const variables in harmony mode are always added to the immediately // enclosing scope. return DeclarationScope(mode)->NewUnresolved( - factory(), name, scanner().location().beg_pos, interface); + factory(), name, interface, scanner().location().beg_pos); } @@ -1954,7 +1953,7 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) { // TODO(1240846): It's weird that native function declarations are // introduced dynamically when we meet their declarations, whereas // other functions are set up when entering the surrounding scope. - VariableProxy* proxy = NewUnresolved(name, VAR); + VariableProxy* proxy = NewUnresolved(name, VAR, Interface::NewValue()); Declaration* declaration = factory()->NewVariableDeclaration(proxy, VAR, top_scope_); Declare(declaration, true, CHECK_OK); @@ -1983,7 +1982,7 @@ Statement* Parser::ParseFunctionDeclaration(ZoneStringList* names, bool* ok) { // scope, we treat is as such and introduce the function with it's // initial value upon entering the corresponding scope. VariableMode mode = is_extended_mode() ? LET : VAR; - VariableProxy* proxy = NewUnresolved(name, mode); + VariableProxy* proxy = NewUnresolved(name, mode, Interface::NewValue()); Declaration* declaration = factory()->NewFunctionDeclaration(proxy, mode, fun, top_scope_); Declare(declaration, true, CHECK_OK); @@ -2215,7 +2214,9 @@ Block* Parser::ParseVariableDeclarations( // For let/const declarations in harmony mode, we can also immediately // pre-resolve the proxy because it resides in the same scope as the // declaration. - VariableProxy* proxy = NewUnresolved(name, mode); + Interface* interface = + is_const ? Interface::NewConst() : Interface::NewValue(); + VariableProxy* proxy = NewUnresolved(name, mode, interface); Declaration* declaration = factory()->NewVariableDeclaration(proxy, mode, top_scope_); Declare(declaration, mode != VAR, CHECK_OK); @@ -2376,7 +2377,7 @@ Block* Parser::ParseVariableDeclarations( // if they are inside a 'with' statement - they may change a 'with' object // property). VariableProxy* proxy = - initialization_scope->NewUnresolved(factory(), name); + initialization_scope->NewUnresolved(factory(), name, interface); Assignment* assignment = factory()->NewAssignment(init_op, proxy, value, position); block->AddStatement(factory()->NewExpressionStatement(assignment), @@ -2884,12 +2885,16 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { for_scope->set_start_position(scanner().location().beg_pos); if (peek() != Token::SEMICOLON) { if (peek() == Token::VAR || peek() == Token::CONST) { + bool is_const = peek() == Token::CONST; Handle name; Block* variable_statement = ParseVariableDeclarations(kForStatement, NULL, NULL, &name, CHECK_OK); if (peek() == Token::IN && !name.is_null()) { - VariableProxy* each = top_scope_->NewUnresolved(factory(), name); + Interface* interface = + is_const ? Interface::NewConst() : Interface::NewValue(); + VariableProxy* each = + top_scope_->NewUnresolved(factory(), name, interface); ForInStatement* loop = factory()->NewForInStatement(labels); Target target(&this->target_stack_, loop); @@ -2936,7 +2941,9 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { // implementing stack allocated block scoped variables. Variable* temp = top_scope_->DeclarationScope()->NewTemporary(name); VariableProxy* temp_proxy = factory()->NewVariableProxy(temp); - VariableProxy* each = top_scope_->NewUnresolved(factory(), name); + Interface* interface = Interface::NewValue(); + VariableProxy* each = + top_scope_->NewUnresolved(factory(), name, interface); ForInStatement* loop = factory()->NewForInStatement(labels); Target target(&this->target_stack_, loop); @@ -3671,7 +3678,7 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) { #endif Interface* interface = Interface::NewUnknown(zone()); result = top_scope_->NewUnresolved( - factory(), name, scanner().location().beg_pos, interface); + factory(), name, interface, scanner().location().beg_pos); break; } @@ -4517,7 +4524,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle function_name, VariableMode fvar_mode = is_extended_mode() ? CONST_HARMONY : CONST; fvar = new(zone()) Variable(top_scope_, function_name, fvar_mode, true /* is valid LHS */, - Variable::NORMAL, kCreatedInitialized); + Variable::NORMAL, kCreatedInitialized, Interface::NewConst()); VariableProxy* proxy = factory()->NewVariableProxy(fvar); VariableDeclaration* fvar_declaration = factory()->NewVariableDeclaration(proxy, fvar_mode, top_scope_); @@ -4607,8 +4614,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle function_name, if (!is_lazily_compiled) { body = new(zone()) ZoneList(8, zone()); if (fvar != NULL) { - VariableProxy* fproxy = - top_scope_->NewUnresolved(factory(), function_name); + VariableProxy* fproxy = top_scope_->NewUnresolved( + factory(), function_name, Interface::NewConst()); fproxy->BindTo(fvar); body->Add(factory()->NewExpressionStatement( factory()->NewAssignment(fvar_init_op, diff --git a/src/parser.h b/src/parser.h index b510456474..715c1dcb52 100644 --- a/src/parser.h +++ b/src/parser.h @@ -771,7 +771,7 @@ class Parser { // Parser support VariableProxy* NewUnresolved(Handle name, VariableMode mode, - Interface* interface = Interface::NewValue()); + Interface* interface); void Declare(Declaration* declaration, bool resolve, bool* ok); bool TargetStackContainsLabel(Handle label); diff --git a/src/rewriter.cc b/src/rewriter.cc index f791dc3e0c..d5f7527aed 100644 --- a/src/rewriter.cc +++ b/src/rewriter.cc @@ -257,7 +257,7 @@ bool Rewriter::Rewrite(CompilationInfo* info) { // coincides with the end of the with scope which is the position of '1'. int position = function->end_position(); VariableProxy* result_proxy = processor.factory()->NewVariableProxy( - result->name(), false, position); + result->name(), false, Interface::NewValue(), position); result_proxy->BindTo(result); Statement* result_statement = processor.factory()->NewReturnStatement(result_proxy); diff --git a/src/scopes.h b/src/scopes.h index f8826c889c..85d0c4598b 100644 --- a/src/scopes.h +++ b/src/scopes.h @@ -166,14 +166,14 @@ class Scope: public ZoneObject { template VariableProxy* NewUnresolved(AstNodeFactory* factory, Handle name, - int position = RelocInfo::kNoPosition, - Interface* interface = Interface::NewValue()) { + Interface* interface = Interface::NewValue(), + int position = RelocInfo::kNoPosition) { // Note that we must not share the unresolved variables with // the same name because they may be removed selectively via // RemoveUnresolved(). ASSERT(!already_resolved()); VariableProxy* proxy = - factory->NewVariableProxy(name, false, position, interface); + factory->NewVariableProxy(name, false, interface, position); unresolved_.Add(proxy, zone_); return proxy; }