[ES6] Implement ModuleNamespaceObject
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Sep 2015 07:22:54 +0000 (07:22 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Sep 2015 07:22:54 +0000 (07:22 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148705

Reviewed by Geoffrey Garen.

Implement Module namespace object.
That is used when importing the module with the form `import * as namespace from "mod"`.
The module namespace object is non-extensible object that has the bindings to the original module
as the property.

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::moduleNamespaceObjectStructure):
* runtime/JSModuleNamespaceObject.cpp: Added.
(JSC::JSModuleNamespaceObject::JSModuleNamespaceObject):
(JSC::JSModuleNamespaceObject::finishCreation):
(JSC::JSModuleNamespaceObject::destroy):
(JSC::JSModuleNamespaceObject::visitChildren):
(JSC::callbackGetter):
(JSC::JSModuleNamespaceObject::getOwnPropertySlot):
(JSC::JSModuleNamespaceObject::put):
(JSC::JSModuleNamespaceObject::putByIndex):
(JSC::JSModuleNamespaceObject::deleteProperty):
(JSC::JSModuleNamespaceObject::getOwnPropertyNames):
(JSC::JSModuleNamespaceObject::defineOwnProperty):
(JSC::moduleNamespaceObjectSymbolIterator):
* runtime/JSModuleNamespaceObject.h: Added.
(JSC::JSModuleNamespaceObject::create):
(JSC::JSModuleNamespaceObject::createStructure):
(JSC::JSModuleNamespaceObject::moduleRecord):
* runtime/JSModuleRecord.cpp:
(JSC::JSModuleRecord::visitChildren):
(JSC::getExportedNames):
(JSC::JSModuleRecord::getModuleNamespace):
(JSC::JSModuleRecord::instantiateDeclarations):
* runtime/JSModuleRecord.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@189429 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSModuleRecord.cpp
Source/JavaScriptCore/runtime/JSModuleRecord.h

index 2db3e29..bf98766 100644 (file)
@@ -541,6 +541,7 @@ set(JavaScriptCore_RUNTIME_SOURCES
     runtime/JSMap.cpp
     runtime/JSMapIterator.cpp
     runtime/JSModuleEnvironment.cpp
+    runtime/JSModuleNamespaceObject.cpp
     runtime/JSModuleRecord.cpp
     runtime/JSNativeStdFunction.cpp
     runtime/JSNotAnObject.cpp
index c3ca776..9851abf 100644 (file)
@@ -1,3 +1,48 @@
+2015-09-05  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Implement ModuleNamespaceObject
+        https://bugs.webkit.org/show_bug.cgi?id=148705
+
+        Reviewed by Geoffrey Garen.
+
+        Implement Module namespace object.
+        That is used when importing the module with the form `import * as namespace from "mod"`.
+        The module namespace object is non-extensible object that has the bindings to the original module
+        as the property.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::moduleNamespaceObjectStructure):
+        * runtime/JSModuleNamespaceObject.cpp: Added.
+        (JSC::JSModuleNamespaceObject::JSModuleNamespaceObject):
+        (JSC::JSModuleNamespaceObject::finishCreation):
+        (JSC::JSModuleNamespaceObject::destroy):
+        (JSC::JSModuleNamespaceObject::visitChildren):
+        (JSC::callbackGetter):
+        (JSC::JSModuleNamespaceObject::getOwnPropertySlot):
+        (JSC::JSModuleNamespaceObject::put):
+        (JSC::JSModuleNamespaceObject::putByIndex):
+        (JSC::JSModuleNamespaceObject::deleteProperty):
+        (JSC::JSModuleNamespaceObject::getOwnPropertyNames):
+        (JSC::JSModuleNamespaceObject::defineOwnProperty):
+        (JSC::moduleNamespaceObjectSymbolIterator):
+        * runtime/JSModuleNamespaceObject.h: Added.
+        (JSC::JSModuleNamespaceObject::create):
+        (JSC::JSModuleNamespaceObject::createStructure):
+        (JSC::JSModuleNamespaceObject::moduleRecord):
+        * runtime/JSModuleRecord.cpp:
+        (JSC::JSModuleRecord::visitChildren):
+        (JSC::getExportedNames):
+        (JSC::JSModuleRecord::getModuleNamespace):
+        (JSC::JSModuleRecord::instantiateDeclarations):
+        * runtime/JSModuleRecord.h:
+
 2015-09-04  Mark Lam  <mark.lam@apple.com>
 
         Rollout r189411, r189413: Broke JSC tests.
index 511f109..b43640d 100644 (file)
     <ClCompile Include="..\runtime\JSMap.cpp" />
     <ClCompile Include="..\runtime\JSMapIterator.cpp" />
     <ClCompile Include="..\runtime\JSModuleEnvironment.cpp" />
+    <ClCompile Include="..\runtime\JSModuleNamespaceObject.cpp" />
     <ClCompile Include="..\runtime\JSModuleRecord.cpp" />
     <ClCompile Include="..\runtime\JSNativeStdFunction.cpp" />
     <ClCompile Include="..\runtime\JSNotAnObject.cpp" />
     <ClInclude Include="..\runtime\JSMap.h" />
     <ClInclude Include="..\runtime\JSMapIterator.h" />
     <ClInclude Include="..\runtime\JSModuleEnvironment.h" />
+    <ClInclude Include="..\runtime\JSModuleNamespaceObject.h" />
     <ClInclude Include="..\runtime\JSModuleRecord.h" />
     <ClInclude Include="..\runtime\JSNativeStdFunction.h" />
     <ClInclude Include="..\runtime\JSNotAnObject.h" />
index 258fb76..7df9afe 100644 (file)
     <ClCompile Include="..\runtime\JSModuleEnvironment.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
+    <ClCompile Include="..\runtime\JSModuleNamespaceObject.cpp">
+      <Filter>runtime</Filter>
+    </ClCompile>
     <ClCompile Include="..\runtime\JSModuleRecord.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
     <ClInclude Include="..\runtime\JSModuleEnvironment.h">
       <Filter>runtime</Filter>
     </ClInclude>
+    <ClInclude Include="..\runtime\JSModuleNamespaceObject.h">
+      <Filter>runtime</Filter>
+    </ClInclude>
     <ClInclude Include="..\runtime\JSModuleRecord.h">
       <Filter>runtime</Filter>
     </ClInclude>
index 04a123f..4651ff2 100644 (file)
                E124A8F80E555775003091F1 /* OpaqueJSString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E124A8F60E555775003091F1 /* OpaqueJSString.cpp */; };
                E18E3A590DF9278C00D90B34 /* VM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E18E3A570DF9278C00D90B34 /* VM.cpp */; };
                E30677981B8BC6F5003F87F0 /* ModuleLoaderObject.js in Resources */ = {isa = PBXBuildFile; fileRef = E30677971B8BC6F5003F87F0 /* ModuleLoaderObject.js */; };
