Basic interface inference for modules.
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 8 Mar 2012 13:03:07 +0000 (13:03 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 8 Mar 2012 13:03:07 +0000 (13:03 +0000)
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

16 files changed:
src/SConscript
src/ast.cc
src/ast.h
src/flag-definitions.h
src/interface.cc [new file with mode: 0644]
src/interface.h [new file with mode: 0644]
src/messages.js
src/parser.cc
src/parser.h
src/scopes.cc
src/scopes.h
src/variables.cc
src/variables.h
test/mjsunit/harmony/module-parsing.js
test/mjsunit/harmony/module-resolution.js [new file with mode: 0644]
tools/gyp/v8.gyp

index 94840dc8b5d4d0b8b51e15e8f431041a83057ea7..40b2c54b9d203716a2fe4cb2250db9aa443a3508 100755 (executable)
@@ -84,6 +84,7 @@ SOURCES = {
     hydrogen-instructions.cc
     ic.cc
     incremental-marking.cc
+    interface.cc
     inspector.cc
     interpreter-irregexp.cc
     isolate.cc
index 65afd9a7b39d0bc0dabb61c57806f53d4e828f8e..7fe02be5d8a750411d6d740004cdd0ca36722f8b 100644 (file)
@@ -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());
 }
index 5398775483ec7c9766922d27c6f7e024bc6f6b4e..3342de479b7d87a1a96d0211e63aeb0b43babe72 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -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)
   }
 
index 4f4ad6a84b4e2eb11757304126839772099f4807..4b5ae580b6b63df3ec9f11c82bd6c82ba456dd55 100644 (file)
@@ -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,
diff --git a/src/interface.cc b/src/interface.cc
new file mode 100644 (file)
index 0000000..e344b86
--- /dev/null
@@ -0,0 +1,226 @@
+// 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
diff --git a/src/interface.h b/src/interface.h
new file mode 100644 (file)
index 0000000..c2991cb
--- /dev/null
@@ -0,0 +1,156 @@
+// 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_
index f39d59c28ad742026690bb3f20a83337999cdff2..a3adcf8634d05440dec2b03ad8ed373992653993 100644 (file)
@@ -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) {
index 5f04cabee3d81c3cd2779c0584870bfc2dd67fab..a253854d6e4a054c55e3f427a3840594f6a1e861 100644 (file)
@@ -757,6 +757,12 @@ void Parser::ReportMessage(const char* type, Vector<const char*> args) {
 }
 
 
+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) {
@@ -1163,6 +1169,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
           this_property_assignment_finder.GetThisPropertyAssignments());
     }
   }
+
   return 0;
 }
 
@@ -1221,12 +1228,28 @@ Block* Parser::ParseModuleDeclaration(ZoneStringList* names, bool* ok) {
   // 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);
@@ -1267,6 +1290,9 @@ Module* Parser::ParseModuleLiteral(bool* ok) {
 
   // 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);
@@ -1292,7 +1318,10 @@ Module* Parser::ParseModuleLiteral(bool* 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());
 }
 
 
@@ -1302,10 +1331,28 @@ Module* Parser::ParseModulePath(bool* ok) {
   //    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;
@@ -1317,8 +1364,13 @@ Module* Parser::ParseModuleVariable(bool* ok) {
   //    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);
 }
 
@@ -1330,6 +1382,11 @@ Module* Parser::ParseModuleUrl(bool* ok) {
   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);
 }
 
@@ -1357,6 +1414,7 @@ Block* Parser::ParseImportDeclaration(bool* ok) {
   ZoneStringList names(1);
 
   Handle<String> name = ParseIdentifierName(CHECK_OK);
+  names.Add(name);
   while (peek() == Token::COMMA) {
     Consume(Token::COMMA);
     name = ParseIdentifierName(CHECK_OK);
@@ -1371,14 +1429,30 @@ Block* Parser::ParseImportDeclaration(bool* 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;
 }
 
@@ -1431,12 +1505,22 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
       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);
@@ -1597,19 +1681,21 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) {
 }
 
 
-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;
@@ -1627,13 +1713,14 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
   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
@@ -1743,7 +1830,30 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
   // 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));
+      }
+    }
+  }
 }
 
 
@@ -3498,8 +3608,14 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) {
     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;
     }
 
index dea73a15fa5e1179c37501bb50363175e44ef0b3..90ef39983e19410a5e7e697c68f440d030ba08c9 100644 (file)
@@ -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);
index 8d71f8ae858d8b44937bea0e25dc69b379a60f7f..766e1ae5cccc7c5e42512cdaccdf2f634b1ca0aa 100644 (file)
@@ -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;
 }
 
 
index 9ce4cd7b1f119ee651f2523e0aa9ea98cf8b167a..d315b7e5dcf806f883c9fcfe66124f23aa03c492 100644 (file)
@@ -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);
index aa6a010facd979a9912b25b90e72c6e9feceabae..32ad5bc5dd4d09329ed1bd702106d3e0ee6dbcf9 100644 (file)
@@ -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.
index f20bd399c542aefc74400fd957ff9c6a7a8fa229..f49b6e127638a4743a7f320ee8df1c15403a2c5a 100644 (file)
@@ -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_;
 };
 
 
index cf5650226c4e24dfe9347cba438bbc0da87b1f26..93e69e3ad9c2690682034befb670518344061d1c 100644 (file)
@@ -1,4 +1,4 @@
-// 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.
 
diff --git a/test/mjsunit/harmony/module-resolution.js b/test/mjsunit/harmony/module-resolution.js
new file mode 100644 (file)
index 0000000..f9f492c
--- /dev/null
@@ -0,0 +1,139 @@
+// 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.")
index b244bc18b2a0757df3fa4821b6fc48433326a154..32a5f56376baf1aace2aebe914aaeb4e799f1b25 100644 (file)
             '../../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',