Bug 930414 - Add module importEntries field r=shu
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 24 Aug 2015 15:58:35 +0100
changeset 259049 7861ef6582a9a013436d2c0f1c70150222775754
parent 259048 7fc1a5cd4b301a3f9507697090e71d9f7e975c9b
child 259050 178d594d4934fa20df30f2883460ede1d15eb3ec
push id29268
push userryanvm@gmail.com
push dateTue, 25 Aug 2015 00:37:23 +0000
treeherdermozilla-central@08015770c9d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs930414
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 930414 - Add module importEntries field r=shu
js/src/builtin/ModuleObject.cpp
js/src/builtin/ModuleObject.h
js/src/gc/Marking.cpp
js/src/jit-test/tests/modules/import-entries.js
js/src/jsprototypes.h
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -1,20 +1,24 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/ModuleObject.h"
 
+#include "gc/Tracer.h"
+
 #include "jsobjinlines.h"
 
 using namespace js;
 
+typedef JS::Rooted<ImportEntryObject*> RootedImportEntry;
+
 template<typename T, Value ValueGetter(T* obj)>
 static bool
 ModuleValueGetterImpl(JSContext* cx, CallArgs args)
 {
     args.rval().set(ValueGetter(&args.thisv().toObject().as<T>()));
     return true;
 }
 
@@ -33,16 +37,93 @@ ModuleValueGetter(JSContext* cx, unsigne
     }                                                                         \
                                                                               \
     static bool                                                               \
     cls##_##name##Getter(JSContext* cx, unsigned argc, Value* vp)             \
     {                                                                         \
         return ModuleValueGetter<cls, cls##_##name##Value>(cx, argc, vp);     \
     }
 
+#define DEFINE_ATOM_ACCESSOR_METHOD(cls, name)                                \
+    JSAtom*                                                                   \
+    cls::name()                                                               \
+    {                                                                         \
+        Value value = cls##_##name##Value(this);                              \
+        return &value.toString()->asAtom();                                   \
+    }
+
+///////////////////////////////////////////////////////////////////////////
+// ImportEntryObject
+
+/* static */ const Class
+ImportEntryObject::class_ = {
+    "ImportEntry",
+    JSCLASS_HAS_RESERVED_SLOTS(ImportEntryObject::SlotCount) |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_ImportEntry) |
+    JSCLASS_IS_ANONYMOUS |
+    JSCLASS_IMPLEMENTS_BARRIERS
+};
+
+DEFINE_GETTER_FUNCTIONS(ImportEntryObject, moduleRequest, ModuleRequestSlot)
+DEFINE_GETTER_FUNCTIONS(ImportEntryObject, importName, ImportNameSlot)
+DEFINE_GETTER_FUNCTIONS(ImportEntryObject, localName, LocalNameSlot)
+
+DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, moduleRequest)
+DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, importName)
+DEFINE_ATOM_ACCESSOR_METHOD(ImportEntryObject, localName)
+
+/* static */ bool
+ImportEntryObject::isInstance(HandleValue value)
+{
+    return value.isObject() && value.toObject().is<ImportEntryObject>();
+}
+
+/* static */ JSObject*
+ImportEntryObject::initClass(JSContext* cx, HandleObject obj)
+{
+    static const JSPropertySpec protoAccessors[] = {
+        JS_PSG("moduleRequest", ImportEntryObject_moduleRequestGetter, 0),
+        JS_PSG("importName", ImportEntryObject_importNameGetter, 0),
+        JS_PSG("localName", ImportEntryObject_localNameGetter, 0),
+        JS_PS_END
+    };
+
+    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+    RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
+    if (!proto)
+        return nullptr;
+
+    if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
+        return nullptr;
+
+    global->setPrototype(JSProto_ImportEntry, ObjectValue(*proto));
+    return proto;
+}
+
+JSObject*
+js::InitImportEntryClass(JSContext* cx, HandleObject obj)
+{
+    return ImportEntryObject::initClass(cx, obj);
+}
+
+/* static */ ImportEntryObject*
+ImportEntryObject::create(JSContext* cx,
+                          HandleAtom moduleRequest,
+                          HandleAtom importName,
+                          HandleAtom localName)
+{
+    RootedImportEntry self(cx, NewBuiltinClassInstance<ImportEntryObject>(cx));
+    if (!self)
+        return nullptr;
+    self->initReservedSlot(ModuleRequestSlot, StringValue(moduleRequest));
+    self->initReservedSlot(ImportNameSlot, StringValue(importName));
+    self->initReservedSlot(LocalNameSlot, StringValue(localName));
+    return self;
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // ModuleObject
 
 /* static */ const Class
 ModuleObject::class_ = {
     "Module",
     JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Module) |
@@ -66,18 +147,17 @@ ModuleObject::class_ = {
 #define DEFINE_ARRAY_SLOT_ACCESSOR(cls, name, slot)                           \
     ArrayObject&                                                              \
     cls::name() const                                                         \
     {                                                                         \
         return getFixedSlot(cls::slot).toObject().as<ArrayObject>();          \
     }
 
 DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, requestedModules, RequestedModulesSlot)
-
-#undef DEFINE_ARRAY_SLOT_ACCESSOR
+DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject, importEntries, ImportEntriesSlot)
 
 /* static */ bool
 ModuleObject::isInstance(HandleValue value)
 {
     return value.isObject() && value.toObject().is<ModuleObject>();
 }
 
 /* static */ ModuleObject*
