hydrogen-instructions.cc
ic.cc
incremental-marking.cc
+ interface.cc
inspector.cc
interpreter-irregexp.cc
isolate.cc
is_this_(var->is_this()),
is_trivial_(false),
is_lvalue_(false),
- position_(RelocInfo::kNoPosition) {
+ position_(RelocInfo::kNoPosition),
+ interface_(var->interface()) {
BindTo(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());
}
#include "token.h"
#include "utils.h"
#include "variables.h"
+#include "interface.h"
#include "zone-inl.h"
namespace v8 {
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_;
};
protected:
template<class> friend class AstNodeFactory;
- explicit ModuleLiteral(Block* body)
- : body_(body) {
+ ModuleLiteral(Block* body, Interface* interface)
+ : Module(interface),
+ body_(body) {
}
private:
protected:
template<class> friend class AstNodeFactory;
- explicit ModuleVariable(VariableProxy* proxy)
- : proxy_(proxy) {
- }
+ inline explicit ModuleVariable(VariableProxy* proxy);
private:
VariableProxy* proxy_;
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; }
VariableProxy(Isolate* isolate,
Handle<String> name,
bool is_this,
- int position);
+ int position,
+ Interface* interface);
Handle<String> name_;
Variable* var_; // resolved variable, or NULL
// or with a increment/decrement operator.
bool is_lvalue_;
int position_;
+ Interface* interface_;
};
};
+// ----------------------------------------------------------------------------
+// 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.
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)
}
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)
}
// 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,
--- /dev/null
+// 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
--- /dev/null
+// 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_
"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) {
}
+void Parser::ReportMessage(const char* type, Vector<Handle<String> > args) {
+ Scanner::Location source_location = scanner().location();
+ ReportMessageAt(source_location, type, args);
+}
+
+
void Parser::ReportMessageAt(Scanner::Location source_location,
const char* type,
Vector<const char*> args) {
this_property_assignment_finder.GetThisPropertyAssignments());
}
}
+
return 0;
}
// Create new block with one expected declaration.
Block* block = factory()->NewBlock(NULL, 1, true);
Handle<String> name = ParseIdentifier(CHECK_OK);
+
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Module %s...\n", name->ToAsciiArray());
+#endif
+
Module* module = ParseModule(CHECK_OK);
- VariableProxy* proxy = NewUnresolved(name, LET);
+ VariableProxy* proxy = NewUnresolved(name, LET, module->interface());
Declaration* declaration =
factory()->NewModuleDeclaration(proxy, module, top_scope_);
Declare(declaration, true, CHECK_OK);
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Module %s.\n", name->ToAsciiArray());
+
+ if (FLAG_print_interfaces) {
+ PrintF("module %s : ", name->ToAsciiArray());
+ module->interface()->Print();
+ }
+#endif
+
// TODO(rossberg): Add initialization statement to block.
if (names) names->Add(name);
// Construct block expecting 16 statements.
Block* body = factory()->NewBlock(NULL, 16, false);
+#ifdef DEBUG
+ if (FLAG_print_interface_details) PrintF("# Literal ");
+#endif
Scope* scope = NewScope(top_scope_, MODULE_SCOPE);
Expect(Token::LBRACE, CHECK_OK);
Expect(Token::RBRACE, CHECK_OK);
scope->set_end_position(scanner().location().end_pos);
body->set_block_scope(scope);
- return factory()->NewModuleLiteral(body);
+
+ scope->interface()->Freeze(ok);
+ ASSERT(ok);
+ return factory()->NewModuleLiteral(body, scope->interface());
}
// ModulePath '.' Identifier
Module* result = ParseModuleVariable(CHECK_OK);
-
while (Check(Token::PERIOD)) {
Handle<String> name = ParseIdentifierName(CHECK_OK);
- result = factory()->NewModulePath(result, name);
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Path .%s ", name->ToAsciiArray());
+#endif
+ Module* member = factory()->NewModulePath(result, name);
+ result->interface()->Add(name, member->interface(), ok);
+ if (!*ok) {
+#ifdef DEBUG
+ if (FLAG_print_interfaces) {
+ PrintF("PATH TYPE ERROR at '%s'\n", name->ToAsciiArray());
+ PrintF("result: ");
+ result->interface()->Print();
+ PrintF("member: ");
+ member->interface()->Print();
+ }
+#endif
+ ReportMessage("invalid_module_path", Vector<Handle<String> >(&name, 1));
+ return NULL;
+ }
+ result = member;
}
return result;
// Identifier
Handle<String> name = ParseIdentifier(CHECK_OK);
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Module variable %s ", name->ToAsciiArray());
+#endif
VariableProxy* proxy = top_scope_->NewUnresolved(
- factory(), name, scanner().location().beg_pos);
+ factory(), name, scanner().location().beg_pos, Interface::NewModule());
+
return factory()->NewModuleVariable(proxy);
}
Expect(Token::STRING, CHECK_OK);
Handle<String> symbol = GetSymbol(CHECK_OK);
+ // TODO(ES6): Request JS resource from environment...
+
+#ifdef DEBUG
+ if (FLAG_print_interface_details) PrintF("# Url ");
+#endif
return factory()->NewModuleUrl(symbol);
}
ZoneStringList names(1);
Handle<String> name = ParseIdentifierName(CHECK_OK);
+ names.Add(name);
while (peek() == Token::COMMA) {
Consume(Token::COMMA);
name = ParseIdentifierName(CHECK_OK);
// TODO(ES6): once we implement destructuring, make that one declaration.
Block* block = factory()->NewBlock(NULL, 1, true);
for (int i = 0; i < names.length(); ++i) {
- VariableProxy* proxy = NewUnresolved(names[i], LET);
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Import %s ", names[i]->ToAsciiArray());
+#endif
+ Interface* interface = Interface::NewUnknown();
+ module->interface()->Add(names[i], interface, ok);
+ if (!*ok) {
+#ifdef DEBUG
+ if (FLAG_print_interfaces) {
+ PrintF("IMPORT TYPE ERROR at '%s'\n", names[i]->ToAsciiArray());
+ PrintF("module: ");
+ module->interface()->Print();
+ }
+#endif
+ ReportMessage("invalid_module_path", Vector<Handle<String> >(&name, 1));
+ return NULL;
+ }
+ VariableProxy* proxy = NewUnresolved(names[i], LET, interface);
Declaration* declaration =
factory()->NewImportDeclaration(proxy, module, top_scope_);
Declare(declaration, true, CHECK_OK);
// TODO(rossberg): Add initialization statement to block.
}
-
return block;
}
return NULL;
}
- // Extract declared names into export declarations.
+ // Extract declared names into export declarations and interface.
+ Interface* interface = top_scope_->interface();
for (int i = 0; i < names.length(); ++i) {
- VariableProxy* proxy = NewUnresolved(names[i], LET);
- Declaration* declaration =
- factory()->NewExportDeclaration(proxy, top_scope_);
- top_scope_->AddDeclaration(declaration);
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Export %s ", names[i]->ToAsciiArray());
+#endif
+ Interface* inner = Interface::NewUnknown();
+ interface->Add(names[i], inner, CHECK_OK);
+ VariableProxy* proxy = NewUnresolved(names[i], LET, inner);
+ USE(proxy);
+ // TODO(rossberg): Rethink whether we actually need to store export
+ // declarations (for compilation?).
+ // ExportDeclaration* declaration =
+ // factory()->NewExportDeclaration(proxy, top_scope_);
+ // top_scope_->AddDeclaration(declaration);
}
ASSERT(result != NULL);
}
-VariableProxy* Parser::NewUnresolved(Handle<String> name, VariableMode mode) {
+VariableProxy* Parser::NewUnresolved(
+ Handle<String> name, VariableMode mode, Interface* interface) {
// If we are inside a function, a declaration of a var/const variable is a
// truly local variable, and the scope of the variable is always the function
// scope.
// Let/const variables in harmony mode are always added to the immediately
// enclosing scope.
return DeclarationScope(mode)->NewUnresolved(
- factory(), name, scanner().location().beg_pos);
+ factory(), name, scanner().location().beg_pos, interface);
}
void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
- Handle<String> name = declaration->proxy()->name();
+ VariableProxy* proxy = declaration->proxy();
+ Handle<String> name = proxy->name();
VariableMode mode = declaration->mode();
Scope* declaration_scope = DeclarationScope(mode);
Variable* var = NULL;
if (declaration_scope->is_function_scope() ||
declaration_scope->is_strict_or_extended_eval_scope() ||
declaration_scope->is_block_scope() ||
- declaration_scope->is_module_scope()) {
+ declaration_scope->is_module_scope() ||
+ declaration->AsModuleDeclaration() != NULL) {
// Declare the variable in the function scope.
var = declaration_scope->LocalLookup(name);
if (var == NULL) {
// Declare the name.
var = declaration_scope->DeclareLocal(
- name, mode, declaration->initialization());
+ name, mode, declaration->initialization(), proxy->interface());
} else {
// The name was declared in this scope before; check for conflicting
// re-declarations. We have a conflict if either of the declarations is
// initialization code. Thus, inside the 'with' statement, we need
// both access to the static and the dynamic context chain; the
// runtime needs to provide both.
- if (resolve && var != NULL) declaration->proxy()->BindTo(var);
+ if (resolve && var != NULL) {
+ proxy->BindTo(var);
+
+ if (FLAG_harmony_modules) {
+ bool ok;
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Declare %s\n", var->name()->ToAsciiArray());
+#endif
+ proxy->interface()->Unify(var->interface(), &ok);
+ if (!ok) {
+#ifdef DEBUG
+ if (FLAG_print_interfaces) {
+ PrintF("DECLARE TYPE ERROR\n");
+ PrintF("proxy: ");
+ proxy->interface()->Print();
+ PrintF("var: ");
+ var->interface()->Print();
+ }
+#endif
+ ReportMessage("module_type_error", Vector<Handle<String> >(&name, 1));
+ }
+ }
+ }
}
case Token::FUTURE_STRICT_RESERVED_WORD: {
Handle<String> name = ParseIdentifier(CHECK_OK);
if (fni_ != NULL) fni_->PushVariableName(name);
+ // The name may refer to a module instance object, so its type is unknown.
+#ifdef DEBUG
+ if (FLAG_print_interface_details)
+ PrintF("# Variable %s ", name->ToAsciiArray());
+#endif
+ Interface* interface = Interface::NewUnknown();
result = top_scope_->NewUnresolved(
- factory(), name, scanner().location().beg_pos);
+ factory(), name, scanner().location().beg_pos, interface);
break;
}
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_; }
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);
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.
mode,
is_valid_lhs,
kind,
- initialization_flag);
+ initialization_flag,
+ interface);
}
return reinterpret_cast<Variable*>(p->value);
}
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
params_(4),
unresolved_(16),
decls_(4),
+ interface_(NULL),
already_resolved_(true) {
SetDefaults(type, NULL, scope_info);
if (!scope_info.is_null()) {
params_(0),
unresolved_(0),
decls_(0),
+ interface_(NULL),
already_resolved_(true) {
SetDefaults(CATCH_SCOPE, NULL, Handle<ScopeInfo>::null());
AddInnerScope(inner_scope);
// 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
: 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) {
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
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);
}
}
-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;
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;
}
}
-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;
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:
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;
}
VariableMode mode,
bool is_valid_lhs,
Variable::Kind kind,
- InitializationFlag initialization_flag);
+ InitializationFlag initialization_flag,
+ Interface* interface = Interface::NewValue());
Variable* Lookup(Handle<String> name);
};
// 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
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;
}
// 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.
// 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.
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_; }
VariableProxy* function_;
// Convenience variable; function scopes only.
Variable* arguments_;
+ // Interface; module scopes only.
+ Interface* interface_;
// Illegal redeclaration.
Expression* illegal_redecl_;
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.
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);
VariableMode mode,
bool is_valid_LHS,
Kind kind,
- InitializationFlag initialization_flag)
+ InitializationFlag initialization_flag,
+ Interface* interface)
: scope_(scope),
name_(name),
mode_(mode),
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.
#define V8_VARIABLES_H_
#include "zone.h"
+#include "interface.h"
namespace v8 {
namespace internal {
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);
InitializationFlag initialization_flag() const {
return initialization_flag_;
}
+ Interface* interface() const { return interface_; }
void AllocateTo(Location location, int index) {
location_ = location;
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:
// Flags: --harmony-modules
-// Test basic module syntax, with and without ASI.
+// Test basic module syntax, with and without automatic semicolon insertion.
module 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;
const c = 9
function f() {}
- module C {
+ module C0 {}
+
+ export module C {
let x
- module D {}
+ export module D { export let x }
let y
}
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
module E2 at "http://where";
module E3 = E1.F
-
// Check that ASI does not interfere.
module X
from
"file://local"
+
module Wrap {
export
x
}
}
+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.
--- /dev/null
+// 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.")
'../../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',