Commit 448b620d authored by rossberg@chromium.org's avatar rossberg@chromium.org

Basic interface inference for modules.

All module expressions, and all variables that might refer to modules,
are assigned interfaces (module types) that are resolved using
unification. This is necessary to deal with the highly recursive
nature of ES6 modules, which does not allow any kind of bottom-up
strategy for resolving module names and paths.

Error messages are rudimental right now. Probably need to track
more information to make them nicer.

R=svenpanne@chromium.org
BUG=v8:1569
TEST=

Review URL: https://chromiumcodereview.appspot.com/9615009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10966 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 1d89a176
......@@ -84,6 +84,7 @@ SOURCES = {
hydrogen-instructions.cc
ic.cc
incremental-marking.cc
interface.cc
inspector.cc
interpreter-irregexp.cc
isolate.cc
......
......@@ -76,7 +76,8 @@ VariableProxy::VariableProxy(Isolate* isolate, Variable* var)
is_this_(var->is_this()),
is_trivial_(false),
is_lvalue_(false),
position_(RelocInfo::kNoPosition) {
position_(RelocInfo::kNoPosition),
interface_(var->interface()) {
BindTo(var);
}
......@@ -84,14 +85,16 @@ VariableProxy::VariableProxy(Isolate* isolate, Variable* var)
VariableProxy::VariableProxy(Isolate* isolate,
Handle<String> name,
bool is_this,
int position)
int position,
Interface* interface)
: Expression(isolate),
name_(name),
var_(NULL),
is_this_(is_this),
is_trivial_(false),
is_lvalue_(false),
position_(position) {
position_(position),
interface_(interface) {
// Names must be canonicalized for fast equality checks.
ASSERT(name->IsSymbol());
}
......
......@@ -41,6 +41,7 @@
#include "token.h"
#include "utils.h"
#include "variables.h"
#include "interface.h"
#include "zone-inl.h"
namespace v8 {
......@@ -589,9 +590,15 @@ class ExportDeclaration: public Declaration {
class Module: public AstNode {
// TODO(rossberg): stuff to come...
public:
Interface* interface() const { return interface_; }
protected:
Module() {}
Module() : interface_(Interface::NewModule()) {}
explicit Module(Interface* interface) : interface_(interface) {}
private:
Interface* interface_;
};
......@@ -604,8 +611,9 @@ class ModuleLiteral: public Module {
protected:
template<class> friend class AstNodeFactory;
explicit ModuleLiteral(Block* body)
: body_(body) {
ModuleLiteral(Block* body, Interface* interface)
: Module(interface),
body_(body) {
}
private:
......@@ -622,9 +630,7 @@ class ModuleVariable: public Module {
protected:
template<class> friend class AstNodeFactory;
explicit ModuleVariable(VariableProxy* proxy)
: proxy_(proxy) {
}
inline explicit ModuleVariable(VariableProxy* proxy);
private:
VariableProxy* proxy_;
......@@ -1451,6 +1457,8 @@ class VariableProxy: public Expression {
Variable* var() const { return var_; }
bool is_this() const { return is_this_; }
int position() const { return position_; }
Interface* interface() const { return interface_; }
void MarkAsTrivial() { is_trivial_ = true; }
void MarkAsLValue() { is_lvalue_ = true; }
......@@ -1466,7 +1474,8 @@ class VariableProxy: public Expression {
VariableProxy(Isolate* isolate,
Handle<String> name,
bool is_this,
int position);
int position,
Interface* interface);
Handle<String> name_;
Variable* var_; // resolved variable, or NULL
......@@ -1476,6 +1485,7 @@ class VariableProxy: public Expression {
// or with a increment/decrement operator.
bool is_lvalue_;
int position_;
Interface* interface_;
};
......@@ -2505,6 +2515,15 @@ class RegExpEmpty: public RegExpTree {
};
// ----------------------------------------------------------------------------
// Out-of-line inline constructors (to side-step cyclic dependencies).
inline ModuleVariable::ModuleVariable(VariableProxy* proxy)
: Module(proxy->interface()),
proxy_(proxy) {
}
// ----------------------------------------------------------------------------
// Basic visitor
// - leaf node visitors are abstract.
......@@ -2639,8 +2658,8 @@ class AstNodeFactory BASE_EMBEDDED {
VISIT_AND_RETURN(ExportDeclaration, decl)
}
ModuleLiteral* NewModuleLiteral(Block* body) {
ModuleLiteral* module = new(zone_) ModuleLiteral(body);
ModuleLiteral* NewModuleLiteral(Block* body, Interface* interface) {
ModuleLiteral* module = new(zone_) ModuleLiteral(body, interface);
VISIT_AND_RETURN(ModuleLiteral, module)
}
......@@ -2796,9 +2815,11 @@ class AstNodeFactory BASE_EMBEDDED {
VariableProxy* NewVariableProxy(Handle<String> name,
bool is_this,
int position = RelocInfo::kNoPosition) {
int position = RelocInfo::kNoPosition,
Interface* interface =
Interface::NewValue()) {
VariableProxy* proxy =
new(zone_) VariableProxy(isolate_, name, is_this, position);
new(zone_) VariableProxy(isolate_, name, is_this, position, interface);
VISIT_AND_RETURN(VariableProxy, proxy)
}
......
......@@ -487,6 +487,11 @@ DEFINE_bool(print_global_handles, false, "report global handles after GC")
// ic.cc
DEFINE_bool(trace_ic, false, "trace inline cache state transitions")
// interface.cc
DEFINE_bool(print_interfaces, false, "print interfaces")
DEFINE_bool(print_interface_details, false, "print interface inference details")
DEFINE_int(print_interface_depth, 5, "depth for printing interfaces")
// objects.cc
DEFINE_bool(trace_normalization,
false,
......
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "v8.h"
#include "interface.h"
namespace v8 {
namespace internal {
static bool Match(void* key1, void* key2) {
String* name1 = *static_cast<String**>(key1);
String* name2 = *static_cast<String**>(key2);
ASSERT(name1->IsSymbol());
ASSERT(name2->IsSymbol());
return name1 == name2;
}
Interface* Interface::Lookup(Handle<String> name) {
ASSERT(IsModule());
ZoneHashMap* map = Chase()->exports_;
if (map == NULL) return NULL;
ZoneHashMap::Entry* p = map->Lookup(name.location(), name->Hash(), false);
if (p == NULL) return NULL;
ASSERT(*static_cast<String**>(p->key) == *name);
ASSERT(p->value != NULL);
return static_cast<Interface*>(p->value);
}
#ifdef DEBUG
// Current nesting depth for debug output.
class Nesting {
public:
Nesting() { current_ += 2; }
~Nesting() { current_ -= 2; }
static int current() { return current_; }
private:
static int current_;
};
int Nesting::current_ = 0;
#endif
void Interface::DoAdd(
void* name, uint32_t hash, Interface* interface, bool* ok) {
MakeModule(ok);
if (!*ok) return;
#ifdef DEBUG
if (FLAG_print_interface_details) {
PrintF("%*s# Adding...\n", Nesting::current(), "");
PrintF("%*sthis = ", Nesting::current(), "");
this->Print(Nesting::current());
PrintF("%*s%s : ", Nesting::current(), "",
(*reinterpret_cast<String**>(name))->ToAsciiArray());
interface->Print(Nesting::current());
}
#endif
ZoneHashMap** map = &Chase()->exports_;
if (*map == NULL) *map = new ZoneHashMap(Match, 8);
ZoneHashMap::Entry* p = (*map)->Lookup(name, hash, !IsFrozen());
if (p == NULL) {
// This didn't have name but was frozen already, that's an error.
*ok = false;
} else if (p->value == NULL) {
p->value = interface;
} else {
#ifdef DEBUG
Nesting nested;
#endif
reinterpret_cast<Interface*>(p->value)->Unify(interface, ok);
}
#ifdef DEBUG
if (FLAG_print_interface_details) {
PrintF("%*sthis' = ", Nesting::current(), "");
this->Print(Nesting::current());
PrintF("%*s# Added.\n", Nesting::current(), "");
}
#endif
}
void Interface::Unify(Interface* that, bool* ok) {
if (this->forward_) return this->Chase()->Unify(that, ok);
if (that->forward_) return this->Unify(that->Chase(), ok);
ASSERT(this->forward_ == NULL);
ASSERT(that->forward_ == NULL);
*ok = true;
if (this == that) return;
if (this->IsValue()) return that->MakeValue(ok);
if (that->IsValue()) return this->MakeValue(ok);
#ifdef DEBUG
if (FLAG_print_interface_details) {
PrintF("%*s# Unifying...\n", Nesting::current(), "");
PrintF("%*sthis = ", Nesting::current(), "");
this->Print(Nesting::current());
PrintF("%*sthat = ", Nesting::current(), "");
that->Print(Nesting::current());
}
#endif
// Merge the smaller interface into the larger, for performance.
if (this->exports_ != NULL && (that->exports_ == NULL ||
this->exports_->occupancy() >= that->exports_->occupancy())) {
this->DoUnify(that, ok);
} else {
that->DoUnify(this, ok);
}
#ifdef DEBUG
if (FLAG_print_interface_details) {
PrintF("%*sthis' = ", Nesting::current(), "");
this->Print(Nesting::current());
PrintF("%*sthat' = ", Nesting::current(), "");
that->Print(Nesting::current());
PrintF("%*s# Unified.\n", Nesting::current(), "");
}
#endif
}
void Interface::DoUnify(Interface* that, bool* ok) {
ASSERT(this->forward_ == NULL);
ASSERT(that->forward_ == NULL);
ASSERT(!this->IsValue());
ASSERT(!that->IsValue());
ASSERT(*ok);
#ifdef DEBUG
Nesting nested;
#endif
// Try to merge all members from that into this.
ZoneHashMap* map = that->exports_;
if (map != NULL) {
for (ZoneHashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) {
this->DoAdd(p->key, p->hash, static_cast<Interface*>(p->value), ok);
if (!*ok) return;
}
}
// If the new interface is larger than that's, then there were members in
// 'this' which 'that' didn't have. If 'that' was frozen that is an error.
int this_size = this->exports_ == NULL ? 0 : this->exports_->occupancy();
int that_size = map == NULL ? 0 : map->occupancy();
if (that->IsFrozen() && this_size > that_size) {
*ok = false;
return;
}
// Merge interfaces.
this->flags_ |= that->flags_;
that->forward_ = this;
}
#ifdef DEBUG
void Interface::Print(int n) {
int n0 = n > 0 ? n : 0;
if (FLAG_print_interface_details) {
PrintF("%p", static_cast<void*>(this));
for (Interface* link = this->forward_; link != NULL; link = link->forward_)
PrintF("->%p", static_cast<void*>(link));
PrintF(" ");
}
if (IsUnknown()) {
PrintF("unknown\n");
} else if (IsValue()) {
PrintF("value\n");
} else if (IsModule()) {
PrintF("module %s{", IsFrozen() ? "" : "(unresolved) ");
ZoneHashMap* map = Chase()->exports_;
if (map == NULL || map->occupancy() == 0) {
PrintF("}\n");
} else if (n < 0 || n0 >= 2 * FLAG_print_interface_depth) {
// Avoid infinite recursion on cyclic types.
PrintF("...}\n");
} else {
PrintF("\n");
for (ZoneHashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) {
String* name = *static_cast<String**>(p->key);
Interface* interface = static_cast<Interface*>(p->value);
PrintF("%*s%s : ", n0 + 2, "", name->ToAsciiArray());
interface->Print(n0 + 2);
}
PrintF("%*s}\n", n0, "");
}
}
}
#endif
} } // namespace v8::internal
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef V8_INTERFACE_H_
#define V8_INTERFACE_H_
#include "zone-inl.h" // For operator new.
namespace v8 {
namespace internal {
// This class implements the following abstract grammar of interfaces
// (i.e. module types):
// interface ::= UNDETERMINED | VALUE | 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.
// Undetermined types are unification variables that can be unified freely.
class Interface : public ZoneObject {
public:
// ---------------------------------------------------------------------------
// Factory methods.
static Interface* NewValue() {
static Interface value_interface(VALUE + FROZEN); // Cached.
return &value_interface;
}
static Interface* NewUnknown() {
return new Interface(NONE);
}
static Interface* NewModule() {
return new Interface(MODULE);
}
// ---------------------------------------------------------------------------
// Mutators.
// Add a name to the list of exports. If it already exists, unify with
// interface, otherwise insert unless this is closed.
void Add(Handle<String> name, Interface* interface, bool* ok) {
DoAdd(name.location(), name->Hash(), interface, ok);
}
// Unify with another interface. If successful, both interface objects will
// represent the same type, and changes to one are reflected in the other.
void Unify(Interface* that, bool* ok);
// Determine this interface to be a value interface.
void MakeValue(bool* ok) {
*ok = !IsModule();
if (*ok) Chase()->flags_ |= VALUE;
}
// Determine this interface to be a module interface.
void MakeModule(bool* ok) {
*ok = !IsValue();
if (*ok) Chase()->flags_ |= MODULE;
}
// Do not allow any further refinements, directly or through unification.
void Freeze(bool* ok) {
*ok = IsValue() || IsModule();
if (*ok) Chase()->flags_ |= FROZEN;
}
// ---------------------------------------------------------------------------
// Accessors.
// Look up an exported name. Returns NULL if not (yet) defined.
Interface* Lookup(Handle<String> name);
// Check whether this is still a fully undetermined type.
bool IsUnknown() { return Chase()->flags_ == NONE; }
// Check whether this is a value type.
bool IsValue() { return Chase()->flags_ & VALUE; }
// Check whether this is a module type.
bool IsModule() { return Chase()->flags_ & MODULE; }
// Check whether this is closed (i.e. fully determined).
bool IsFrozen() { return Chase()->flags_ & FROZEN; }
// ---------------------------------------------------------------------------
// Debugging.
#ifdef DEBUG
void Print(int n = 0); // n = indentation; n < 0 => don't print recursively
#endif
// ---------------------------------------------------------------------------
// Implementation.
private:
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
};
int flags_;
Interface* forward_; // Unification link
ZoneHashMap* exports_; // Module exports and their types (allocated lazily)
explicit Interface(int flags)
: flags_(flags),
forward_(NULL),
exports_(NULL) {
#ifdef DEBUG
if (FLAG_print_interface_details)
PrintF("# Creating %p\n", static_cast<void*>(this));
#endif
}
Interface* Chase() {
Interface* result = this;
while (result->forward_ != NULL) result = result->forward_;
if (result != this) forward_ = result; // On-the-fly path compression.
return result;
}
void DoAdd(void* name, uint32_t hash, Interface* interface, bool* ok);
void DoUnify(Interface* that, bool* ok);
};
} } // namespace v8::internal
#endif // V8_INTERFACE_H_
......@@ -246,6 +246,8 @@ function FormatMessage(message) {
"cant_prevent_ext_external_array_elements", ["Cannot prevent extension of an object with external array elements"],
"redef_external_array_element", ["Cannot redefine a property of an object with external array elements"],
"harmony_const_assign", ["Assignment to constant variable."],
"invalid_module_path", ["Module does not export '", "%0", "', or export is not itself a module"],
"module_type_error", ["Module '", "%0", "' used improperly"],
];
var messages = { __proto__ : null };
for (var i = 0; i < messagesDictionary.length; i += 2) {
......
This diff is collapsed.
......@@ -557,6 +557,7 @@ class Parser {
void ReportUnexpectedToken(Token::Value token);
void ReportInvalidPreparseData(Handle<String> name, bool* ok);
void ReportMessage(const char* message, Vector<const char*> args);
void ReportMessage(const char* message, Vector<Handle<String> > args);
bool inside_with() const { return top_scope_->inside_with(); }
Scanner& scanner() { return scanner_; }
......@@ -764,7 +765,9 @@ class Parser {
void CheckConflictingVarDeclarations(Scope* scope, bool* ok);
// Parser support
VariableProxy* NewUnresolved(Handle<String> name, VariableMode mode);
VariableProxy* NewUnresolved(Handle<String> name,
VariableMode mode,
Interface* interface = Interface::NewValue());
void Declare(Declaration* declaration, bool resolve, bool* ok);
bool TargetStackContainsLabel(Handle<String> label);
......
......@@ -67,7 +67,8 @@ Variable* VariableMap::Declare(
VariableMode mode,
bool is_valid_lhs,
Variable::Kind kind,
InitializationFlag initialization_flag) {
InitializationFlag initialization_flag,
Interface* interface) {
Entry* p = ZoneHashMap::Lookup(name.location(), name->Hash(), true);
if (p->value == NULL) {
// The variable has not been declared yet -> insert it.
......@@ -77,7 +78,8 @@ Variable* VariableMap::Declare(
mode,
is_valid_lhs,
kind,
initialization_flag);
initialization_flag,
interface);
}
return reinterpret_cast<Variable*>(p->value);
}
......@@ -105,6 +107,9 @@ Scope::Scope(Scope* outer_scope, ScopeType type)
params_(4),
unresolved_(16),
decls_(4),
interface_(FLAG_harmony_modules &&
(type == MODULE_SCOPE || type == GLOBAL_SCOPE)
? Interface::NewModule() : NULL),
already_resolved_(false) {
SetDefaults(type, outer_scope, Handle<ScopeInfo>::null());
// At some point we might want to provide outer scopes to
......@@ -125,6 +130,7 @@ Scope::Scope(Scope* inner_scope,
params_(4),
unresolved_(16),
decls_(4),
interface_(NULL),
already_resolved_(true) {
SetDefaults(type, NULL, scope_info);
if (!scope_info.is_null()) {
......@@ -145,6 +151,7 @@ Scope::Scope(Scope* inner_scope, Handle<String> catch_variable_name)
params_(0),
unresolved_(0),
decls_(0),
interface_(NULL),
already_resolved_(true) {
SetDefaults(CATCH_SCOPE, NULL, Handle<ScopeInfo>::null());
AddInnerScope(inner_scope);
......@@ -255,7 +262,7 @@ bool Scope::Analyze(CompilationInfo* info) {
// Allocate the variables.
{
AstNodeFactory<AstNullVisitor> ast_node_factory(info->isolate());
top->AllocateVariables(info->global_scope(), &ast_node_factory);
if (!top->AllocateVariables(info, &ast_node_factory)) return false;
}
#ifdef DEBUG
......@@ -264,6 +271,11 @@ bool Scope::Analyze(CompilationInfo* info) {
: FLAG_print_scopes) {
scope->Print();
}
if (FLAG_harmony_modules && FLAG_print_interfaces && top->is_global_scope()) {
PrintF("global : ");
top->interface()->Print();
}
#endif
if (FLAG_harmony_scoping) {
......@@ -438,7 +450,8 @@ void Scope::DeclareParameter(Handle<String> name, VariableMode mode) {
Variable* Scope::DeclareLocal(Handle<String> name,
VariableMode mode,
InitializationFlag init_flag) {
InitializationFlag init_flag,
Interface* interface) {
ASSERT(!already_resolved());
// This function handles VAR and CONST modes. DYNAMIC variables are
// introduces during variable allocation, INTERNAL variables are allocated
......@@ -448,8 +461,8 @@ Variable* Scope::DeclareLocal(Handle<String> name,
mode == CONST_HARMONY ||
mode == LET);
++num_var_or_const_;
return
variables_.Declare(this, name, mode, true, Variable::NORMAL, init_flag);
return variables_.Declare(
this, name, mode, true, Variable::NORMAL, init_flag, interface);
}
......@@ -586,7 +599,7 @@ void Scope::CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
}
void Scope::AllocateVariables(Scope* global_scope,
bool Scope::AllocateVariables(CompilationInfo* info,
AstNodeFactory<AstNullVisitor>* factory) {
// 1) Propagate scope information.
bool outer_scope_calls_non_strict_eval = false;
......@@ -598,10 +611,12 @@ void Scope::AllocateVariables(Scope* global_scope,
PropagateScopeInfo(outer_scope_calls_non_strict_eval);
// 2) Resolve variables.
ResolveVariablesRecursively(global_scope, factory);
if (!ResolveVariablesRecursively(info, factory)) return false;
// 3) Allocate variables.
AllocateVariablesRecursively();
return true;
}
......@@ -916,14 +931,14 @@ Variable* Scope::LookupRecursive(Handle<String> name,
}
void Scope::ResolveVariable(Scope* global_scope,
bool Scope::ResolveVariable(CompilationInfo* info,
VariableProxy* proxy,
AstNodeFactory<AstNullVisitor>* factory) {
ASSERT(global_scope == NULL || global_scope->is_global_scope());
ASSERT(info->global_scope()->is_global_scope());
// If the proxy is already resolved there's nothing to do
// (functions and consts may be resolved by the parser).
if (proxy->var() != NULL) return;
if (proxy->var() != NULL) return true;
// Otherwise, try to resolve the variable.
BindingKind binding_kind;
......@@ -947,8 +962,7 @@ void Scope::ResolveVariable(Scope* global_scope,
case UNBOUND:
// No binding has been found. Declare a variable in global scope.
ASSERT(global_scope != NULL);
var = global_scope->DeclareGlobal(proxy->name());
var = info->global_scope()->DeclareGlobal(proxy->name());
break;
case UNBOUND_EVAL_SHADOWED:
......@@ -965,23 +979,62 @@ void Scope::ResolveVariable(Scope* global_scope,
ASSERT(var != NULL);
proxy->BindTo(var);
if (FLAG_harmony_modules) {
bool ok;
#ifdef DEBUG
if (FLAG_print_interface_details)
PrintF("# Resolve %s:\n", var->name()->ToAsciiArray());
#endif
proxy->interface()->Unify(var->interface(), &ok);
if (!ok) {
#ifdef DEBUG
if (FLAG_print_interfaces) {
PrintF("SCOPES TYPE ERROR\n");
PrintF("proxy: ");
proxy->interface()->Print();
PrintF("var: ");
var->interface()->Print();
}
#endif
// Inconsistent use of module. Throw a syntax error.
// TODO(rossberg): generate more helpful error message.
MessageLocation location(info->script(),
proxy->position(),
proxy->position());
Isolate* isolate = Isolate::Current();
Factory* factory = isolate->factory();
Handle<JSArray> array = factory->NewJSArray(1);
array->SetElement(array, 0, var->name(), NONE, kStrictMode);
Handle<Object> result =
factory->NewSyntaxError("module_type_error", array);
isolate->Throw(*result, &location);
return false;
}
}
return true;
}
void Scope::ResolveVariablesRecursively(
Scope* global_scope,
bool Scope::ResolveVariablesRecursively(
CompilationInfo* info,
AstNodeFactory<AstNullVisitor>* factory) {
ASSERT(global_scope == NULL || global_scope->is_global_scope());
ASSERT(info->global_scope()->is_global_scope());
// Resolve unresolved variables for this scope.
for (int i = 0; i < unresolved_.length(); i++) {
ResolveVariable(global_scope, unresolved_[i], factory);
if (!ResolveVariable(info, unresolved_[i], factory)) return false;
}
// Resolve unresolved variables for inner scopes.
for (int i = 0; i < inner_scopes_.length(); i++) {
inner_scopes_[i]->ResolveVariablesRecursively(global_scope, factory);
if (!inner_scopes_[i]->ResolveVariablesRecursively(info, factory))
return false;
}
return true;
}
......
......@@ -49,7 +49,8 @@ class VariableMap: public ZoneHashMap {
VariableMode mode,
bool is_valid_lhs,
Variable::Kind kind,
InitializationFlag initialization_flag);
InitializationFlag initialization_flag,
Interface* interface = Interface::NewValue());
Variable* Lookup(Handle<String> name);
};
......@@ -145,7 +146,8 @@ class Scope: public ZoneObject {
// declared before, the previously declared variable is returned.
Variable* DeclareLocal(Handle<String> name,
VariableMode mode,
InitializationFlag init_flag);
InitializationFlag init_flag,
Interface* interface = Interface::NewValue());
// Declare an implicit global variable in this scope which must be a
// global scope. The variable was introduced (possibly from an inner
......@@ -157,12 +159,14 @@ class Scope: public ZoneObject {
template<class Visitor>
VariableProxy* NewUnresolved(AstNodeFactory<Visitor>* factory,
Handle<String> name,
int position = RelocInfo::kNoPosition) {
int position = RelocInfo::kNoPosition,
Interface* interface = Interface::NewValue()) {
// 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);
VariableProxy* proxy =
factory->NewVariableProxy(name, false, position, interface);
unresolved_.Add(proxy);
return proxy;
}
......@@ -295,9 +299,6 @@ class Scope: public ZoneObject {
// Does this scope contain a with statement.
bool contains_with() const { return scope_contains_with_; }
// The scope immediately surrounding this scope, or NULL.
Scope* outer_scope() const { return outer_scope_; }
// ---------------------------------------------------------------------------
// Accessors.
......@@ -336,6 +337,12 @@ class Scope: public ZoneObject {
// Inner scope list.
ZoneList<Scope*>* inner_scopes() { return &inner_scopes_; }
// The scope immediately surrounding this scope, or NULL.
Scope* outer_scope() const { return outer_scope_; }
// The interface as inferred so far; only for module scopes.
Interface* interface() const { return interface_; }
// ---------------------------------------------------------------------------
// Variable allocation.
......@@ -345,17 +352,6 @@ class Scope: public ZoneObject {
void CollectStackAndContextLocals(ZoneList<Variable*>* stack_locals,
ZoneList<Variable*>* context_locals);
// Resolve and fill in the allocation information for all variables
// in this scopes. Must be called *after* all scopes have been
// processed (parsed) to ensure that unresolved variables can be
// resolved properly.
//
// In the case of code compiled and run using 'eval', the context
// parameter is the context in which eval was called. In all other
// cases the context parameter is an empty handle.
void AllocateVariables(Scope* global_scope,
AstNodeFactory<AstNullVisitor>* factory);
// Current number of var or const locals.
int num_var_or_const() { return num_var_or_const_; }
......@@ -453,6 +449,8 @@ class Scope: public ZoneObject {
VariableProxy* function_;
// Convenience variable; function scopes only.
Variable* arguments_;
// Interface; module scopes only.
Interface* interface_;
// Illegal redeclaration.
Expression* illegal_redecl_;
......@@ -548,10 +546,12 @@ class Scope: public ZoneObject {
Variable* LookupRecursive(Handle<String> name,
BindingKind* binding_kind,
AstNodeFactory<AstNullVisitor>* factory);
void ResolveVariable(Scope* global_scope,
MUST_USE_RESULT
bool ResolveVariable(CompilationInfo* info,
VariableProxy* proxy,
AstNodeFactory<AstNullVisitor>* factory);
void ResolveVariablesRecursively(Scope* global_scope,
MUST_USE_RESULT
bool ResolveVariablesRecursively(CompilationInfo* info,
AstNodeFactory<AstNullVisitor>* factory);
// Scope analysis.
......@@ -571,6 +571,18 @@ class Scope: public ZoneObject {
void AllocateNonParameterLocals();
void AllocateVariablesRecursively();
// Resolve and fill in the allocation information for all variables
// in this scopes. Must be called *after* all scopes have been
// processed (parsed) to ensure that unresolved variables can be
// resolved properly.
//
// In the case of code compiled and run using 'eval', the context
// parameter is the context in which eval was called. In all other
// cases the context parameter is an empty handle.
MUST_USE_RESULT
bool AllocateVariables(CompilationInfo* info,
AstNodeFactory<AstNullVisitor>* factory);
private:
// Construct a scope based on the scope info.
Scope(Scope* inner_scope, ScopeType type, Handle<ScopeInfo> scope_info);
......
......@@ -59,7 +59,8 @@ Variable::Variable(Scope* scope,
VariableMode mode,
bool is_valid_LHS,
Kind kind,
InitializationFlag initialization_flag)
InitializationFlag initialization_flag,
Interface* interface)
: scope_(scope),
name_(name),
mode_(mode),
......@@ -71,7 +72,8 @@ Variable::Variable(Scope* scope,
is_valid_LHS_(is_valid_LHS),
force_context_allocation_(false),
is_used_(false),
initialization_flag_(initialization_flag) {
initialization_flag_(initialization_flag),
interface_(interface) {
// Names must be canonicalized for fast equality checks.
ASSERT(name->IsSymbol());
// Var declared variables never need initialization.
......
......@@ -29,6 +29,7 @@
#define V8_VARIABLES_H_
#include "zone.h"
#include "interface.h"
namespace v8 {
namespace internal {
......@@ -78,7 +79,8 @@ class Variable: public ZoneObject {
VariableMode mode,
bool is_valid_lhs,
Kind kind,
InitializationFlag initialization_flag);
InitializationFlag initialization_flag,
Interface* interface = Interface::NewValue());
// Printing support
static const char* Mode2String(VariableMode mode);
......@@ -153,6 +155,7 @@ class Variable: public ZoneObject {
InitializationFlag initialization_flag() const {
return initialization_flag_;
}
Interface* interface() const { return interface_; }
void AllocateTo(Location location, int index) {
location_ = location;
......@@ -183,6 +186,9 @@ class Variable: public ZoneObject {
bool force_context_allocation_; // set by variable resolver
bool is_used_;
InitializationFlag initialization_flag_;
// Module type info.
Interface* interface_;
};
......
// Copyright 2011 the V8 project authors. All rights reserved.
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
......@@ -27,7 +27,7 @@
// Flags: --harmony-modules
// Test basic module syntax, with and without ASI.
// Test basic module syntax, with and without automatic semicolon insertion.
module A {}
......@@ -36,8 +36,8 @@ module A2 = A;
module A3 = A2
module B {
export x
export y, z, c, f
export vx
export vy, lz, c, f
var vx
var vx, vy;
......@@ -47,9 +47,11 @@ module B {
const c = 9
function f() {}
module C {
module C0 {}
export module C {
let x
module D {}
export module D { export let x }
let y
}
......@@ -67,10 +69,15 @@ module B {
export module M3 at "http://where"
import i0 from I
import i1, i2, i3 from I
import i1, i2, i3, M from I
import i4, i5 from "http://where"
}
module I {
export let i0, i1, i2, i3;
export module M {}
}
module C1 = B.C;
module D1 = B.C.D
module D2 = C1.D
......@@ -80,7 +87,6 @@ module E1 at "http://where"
module E2 at "http://where";
module E3 = E1.F
// Check that ASI does not interfere.
module X
......@@ -103,6 +109,7 @@ y
from
"file://local"
module Wrap {
export
x
......@@ -135,6 +142,9 @@ module V
}
}
export A, A1, A2, A3, B, I, C1, D1, D2, D3, E1, E2, E3, X, Y, Z, Wrap, x, y, UU
// Check that 'module' still works as an identifier.
......
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --harmony-modules --harmony-scoping
// Test basic module interface inference.
"use strict";
print("begin.")
export let x = print("0")
export module B = A.B
export module A {
export let x = print("1")
export let f = function() { return B.x }
export module B {
module BB = B
export BB, x
let x = print("2")
let y = print("3")
let Ax = A.x
let ABx = A.B.x
let Ay = A.y
let BBx = BB.x
let Af = A.f
function f(x,y) { return x }
}
export let y = print("4")
let Ax = A.x
let Bx = B.x
let ABx = A.B.x
module C {
export let z = print("5")
export module D = B
// TODO(rossberg): turn these into proper negative test cases once we have
// suitable error messages.
// import C.z // multiple declarations
import x from B
}
module D {
// TODO(rossberg): Handle import *.
// import A.* // invalid forward import
}
module M {}
// TODO(rossberg): Handle import *.
// import M.* // invalid forward import
let Cz = C.z
let CDx = C.D.x
}
export module Imports {
module A1 {
export module A2 {}
}
module B {
// TODO(rossberg): Handle import *.
// import A1.*
// import A2.* // unbound variable A2
}
}
export module E {
export let xx = x
export y, B
let Bx = B.x
// TODO(rossberg): Handle import *.
// import A.*
}
export module M1 {
export module A2 = M2
}
export module M2 {
export module A1 = M1
}
// TODO(rossberg): turn these into proper negative test cases once we have
// suitable error messages.
// module W1 = W2.W
// module W2 = { export module W = W3 }
// module W3 = W1 // cyclic module definition
// module W1 = W2.W3
// module W2 = {
// export module W3 = W4
// export module W4 = W1
// } // cyclic module definition
// TODO(rossberg): Handle import *.
//module M3B = M3.B
//export module M3 {
// export module B { export let x = "" }
// module C1 = { import M3.* }
// module C2 = { import M3.B.* }
// module C3 = { import M3B.* }
// module C4 = { export x import B.* }
//// TODO(rossberg): turn these into proper negative test cases once we have
//// suitable error messages.
//// export module C5 = { import C5.* } // invalid forward import
//// export module C6 = { import M3.C6.* } // invalid forward import
//}
export module External at "external.js"
export module External1 = External
export module ExternalA = External.A
export module InnerExternal {
export module E at "external.js"
}
export module External2 = InnerExternal.E
//export let xxx = InnerExternal.E.A.x
print("end.")
......@@ -342,6 +342,8 @@
'../../src/incremental-marking.h',
'../../src/inspector.cc',
'../../src/inspector.h',
'../../src/interface.cc',
'../../src/interface.h',
'../../src/interpreter-irregexp.cc',
'../../src/interpreter-irregexp.h',
'../../src/json-parser.h',
......
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