+               E318CBC01B8AEF5100A2929D /* JSModuleNamespaceObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E318CBBE1B8AEF5100A2929D /* JSModuleNamespaceObject.cpp */; };
+               E318CBC11B8AEF5100A2929D /* JSModuleNamespaceObject.h in Headers */ = {isa = PBXBuildFile; fileRef = E318CBBF1B8AEF5100A2929D /* JSModuleNamespaceObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E33637A51B63220200EE0840 /* ReflectObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E33637A31B63220200EE0840 /* ReflectObject.cpp */; };
                E33637A61B63220200EE0840 /* ReflectObject.h in Headers */ = {isa = PBXBuildFile; fileRef = E33637A41B63220200EE0840 /* ReflectObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E33B3E261B7ABD750048DB2E /* InspectorInstrumentationObject.lut.h in Headers */ = {isa = PBXBuildFile; fileRef = E33B3E251B7ABD750048DB2E /* InspectorInstrumentationObject.lut.h */; };
                E18E3A560DF9278C00D90B34 /* VM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = VM.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
                E18E3A570DF9278C00D90B34 /* VM.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = VM.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
                E30677971B8BC6F5003F87F0 /* ModuleLoaderObject.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = ModuleLoaderObject.js; sourceTree = "<group>"; };
+               E318CBBE1B8AEF5100A2929D /* JSModuleNamespaceObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSModuleNamespaceObject.cpp; sourceTree = "<group>"; };
+               E318CBBF1B8AEF5100A2929D /* JSModuleNamespaceObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSModuleNamespaceObject.h; sourceTree = "<group>"; };
                E33637A31B63220200EE0840 /* ReflectObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ReflectObject.cpp; sourceTree = "<group>"; };
                E33637A41B63220200EE0840 /* ReflectObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReflectObject.h; sourceTree = "<group>"; };
                E33B3E251B7ABD750048DB2E /* InspectorInstrumentationObject.lut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorInstrumentationObject.lut.h; sourceTree = "<group>"; };
                                A74DEF90182D991400522C22 /* JSMapIterator.h */,
                                E3D239C61B829C1C00BBEF67 /* JSModuleEnvironment.cpp */,
                                E3D239C71B829C1C00BBEF67 /* JSModuleEnvironment.h */,
+                               E318CBBE1B8AEF5100A2929D /* JSModuleNamespaceObject.cpp */,
+                               E318CBBF1B8AEF5100A2929D /* JSModuleNamespaceObject.h */,
                                E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */,
                                E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */,
                                E33E8D1A1B9013C300346B52 /* JSNativeStdFunction.cpp */,
                                7B0247571B8682E400542440 /* WASMFunctionParser.h in Headers */,
                                0FEA0A10170513DB00BB722C /* FTLLowerDFGToLLVM.h in Headers */,
                                A7D89D0217A0B90400773AD8 /* FTLLoweredNodeValue.h in Headers */,
+                               E318CBC11B8AEF5100A2929D /* JSModuleNamespaceObject.h in Headers */,
                                0FD8A31C17D51F2200CA2C40 /* FTLOSREntry.h in Headers */,
                                0F235BDD17178E1C00690C7F /* FTLOSRExit.h in Headers */,
                                0F235BDE17178E1C00690C7F /* FTLOSRExitCompilationInfo.h in Headers */,
                                0FD120331A8C85BD000F5280 /* FTLJSCallVarargs.cpp in Sources */,
                                0F9D339617FFC4E60073C2BC /* DFGFlushedAt.cpp in Sources */,
                                E33F50841B8437A000413856 /* JSInternalPromiseDeferred.cpp in Sources */,
+                               E318CBC01B8AEF5100A2929D /* JSModuleNamespaceObject.cpp in Sources */,
                                0F9D36941AE9CC33000D4DFB /* DFGCleanUpPhase.cpp in Sources */,
                                A7D89CF717A0B8CC00773AD8 /* DFGFlushFormat.cpp in Sources */,
                                0F3BD1B71B896A0700598AA6 /* DFGInsertionSet.cpp in Sources */,
index 0e77cd2..03e89b9 100644 (file)
@@ -85,6 +85,7 @@
 #include "JSMap.h"
 #include "JSMapIterator.h"
 #include "JSModuleEnvironment.h"
+#include "JSModuleNamespaceObject.h"
 #include "JSModuleRecord.h"
 #include "JSNativeStdFunction.h"
 #include "JSONObject.h"
@@ -351,6 +352,7 @@ void JSGlobalObject::init(VM& vm)
     m_regExpMatchesArrayStructure.set(vm, this, createRegExpMatchesArrayStructure(vm, *this));
 
     m_moduleRecordStructure.set(vm, this, JSModuleRecord::createStructure(vm, this, m_objectPrototype.get()));
+    m_moduleNamespaceObjectStructure.set(vm, this, JSModuleNamespaceObject::createStructure(vm, this, jsNull()));
     
 #if ENABLE(WEBASSEMBLY)
     m_wasmModuleStructure.set(vm, this, JSWASMModule::createStructure(vm, this));
@@ -824,6 +826,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(&thisObject->m_regExpStructure);
     visitor.append(&thisObject->m_regExpMatchesArrayStructure);
     visitor.append(&thisObject->m_moduleRecordStructure);
+    visitor.append(&thisObject->m_moduleNamespaceObjectStructure);
     visitor.append(&thisObject->m_consoleStructure);
     visitor.append(&thisObject->m_dollarVMStructure);
     visitor.append(&thisObject->m_internalFunctionStructure);
index ec66c1a..dcb3a08 100644 (file)
@@ -268,6 +268,7 @@ protected:
     WriteBarrier<Structure> m_iteratorResultStructure;
     WriteBarrier<Structure> m_regExpMatchesArrayStructure;
     WriteBarrier<Structure> m_moduleRecordStructure;
+    WriteBarrier<Structure> m_moduleNamespaceObjectStructure;
 #if ENABLE(WEBASSEMBLY)
     WriteBarrier<Structure> m_wasmModuleStructure;
 #endif
@@ -506,6 +507,7 @@ public:
     static ptrdiff_t iteratorResultStructureOffset() { return OBJECT_OFFSETOF(JSGlobalObject, m_iteratorResultStructure); }
     Structure* regExpMatchesArrayStructure() const { return m_regExpMatchesArrayStructure.get(); }
     Structure* moduleRecordStructure() const { return m_moduleRecordStructure.get(); }
+    Structure* moduleNamespaceObjectStructure() const { return m_moduleNamespaceObjectStructure.get(); }
 #if ENABLE(WEBASSEMBLY)
     Structure* wasmModuleStructure() const { return m_wasmModuleStructure.get(); }
 #endif
diff --git a/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp b/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp
new file mode 100644 (file)
index 0000000..cb836a1
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "config.h"
+#include "JSModuleNamespaceObject.h"
+
+#include "Error.h"
+#include "IdentifierInlines.h"
+#include "JSCJSValueInlines.h"
+#include "JSCellInlines.h"
+#include "JSModuleEnvironment.h"
+#include "JSModuleRecord.h"
+#include "JSPropertyNameIterator.h"
+#include "SlotVisitorInlines.h"
+#include "StructureInlines.h"
+
+namespace JSC {
+
+static EncodedJSValue JSC_HOST_CALL moduleNamespaceObjectSymbolIterator(ExecState*);
+
+const ClassInfo JSModuleNamespaceObject::s_info = { "ModuleNamespaceObject", &Base::s_info, nullptr, CREATE_METHOD_TABLE(JSModuleNamespaceObject) };
+
+
+JSModuleNamespaceObject::JSModuleNamespaceObject(VM& vm, Structure* structure)
+    : Base(vm, structure)
+    , m_exports()
+{
+}
+
+void JSModuleNamespaceObject::finishCreation(VM& vm, JSGlobalObject* globalObject, JSModuleRecord* moduleRecord, const IdentifierSet& exports)
+{
+    Base::finishCreation(vm);
+    ASSERT(inherits(info()));
+
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects
+    // Quoted from the spec:
+    //     A List containing the String values of the exported names exposed as own properties of this object.
+    //     The list is ordered as if an Array of those String values had been sorted using Array.prototype.sort using SortCompare as comparefn.
+    //
+    // Sort the exported names by the code point order.
+    Vector<UniquedStringImpl*> temporaryVector(exports.size(), nullptr);
+    std::transform(exports.begin(), exports.end(), temporaryVector.begin(), [](const RefPtr<WTF::UniquedStringImpl>& ref) {
+        return ref.get();
+    });
+    std::sort(temporaryVector.begin(), temporaryVector.end(), [] (UniquedStringImpl* lhs, UniquedStringImpl* rhs) {
+        return codePointCompare(lhs, rhs) < 0;
+    });
+    for (auto* identifier : temporaryVector)
+        m_exports.add(identifier);
+
+    m_moduleRecord.set(vm, this, moduleRecord);
+    JSC_NATIVE_FUNCTION(vm.propertyNames->iteratorSymbol, moduleNamespaceObjectSymbolIterator, DontEnum, 0);
+
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getprototypeof
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-setprototypeof-v
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-isextensible
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-preventextensions
+    preventExtensions(vm);
+}
+
+void JSModuleNamespaceObject::destroy(JSCell* cell)
+{
+    JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
+    thisObject->JSModuleNamespaceObject::~JSModuleNamespaceObject();
+}
+
+void JSModuleNamespaceObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(&thisObject->m_moduleRecord);
+}
+
+static EncodedJSValue callbackGetter(ExecState* exec, JSObject*, EncodedJSValue thisValue, PropertyName propertyName)
+{
+    JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(JSValue::decode(thisValue));
+    JSModuleRecord* moduleRecord = thisObject->moduleRecord();
+
+    JSModuleRecord::Resolution resolution = moduleRecord->resolveExport(exec, Identifier::fromUid(exec, propertyName.uid()));
+    ASSERT(resolution.type != JSModuleRecord::Resolution::Type::NotFound && resolution.type != JSModuleRecord::Resolution::Type::Ambiguous);
+
+    JSModuleRecord* targetModule = resolution.moduleRecord;
+    JSModuleEnvironment* targetEnvironment = targetModule->moduleEnvironment();
+
+    PropertySlot trampolineSlot(targetEnvironment);
+    if (!targetEnvironment->methodTable(exec->vm())->getOwnPropertySlot(targetEnvironment, exec, resolution.localName, trampolineSlot))
+        return JSValue::encode(jsUndefined());
+
+    JSValue value = trampolineSlot.getValue(exec, propertyName);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+
+    // If the value is filled with TDZ value, throw a reference error.
+    if (!value)
+        return throwVMError(exec, createTDZError(exec));
+    return JSValue::encode(value);
+}
+
+bool JSModuleNamespaceObject::getOwnPropertySlot(JSObject* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getownproperty-p
+
+    JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
+
+    // step 1.
+    // If the property name is a symbol, we don't look into the imported bindings.
+    // It may return the descriptor with writable: true, but namespace objects does not allow it in [[Set]] / [[DefineOwnProperty]] side.
+    if (propertyName.isSymbol())
+        return JSObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
+
+    if (!thisObject->m_exports.contains(propertyName.uid()))
+        return false;
+
+    // https://esdiscuss.org/topic/march-24-meeting-notes
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-getownproperty-p
+    // section 9.4.6.5, step 6.
+    // This property will be seen as writable: true, enumerable:true, configurable: false.
+    // But this does not mean that this property is writable by users.
+    //
+    // In JSC, getOwnPropertySlot is not designed to throw any errors. But looking up the value from the module
+    // environment may throw error if the loaded variable is the TDZ value. To workaround, we set the custom
+    // getter function. When it is called, it looks up the variable and throws an error if the variable is not
+    // initialized.
+    slot.setCustom(thisObject, DontDelete, callbackGetter);
+    return true;
+}
+
+void JSModuleNamespaceObject::put(JSCell*, ExecState* exec, PropertyName, JSValue, PutPropertySlot& slot)
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-set-p-v-receiver
+    if (slot.isStrictMode())
+        throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
+}
+
+void JSModuleNamespaceObject::putByIndex(JSCell*, ExecState* exec, unsigned, JSValue, bool shouldThrow)
+{
+    if (shouldThrow)
+        throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
+}
+
+bool JSModuleNamespaceObject::deleteProperty(JSCell* cell, ExecState*, PropertyName propertyName)
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-delete-p
+    JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
+    return !thisObject->m_exports.contains(propertyName.uid());
+}
+
+void JSModuleNamespaceObject::getOwnPropertyNames(JSObject* cell, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-ownpropertykeys
+    JSModuleNamespaceObject* thisObject = jsCast<JSModuleNamespaceObject*>(cell);
+    for (const auto& name : thisObject->m_exports)
+        propertyNames.add(name.get());
+    return JSObject::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
+}
+
+bool JSModuleNamespaceObject::defineOwnProperty(JSObject*, ExecState* exec, PropertyName, const PropertyDescriptor&, bool shouldThrow)
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-namespace-exotic-objects-defineownproperty-p-desc
+    if (shouldThrow)
+        throwTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible."));
+    return false;
+}
+
+EncodedJSValue JSC_HOST_CALL moduleNamespaceObjectSymbolIterator(ExecState* exec)
+{
+    JSValue thisValue = exec->thisValue();
+    if (!thisValue.isObject())
+        return JSValue::encode(throwTypeError(exec, ASCIILiteral("|this| should be an object")));
+    return JSValue::encode(JSPropertyNameIterator::create(exec, exec->lexicalGlobalObject()->propertyNameIteratorStructure(), asObject(thisValue)));
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h b/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.h
new file mode 100644 (file)
index 0000000..4d04f56
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 JSModuleNamespaceObject_h
+#define JSModuleNamespaceObject_h
+
+#include "JSDestructibleObject.h"
+#include <wtf/ListHashSet.h>
+
+namespace JSC {
+
+class JSModuleRecord;
+
+class JSModuleNamespaceObject : public JSDestructibleObject {
+public:
+    typedef JSDestructibleObject Base;
+    static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames;
+
+    static JSModuleNamespaceObject* create(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, JSModuleRecord* moduleRecord, const IdentifierSet& exports)
+    {
+        JSModuleNamespaceObject* object = new (NotNull, allocateCell<JSModuleNamespaceObject>(exec->vm().heap)) JSModuleNamespaceObject(exec->vm(), structure);
+        object->finishCreation(exec->vm(), globalObject, moduleRecord, exports);
+        return object;
+    }
+
+    JS_EXPORT_PRIVATE static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
+    JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
+    JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
+    JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
+    JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+    JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
+
+    DECLARE_EXPORT_INFO;
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+    {
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+    }
+
+    JSModuleRecord* moduleRecord() { return m_moduleRecord.get(); }
+
+protected:
+    JS_EXPORT_PRIVATE void finishCreation(VM&, JSGlobalObject*, JSModuleRecord*, const IdentifierSet& exports);
+    JS_EXPORT_PRIVATE JSModuleNamespaceObject(VM&, Structure*);
+
+private:
+    static void destroy(JSCell*);
+    static void visitChildren(JSCell*, SlotVisitor&);
+
+    typedef WTF::ListHashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> OrderedIdentifierSet;
+
+    OrderedIdentifierSet m_exports;
+    WriteBarrier<JSModuleRecord> m_moduleRecord;
+};
+
+} // namespace JSC
+
+#endif // JSModuleNamespaceObject_h
index 2cefbe9..65da82f 100644 (file)
@@ -33,6 +33,7 @@
 #include "JSCellInlines.h"
 #include "JSMap.h"
 #include "JSModuleEnvironment.h"
+#include "JSModuleNamespaceObject.h"
 #include "SlotVisitorInlines.h"
 #include "StructureInlines.h"
 
@@ -62,6 +63,7 @@ void JSModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
     JSModuleRecord* thisObject = jsCast<JSModuleRecord*>(cell);
     Base::visitChildren(thisObject, visitor);
     visitor.append(&thisObject->m_moduleEnvironment);
+    visitor.append(&thisObject->m_moduleNamespaceObject);
     visitor.append(&thisObject->m_moduleProgramExecutable);
     visitor.append(&thisObject->m_dependenciesMap);
 }