@@ -88,19 +168,21 @@ ModuleObject::create(ExclusiveContext* c
 
 void
 ModuleObject::init(HandleScript script)
 {
     initReservedSlot(ScriptSlot, PrivateValue(script));
 }
 
 void
-ModuleObject::initImportExportData(HandleArrayObject requestedModules)
+ModuleObject::initImportExportData(HandleArrayObject requestedModules,
+                                   HandleArrayObject importEntries)
 {
     initReservedSlot(RequestedModulesSlot, ObjectValue(*requestedModules));
+    initReservedSlot(ImportEntriesSlot, ObjectValue(*importEntries));
 }
 
 JSScript*
 ModuleObject::script() const
 {
     return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
 }
 
@@ -109,46 +191,52 @@ ModuleObject::trace(JSTracer* trc, JSObj
 {
     ModuleObject& module = obj->as<ModuleObject>();
     JSScript* script = module.script();
     TraceManuallyBarrieredEdge(trc, &script, "Module script");
     module.setReservedSlot(ScriptSlot, PrivateValue(script));
 }
 
 DEFINE_GETTER_FUNCTIONS(ModuleObject, requestedModules, RequestedModulesSlot)
-
-#undef DEFINE_GETTER_FUNCTIONS
+DEFINE_GETTER_FUNCTIONS(ModuleObject, importEntries, ImportEntriesSlot)
 
 JSObject*
 js::InitModuleClass(JSContext* cx, HandleObject obj)
 {
     static const JSPropertySpec protoAccessors[] = {
         JS_PSG("requestedModules", ModuleObject_requestedModulesGetter, 0),
+        JS_PSG("importEntries", ModuleObject_importEntriesGetter, 0),
         JS_PS_END
     };
 
     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
 
     RootedObject proto(cx, global->createBlankPrototype<PlainObject>(cx));
     if (!proto)
         return nullptr;
 
     if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
         return nullptr;
 
     global->setPrototype(JSProto_Module, ObjectValue(*proto));
     return proto;
 }
 
+#undef DEFINE_GETTER_FUNCTIONS
+#undef DEFINE_STRING_ACCESSOR_METHOD
+#undef DEFINE_ARRAY_SLOT_ACCESSOR
+
 ///////////////////////////////////////////////////////////////////////////
 // ModuleBuilder
 
 ModuleBuilder::ModuleBuilder(JSContext* cx)
   : cx_(cx),
-    requestedModules_(cx, AtomVector(cx))
+    requestedModules_(cx, AtomVector(cx)),
+    importedBoundNames_(cx, AtomVector(cx)),
+    importEntries_(cx, ImportEntryVector(cx))
 {}
 
 bool
 ModuleBuilder::buildAndInit(frontend::ParseNode* moduleNode, HandleModuleObject module)
 {
     MOZ_ASSERT(moduleNode->isKind(PNK_MODULE));
 
     ParseNode* stmtsNode = moduleNode->pn_expr;
@@ -177,31 +265,59 @@ ModuleBuilder::buildAndInit(frontend::Pa
             break;
         }
     }
 
     RootedArrayObject requestedModules(cx_, createArray<JSAtom*>(requestedModules_));
     if (!requestedModules)
         return false;
 
-    module->initImportExportData(requestedModules);
+    RootedArrayObject importEntries(cx_, createArray<ImportEntryObject*>(importEntries_));
+    if (!importEntries)
+        return false;
+
+    module->initImportExportData(requestedModules,
+                                 importEntries);
+
     return true;
 }
 
 bool
 ModuleBuilder::processImport(frontend::ParseNode* pn)
 {
     MOZ_ASSERT(pn->isArity(PN_BINARY));
     MOZ_ASSERT(pn->pn_left->isKind(PNK_IMPORT_SPEC_LIST));
     MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
 
     RootedAtom module(cx_, pn->pn_right->pn_atom);
     if (!maybeAppendRequestedModule(module))
         return false;
 
+    for (ParseNode* spec = pn->pn_left->pn_head; spec; spec = spec->pn_next) {
+        MOZ_ASSERT(spec->isKind(PNK_IMPORT_SPEC));
+        MOZ_ASSERT(spec->pn_left->isArity(PN_NAME));
+        MOZ_ASSERT(spec->pn_right->isArity(PN_NAME));
+
+        RootedAtom importName(cx_, spec->pn_left->pn_atom);
+        RootedAtom localName(cx_, spec->pn_right->pn_atom);
+
+        if (!importedBoundNames_.append(localName))
+            return false;
+
+        RootedImportEntry importEntry(cx_);
+        importEntry = ImportEntryObject::create(cx_, module, importName, localName);
+        if (!importEntry)
+            return false;
+
+        if (!importEntries_.append(importEntry)) {
+            ReportOutOfMemory(cx_);
+            return false;
+        }
+    }
+
     return true;
 }
 
 bool
 ModuleBuilder::processExportFrom(frontend::ParseNode* pn)
 {
     MOZ_ASSERT(pn->isArity(PN_BINARY));
     MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
@@ -224,16 +340,22 @@ ModuleBuilder::maybeAppendRequestedModul
 }
 
 static Value
 MakeElementValue(JSString *string)
 {
     return StringValue(string);
 }
 
+static Value
+MakeElementValue(JSObject *object)
+{
+    return ObjectValue(*object);
+}
+
 template <typename T>
 ArrayObject* ModuleBuilder::createArray(const TraceableVector<T>& vector)
 {
     uint32_t length = vector.length();
     RootedArrayObject array(cx_, NewDenseFullyAllocatedArray(cx_, length));
     if (!array)
         return nullptr;
 
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -14,36 +14,62 @@
 #include "vm/NativeObject.h"
 
 namespace js {
 
 namespace frontend {
 class ParseNode;
 } /* namespace frontend */
 
+class ImportEntryObject : public NativeObject
+{
+  public:
+    enum
+    {
+        ModuleRequestSlot = 0,
+        ImportNameSlot,
+        LocalNameSlot,
+        SlotCount
+    };
+
+    static const Class class_;
+    static JSObject* initClass(JSContext* cx, HandleObject obj);
+    static bool isInstance(HandleValue value);
+    static ImportEntryObject* create(JSContext* cx,
+                                     HandleAtom moduleRequest,
+                                     HandleAtom importName,
+                                     HandleAtom localName);
+    JSAtom* moduleRequest();
+    JSAtom* importName();
+    JSAtom* localName();
+};
+
 class ModuleObject : public NativeObject
 {
   public:
     enum
     {
         ScriptSlot = 0,
         RequestedModulesSlot,
+        ImportEntriesSlot,
         SlotCount
     };
 
     static const Class class_;
 
     static bool isInstance(HandleValue value);
 
     static ModuleObject* create(ExclusiveContext* cx);
     void init(HandleScript script);
-    void initImportExportData(HandleArrayObject requestedModules);
+    void initImportExportData(HandleArrayObject requestedModules,
+                              HandleArrayObject importEntries);
 
     JSScript* script() const;
     ArrayObject& requestedModules() const;
+    ArrayObject& importEntries() const;
 
   private:
     static void trace(JSTracer* trc, JSObject* obj);
 };
 
 typedef Rooted<ModuleObject*> RootedModuleObject;
 typedef Handle<ModuleObject*> HandleModuleObject;
 
@@ -54,26 +80,31 @@ class MOZ_STACK_CLASS ModuleBuilder
   public:
     explicit ModuleBuilder(JSContext* cx);
 
     bool buildAndInit(frontend::ParseNode* pn, HandleModuleObject module);
 
   private:
     using AtomVector = TraceableVector<JSAtom*>;
     using RootedAtomVector = JS::Rooted<AtomVector>;
+    using ImportEntryVector = TraceableVector<ImportEntryObject*>;
+    using RootedImportEntryVector = JS::Rooted<ImportEntryVector>;
 
     JSContext* cx_;
     RootedAtomVector requestedModules_;
+    RootedAtomVector importedBoundNames_;
+    RootedImportEntryVector importEntries_;
 
     bool processImport(frontend::ParseNode* pn);
     bool processExportFrom(frontend::ParseNode* pn);
 
     bool maybeAppendRequestedModule(HandleAtom module);
 
     template <typename T>
     ArrayObject* createArray(const TraceableVector<T>& vector);
 };
 
 JSObject* InitModuleClass(JSContext* cx, HandleObject obj);
+JSObject* InitImportEntryClass(JSContext* cx, HandleObject obj);
 
 } // namespace js
 
 #endif /* builtin_ModuleObject_h */
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/ReentrancyGuard.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jsgc.h"
 #include "jsprf.h"
 
+#include "builtin/ModuleObject.h"
 #include "gc/GCInternals.h"
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/ScopeObject.h"
 #include "vm/Shape.h"
 #include "vm/Symbol.h"
@@ -364,16 +365,17 @@ AssertRootMarkingPhase(JSTracer* trc)
     D(ModuleObject*)      \
     D(NestedScopeObject*) \
     D(PlainObject*) \
     D(SavedFrame*) \
     D(ScopeObject*) \
     D(ScriptSourceObject*) \
     D(SharedArrayBufferObject*) \
     D(SharedTypedArrayObject*) \
+    D(ImportEntryObject*)      \
     D(JSScript*) \
     D(LazyScript*) \
     D(Shape*) \
     D(JSAtom*) \
     D(JSString*) \
     D(JSFlatString*) \
     D(JSLinearString*) \
     D(PropertyName*) \
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/import-entries.js
@@ -0,0 +1,33 @@
+// Test importEntries property
+
+function testImportEntries(source, expected) {
+    var module = parseModule(source);
+    var actual = module.importEntries;
+    assertEq(actual.length, expected.length);
+    for (var i = 0; i < actual.length; i++) {
+        for (var property in expected[i]) {
+            assertEq(actual[i][property], expected[i][property]);
+        }
+    }
+}
+
+testImportEntries('', []);
+
+testImportEntries('import v from "mod";',
+                  [{moduleRequest: 'mod', importName: 'default', localName: 'v'}]);
+
+testImportEntries('import * as ns from "mod";',
+                  [{moduleRequest: 'mod', importName: '*', localName: 'ns'}]);
+
+testImportEntries('import {x} from "mod";',
+                  [{moduleRequest: 'mod', importName: 'x', localName: 'x'}]);
+
+testImportEntries('import {x as v} from "mod";',
+                  [{moduleRequest: 'mod', importName: 'x', localName: 'v'}]);
+
+testImportEntries('import "mod";',
+                  []);
+
+testImportEntries('import {x} from "a"; import {y} from "b";',
+                  [{moduleRequest: 'a', importName: 'x', localName: 'x'},
+                   {moduleRequest: 'b', importName: 'y', localName: 'y'}]);
--- a/js/src/jsprototypes.h
+++ b/js/src/jsprototypes.h
@@ -109,12 +109,13 @@ IF_SAB(real,imaginary)(SharedUint32Array
 IF_SAB(real,imaginary)(SharedFloat32Array,      49,     InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Float32)) \
 IF_SAB(real,imaginary)(SharedFloat64Array,      50,     InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Float64)) \
 IF_SAB(real,imaginary)(SharedUint8ClampedArray, 51,     InitViaClassSpec,       SHARED_TYPED_ARRAY_CLASP(Uint8Clamped)) \
     real(TypedArray,            52,      InitViaClassSpec,      &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
 IF_SAB(real,imaginary)(Atomics,                 53,     InitAtomicsClass, OCLASP(Atomics)) \
     real(SavedFrame,            54,      InitViaClassSpec,      &js::SavedFrame::class_) \
     real(Reflect,               55,      InitReflect,           nullptr) \
     real(Module,                56,      InitModuleClass,       OCLASP(Module)) \
+    real(ImportEntry,           57,      InitImportEntryClass,  OCLASP(ImportEntry)) \
 
 #define JS_FOR_EACH_PROTOTYPE(macro) JS_FOR_PROTOTYPES(macro,macro)
 
 #endif /* jsprototypes_h */