@@ -421,6 +423,75 @@ auto JSModuleRecord::resolveExport(ExecState* exec, const Identifier& exportName
     return resolveExportLoop(exec, ResolveQuery(this, exportName.impl()));
 }
 
+static void getExportedNames(ExecState* exec, JSModuleRecord* root, IdentifierSet& exportedNames)
+{
+    HashSet<JSModuleRecord*> exportStarSet;
+    Vector<JSModuleRecord*, 8> pendingModules;
+
+    pendingModules.append(root);
+
+    while (!pendingModules.isEmpty()) {
+        JSModuleRecord* moduleRecord = pendingModules.takeLast();
+        if (exportStarSet.contains(moduleRecord))
+            continue;
+        exportStarSet.add(moduleRecord);
+
+        for (const auto& pair : moduleRecord->exportEntries()) {
+            const JSModuleRecord::ExportEntry& exportEntry = pair.value;
+            switch (exportEntry.type) {
+            case JSModuleRecord::ExportEntry::Type::Local:
+            case JSModuleRecord::ExportEntry::Type::Indirect:
+                if (moduleRecord == root || exec->propertyNames().defaultKeyword != exportEntry.exportName)
+                    exportedNames.add(exportEntry.exportName.impl());
+                break;
+
+            case JSModuleRecord::ExportEntry::Type::Namespace:
+                break;
+            }
+        }
+
+        for (const auto& starModuleName : moduleRecord->starExportEntries()) {
+            JSModuleRecord* requestedModuleRecord = moduleRecord->hostResolveImportedModule(exec, Identifier::fromUid(exec, starModuleName.get()));
+            pendingModules.append(requestedModuleRecord);
+        }
+    }
+}
+
+JSModuleNamespaceObject* JSModuleRecord::getModuleNamespace(ExecState* exec)
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-getmodulenamespace
+    if (m_moduleNamespaceObject)
+        return m_moduleNamespaceObject.get();
+
+    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
+    IdentifierSet exportedNames;
+    getExportedNames(exec, this, exportedNames);
+
+    IdentifierSet unambiguousNames;
+    for (auto& name : exportedNames) {
+        const JSModuleRecord::Resolution resolution = resolveExport(exec, Identifier::fromUid(exec, name.get()));
+        switch (resolution.type) {
+        case Resolution::Type::NotFound:
+            throwSyntaxError(exec, makeString("Exported binding name '", String(name.get()), "' is not found."));
+            return nullptr;
+
+        case Resolution::Type::Error:
+            throwSyntaxError(exec, makeString("Exported binding name 'default' cannot be resolved by star export entries."));
+            return nullptr;
+
+        case Resolution::Type::Ambiguous:
+            break;
+
+        case Resolution::Type::Resolved:
+            unambiguousNames.add(name);
+            break;
+        }
+    }
+
+    m_moduleNamespaceObject.set(exec->vm(), this, JSModuleNamespaceObject::create(exec, globalObject, globalObject->moduleNamespaceObjectStructure(), this, unambiguousNames));
+    return m_moduleNamespaceObject.get();
+}
+
 void JSModuleRecord::link(ExecState* exec)
 {
     ModuleProgramExecutable* executable = ModuleProgramExecutable::create(exec, sourceCode());
@@ -477,8 +548,10 @@ void JSModuleRecord::instantiateDeclarations(ExecState* exec, ModuleProgramExecu
         const ImportEntry& importEntry = pair.value;
         JSModuleRecord* importedModule = hostResolveImportedModule(exec, importEntry.moduleRequest);
         if (importEntry.isNamespace(vm)) {
-            // FIXME: ModuleNamespaceObject will be implemented.
-            // https://bugs.webkit.org/show_bug.cgi?id=148705
+            JSModuleNamespaceObject* namespaceObject = importedModule->getModuleNamespace(exec);
+            if (exec->hadException())
+                return;
+            symbolTablePut(moduleEnvironment, exec, importEntry.localName, namespaceObject, /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true);
         } else {
             Resolution resolution = importedModule->resolveExport(exec, importEntry.importName);
             switch (resolution.type) {
@@ -509,7 +582,7 @@ void JSModuleRecord::instantiateDeclarations(ExecState* exec, ModuleProgramExecu
         SymbolTableEntry entry = symbolTable->get(variable.key.get());
         VarOffset offset = entry.varOffset();
         if (!offset.isStack())
-            symbolTablePut(moduleEnvironment, exec, Identifier::fromUid(exec, variable.key.get()), jsUndefined(), false, true);
+            symbolTablePut(moduleEnvironment, exec, Identifier::fromUid(exec, variable.key.get()), jsUndefined(), /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true);
     }
 
     // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation
@@ -529,7 +602,7 @@ void JSModuleRecord::instantiateDeclarations(ExecState* exec, ModuleProgramExecu
                     unlinkedFunctionExecutable->typeProfilingEndOffset());
             }
             JSFunction* function = JSFunction::create(vm, unlinkedFunctionExecutable->link(vm, moduleProgramExecutable->source()), moduleEnvironment);
-            symbolTablePut(moduleEnvironment, exec, unlinkedFunctionExecutable->name(), function, false, true);
+            symbolTablePut(moduleEnvironment, exec, unlinkedFunctionExecutable->name(), function, /* shouldThrowReadOnlyError */ false, /* ignoreReadOnlyErrors */ true);
         }
     }
 
index 08521b4..70a190f 100644 (file)
@@ -156,6 +156,8 @@ private:
 
     void finishCreation(VM&);
 
+    JSModuleNamespaceObject* getModuleNamespace(ExecState*);
+
     static void visitChildren(JSCell*, SlotVisitor&);
     static void destroy(JSCell*);
 
@@ -199,6 +201,7 @@ private:
 
     WriteBarrier<ModuleProgramExecutable> m_moduleProgramExecutable;
     WriteBarrier<JSModuleEnvironment> m_moduleEnvironment;
+    WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
 };
 
 } // namespace JSC