[ES6] Instantiate Module Environment bindings and execute module
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Sep 2015 04:29:04 +0000 (04:29 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Sep 2015 04:29:04 +0000 (04:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148053

Reviewed by Saam Barati.

This patch implements Module Environment binding instantiation.
And since the layout of the module environment is tightly coupled with the variable
look up in LLInt / Baseline / DFG, we implement the execution part at the same time.

For the instantiation, we implement the several operations (like resolveExport)
specified in the spec. The original algorithm contains the recursive call, but it is not
good for C++ code. We flatten the algorithm by using the manual frames to avoid recursions.
By leveraging the information retrieved by the above operations, we instantiate and
initialize the slots of the module environment.

The module namespace object is not implemented yet in this patch. It will be implemented
and instantiated in the module environment in the subsequent patch[1].

To look up the imported module bindings in the JS code, we introduce the "ModuleVar" resolve
type for resolve_scope, get_from_scope and put_to_scope. This "ModuleVar" will be filled
when linking the CodeBlock. This type is used when treating the imported bindings.

1. For resolve_scope, when linking, we resolve the actual module environment where
    looked up variable resides and store it directly to the instruction. And resolve_scope
    simply retrieve the stored pointer from the instruction.

2. For get_from_scope, since "ModuleVar" behavior for get_from_scope is completely same
    to the "ClosureVar", we just store "ClosureVar" for get_from_scope to eliminate
    unnecessary branch in LLInt layer.

3. For put_to_scope, we always emit the function call that immediately raises the error.
    Because all the imported bindings are immutable and module code is always strict code.
    In DFG, we just emit the ForceOSRExit. We don't make put_to_scope with "ModuleVar"
    "CannotCompile" because it disables DFG compiling for the function even if this
    problematic instruction is never executed.

Exported module variables inside the original module environment are just treated as the
usual heap variables. So the types for closure variables are just used. ("ClosureVar" etc.)

[1]: https://bugs.webkit.org/show_bug.cgi?id=148705

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::CodeBlock):
* bytecode/CodeBlock.h:
* bytecode/UnlinkedCodeBlock.cpp:
(JSC::UnlinkedModuleProgramCodeBlock::visitChildren): Deleted.
* bytecode/UnlinkedCodeBlock.h:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* interpreter/Interpreter.cpp:
(JSC::Interpreter::execute):
* interpreter/Interpreter.h:
* jit/JITOperations.cpp:
* jit/JITPropertyAccess.cpp:
(JSC::JIT::emit_op_resolve_scope):
(JSC::JIT::emitSlow_op_resolve_scope):
(JSC::JIT::emit_op_get_from_scope):
(JSC::JIT::emit_op_put_to_scope):
(JSC::JIT::emitSlow_op_put_to_scope):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_resolve_scope):
(JSC::JIT::emitSlow_op_resolve_scope):
(JSC::JIT::emit_op_get_from_scope):
(JSC::JIT::emit_op_put_to_scope):
(JSC::JIT::emitSlow_op_put_to_scope):
* llint/LLIntData.cpp:
(JSC::LLInt::Data::performAssertions):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* parser/ModuleAnalyzer.cpp:
(JSC::ModuleAnalyzer::exportVariable):
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
* runtime/CommonSlowPaths.h:
* runtime/Error.cpp:
(JSC::throwSyntaxError):
* runtime/Error.h:
* runtime/Executable.cpp:
(JSC::ModuleProgramExecutable::create):
(JSC::ModuleProgramExecutable::visitChildren):
(JSC::ModuleProgramExecutable::clearCode):
* runtime/Executable.h:
* runtime/GetPutInfo.h:
(JSC::resolveTypeName):
(JSC::makeType):
(JSC::needsVarInjectionChecks):
(JSC::ResolveOp::ResolveOp):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::moduleEnvironmentStructure):
* runtime/JSLexicalEnvironment.h:
* runtime/JSModuleEnvironment.cpp: Added.
(JSC::JSModuleEnvironment::create):
(JSC::JSModuleEnvironment::finishCreation):
(JSC::JSModuleEnvironment::visitChildren):
(JSC::JSModuleEnvironment::getOwnPropertySlot):
(JSC::JSModuleEnvironment::getOwnNonIndexPropertyNames):
(JSC::JSModuleEnvironment::put):
(JSC::JSModuleEnvironment::deleteProperty):
* runtime/JSModuleEnvironment.h: Copied from Source/JavaScriptCore/runtime/JSLexicalEnvironment.h.
(JSC::JSModuleEnvironment::create):
(JSC::JSModuleEnvironment::createStructure):
(JSC::JSModuleEnvironment::offsetOfModuleRecord):
(JSC::JSModuleEnvironment::allocationSize):
(JSC::JSModuleEnvironment::moduleRecord):
(JSC::JSModuleEnvironment::moduleRecordSlot):
(JSC::JSModuleEnvironment::JSModuleEnvironment):
* runtime/JSModuleRecord.cpp:
(JSC::JSModuleRecord::visitChildren):
(JSC::JSModuleRecord::appendRequestedModule):
(JSC::JSModuleRecord::addStarExportEntry):
(JSC::JSModuleRecord::addImportEntry):
(JSC::JSModuleRecord::addExportEntry):
(JSC::ResolveQuery::ResolveQuery):
(JSC::ResolveQuery::isEmptyValue):
(JSC::ResolveQuery::isDeletedValue):
(JSC::ResolveQueryHash::hash):
(JSC::ResolveQueryHash::equal):
(JSC::resolveExportLoop):
(JSC::JSModuleRecord::link):
(JSC::JSModuleRecord::instantiateDeclarations):
(JSC::JSModuleRecord::execute):
(JSC::JSModuleRecord::dump):
* runtime/JSModuleRecord.h:
(JSC::JSModuleRecord::exportEntries):
(JSC::JSModuleRecord::importEntries):
(JSC::JSModuleRecord::starExportEntries):
(JSC::JSModuleRecord::moduleEnvironment):
(JSC::JSModuleRecord::appendRequestedModule): Deleted.
(JSC::JSModuleRecord::addImportEntry): Deleted.
(JSC::JSModuleRecord::addExportEntry): Deleted.
(JSC::JSModuleRecord::addStarExportEntry): Deleted.
* runtime/JSScope.cpp:
(JSC::abstractAccess):
(JSC::JSScope::collectVariablesUnderTDZ):
(JSC::JSScope::isModuleScope):
* runtime/JSScope.h:
* runtime/ModuleLoaderObject.cpp:

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

39 files changed:
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/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/Interpreter.h
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/jit/JITPropertyAccess.cpp
Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
Source/JavaScriptCore/llint/LLIntData.cpp
Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter.asm
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/parser/ModuleAnalyzer.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Source/JavaScriptCore/runtime/CommonSlowPaths.h
Source/JavaScriptCore/runtime/Error.cpp
Source/JavaScriptCore/runtime/Error.h
Source/JavaScriptCore/runtime/Executable.cpp
Source/JavaScriptCore/runtime/Executable.h
Source/JavaScriptCore/runtime/GetPutInfo.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSLexicalEnvironment.h
Source/JavaScriptCore/runtime/JSModuleEnvironment.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSModuleEnvironment.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/JSModuleRecord.cpp
Source/JavaScriptCore/runtime/JSModuleRecord.h
Source/JavaScriptCore/runtime/JSScope.cpp
Source/JavaScriptCore/runtime/JSScope.h
Source/JavaScriptCore/runtime/ModuleLoaderObject.cpp

index 23919f0..c18a318 100644 (file)
@@ -539,6 +539,7 @@ set(JavaScriptCore_RUNTIME_SOURCES
     runtime/JSLock.cpp
     runtime/JSMap.cpp
     runtime/JSMapIterator.cpp
+    runtime/JSModuleEnvironment.cpp
     runtime/JSModuleRecord.cpp
     runtime/JSNativeStdFunction.cpp
     runtime/JSNotAnObject.cpp
index 82bda39..940bf84 100644 (file)
@@ -1,3 +1,155 @@
+2015-09-03  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Instantiate Module Environment bindings and execute module
+        https://bugs.webkit.org/show_bug.cgi?id=148053
+
+        Reviewed by Saam Barati.
+
+        This patch implements Module Environment binding instantiation.
+        And since the layout of the module environment is tightly coupled with the variable
+        look up in LLInt / Baseline / DFG, we implement the execution part at the same time.
+
+        For the instantiation, we implement the several operations (like resolveExport)
+        specified in the spec. The original algorithm contains the recursive call, but it is not
+        good for C++ code. We flatten the algorithm by using the manual frames to avoid recursions.
+        By leveraging the information retrieved by the above operations, we instantiate and
+        initialize the slots of the module environment.
+
+        The module namespace object is not implemented yet in this patch. It will be implemented
+        and instantiated in the module environment in the subsequent patch[1].
+
+        To look up the imported module bindings in the JS code, we introduce the "ModuleVar" resolve
+        type for resolve_scope, get_from_scope and put_to_scope. This "ModuleVar" will be filled
+        when linking the CodeBlock. This type is used when treating the imported bindings.
+
+        1. For resolve_scope, when linking, we resolve the actual module environment where
+            looked up variable resides and store it directly to the instruction. And resolve_scope
+            simply retrieve the stored pointer from the instruction.
+
+        2. For get_from_scope, since "ModuleVar" behavior for get_from_scope is completely same
+            to the "ClosureVar", we just store "ClosureVar" for get_from_scope to eliminate
+            unnecessary branch in LLInt layer.
+
+        3. For put_to_scope, we always emit the function call that immediately raises the error.
+            Because all the imported bindings are immutable and module code is always strict code.
+            In DFG, we just emit the ForceOSRExit. We don't make put_to_scope with "ModuleVar"
+            "CannotCompile" because it disables DFG compiling for the function even if this
+            problematic instruction is never executed.
+
+        Exported module variables inside the original module environment are just treated as the
+        usual heap variables. So the types for closure variables are just used. ("ClosureVar" etc.)
+
+        [1]: https://bugs.webkit.org/show_bug.cgi?id=148705
+
+        * CMakeLists.txt:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::CodeBlock):
+        * bytecode/CodeBlock.h:
+        * bytecode/UnlinkedCodeBlock.cpp:
+        (JSC::UnlinkedModuleProgramCodeBlock::visitChildren): Deleted.
+        * bytecode/UnlinkedCodeBlock.h:
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * interpreter/Interpreter.cpp:
+        (JSC::Interpreter::execute):
+        * interpreter/Interpreter.h:
+        * jit/JITOperations.cpp:
+        * jit/JITPropertyAccess.cpp:
+        (JSC::JIT::emit_op_resolve_scope):
+        (JSC::JIT::emitSlow_op_resolve_scope):
+        (JSC::JIT::emit_op_get_from_scope):
+        (JSC::JIT::emit_op_put_to_scope):
+        (JSC::JIT::emitSlow_op_put_to_scope):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emit_op_resolve_scope):
+        (JSC::JIT::emitSlow_op_resolve_scope):
+        (JSC::JIT::emit_op_get_from_scope):
+        (JSC::JIT::emit_op_put_to_scope):
+        (JSC::JIT::emitSlow_op_put_to_scope):
+        * llint/LLIntData.cpp:
+        (JSC::LLInt::Data::performAssertions):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LowLevelInterpreter.asm:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * parser/ModuleAnalyzer.cpp:
+        (JSC::ModuleAnalyzer::exportVariable):
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::SLOW_PATH_DECL):
+        * runtime/CommonSlowPaths.h:
+        * runtime/Error.cpp:
+        (JSC::throwSyntaxError):
+        * runtime/Error.h:
+        * runtime/Executable.cpp:
+        (JSC::ModuleProgramExecutable::create):
+        (JSC::ModuleProgramExecutable::visitChildren):
+        (JSC::ModuleProgramExecutable::clearCode):
+        * runtime/Executable.h:
+        * runtime/GetPutInfo.h:
+        (JSC::resolveTypeName):
+        (JSC::makeType):
+        (JSC::needsVarInjectionChecks):
+        (JSC::ResolveOp::ResolveOp):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::moduleEnvironmentStructure):
+        * runtime/JSLexicalEnvironment.h:
+        * runtime/JSModuleEnvironment.cpp: Added.
+        (JSC::JSModuleEnvironment::create):
+        (JSC::JSModuleEnvironment::finishCreation):
+        (JSC::JSModuleEnvironment::visitChildren):
+        (JSC::JSModuleEnvironment::getOwnPropertySlot):
+        (JSC::JSModuleEnvironment::getOwnNonIndexPropertyNames):
+        (JSC::JSModuleEnvironment::put):
+        (JSC::JSModuleEnvironment::deleteProperty):
+        * runtime/JSModuleEnvironment.h: Copied from Source/JavaScriptCore/runtime/JSLexicalEnvironment.h.
+        (JSC::JSModuleEnvironment::create):
+        (JSC::JSModuleEnvironment::createStructure):
+        (JSC::JSModuleEnvironment::offsetOfModuleRecord):
+        (JSC::JSModuleEnvironment::allocationSize):
+        (JSC::JSModuleEnvironment::moduleRecord):
+        (JSC::JSModuleEnvironment::moduleRecordSlot):
+        (JSC::JSModuleEnvironment::JSModuleEnvironment):
+        * runtime/JSModuleRecord.cpp:
+        (JSC::JSModuleRecord::visitChildren):
+        (JSC::JSModuleRecord::appendRequestedModule):
+        (JSC::JSModuleRecord::addStarExportEntry):
+        (JSC::JSModuleRecord::addImportEntry):
+        (JSC::JSModuleRecord::addExportEntry):
+        (JSC::ResolveQuery::ResolveQuery):
+        (JSC::ResolveQuery::isEmptyValue):
+        (JSC::ResolveQuery::isDeletedValue):
+        (JSC::ResolveQueryHash::hash):
+        (JSC::ResolveQueryHash::equal):
+        (JSC::resolveExportLoop):
+        (JSC::JSModuleRecord::link):
+        (JSC::JSModuleRecord::instantiateDeclarations):
+        (JSC::JSModuleRecord::execute):
+        (JSC::JSModuleRecord::dump):
+        * runtime/JSModuleRecord.h:
+        (JSC::JSModuleRecord::exportEntries):
+        (JSC::JSModuleRecord::importEntries):
+        (JSC::JSModuleRecord::starExportEntries):
+        (JSC::JSModuleRecord::moduleEnvironment):
+        (JSC::JSModuleRecord::appendRequestedModule): Deleted.
+        (JSC::JSModuleRecord::addImportEntry): Deleted.
+        (JSC::JSModuleRecord::addExportEntry): Deleted.
+        (JSC::JSModuleRecord::addStarExportEntry): Deleted.
+        * runtime/JSScope.cpp:
+        (JSC::abstractAccess):
+        (JSC::JSScope::collectVariablesUnderTDZ):
+        (JSC::JSScope::isModuleScope):
+        * runtime/JSScope.h:
+        * runtime/ModuleLoaderObject.cpp:
+
 2015-09-03  Brian Burg  <bburg@apple.com>
 
         Web Inspector: InspectorController should support multiple frontend channels
index b81560c..25390fb 100644 (file)
     <ClCompile Include="..\runtime\JSLock.cpp" />
     <ClCompile Include="..\runtime\JSMap.cpp" />
     <ClCompile Include="..\runtime\JSMapIterator.cpp" />
+    <ClCompile Include="..\runtime\JSModuleEnvironment.cpp" />
     <ClCompile Include="..\runtime\JSModuleRecord.cpp" />
     <ClCompile Include="..\runtime\JSNativeStdFunction.cpp" />
     <ClCompile Include="..\runtime\JSNotAnObject.cpp" />
     <ClInclude Include="..\runtime\JSLock.h" />
     <ClInclude Include="..\runtime\JSMap.h" />
     <ClInclude Include="..\runtime\JSMapIterator.h" />
+    <ClInclude Include="..\runtime\JSModuleEnvironment.h" />
     <ClInclude Include="..\runtime\JSModuleRecord.h" />
     <ClInclude Include="..\runtime\JSNativeStdFunction.h" />
     <ClInclude Include="..\runtime\JSNotAnObject.h" />
index a8d89b6..cc13f2c 100644 (file)
     <ClCompile Include="..\runtime\JSMap.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
+    <ClCompile Include="..\runtime\JSModuleEnvironment.cpp">
+      <Filter>runtime</Filter>
+    </ClCompile>
     <ClCompile Include="..\runtime\JSModuleRecord.cpp">
       <Filter>runtime</Filter>
     </ClCompile>
     <ClInclude Include="..\runtime\JSMap.h">
       <Filter>runtime</Filter>
     </ClInclude>
+    <ClInclude Include="..\runtime\JSModuleEnvironment.h">
+      <Filter>runtime</Filter>
+    </ClInclude>
     <ClInclude Include="..\runtime\JSModuleRecord.h">
       <Filter>runtime</Filter>
     </ClInclude>
index f3ba86d..9867e86 100644 (file)
                E3963CEE1B73F75000EB4CE5 /* NodesAnalyzeModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3963CEC1B73F75000EB4CE5 /* NodesAnalyzeModule.cpp */; };
                E39DA4A61B7E8B7C0084F33A /* JSModuleRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */; };
                E39DA4A71B7E8B7C0084F33A /* JSModuleRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E3D239C81B829C1C00BBEF67 /* JSModuleEnvironment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3D239C61B829C1C00BBEF67 /* JSModuleEnvironment.cpp */; };
+               E3D239C91B829C1C00BBEF67 /* JSModuleEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = E3D239C71B829C1C00BBEF67 /* JSModuleEnvironment.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E3EF88741B66DF23003F26CB /* JSPropertyNameIterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3EF88721B66DF23003F26CB /* JSPropertyNameIterator.cpp */; };
                E3EF88751B66DF23003F26CB /* JSPropertyNameIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E3EF88731B66DF23003F26CB /* JSPropertyNameIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E49DC16B12EF293E00184A1F /* SourceProviderCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E49DC15512EF277200184A1F /* SourceProviderCache.cpp */; };
                E3963CEC1B73F75000EB4CE5 /* NodesAnalyzeModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NodesAnalyzeModule.cpp; sourceTree = "<group>"; };
                E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSModuleRecord.cpp; sourceTree = "<group>"; };
                E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSModuleRecord.h; sourceTree = "<group>"; };
+               E3D239C61B829C1C00BBEF67 /* JSModuleEnvironment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSModuleEnvironment.cpp; sourceTree = "<group>"; };
+               E3D239C71B829C1C00BBEF67 /* JSModuleEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSModuleEnvironment.h; sourceTree = "<group>"; };
                E3EF88721B66DF23003F26CB /* JSPropertyNameIterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPropertyNameIterator.cpp; sourceTree = "<group>"; };
                E3EF88731B66DF23003F26CB /* JSPropertyNameIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPropertyNameIterator.h; sourceTree = "<group>"; };
                E49DC14912EF261A00184A1F /* SourceProviderCacheItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SourceProviderCacheItem.h; sourceTree = "<group>"; };
                                A700874017CBE8EB00C3E643 /* JSMap.h */,
                                A74DEF8F182D991400522C22 /* JSMapIterator.cpp */,
                                A74DEF90182D991400522C22 /* JSMapIterator.h */,
+                               E3D239C61B829C1C00BBEF67 /* JSModuleEnvironment.cpp */,
+                               E3D239C71B829C1C00BBEF67 /* JSModuleEnvironment.h */,
                                E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */,
                                E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */,
                                E33E8D1A1B9013C300346B52 /* JSNativeStdFunction.cpp */,
                                0F2B9CE919D0BA7D00B1D1B5 /* DFGObjectMaterializationData.h in Headers */,
                                A7D9A29817A0BC7400EE2618 /* DFGLICMPhase.h in Headers */,
                                A7D89CFC17A0B8CC00773AD8 /* DFGLivenessAnalysisPhase.h in Headers */,
+                               E3D239C91B829C1C00BBEF67 /* JSModuleEnvironment.h in Headers */,
                                0FF0F19B16B729FA005DF95B /* DFGLongLivedState.h in Headers */,
                                709FB8681AE335C60039D069 /* JSWeakSet.h in Headers */,
                                A767B5B617A0B9650063D940 /* DFGLoopPreHeaderCreationPhase.h in Headers */,
                                0F6C73501AC9F99F00BE1682 /* VariableWriteFireDetail.cpp in Sources */,
                                86B5826914D2797000A9C306 /* CodeProfiling.cpp in Sources */,
                                0F8F943C1667631300D61971 /* CodeSpecializationKind.cpp in Sources */,
+                               E3D239C81B829C1C00BBEF67 /* JSModuleEnvironment.cpp in Sources */,
                                0F8F94421667633500D61971 /* CodeType.cpp in Sources */,
                                147F39C1107EC37600427A48 /* CommonIdentifiers.cpp in Sources */,
                                0F9C5E5E18E35F5E00D431C3 /* FTLDWARFRegister.cpp in Sources */,
index aff2230..d746e5b 100644 (file)
@@ -49,6 +49,7 @@
 #include "JSCJSValue.h"
 #include "JSFunction.h"
 #include "JSLexicalEnvironment.h"
+#include "JSModuleEnvironment.h"
 #include "LLIntEntrypoint.h"
 #include "LowLevelInterpreter.h"
 #include "JSCInlines.h"
@@ -1785,6 +1786,17 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
         }
     }
 
+    // We already have the cloned symbol table for the module environment since we need to instantiate
+    // the module environments before linking the code block. We replace the stored symbol table with the already cloned one.
+    if (UnlinkedModuleProgramCodeBlock* unlinkedModuleProgramCodeBlock = jsDynamicCast<UnlinkedModuleProgramCodeBlock*>(unlinkedCodeBlock)) {
+        SymbolTable* clonedSymbolTable = jsCast<ModuleProgramExecutable*>(ownerExecutable)->moduleEnvironmentSymbolTable();
+        if (m_vm->typeProfiler()) {
+            ConcurrentJITLocker locker(clonedSymbolTable->m_lock);
+            clonedSymbolTable->prepareForTypeProfiling(locker);
+        }
+        replaceConstant(unlinkedModuleProgramCodeBlock->moduleEnvironmentSymbolTableConstantRegisterOffset(), clonedSymbolTable);
+    }
+
     m_functionDecls.resizeToFit(unlinkedCodeBlock->numberOfFunctionDecls());
     for (size_t count = unlinkedCodeBlock->numberOfFunctionDecls(), i = 0; i < count; ++i) {
         UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionDecl(i);
@@ -1863,6 +1875,9 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
     unsigned instructionCount = unlinkedCodeBlock->instructions().count();
     UnlinkedInstructionStream::Reader instructionReader(unlinkedCodeBlock->instructions());
 
+    // Bookkeep the strongly referenced module environments.
+    HashSet<JSModuleEnvironment*> stronglyReferencedModuleEnvironments;
+
     Vector<Instruction, 0, UnsafeVectorOverflow> instructions(instructionCount);
 
     for (unsigned i = 0; !instructionReader.atEnd(); ) {
@@ -1972,9 +1987,15 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Get, type, NotInitialization);
             instructions[i + 4].u.operand = op.type;
             instructions[i + 5].u.operand = op.depth;
-            if (op.lexicalEnvironment)
-                instructions[i + 6].u.symbolTable.set(*vm(), ownerExecutable, op.lexicalEnvironment->symbolTable());
-            else if (JSScope* constantScope = JSScope::constantScopeForCodeBlock(op.type, this))
+            if (op.lexicalEnvironment) {
+                if (op.type == ModuleVar) {
+                    // Keep the linked module environment strongly referenced.
+                    if (stronglyReferencedModuleEnvironments.add(jsCast<JSModuleEnvironment*>(op.lexicalEnvironment)).isNewEntry)
+                        addConstant(op.lexicalEnvironment);
+                    instructions[i + 6].u.jsCell.set(*vm(), ownerExecutable, op.lexicalEnvironment);
+                } else
+                    instructions[i + 6].u.symbolTable.set(*vm(), ownerExecutable, op.lexicalEnvironment->symbolTable());
+            } else if (JSScope* constantScope = JSScope::constantScopeForCodeBlock(op.type, this))
                 instructions[i + 6].u.jsCell.set(*vm(), ownerExecutable, constantScope);
             else
                 instructions[i + 6].u.pointer = nullptr;
@@ -2003,6 +2024,8 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
             ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Get, getPutInfo.resolveType(), NotInitialization);
 
             instructions[i + 4].u.operand = GetPutInfo(getPutInfo.resolveMode(), op.type, getPutInfo.initializationMode()).operand();
+            if (op.type == ModuleVar)
+                instructions[i + 4].u.operand = GetPutInfo(getPutInfo.resolveMode(), ClosureVar, getPutInfo.initializationMode()).operand();
             if (op.type == GlobalVar || op.type == GlobalVarWithVarInjectionChecks || op.type == GlobalLexicalVar || op.type == GlobalLexicalVarWithVarInjectionChecks)
                 instructions[i + 5].u.watchpointSet = op.watchpointSet;
             else if (op.structure)
@@ -2070,17 +2093,18 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
                 // we're abstractly "read"ing from a JSScope.
                 ResolveOp op = JSScope::abstractResolve(m_globalObject->globalExec(), localScopeDepth, scope, ident, Get, type, NotInitialization);
 
-                if (op.type == ClosureVar)
+                if (op.type == ClosureVar || op.type == ModuleVar)
                     symbolTable = op.lexicalEnvironment->symbolTable();
                 else if (op.type == GlobalVar)
                     symbolTable = m_globalObject.get()->symbolTable();
-                
+
+                UniquedStringImpl* impl = (op.type == ModuleVar) ? op.importedName.get() : ident.impl();
                 if (symbolTable) {
                     ConcurrentJITLocker locker(symbolTable->m_lock);
                     // If our parent scope was created while profiling was disabled, it will not have prepared for profiling yet.
                     symbolTable->prepareForTypeProfiling(locker);
-                    globalVariableID = symbolTable->uniqueIDForVariable(locker, ident.impl(), *vm());
-                    globalTypeSet = symbolTable->globalTypeSetForVariable(locker, ident.impl(), *vm());
+                    globalVariableID = symbolTable->uniqueIDForVariable(locker, impl, *vm());
+                    globalTypeSet = symbolTable->globalTypeSetForVariable(locker, impl, *vm());
                 } else
                     globalVariableID = TypeProfilerNoGlobalIDExists;
 
index 99493f7..b4f18b2 100644 (file)
@@ -81,6 +81,7 @@ namespace JSC {
 class ExecState;
 class LLIntOffsetsExtractor;
 class TypeLocation;
+class JSModuleEnvironment;
 
 enum ReoptimizationMode { DontCountReoptimization, CountReoptimization };
 
index 2fe1bb8..c114b9b 100644 (file)
@@ -297,7 +297,6 @@ void UnlinkedModuleProgramCodeBlock::visitChildren(JSCell* cell, SlotVisitor& vi
     UnlinkedModuleProgramCodeBlock* thisObject = jsCast<UnlinkedModuleProgramCodeBlock*>(cell);
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     Base::visitChildren(thisObject, visitor);
-    visitor.append(&thisObject->m_moduleEnvironmentSymbolTable);
 }
 
 UnlinkedCodeBlock::~UnlinkedCodeBlock()
index f749bb0..fa514c1 100644 (file)
@@ -38,7 +38,6 @@
 #include "ParserModes.h"
 #include "RegExp.h"
 #include "SpecialPointer.h"
-#include "SymbolTable.h"
 #include "UnlinkedFunctionExecutable.h"
 #include "VariableEnvironment.h"
 #include "VirtualRegister.h"
@@ -55,7 +54,6 @@ class ParserError;
 class ScriptExecutable;
 class SourceCode;
 class SourceProvider;
-class SymbolTable;
 class UnlinkedCodeBlock;
 class UnlinkedFunctionCodeBlock;
 class UnlinkedFunctionExecutable;
@@ -531,29 +529,35 @@ public:
 
     static void visitChildren(JSCell*, SlotVisitor&);
 
-    // This symbol table represents the layout of the module environment. This symbol table will be modified by the byte
-    // code generator since the module environment includes the top-most lexical captured variables inside the module code.
-    // This means that, once the module environment is allocated and instantiated from this symbol table, it is titely
-    // coupled with the specific unlinked module program code block and the symbol table. So before executing the module
-    // code, we should not clear the unlinked module program code block in the module executable. This requirement is met
-    // because the garbage collector only clears unlinked code in (1) unmarked executables and (2) function executables.
+    // This offset represents the constant register offset to the stored symbol table that represents the layout of the
+    // module environment. This symbol table is created by the byte code generator since the module environment includes
+    // the top-most lexical captured variables inside the module code. This means that, once the module environment is
+    // allocated and instantiated from this symbol table, it is titely coupled with the specific unlinked module program
+    // code block and the stored symbol table. So before executing the module code, we should not clear the unlinked module
+    // program code block in the module executable. This requirement is met because the garbage collector only clears
+    // unlinked code in (1) unmarked executables and (2) function executables.
     //
     // Since the function code may be executed repeatedly and the environment of each function execution is different,
     // the function code need to allocate and instantiate the environment in the prologue of the function code. On the
     // other hand, the module code is executed only once. So we can instantiate the module environment outside the module
-    // code. At that time, we construct the module environment by using this symbol table before executing the module code.
-    // Instantiating the module environment before executing the module code is required to link the imported bindings
-    // between the modules.
+    // code. At that time, we construct the module environment by using the symbol table that is held by the module executable.
+    // The symbol table held by the executable is the cloned one from one in the unlinked code block. Instantiating the module
+    // environment before executing and linking the module code is required to link the imported bindings between the modules.
     //
-    // The unlinked module program code block only holds the symbol table. It does not hold the module environment. So while
-    // the module environment requires the specific unlinked module program code block, the unlinked module code block
-    // can be used for the module environment instantiated from this unlinked code block. There is 1:N relation between
-    // the unlinked module code block and the module environments. So the unlinked module program code block can be cached.
+    // The unlinked module program code block only holds the pre-cloned symbol table in its constant register pool. It does
+    // not hold the instantiated module environment. So while the module environment requires the specific unlinked module
+    // program code block, the unlinked module code block can be used for the module environment instantiated from this
+    // unlinked code block. There is 1:N relation between the unlinked module code block and the module environments. So the
+    // unlinked module program code block can be cached.
     //
     // On the other hand, the linked code block for the module environment includes the resolved references to the imported
     // bindings. The imported binding references the other module environment, so the linked code block is titly coupled
     // with the specific set of the module environments. Thus, the linked code block should not be cached.
-    SymbolTable* moduleEnvironmentSymbolTable() { return m_moduleEnvironmentSymbolTable.get(); }
+    int moduleEnvironmentSymbolTableConstantRegisterOffset() { return m_moduleEnvironmentSymbolTableConstantRegisterOffset; }
+    void setModuleEnvironmentSymbolTableConstantRegisterOffset(int offset)
+    {
+        m_moduleEnvironmentSymbolTableConstantRegisterOffset = offset;
+    }
 
 private:
     UnlinkedModuleProgramCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info)
@@ -561,13 +565,7 @@ private:
     {
     }
 
-    void finishCreation(VM& vm)
-    {
-        Base::finishCreation(vm);
-        m_moduleEnvironmentSymbolTable.set(vm, this, SymbolTable::create(vm));
-    }
-
-    WriteBarrier<SymbolTable> m_moduleEnvironmentSymbolTable;
+    int m_moduleEnvironmentSymbolTableConstantRegisterOffset { 0 };
 
 public:
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
index 274024c..03a0fad 100644 (file)
@@ -537,9 +537,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ModuleProgramNode* moduleProgramNod
     if (m_isBuiltinFunction)
         m_shouldEmitDebugHooks = false;
 
-    // Use the symbol table allocated in the unlinked code block. This symbol table
-    // will be used to allocate and instantiate the module environment.
-    SymbolTable* moduleEnvironmentSymbolTable = codeBlock->moduleEnvironmentSymbolTable();
+    SymbolTable* moduleEnvironmentSymbolTable = SymbolTable::create(*m_vm);
     moduleEnvironmentSymbolTable->setUsesNonStrictEval(m_usesNonStrictEval);
     moduleEnvironmentSymbolTable->setScopeType(SymbolTable::ScopeType::LexicalScope);
 
@@ -593,6 +591,7 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ModuleProgramNode* moduleProgramNod
     VariableEnvironment& lexicalVariables = moduleProgramNode->lexicalVariables();
     instantiateLexicalVariables(lexicalVariables, moduleEnvironmentSymbolTable, ScopeRegisterType::Block, lookUpVarKind);
 
+    // We keep the symbol table in the constant pool.
     RegisterID* constantSymbolTable = nullptr;
     if (vm.typeProfiler())
         constantSymbolTable = addConstantValue(moduleEnvironmentSymbolTable);
@@ -657,6 +656,11 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ModuleProgramNode* moduleProgramNod
             m_functionsToInitialize.append(std::make_pair(function, NormalFunctionVariable));
         }
     }
+
+    // Remember the constant register offset to the top-most symbol table. This symbol table will be
+    // cloned in the code block linking. After that, to create the module environment, we retrieve
+    // the cloned symbol table from the linked code block by using this offset.
+    codeBlock->setModuleEnvironmentSymbolTableConstantRegisterOffset(constantSymbolTable->index());
 }
 
 BytecodeGenerator::~BytecodeGenerator()
index 2b89e3a..786b921 100644 (file)
@@ -42,6 +42,7 @@
 #include "Heap.h"
 #include "JSLexicalEnvironment.h"
 #include "JSCInlines.h"
+#include "JSModuleEnvironment.h"
 #include "PreciseJumpTargets.h"
 #include "PutByIdStatus.h"
 #include "StackAlignment.h"
@@ -3839,6 +3840,14 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 addToGraph(Phantom, get(VirtualRegister(scope)));
                 break;
             }
+            case ModuleVar: {
+                // Since the value of the "scope" virtual register is not used in LLInt / baseline op_resolve_scope with ModuleVar,
+                // we need not to keep it alive by the Phantom node.
+                JSModuleEnvironment* moduleEnvironment = jsCast<JSModuleEnvironment*>(currentInstruction[6].u.jsCell.get());
+                // Module environment is already strongly referenced by the CodeBlock.
+                set(VirtualRegister(dst), weakJSConstant(moduleEnvironment));
+                break;
+            }
             case LocalClosureVar:
             case ClosureVar:
             case ClosureVarWithVarInjectionChecks: {
@@ -4034,6 +4043,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 set(VirtualRegister(dst), addToGraph(JSConstant, OpInfo(m_constantUndefined)));
                 break;
             }
+            case ModuleVar:
             case Dynamic:
                 RELEASE_ASSERT_NOT_REACHED();
                 break;
@@ -4128,6 +4138,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 }
                 break;
             }
+
             case UnresolvedProperty:
             case UnresolvedPropertyWithVarInjectionChecks: {
                 addToGraph(ForceOSRExit);
@@ -4135,6 +4146,13 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                 addToGraph(Phantom, scopeNode);
                 break;
             }
+
+            case ModuleVar:
+                // Need not to keep "scope" and "value" register values here by Phantom because
+                // they are not used in LLInt / baseline op_put_to_scope with ModuleVar.
+                addToGraph(ForceOSRExit);
+                break;
+
             case Dynamic:
                 RELEASE_ASSERT_NOT_REACHED();
                 break;
index 1f99e2c..4b0f12c 100644 (file)
@@ -49,6 +49,7 @@
 #include "JSBoundFunction.h"
 #include "JSCInlines.h"
 #include "JSLexicalEnvironment.h"
+#include "JSModuleEnvironment.h"
 #include "JSNotAnObject.h"
 #include "JSStackInlines.h"
 #include "JSString.h"
@@ -1215,6 +1216,54 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue
     return checkedReturn(result);
 }
 
+JSValue Interpreter::execute(ModuleProgramExecutable* executable, CallFrame* callFrame, JSModuleEnvironment* scope)
+{
+    VM& vm = *scope->vm();
+    SamplingScope samplingScope(this);
+
+    ASSERT(scope->vm() == &callFrame->vm());
+    ASSERT(!vm.exception());
+    ASSERT(!vm.isCollectorBusy());
+    RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
+    if (vm.isCollectorBusy())
+        return jsNull();
+
+    VMEntryScope entryScope(vm, scope->globalObject());
+    if (!vm.isSafeToRecurse())
+        return checkedReturn(throwStackOverflowError(callFrame));
+
+    JSObject* compileError = executable->prepareForExecution(callFrame, nullptr, scope, CodeForCall);
+    if (UNLIKELY(!!compileError))
+        return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
+    ModuleProgramCodeBlock* codeBlock = executable->codeBlock();
+
+    if (UNLIKELY(vm.watchdog && vm.watchdog->didFire(callFrame)))
+        return throwTerminatedExecutionException(callFrame);
+
+    ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
+
+    // The |this| of the module is always `undefined`.
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-environment-records-hasthisbinding
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-module-environment-records-getthisbinding
+    ProtoCallFrame protoCallFrame;
+    protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), jsUndefined(), 1);
+
+    if (LegacyProfiler* profiler = vm.enabledProfiler())
+        profiler->willExecute(callFrame, executable->sourceURL(), executable->firstLine(), executable->startColumn());
+
+    // Execute the code:
+    JSValue result;
+    {
+        SamplingTool::CallRecord callRecord(m_sampler.get());
+        result = executable->generatedJITCode()->execute(&vm, &protoCallFrame);
+    }
+
+    if (LegacyProfiler* profiler = vm.enabledProfiler())
+        profiler->didExecute(callFrame, executable->sourceURL(), executable->firstLine(), executable->startColumn());
+
+    return checkedReturn(result);
+}
+
 NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID)
 {
     Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger();
index 038bca5..4fefaa6 100644 (file)
@@ -54,6 +54,7 @@ namespace JSC {
     class JSArrowFunction;
     class JSFunction;
     class JSGlobalObject;
+    class JSModuleEnvironment;
     class JSModuleRecord;
     class LLIntOffsetsExtractor;
     class ProgramExecutable;
@@ -215,6 +216,7 @@ namespace JSC {
         JSValue executeCall(CallFrame*, JSObject* function, CallType, const CallData&, JSValue thisValue, const ArgList&);
         JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget);
         JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*);
+        JSValue execute(ModuleProgramExecutable*, CallFrame*, JSModuleEnvironment*);
 
         void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc);
         
index d66a214..8c6bee6 100644 (file)
@@ -1938,6 +1938,9 @@ EncodedJSValue JIT_OPERATION operationGetFromScope(ExecState* exec, Instruction*
     JSObject* scope = jsCast<JSObject*>(exec->uncheckedR(pc[2].u.operand).jsValue());
     GetPutInfo getPutInfo(pc[4].u.operand);
 
+    // ModuleVar is always converted to ClosureVar for get_from_scope.
+    ASSERT(getPutInfo.resolveType() != ModuleVar);
+
     PropertySlot slot(scope);
     if (!scope->getPropertySlot(exec, ident, slot)) {
         if (getPutInfo.resolveMode() == ThrowIfNotFound)
@@ -1973,6 +1976,10 @@ void JIT_OPERATION operationPutToScope(ExecState* exec, Instruction* bytecodePC)
     JSObject* scope = jsCast<JSObject*>(exec->uncheckedR(pc[1].u.operand).jsValue());
     JSValue value = exec->r(pc[3].u.operand).jsValue();
     GetPutInfo getPutInfo = GetPutInfo(pc[4].u.operand);
+
+    // ModuleVar does not keep the scope register value alive in DFG.
+    ASSERT(getPutInfo.resolveType() != ModuleVar);
+
     if (getPutInfo.resolveType() == LocalClosureVar) {
         JSLexicalEnvironment* environment = jsCast<JSLexicalEnvironment*>(scope);
         environment->variableAt(ScopeOffset(pc[6].u.operand)).set(vm, environment, value);
index a4defad..94ffd14 100644 (file)
@@ -709,6 +709,10 @@ void JIT::emit_op_resolve_scope(Instruction* currentInstruction)
     case ClosureVarWithVarInjectionChecks:
         emitResolveClosure(dst, scope, needsVarInjectionChecks(resolveType), depth);
         break;
+    case ModuleVar:
+        move(TrustedImmPtr(currentInstruction[6].u.jsCell.get()), regT0);
+        emitPutVirtualRegister(dst);
+        break;
     case UnresolvedProperty:
     case UnresolvedPropertyWithVarInjectionChecks:
     case Dynamic:
@@ -722,9 +726,10 @@ void JIT::emit_op_resolve_scope(Instruction* currentInstruction)
 void JIT::emitSlow_op_resolve_scope(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
 {
     ResolveType resolveType = static_cast<ResolveType>(currentInstruction[4].u.operand);
-    if (resolveType == GlobalProperty || resolveType == GlobalVar || resolveType == ClosureVar || resolveType == GlobalLexicalVar)
+    if (resolveType == GlobalProperty || resolveType == GlobalVar || resolveType == ClosureVar || resolveType == GlobalLexicalVar || resolveType == ModuleVar)
         return;
 
+
     linkSlowCase(iter);
     JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_resolve_scope);
     slowPathCall.call();
@@ -789,6 +794,7 @@ void JIT::emit_op_get_from_scope(Instruction* currentInstruction)
     case Dynamic:
         addSlowCase(jump());
         break;
+    case ModuleVar:
     case LocalClosureVar:
         RELEASE_ASSERT_NOT_REACHED();
     }
@@ -882,6 +888,7 @@ void JIT::emit_op_put_to_scope(Instruction* currentInstruction)
         break;
     case UnresolvedProperty:
     case UnresolvedPropertyWithVarInjectionChecks:
+    case ModuleVar:
     case Dynamic:
         addSlowCase(jump());
         break;
@@ -908,7 +915,12 @@ void JIT::emitSlow_op_put_to_scope(Instruction* currentInstruction, Vector<SlowC
         return;
     while (linkCount--)
         linkSlowCase(iter);
-    callOperation(operationPutToScope, currentInstruction);
+
+    if (resolveType == ModuleVar) {
+        JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_throw_strict_mode_readonly_property_write_error);
+        slowPathCall.call();
+    } else
+        callOperation(operationPutToScope, currentInstruction);
 }
 
 void JIT::emit_op_get_from_arguments(Instruction* currentInstruction)
index 79a1df8..b135d41 100644 (file)
@@ -741,6 +741,11 @@ void JIT::emit_op_resolve_scope(Instruction* currentInstruction)
     case ClosureVarWithVarInjectionChecks:
         emitResolveClosure(dst, scope, needsVarInjectionChecks(resolveType), depth);
         break;
+    case ModuleVar:
+        move(TrustedImm32(JSValue::CellTag), regT1);
+        move(TrustedImmPtr(currentInstruction[6].u.jsCell.get()), regT0);
+        emitStore(dst, regT1, regT0);
+        break;
     case UnresolvedProperty:
     case UnresolvedPropertyWithVarInjectionChecks:
     case Dynamic:
@@ -755,7 +760,7 @@ void JIT::emitSlow_op_resolve_scope(Instruction* currentInstruction, Vector<Slow
 {
     ResolveType resolveType = static_cast<ResolveType>(currentInstruction[4].u.operand);
 
-    if (resolveType == GlobalProperty || resolveType == GlobalVar || resolveType == ClosureVar || resolveType == GlobalLexicalVar)
+    if (resolveType == GlobalProperty || resolveType == GlobalVar || resolveType == ClosureVar || resolveType == GlobalLexicalVar || resolveType == ModuleVar)
         return;
 
     linkSlowCase(iter);
@@ -823,6 +828,7 @@ void JIT::emit_op_get_from_scope(Instruction* currentInstruction)
     case Dynamic:
         addSlowCase(jump());
         break;
+    case ModuleVar:
     case LocalClosureVar:
         RELEASE_ASSERT_NOT_REACHED();
     }
@@ -912,6 +918,7 @@ void JIT::emit_op_put_to_scope(Instruction* currentInstruction)
         emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
         emitPutClosureVar(scope, *operandSlot, value, currentInstruction[5].u.watchpointSet);
         break;
+    case ModuleVar:
     case UnresolvedProperty:
     case UnresolvedPropertyWithVarInjectionChecks:
     case Dynamic:
@@ -937,7 +944,12 @@ void JIT::emitSlow_op_put_to_scope(Instruction* currentInstruction, Vector<SlowC
         return;
     while (linkCount--)
         linkSlowCase(iter);
-    callOperation(operationPutToScope, currentInstruction);
+
+    if (resolveType == ModuleVar) {
+        JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_throw_strict_mode_readonly_property_write_error);
+        slowPathCall.call();
+    } else
+        callOperation(operationPutToScope, currentInstruction);
 }
 
 void JIT::emit_op_get_from_arguments(Instruction* currentInstruction)
index f1f3ea2..8e73edf 100644 (file)
@@ -148,10 +148,11 @@ void Data::performAssertions(VM& vm)
     static_assert(GlobalLexicalVar == 2, "LLInt assumes GlobalLexicalVar ResultType is == 2");
     static_assert(ClosureVar == 3, "LLInt assumes ClosureVar ResultType is == 3");
     static_assert(LocalClosureVar == 4, "LLInt assumes LocalClosureVar ResultType is == 4");
-    static_assert(GlobalPropertyWithVarInjectionChecks == 5, "LLInt assumes GlobalPropertyWithVarInjectionChecks ResultType is == 5");
-    static_assert(GlobalVarWithVarInjectionChecks == 6, "LLInt assumes GlobalVarWithVarInjectionChecks ResultType is == 6");
-    static_assert(GlobalLexicalVarWithVarInjectionChecks == 7, "LLInt assumes GlobalLexicalVarWithVarInjectionChecks ResultType is == 7");
-    static_assert(ClosureVarWithVarInjectionChecks == 8, "LLInt assumes ClosureVarWithVarInjectionChecks ResultType is == 8");
+    static_assert(ModuleVar == 5, "LLInt assumes ModuleVar ResultType is == 5");
+    static_assert(GlobalPropertyWithVarInjectionChecks == 6, "LLInt assumes GlobalPropertyWithVarInjectionChecks ResultType is == 6");
+    static_assert(GlobalVarWithVarInjectionChecks == 7, "LLInt assumes GlobalVarWithVarInjectionChecks ResultType is == 7");
+    static_assert(GlobalLexicalVarWithVarInjectionChecks == 8, "LLInt assumes GlobalLexicalVarWithVarInjectionChecks ResultType is == 8");
+    static_assert(ClosureVarWithVarInjectionChecks == 9, "LLInt assumes ClosureVarWithVarInjectionChecks ResultType is == 9");
 
     static_assert(InitializationMode::Initialization == 0, "LLInt assumes that InitializationMode::Initialization is 0");
     
index 7eda2ba..3fb485d 100644 (file)
@@ -1366,6 +1366,9 @@ LLINT_SLOW_PATH_DECL(slow_path_get_from_scope)
     JSObject* scope = jsCast<JSObject*>(LLINT_OP(2).jsValue());
     GetPutInfo getPutInfo(pc[4].u.operand);
 
+    // ModuleVar is always converted to ClosureVar for get_from_scope.
+    ASSERT(getPutInfo.resolveType() != ModuleVar);
+
     PropertySlot slot(scope);
     if (!scope->getPropertySlot(exec, ident, slot)) {
         if (getPutInfo.resolveMode() == ThrowIfNotFound)
index 9cfa821..aef291c 100644 (file)
@@ -328,10 +328,11 @@ const GlobalVar = 1
 const GlobalLexicalVar = 2
 const ClosureVar = 3
 const LocalClosureVar = 4
-const GlobalPropertyWithVarInjectionChecks = 5
-const GlobalVarWithVarInjectionChecks = 6
-const GlobalLexicalVarWithVarInjectionChecks = 7
-const ClosureVarWithVarInjectionChecks = 8
+const ModuleVar = 5
+const GlobalPropertyWithVarInjectionChecks = 6
+const GlobalVarWithVarInjectionChecks = 7
+const GlobalLexicalVarWithVarInjectionChecks = 8
+const ClosureVarWithVarInjectionChecks = 9
 
 const ResolveTypeMask = 0x3ff
 const InitializationModeMask = 0xffc00
index eb697fb..4d3dc87 100644 (file)
@@ -2035,10 +2035,15 @@ _llint_op_resolve_scope:
     dispatch(7)
 
 .rClosureVar:
-    bineq t0, ClosureVar, .rGlobalPropertyWithVarInjectionChecks
+    bineq t0, ClosureVar, .rModuleVar
     resolveScope()
     dispatch(7)
 
+.rModuleVar:
+    bineq t0, ModuleVar, .rGlobalPropertyWithVarInjectionChecks
+    getConstantScope(1)
+    dispatch(7)
+
 .rGlobalPropertyWithVarInjectionChecks:
     bineq t0, GlobalPropertyWithVarInjectionChecks, .rGlobalVarWithVarInjectionChecks
     varInjectionCheck(.rDynamic)
@@ -2266,13 +2271,18 @@ _llint_op_put_to_scope:
     dispatch(7)
 
 .pClosureVarWithVarInjectionChecks:
-    bineq t0, ClosureVarWithVarInjectionChecks, .pDynamic
+    bineq t0, ClosureVarWithVarInjectionChecks, .pModuleVar
     writeBarrierOnOperands(1, 3)
     varInjectionCheck(.pDynamic)
     loadVariable(1, t2, t1, t0)
     putClosureVar()
     dispatch(7)
 
+.pModuleVar:
+    bineq t0, ModuleVar, .pDynamic
+    callSlowPath(_slow_path_throw_strict_mode_readonly_property_write_error)
+    dispatch(7)
+
 .pDynamic:
     callSlowPath(_llint_slow_path_put_to_scope)
     dispatch(7)
index 1f321ca..f80fb6f 100644 (file)
@@ -1915,10 +1915,15 @@ _llint_op_resolve_scope:
     dispatch(7)
 
 .rClosureVar:
-    bineq t0, ClosureVar, .rGlobalPropertyWithVarInjectionChecks
+    bineq t0, ClosureVar, .rModuleVar
     resolveScope()
     dispatch(7)
 
+.rModuleVar:
+    bineq t0, ModuleVar, .rGlobalPropertyWithVarInjectionChecks
+    getConstantScope(1)
+    dispatch(7)
+
 .rGlobalPropertyWithVarInjectionChecks:
     bineq t0, GlobalPropertyWithVarInjectionChecks, .rGlobalVarWithVarInjectionChecks
     varInjectionCheck(.rDynamic)
@@ -2152,13 +2157,18 @@ _llint_op_put_to_scope:
     dispatch(7)
 
 .pClosureVarWithVarInjectionChecks:
-    bineq t0, ClosureVarWithVarInjectionChecks, .pDynamic
+    bineq t0, ClosureVarWithVarInjectionChecks, .pModuleVar
     writeBarrierOnOperands(1, 3)
     varInjectionCheck(.pDynamic)
     loadVariable(1, t0)
     putClosureVar()
     dispatch(7)
 
+.pModuleVar:
+    bineq t0, ModuleVar, .pDynamic
+    callSlowPath(_slow_path_throw_strict_mode_readonly_property_write_error)
+    dispatch(7)
+
 .pDynamic:
     callSlowPath(_llint_slow_path_put_to_scope)
     dispatch(7)
index f6238fb..c1a3cc2 100644 (file)
@@ -82,7 +82,9 @@ void ModuleAnalyzer::exportVariable(const RefPtr<UniquedStringImpl>& localName,
         return;
     }
 
-    const auto& importEntry = moduleRecord()->lookUpImportEntry(localName);
+    Optional<JSModuleRecord::ImportEntry> optionalImportEntry = moduleRecord()->tryGetImportEntry(localName.get());
+    ASSERT(optionalImportEntry);
+    const JSModuleRecord::ImportEntry& importEntry = *optionalImportEntry;
     if (variable.isImportedNamespace()) {
         // Exported namespace binding.
         // import * as namespace from "mod"
index e829f85..e4861ba 100644 (file)
@@ -270,6 +270,12 @@ SLOW_PATH_DECL(slow_path_throw_tdz_error)
     THROW(createTDZError(exec));
 }
 
+SLOW_PATH_DECL(slow_path_throw_strict_mode_readonly_property_write_error)
+{
+    BEGIN();
+    THROW(createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)));
+}
+
 SLOW_PATH_DECL(slow_path_not)
 {
     BEGIN();
@@ -668,6 +674,10 @@ SLOW_PATH_DECL(slow_path_resolve_scope)
     JSValue resolvedScope = JSScope::resolve(exec, scope, ident);
 
     ResolveType resolveType = static_cast<ResolveType>(pc[4].u.operand);
+
+    // ModuleVar does not keep the scope register value alive in DFG.
+    ASSERT(resolveType != ModuleVar);
+
     if (resolveType == UnresolvedProperty || resolveType == UnresolvedPropertyWithVarInjectionChecks) {
         if (JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsDynamicCast<JSGlobalLexicalEnvironment*>(resolvedScope)) {
             if (resolveType == UnresolvedProperty)
index 8c9867f..fbefcd0 100644 (file)
@@ -241,6 +241,7 @@ SLOW_PATH_HIDDEN_DECL(slow_path_enter);
 SLOW_PATH_HIDDEN_DECL(slow_path_get_callee);
 SLOW_PATH_HIDDEN_DECL(slow_path_to_this);
 SLOW_PATH_HIDDEN_DECL(slow_path_throw_tdz_error);
+SLOW_PATH_HIDDEN_DECL(slow_path_throw_strict_mode_readonly_property_write_error);
 SLOW_PATH_HIDDEN_DECL(slow_path_not);
 SLOW_PATH_HIDDEN_DECL(slow_path_eq);
 SLOW_PATH_HIDDEN_DECL(slow_path_neq);
index 00285a6..1b93e8f 100644 (file)
@@ -219,6 +219,10 @@ JSObject* throwSyntaxError(ExecState* exec)
     return exec->vm().throwException(exec, createSyntaxError(exec, ASCIILiteral("Syntax error")));
 }
 
+JSObject* throwSyntaxError(ExecState* exec, const String& message)
+{
+    return exec->vm().throwException(exec, createSyntaxError(exec, message));
+}
 
 JSObject* createError(ExecState* exec, const String& message)
 {
index ec30a3d..f46fe37 100644 (file)
@@ -74,6 +74,7 @@ JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&)
 // Convenience wrappers, create an throw an exception with a default message.
 JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*);
 JS_EXPORT_PRIVATE JSObject* throwSyntaxError(ExecState*);
+JS_EXPORT_PRIVATE JSObject* throwSyntaxError(ExecState*, const String& errorMessage);
 inline JSObject* throwRangeError(ExecState* state, const String& errorMessage) { return state->vm().throwException(state, createRangeError(state, errorMessage)); }
 
 // Convenience wrappers, wrap result as an EncodedJSValue.
index b3172e6..fc099e6 100644 (file)
@@ -439,6 +439,8 @@ ModuleProgramExecutable* ModuleProgramExecutable::create(ExecState* exec, const
         return nullptr;
     executable->m_unlinkedModuleProgramCodeBlock.set(exec->vm(), executable, unlinkedModuleProgramCode);
 
+    executable->m_moduleEnvironmentSymbolTable.set(exec->vm(), executable, jsCast<SymbolTable*>(unlinkedModuleProgramCode->constantRegister(unlinkedModuleProgramCode->moduleEnvironmentSymbolTableConstantRegisterOffset()).get())->cloneScopePart(exec->vm()));
+
     return executable;
 }
 
@@ -633,6 +635,7 @@ void ModuleProgramExecutable::visitChildren(JSCell* cell, SlotVisitor& visitor)
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
     ScriptExecutable::visitChildren(thisObject, visitor);
     visitor.append(&thisObject->m_unlinkedModuleProgramCodeBlock);
+    visitor.append(&thisObject->m_moduleEnvironmentSymbolTable);
     if (thisObject->m_moduleProgramCodeBlock)
         thisObject->m_moduleProgramCodeBlock->visitAggregate(visitor);
 }
@@ -641,6 +644,7 @@ void ModuleProgramExecutable::clearCode()
 {
     m_moduleProgramCodeBlock = nullptr;
     m_unlinkedModuleProgramCodeBlock.clear();
+    m_moduleEnvironmentSymbolTable.clear();
     Base::clearCode();
 }
 
index cdceb30..f3ef4bb 100644 (file)
@@ -57,6 +57,9 @@ class ProgramCodeBlock;
 class ModuleProgramCodeBlock;
 class JSScope;
 class WebAssemblyCodeBlock;
+class ModuleProgramCodeBlock;
+class JSModuleRecord;
+class JSScope;
 
 enum CompilationKind { FirstCompilation, OptimizingCompilation };
 
@@ -589,6 +592,8 @@ public:
     ExecutableInfo executableInfo() const { return ExecutableInfo(needsActivation(), usesEval(), isStrictMode(), false, false, ConstructorKind::None, false); }
     UnlinkedModuleProgramCodeBlock* unlinkedModuleProgramCodeBlock() { return m_unlinkedModuleProgramCodeBlock.get(); }
 
+    SymbolTable* moduleEnvironmentSymbolTable() { return m_moduleEnvironmentSymbolTable.get(); }
+
 private:
     friend class ScriptExecutable;
 
@@ -597,6 +602,7 @@ private:
     static void visitChildren(JSCell*, SlotVisitor&);
 
     WriteBarrier<UnlinkedModuleProgramCodeBlock> m_unlinkedModuleProgramCodeBlock;
+    WriteBarrier<SymbolTable> m_moduleEnvironmentSymbolTable;
     RefPtr<ModuleProgramCodeBlock> m_moduleProgramCodeBlock;
 };
 
index 2e7a2ef..f499bcd 100644 (file)
@@ -26,6 +26,8 @@
 #ifndef GetPutInfo_h
 #define GetPutInfo_h
 
+#include <wtf/text/UniquedStringImpl.h>
+
 namespace JSC {
 
 class Structure;
@@ -44,6 +46,7 @@ enum ResolveType {
     GlobalLexicalVar,
     ClosureVar,
     LocalClosureVar,
+    ModuleVar,
 
     // Ditto, but at least one intervening scope used non-strict eval, which
     // can inject an intercepting var delcaration at runtime.
@@ -84,6 +87,7 @@ ALWAYS_INLINE const char* resolveTypeName(ResolveType type)
         "GlobalLexicalVar",
         "ClosureVar",
         "LocalClosureVar",
+        "ModuleVar",
         "GlobalPropertyWithVarInjectionChecks",
         "GlobalVarWithVarInjectionChecks",
         "GlobalLexicalVarWithVarInjectionChecks",
@@ -122,6 +126,7 @@ ALWAYS_INLINE ResolveType makeType(ResolveType type, bool needsVarInjectionCheck
         return ClosureVarWithVarInjectionChecks;
     case UnresolvedProperty:
         return UnresolvedPropertyWithVarInjectionChecks;
+    case ModuleVar:
     case GlobalPropertyWithVarInjectionChecks:
     case GlobalVarWithVarInjectionChecks:
     case GlobalLexicalVarWithVarInjectionChecks:
@@ -143,6 +148,7 @@ ALWAYS_INLINE bool needsVarInjectionChecks(ResolveType type)
     case GlobalLexicalVar:
     case ClosureVar:
     case LocalClosureVar:
+    case ModuleVar:
     case UnresolvedProperty:
         return false;
     case GlobalPropertyWithVarInjectionChecks:
@@ -159,13 +165,14 @@ ALWAYS_INLINE bool needsVarInjectionChecks(ResolveType type)
 }
 
 struct ResolveOp {
-    ResolveOp(ResolveType type, size_t depth, Structure* structure, JSLexicalEnvironment* lexicalEnvironment, WatchpointSet* watchpointSet, uintptr_t operand)
+    ResolveOp(ResolveType type, size_t depth, Structure* structure, JSLexicalEnvironment* lexicalEnvironment, WatchpointSet* watchpointSet, uintptr_t operand, UniquedStringImpl* importedName = nullptr)
         : type(type)
         , depth(depth)
         , structure(structure)
         , lexicalEnvironment(lexicalEnvironment)
         , watchpointSet(watchpointSet)
         , operand(operand)
+        , importedName(importedName)
     {
     }
 
@@ -175,6 +182,7 @@ struct ResolveOp {
     JSLexicalEnvironment* lexicalEnvironment;
     WatchpointSet* watchpointSet;
     uintptr_t operand;
+    RefPtr<UniquedStringImpl> importedName;
 };
 
 class GetPutInfo {
index ad37bae..0e77cd2 100644 (file)
@@ -84,6 +84,7 @@
 #include "JSLock.h"
 #include "JSMap.h"
 #include "JSMapIterator.h"
+#include "JSModuleEnvironment.h"
 #include "JSModuleRecord.h"
 #include "JSNativeStdFunction.h"
 #include "JSONObject.h"
@@ -313,6 +314,7 @@ void JSGlobalObject::init(VM& vm)
     m_typedArrays[toIndex(TypeDataView)].structure.set(vm, this, JSDataView::createStructure(vm, this, m_typedArrays[toIndex(TypeDataView)].prototype.get()));
     
     m_lexicalEnvironmentStructure.set(vm, this, JSLexicalEnvironment::createStructure(vm, this));
+    m_moduleEnvironmentStructure.set(vm, this, JSModuleEnvironment::createStructure(vm, this));
     m_strictEvalActivationStructure.set(vm, this, StrictEvalActivation::createStructure(vm, this, jsNull()));
     m_debuggerScopeStructure.set(m_vm, this, DebuggerScope::createStructure(m_vm, this));
     m_withScopeStructure.set(vm, this, JSWithScope::createStructure(vm, this, jsNull()));
@@ -793,6 +795,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(&thisObject->m_withScopeStructure);
     visitor.append(&thisObject->m_strictEvalActivationStructure);
     visitor.append(&thisObject->m_lexicalEnvironmentStructure);
+    visitor.append(&thisObject->m_moduleEnvironmentStructure);
     visitor.append(&thisObject->m_directArgumentsStructure);
     visitor.append(&thisObject->m_scopedArgumentsStructure);
     visitor.append(&thisObject->m_outOfBandArgumentsStructure);
index 461959d..ec66c1a 100644 (file)
@@ -234,6 +234,7 @@ protected:
     WriteBarrier<Structure> m_withScopeStructure;
     WriteBarrier<Structure> m_strictEvalActivationStructure;
     WriteBarrier<Structure> m_lexicalEnvironmentStructure;
+    WriteBarrier<Structure> m_moduleEnvironmentStructure;
     WriteBarrier<Structure> m_directArgumentsStructure;
     WriteBarrier<Structure> m_scopedArgumentsStructure;
     WriteBarrier<Structure> m_outOfBandArgumentsStructure;
@@ -450,6 +451,7 @@ public:
     Structure* withScopeStructure() const { return m_withScopeStructure.get(); }
     Structure* strictEvalActivationStructure() const { return m_strictEvalActivationStructure.get(); }
     Structure* activationStructure() const { return m_lexicalEnvironmentStructure.get(); }
+    Structure* moduleEnvironmentStructure() const { return m_moduleEnvironmentStructure.get(); }
     Structure* directArgumentsStructure() const { return m_directArgumentsStructure.get(); }
     Structure* scopedArgumentsStructure() const { return m_scopedArgumentsStructure.get(); }
     Structure* outOfBandArgumentsStructure() const { return m_outOfBandArgumentsStructure.get(); }
index 507312e..bbe3161 100644 (file)
@@ -39,7 +39,7 @@ namespace JSC {
 class Register;
     
 class JSLexicalEnvironment : public JSEnvironmentRecord {
-private:
+protected:
     JSLexicalEnvironment(VM&, Structure*, JSScope*, SymbolTable*);
     
 public:
diff --git a/Source/JavaScriptCore/runtime/JSModuleEnvironment.cpp b/Source/JavaScriptCore/runtime/JSModuleEnvironment.cpp
new file mode 100644 (file)
index 0000000..c701731
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ * 3.  Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "JSModuleEnvironment.h"
+
+#include "Interpreter.h"
+#include "JSCInlines.h"
+#include "JSFunction.h"
+
+using namespace std;
+
+namespace JSC {
+
+const ClassInfo JSModuleEnvironment::s_info = { "JSModuleEnvironment", &Base::s_info, 0, CREATE_METHOD_TABLE(JSModuleEnvironment) };
+
+JSModuleEnvironment* JSModuleEnvironment::create(
+    VM& vm, Structure* structure, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue, JSModuleRecord* moduleRecord)
+{
+    // JSLexicalEnvironment (precisely, JSEnvironmentRecord) has the storage to store the variable slots after the its class storage.
+    // Because the offset of the variable slots are fixed in the JSEnvironmentRecord, inheritting these class and adding new member field is not allowed,
+    // the new member will overlap the variable slots.
+    // To keep the JSModuleEnvironment compatible to the JSLexicalEnvironment but add the new member to store the JSModuleRecord, we additionally allocate
+    // the storage after the variable slots.
+    //
+    // JSLexicalEnvironment:
+    //     [ JSLexicalEnvironment ][ variable slots ]
+    //
+    // JSModuleEnvironment:
+    //     [ JSLexicalEnvironment ][ variable slots ][ additional slots for JSModuleEnvironment ]
+    JSModuleEnvironment* result =
+        new (
+            NotNull,
+            allocateCell<JSModuleEnvironment>(vm.heap, JSModuleEnvironment::allocationSize(symbolTable)))
+        JSModuleEnvironment(vm, structure, currentScope, symbolTable);
+    result->finishCreation(vm, initialValue, moduleRecord);
+    return result;
+}
+
+void JSModuleEnvironment::finishCreation(VM& vm, JSValue initialValue, JSModuleRecord* moduleRecord)
+{
+    Base::finishCreation(vm, initialValue);
+    this->moduleRecordSlot().set(vm, this, moduleRecord);
+}
+
+void JSModuleEnvironment::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell);
+    Base::visitChildren(thisObject, visitor);
+    visitor.appendValues(thisObject->variables(), thisObject->symbolTable()->scopeSize());
+    visitor.append(&thisObject->moduleRecordSlot());
+}
+
+bool JSModuleEnvironment::getOwnPropertySlot(JSObject* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+{
+    JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell);
+    JSModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid()));
+    if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) {
+        // When resolveImport resolves the resolution, the imported module environment must have the binding.
+        JSModuleEnvironment* importedModuleEnvironment = resolution.moduleRecord->moduleEnvironment();
+        PropertySlot redirectSlot(importedModuleEnvironment);
+        bool result = importedModuleEnvironment->methodTable(exec->vm())->getOwnPropertySlot(importedModuleEnvironment, exec, resolution.localName, redirectSlot);
+        ASSERT_UNUSED(result, result);
+        ASSERT(redirectSlot.isValue());
+        JSValue value = redirectSlot.getValue(exec, resolution.localName);
+        ASSERT(!exec->hadException());
+        slot.setValue(thisObject, redirectSlot.attributes(), value);
+        return true;
+    }
+    return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
+}
+
+void JSModuleEnvironment::getOwnNonIndexPropertyNames(JSObject* cell, ExecState* exec, PropertyNameArray& propertyNamesArray, EnumerationMode mode)
+{
+    JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell);
+    if (propertyNamesArray.includeStringProperties()) {
+        for (const auto& pair : thisObject->moduleRecord()->importEntries()) {
+            const JSModuleRecord::ImportEntry& importEntry = pair.value;
+            if (!importEntry.isNamespace(exec->vm()))
+                propertyNamesArray.add(importEntry.localName);
+        }
+    }
+    return Base::getOwnNonIndexPropertyNames(thisObject, exec, propertyNamesArray, mode);
+}
+
+void JSModuleEnvironment::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
+{
+    JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell);
+    // All imported bindings are immutable.
+    JSModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid()));
+    if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) {
+        throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
+        return;
+    }
+    return Base::put(thisObject, exec, propertyName, value, slot);
+}
+
+bool JSModuleEnvironment::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
+{
+    JSModuleEnvironment* thisObject = jsCast<JSModuleEnvironment*>(cell);
+    // All imported bindings are immutable.
+    JSModuleRecord::Resolution resolution = thisObject->moduleRecord()->resolveImport(exec, Identifier::fromUid(exec, propertyName.uid()));
+    if (resolution.type == JSModuleRecord::Resolution::Type::Resolved)
+        return false;
+    return Base::deleteProperty(thisObject, exec, propertyName);
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/JSModuleEnvironment.h b/Source/JavaScriptCore/runtime/JSModuleEnvironment.h
new file mode 100644 (file)
index 0000000..f7efa62
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ * 3.  Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 JSModuleEnvironment_h
+#define JSModuleEnvironment_h
+
+#include "JSLexicalEnvironment.h"
+#include "JSModuleRecord.h"
+
+namespace JSC {
+
+class Register;
+
+class JSModuleEnvironment : public JSLexicalEnvironment {
+    friend class JIT;
+    friend class LLIntOffsetsExtractor;
+public:
+    typedef JSLexicalEnvironment Base;
+    static const unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | OverridesGetPropertyNames;
+
+    static JSModuleEnvironment* create(VM&, Structure*, JSScope*, SymbolTable*, JSValue initialValue, JSModuleRecord*);
+
+    static JSModuleEnvironment* create(VM& vm, JSGlobalObject* globalObject, JSScope* currentScope, SymbolTable* symbolTable, JSValue initialValue, JSModuleRecord* moduleRecord)
+    {
+        Structure* structure = globalObject->moduleEnvironmentStructure();
+        return create(vm, structure, currentScope, symbolTable, initialValue, moduleRecord);
+    }
+
+    DECLARE_INFO;
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject)
+    {
+        return Structure::create(vm, globalObject, jsNull(), TypeInfo(ObjectType, StructureFlags), info());
+    }
+
+    static size_t offsetOfModuleRecord(SymbolTable* symbolTable)
+    {
+        size_t offset = Base::allocationSize(symbolTable);
+        ASSERT(WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSModuleRecord>)>(offset) == offset);
+        return offset;
+    }
+
+    static size_t allocationSize(SymbolTable* symbolTable)
+    {
+        return offsetOfModuleRecord(symbolTable) + sizeof(WriteBarrier<JSModuleRecord>);
+    }
+
+    JSModuleRecord* moduleRecord()
+    {
+        return moduleRecordSlot().get();
+    }
+
+    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
+    static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+    static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
+    static bool deleteProperty(JSCell*, ExecState*, PropertyName);
+
+private:
+    JSModuleEnvironment(VM&, Structure*, JSScope*, SymbolTable*);
+
+    void finishCreation(VM&, JSValue initialValue, JSModuleRecord*);
+
+    WriteBarrierBase<JSModuleRecord>& moduleRecordSlot()
+    {
+        return *bitwise_cast<WriteBarrierBase<JSModuleRecord>*>(bitwise_cast<char*>(this) + offsetOfModuleRecord(symbolTable()));
+    }
+
+    static void visitChildren(JSCell*, SlotVisitor&);
+};
+
+inline JSModuleEnvironment::JSModuleEnvironment(VM& vm, Structure* structure, JSScope* currentScope, SymbolTable* symbolTable)
+    : Base(vm, structure, currentScope, symbolTable)
+{
+}
+
+} // namespace JSC
+
+#endif // JSModuleEnvironment_h
index 7b60637..2cefbe9 100644 (file)
@@ -32,6 +32,7 @@
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
 #include "JSMap.h"
+#include "JSModuleEnvironment.h"
 #include "SlotVisitorInlines.h"
 #include "StructureInlines.h"
 
@@ -60,10 +61,49 @@ 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_moduleProgramExecutable);
     visitor.append(&thisObject->m_dependenciesMap);
 }
 
+void JSModuleRecord::appendRequestedModule(const Identifier& moduleName)
+{
+    m_requestedModules.add(moduleName.impl());
+}
+
+void JSModuleRecord::addStarExportEntry(const Identifier& moduleName)
+{
+    m_starExportEntries.add(moduleName.impl());
+}
+
+void JSModuleRecord::addImportEntry(const ImportEntry& entry)
+{
+    bool isNewEntry = m_importEntries.add(entry.localName.impl(), entry).isNewEntry;
+    ASSERT_UNUSED(isNewEntry, isNewEntry); // This is guaranteed by the parser.
+}
+
+void JSModuleRecord::addExportEntry(const ExportEntry& entry)
+{
+    bool isNewEntry = m_exportEntries.add(entry.exportName.impl(), entry).isNewEntry;
+    ASSERT_UNUSED(isNewEntry, isNewEntry); // This is guaranteed by the parser.
+}
+
+auto JSModuleRecord::tryGetImportEntry(UniquedStringImpl* localName) -> Optional<ImportEntry>
+{
+    const auto iterator = m_importEntries.find(localName);
+    if (iterator == m_importEntries.end())
+        return Nullopt;
+    return Optional<ImportEntry>(iterator->value);
+}
+
+auto JSModuleRecord::tryGetExportEntry(UniquedStringImpl* exportName) -> Optional<ExportEntry>
+{
+    const auto iterator = m_exportEntries.find(exportName);
+    if (iterator == m_exportEntries.end())
+        return Nullopt;
+    return Optional<ExportEntry>(iterator->value);
+}
+
 auto JSModuleRecord::ExportEntry::createLocal(const Identifier& exportName, const Identifier& localName, const VariableEnvironmentEntry& variable) -> ExportEntry
 {
     return ExportEntry { Type::Local, exportName, Identifier(), Identifier(), localName, variable };
@@ -79,6 +119,21 @@ auto JSModuleRecord::ExportEntry::createIndirect(const Identifier& exportName, c
     return ExportEntry { Type::Indirect, exportName, moduleName, importName, Identifier(), VariableEnvironmentEntry() };
 }
 
+auto JSModuleRecord::Resolution::notFound() -> Resolution
+{
+    return Resolution { Type::NotFound, nullptr, Identifier() };
+}
+
+auto JSModuleRecord::Resolution::error() -> Resolution
+{
+    return Resolution { Type::Error, nullptr, Identifier() };
+}
+
+auto JSModuleRecord::Resolution::ambiguous() -> Resolution
+{
+    return Resolution { Type::Ambiguous, nullptr, Identifier() };
+}
+
 static JSValue identifierToJSValue(ExecState* exec, const Identifier& identifier)
 {
     if (identifier.isSymbol())
@@ -93,6 +148,279 @@ JSModuleRecord* JSModuleRecord::hostResolveImportedModule(ExecState* exec, const
     return jsCast<JSModuleRecord*>(pair.get(exec, Identifier::fromString(exec, "value")));
 }
 
+auto JSModuleRecord::resolveImport(ExecState* exec, const Identifier& localName) -> Resolution
+{
+    Optional<ImportEntry> optionalImportEntry = tryGetImportEntry(localName.impl());
+    if (!optionalImportEntry)
+        return Resolution::notFound();
+
+    const ImportEntry& importEntry = *optionalImportEntry;
+    if (importEntry.isNamespace(exec->vm()))
+        return Resolution::notFound();
+
+    JSModuleRecord* importedModule = hostResolveImportedModule(exec, importEntry.moduleRequest);
+    return importedModule->resolveExport(exec, importEntry.importName);
+}
+
+struct ResolveQuery {
+    ResolveQuery(JSModuleRecord* moduleRecord, UniquedStringImpl* exportName)
+        : moduleRecord(moduleRecord)
+        , exportName(exportName)
+    {
+    }
+
+    ResolveQuery(JSModuleRecord* moduleRecord, const Identifier& exportName)
+        : ResolveQuery(moduleRecord, exportName.impl())
+    {
+    }
+
+    enum EmptyValueTag { EmptyValue };
+    ResolveQuery(EmptyValueTag)
+    {
+    }
+
+    enum DeletedValueTag { DeletedValue };
+    ResolveQuery(DeletedValueTag)
+        : moduleRecord(nullptr)
+        , exportName(WTF::HashTableDeletedValue)
+    {
+    }
+
+    bool isEmptyValue() const
+    {
+        return !exportName;
+    }
+
+    bool isDeletedValue() const
+    {
+        return exportName.isHashTableDeletedValue();
+    }
+
+    // The module record is not marked from the GC. But these records are reachable from the JSGlobalObject.
+    // So we don't care the reachability to this record.
+    JSModuleRecord* moduleRecord;
+    RefPtr<UniquedStringImpl> exportName;
+};
+
+struct ResolveQueryHash {
+    static unsigned hash(const ResolveQuery&);
+    static bool equal(const ResolveQuery&, const ResolveQuery&);
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+inline unsigned ResolveQueryHash::hash(const ResolveQuery& query)
+{
+    return WTF::PtrHash<JSModuleRecord*>::hash(query.moduleRecord) + IdentifierRepHash::hash(query.exportName);
+}
+
+inline bool ResolveQueryHash::equal(const ResolveQuery& lhs, const ResolveQuery& rhs)
+{
+    return lhs.moduleRecord == rhs.moduleRecord && lhs.exportName == rhs.exportName;
+}
+
+static JSModuleRecord::Resolution resolveExportLoop(ExecState* exec, const ResolveQuery& root)
+{
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-resolveexport
+    // This function avoids C++ recursion of the naive ResolveExport implementation.
+    // Flatten the recursion to the loop with the task queue and frames.
+    //
+    // 1. pendingTasks
+    //     We enqueue the recursive resolveExport call to this queue to avoid recursive calls in C++.
+    //     The task has 3 types. (1) Query, (2) IndirectFallback and (3) GatherStars.
+    //     (1) Query
+    //         Querying the resolution to the current module.
+    //     (2) IndirectFallback
+    //         Examine the result of the indirect export resolution. Only when the indirect export resolution fails,
+    //         we look into the star exports. (step 5-a-vi).
+    //     (3) GatherStars
+    //         Examine the result of the star export resolutions.
+    //
+    // 2. frames
+    //     When the spec calls the resolveExport recursively, instead we append the frame
+    //     (that holds the result resolution) to the frames and enqueue the task to the pendingTasks.
+    //     The entry in the frames means the *local* resolution result of the specific recursive resolveExport.
+    //
+    // We should maintain the local resolution result instead of holding the global resolution result only.
+    // For example,
+    //
+    //     star
+    // (1) ---> (2) "Resolve"
+    //      |
+    //      |
+    //      +-> (3) "NotFound"
+    //      |
+    //      |       star
+    //      +-> (4) ---> (5) "Resolve" [here]
+    //               |
+    //               |
+    //               +-> (6) "Error"
+    //
+    // Consider the above graph. The numbers represents the modules. Now we are [here].
+    // If we only hold the global resolution result during the resolveExport operation, [here],
+    // we decide the entire result of resolveExport is "Ambiguous", because there are multiple
+    // "Reslove" (in module (2) and (5)). However, this should become "Error" because (6) will
+    // propagate "Error" state to the (4), (4) will become "Error" and then, (1) will become
+    // "Error". We should aggregate the results at the star exports point ((4) and (1)).
+    //
+    // Usually, both "Error" and "Ambiguous" states will throw the syntax error. So except for the content of the
+    // error message, there are no difference. (And if we fix the (6) that raises "Error", next, it will produce
+    // the "Ambiguous" error due to (5). Anyway, user need to fix the both. So which error should be raised at first
+    // doesn't matter so much.
+    //
+    // However, this may become the problem under the module namespace creation.
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-getmodulenamespace
+    // section 15.2.1.18, step 3-d-ii
+    // Here, we distinguish "Ambiguous" and "Error". When "Error" state is produced, we need to throw the propagated error.
+    // But if "Ambiguous" state comes, we just ignore the result.
+    // To follow the requirement strictly, in this implementation, we keep the local resolution result to produce the
+    // correct result under the above complex cases.
+
+    using Resolution = JSModuleRecord::Resolution;
+    typedef WTF::HashSet<ResolveQuery, ResolveQueryHash, WTF::CustomHashTraits<ResolveQuery>> ResolveSet;
+    enum class Type { Query, IndirectFallback, GatherStars };
+    struct Task {
+        ResolveQuery query;
+        Type type;
+    };
+
+    Vector<Task, 8> pendingTasks;
+    ResolveSet resolveSet;
+    HashSet<JSModuleRecord*> starSet;
+    Vector<Resolution, 8> frames;
+
+    frames.append(Resolution::notFound());
+
+    // Call when the query is not resolved in the current module.
+    // It will enqueue the star resolution requests. Return "false" if the error occurs.
+    auto resolveNonLocal = [&](const ResolveQuery& query) -> bool {
+        // http://www.ecma-international.org/ecma-262/6.0/#sec-resolveexport
+        // section 15.2.1.16.3, step 6
+        // If the "default" name is not resolved in the current module, we need to throw an error and stop resolution immediately,
+        // Rationale to this error: A default export cannot be provided by an export *.
+        if (query.exportName == exec->propertyNames().defaultKeyword.impl())
+            return false;
+
+        // step 7, If exportStarSet contains module, then return null.
+        if (!starSet.add(query.moduleRecord).isNewEntry)
+            return true;
+
+        // Enqueue the task to gather the results of the stars.
+        // And append the new Resolution frame to gather the local result of the stars.
+        pendingTasks.append(Task { query, Type::GatherStars });
+        frames.append(Resolution::notFound());
+
+        // Enqueue the tasks in reverse order.
+        for (auto iterator = query.moduleRecord->starExportEntries().rbegin(), end = query.moduleRecord->starExportEntries().rend(); iterator != end; ++iterator) {
+            const RefPtr<UniquedStringImpl>& starModuleName = *iterator;
+            JSModuleRecord* importedModuleRecord = query.moduleRecord->hostResolveImportedModule(exec, Identifier::fromUid(exec, starModuleName.get()));
+            pendingTasks.append(Task { ResolveQuery(importedModuleRecord, query.exportName.get()), Type::Query });
+        }
+        return true;
+    };
+
+    // Return the current resolution value of the top frame.
+    auto currentTop = [&] () -> Resolution& {
+        ASSERT(!frames.isEmpty());
+        return frames.last();
+    };
+
+    // Merge the given resolution to the current resolution value of the top frame.
+    // If there is ambiguity, return "false". When the "false" is returned, we should make the result "ambiguous".
+    auto mergeToCurrentTop = [&] (const Resolution& resolution) -> bool {
+        if (resolution.type == Resolution::Type::NotFound)
+            return true;
+
+        if (currentTop().type == Resolution::Type::NotFound) {
+            currentTop() = resolution;
+            return true;
+        }
+
+        if (currentTop().moduleRecord != resolution.moduleRecord || currentTop().localName != resolution.localName)
+            return false;
+
+        return true;
+    };
+
+    pendingTasks.append(Task { root, Type::Query });
+    while (!pendingTasks.isEmpty()) {
+        const Task task = pendingTasks.takeLast();
+        const ResolveQuery& query = task.query;
+
+        switch (task.type) {
+        case Type::Query: {
+            if (!resolveSet.add(task.query).isNewEntry)
+                continue;
+
+            JSModuleRecord* moduleRecord = query.moduleRecord;
+            const Optional<JSModuleRecord::ExportEntry> optionalExportEntry = moduleRecord->tryGetExportEntry(query.exportName.get());
+            if (!optionalExportEntry) {
+                // If there is no matched exported binding in the current module,
+                // we need to look into the stars.
+                if (!resolveNonLocal(task.query))
+                    return Resolution::error();
+                continue;
+            }
+
+            const JSModuleRecord::ExportEntry& exportEntry = *optionalExportEntry;
+            switch (exportEntry.type) {
+            case JSModuleRecord::ExportEntry::Type::Local:
+            case JSModuleRecord::ExportEntry::Type::Namespace:
+                if (!mergeToCurrentTop(Resolution { Resolution::Type::Resolved, moduleRecord, exportEntry.localName }))
+                    return Resolution::ambiguous();
+                continue;
+
+            case JSModuleRecord::ExportEntry::Type::Indirect: {
+                JSModuleRecord* importedModuleRecord = moduleRecord->hostResolveImportedModule(exec, exportEntry.moduleName);
+
+                // When the imported module does not produce any resolved binding, we need to look into the stars in the *current*
+                // module. To do this, we append the `IndirectFallback` task to the task queue.
+                pendingTasks.append(Task { query, Type::IndirectFallback });
+                // And append the new Resolution frame to check the indirect export will be resolved or not.
+                frames.append(Resolution::notFound());
+                pendingTasks.append(Task { ResolveQuery(importedModuleRecord, exportEntry.importName), Type::Query });
+                continue;
+            }
+            }
+            break;
+        }
+
+        case Type::IndirectFallback: {
+            JSModuleRecord::Resolution resolution = frames.takeLast();
+
+            if (resolution.type == Resolution::Type::NotFound) {
+                // Indirect export entry does not produce any resolved binding.
+                // So we will investigate the stars.
+                if (!resolveNonLocal(task.query))
+                    return Resolution::error();
+                continue;
+            }
+
+            // If indirect export entry produces Resolved, we should merge it to the upper frame.
+            // And do not investigate the stars of the current module.
+            if (!mergeToCurrentTop(resolution))
+                return Resolution::ambiguous();
+            break;
+        }
+
+        case Type::GatherStars: {
+            // Merge the star resolution to the upper frame.
+            JSModuleRecord::Resolution starResolution = frames.takeLast();
+            if (!mergeToCurrentTop(starResolution))
+                return Resolution::ambiguous();
+            break;
+        }
+        }
+    }
+
+    ASSERT(frames.size() == 1);
+    return frames[0];
+}
+
+auto JSModuleRecord::resolveExport(ExecState* exec, const Identifier& exportName) -> Resolution
+{
+    return resolveExportLoop(exec, ResolveQuery(this, exportName.impl()));
+}
+
 void JSModuleRecord::link(ExecState* exec)
 {
     ModuleProgramExecutable* executable = ModuleProgramExecutable::create(exec, sourceCode());
@@ -101,11 +429,120 @@ void JSModuleRecord::link(ExecState* exec)
         return;
     }
     m_moduleProgramExecutable.set(exec->vm(), this, executable);
+    instantiateDeclarations(exec, executable);
 }
 
-JSValue JSModuleRecord::execute(ExecState*)
+void JSModuleRecord::instantiateDeclarations(ExecState* exec, ModuleProgramExecutable* moduleProgramExecutable)
 {
-    return jsUndefined();
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation
+
+    SymbolTable* symbolTable = moduleProgramExecutable->moduleEnvironmentSymbolTable();
+    JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(exec->vm(), exec->lexicalGlobalObject(), exec->lexicalGlobalObject(), symbolTable, jsTDZValue(), this);
+
+    VM& vm = exec->vm();
+
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation
+    // section 15.2.1.16.4 step 9.
+    // Ensure all the indirect exports are correctly resolved to unique bindings.
+    // Even if we avoided duplicate exports in the parser, still ambiguous exports occur due to the star export (`export * from "mod"`).
+    // When we see this type of ambiguity for the indirect exports here, throw a syntax error.
+    for (const auto& pair : m_exportEntries) {
+        const ExportEntry& exportEntry = pair.value;
+        if (exportEntry.type == JSModuleRecord::ExportEntry::Type::Indirect) {
+            Resolution resolution = resolveExport(exec, exportEntry.exportName);
+            switch (resolution.type) {
+            case Resolution::Type::NotFound:
+                throwSyntaxError(exec, makeString("Indirectly exported binding name '", String(exportEntry.exportName.impl()), "' is not found."));
+                return;
+
+            case Resolution::Type::Ambiguous:
+                throwSyntaxError(exec, makeString("Indirectly exported binding name '", String(exportEntry.exportName.impl()), "' cannot be resolved due to ambiguous multiple bindings."));
+                return;
+
+            case Resolution::Type::Error:
+                throwSyntaxError(exec, makeString("Indirectly exported binding name 'default' cannot be resolved by star export entries."));
+                return;
+
+            case Resolution::Type::Resolved:
+                break;
+            }
+        }
+    }
+
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation
+    // section 15.2.1.16.4 step 12.
+    // Instantiate namespace objects and initialize the bindings with them if required.
+    // And ensure that all the imports correctly resolved to unique bindings.
+    for (const auto& pair : m_importEntries) {
+        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
+        } else {
+            Resolution resolution = importedModule->resolveExport(exec, importEntry.importName);
+            switch (resolution.type) {
+            case Resolution::Type::NotFound:
+                throwSyntaxError(exec, makeString("Importing binding name '", String(importEntry.importName.impl()), "' is not found."));
+                return;
+
+            case Resolution::Type::Ambiguous:
+                throwSyntaxError(exec, makeString("Importing binding name '", String(importEntry.importName.impl()), "' cannot be resolved due to ambiguous multiple bindings."));
+                return;
+
+            case Resolution::Type::Error:
+                throwSyntaxError(exec, makeString("Importing binding name 'default' cannot be resolved by star export entries."));
+                return;
+
+            case Resolution::Type::Resolved:
+                break;
+            }
+        }
+    }
+
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation
+    // section 15.2.1.16.4 step 14.
+    // Module environment contains the heap allocated "var", "function", "let", "const", and "class".
+    // When creating the environment, we initialized all the slots with empty, it's ok for lexical values.
+    // But for "var" and "function", we should initialize it with undefined. They are contained in the declared variables.
+    for (const auto& variable : m_declaredVariables) {
+        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);
+    }
+
+    // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation
+    // section 15.2.1.16.4 step 16-a-iv.
+    // Initialize heap allocated function declarations.
+    // They can be called before the body of the module is executed under circular dependencies.
+    UnlinkedModuleProgramCodeBlock* unlinkedCodeBlock = moduleProgramExecutable->unlinkedModuleProgramCodeBlock();
+    for (size_t i = 0, numberOfFunctions = unlinkedCodeBlock->numberOfFunctionDecls(); i < numberOfFunctions; ++i) {
+        UnlinkedFunctionExecutable* unlinkedFunctionExecutable = unlinkedCodeBlock->functionDecl(i);
+        SymbolTableEntry entry = symbolTable->get(unlinkedFunctionExecutable->name().impl());
+        VarOffset offset = entry.varOffset();
+        if (!offset.isStack()) {
+            ASSERT(!unlinkedFunctionExecutable->name().isEmpty());
+            if (vm.typeProfiler() || vm.controlFlowProfiler()) {
+                vm.functionHasExecutedCache()->insertUnexecutedRange(moduleProgramExecutable->sourceID(),
+                    unlinkedFunctionExecutable->typeProfilingStartOffset(),
+                    unlinkedFunctionExecutable->typeProfilingEndOffset());
+            }
+            JSFunction* function = JSFunction::create(vm, unlinkedFunctionExecutable->link(vm, moduleProgramExecutable->source()), moduleEnvironment);
+            symbolTablePut(moduleEnvironment, exec, unlinkedFunctionExecutable->name(), function, false, true);
+        }
+    }
+
+    m_moduleEnvironment.set(vm, this, moduleEnvironment);
+}
+
+JSValue JSModuleRecord::execute(ExecState* exec)
+{
+    if (!m_moduleProgramExecutable)
+        return jsUndefined();
+    JSValue result = exec->interpreter()->execute(m_moduleProgramExecutable.get(), exec, m_moduleEnvironment.get());
+    m_moduleProgramExecutable.clear();
+    return result;
 }
 
 static String printableName(const RefPtr<UniquedStringImpl>& uid)
@@ -129,23 +566,25 @@ void JSModuleRecord::dump()
         dataLog("      module(", printableName(moduleName), ")\n");
 
     dataLog("    Import: ", m_importEntries.size(), " entries\n");
-    for (const auto& pair : m_importEntries)
-        dataLog("      import(", printableName(pair.value.importName), "), local(", printableName(pair.value.localName), "), module(", printableName(pair.value.moduleRequest), ")\n");
+    for (const auto& pair : m_importEntries) {
+        const ImportEntry& importEntry = pair.value;
+        dataLog("      import(", printableName(importEntry.importName), "), local(", printableName(importEntry.localName), "), module(", printableName(importEntry.moduleRequest), ")\n");
+    }
 
     dataLog("    Export: ", m_exportEntries.size(), " entries\n");
     for (const auto& pair : m_exportEntries) {
-        const ExportEntry& entry = pair.value;
-        switch (entry.type) {
+        const ExportEntry& exportEntry = pair.value;
+        switch (exportEntry.type) {
         case ExportEntry::Type::Local:
-            dataLog("      [Local] ", "export(", printableName(entry.exportName), "), local(", printableName(entry.localName), ")\n");
+            dataLog("      [Local] ", "export(", printableName(exportEntry.exportName), "), local(", printableName(exportEntry.localName), ")\n");
             break;
 
         case ExportEntry::Type::Namespace:
-            dataLog("      [Namespace] ", "export(", printableName(entry.exportName), "), module(", printableName(entry.moduleName), ")\n");
+            dataLog("      [Namespace] ", "export(", printableName(exportEntry.exportName), "), module(", printableName(exportEntry.moduleName), ")\n");
             break;
 
         case ExportEntry::Type::Indirect:
-            dataLog("      [Indirect] ", "export(", printableName(entry.exportName), "), import(", printableName(entry.importName), "), module(", printableName(entry.moduleName), ")\n");
+            dataLog("      [Indirect] ", "export(", printableName(exportEntry.exportName), "), import(", printableName(exportEntry.importName), "), module(", printableName(exportEntry.moduleName), ")\n");
             break;
         }
     }
index 81da396..08521b4 100644 (file)
@@ -32,6 +32,7 @@
 #include "VariableEnvironment.h"
 #include <wtf/HashMap.h>
 #include <wtf/ListHashSet.h>
+#include <wtf/Optional.h>
 
 namespace JSC {
 
@@ -78,8 +79,8 @@ public:
     };
 
     typedef WTF::ListHashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> OrderedIdentifierSet;
-    typedef HashMap<RefPtr<UniquedStringImpl>, ImportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ImportMap;
-    typedef HashMap<RefPtr<UniquedStringImpl>, ExportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ExportMap;
+    typedef HashMap<RefPtr<UniquedStringImpl>, ImportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ImportEntries;
+    typedef HashMap<RefPtr<UniquedStringImpl>, ExportEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>> ExportEntries;
 
     DECLARE_EXPORT_INFO;
 
@@ -100,25 +101,49 @@ public:
     void addImportEntry(const ImportEntry&);
     void addExportEntry(const ExportEntry&);
 
-    const ImportEntry& lookUpImportEntry(const RefPtr<UniquedStringImpl>& localName);
+    Optional<ImportEntry> tryGetImportEntry(UniquedStringImpl* localName);
+    Optional<ExportEntry> tryGetExportEntry(UniquedStringImpl* exportName);
 
     const SourceCode& sourceCode() const { return m_sourceCode; }
     const Identifier& moduleKey() const { return m_moduleKey; }
     const OrderedIdentifierSet& requestedModules() const { return m_requestedModules; }
-    const ExportMap& exportEntries() const { return m_exportEntries; }
-    const ImportMap& importEntries() const { return m_importEntries; }
+    const ExportEntries& exportEntries() const { return m_exportEntries; }
+    const ImportEntries& importEntries() const { return m_importEntries; }
+    const OrderedIdentifierSet& starExportEntries() const { return m_starExportEntries; }
 
     const VariableEnvironment& declaredVariables() const { return m_declaredVariables; }
     const VariableEnvironment& lexicalVariables() const { return m_lexicalVariables; }
 
     void dump();
 
+    JSModuleEnvironment* moduleEnvironment()
+    {
+        ASSERT(m_moduleEnvironment);
+        return m_moduleEnvironment.get();
+    }
 
     void link(ExecState*);
     JSValue execute(ExecState*);
 
     ModuleProgramExecutable* moduleProgramExecutable() const { return m_moduleProgramExecutable.get(); }
 
+    struct Resolution {
+        enum class Type { Resolved, NotFound, Ambiguous, Error };
+
+        static Resolution notFound();
+        static Resolution error();
+        static Resolution ambiguous();
+
+        Type type;
+        JSModuleRecord* moduleRecord;
+        Identifier localName;
+    };
+
+    Resolution resolveExport(ExecState*, const Identifier& exportName);
+    Resolution resolveImport(ExecState*, const Identifier& localName);
+
+    JSModuleRecord* hostResolveImportedModule(ExecState*, const Identifier& moduleName);
+
 private:
     JSModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey, const SourceCode& sourceCode, const VariableEnvironment& declaredVariables, const VariableEnvironment& lexicalVariables)
         : Base(vm, structure)
@@ -134,7 +159,7 @@ private:
     static void visitChildren(JSCell*, SlotVisitor&);
     static void destroy(JSCell*);
 
-    JSModuleRecord* hostResolveImportedModule(ExecState*, const Identifier& moduleName);
+    void instantiateDeclarations(ExecState*, ModuleProgramExecutable*);
 
     // The loader resolves the given module name to the module key. The module key is the unique value to represent this module.
     Identifier m_moduleKey;
@@ -144,46 +169,37 @@ private:
     VariableEnvironment m_declaredVariables;
     VariableEnvironment m_lexicalVariables;
 
+    // Currently, we don't keep the occurrence order of the import / export entries.
+    // So, we does not guarantee the order of the errors.
+    // e.g. The import declaration that occurr later than the another import declaration may
+    //      throw the error even if the former import declaration also has the invalid content.
+    //
+    //      import ... // (1) this has some invalid content.
+    //      import ... // (2) this also has some invalid content.
+    //
+    //      In the above case, (2) may throw the error earlier than (1)
+    //
+    // But, in the all cases, we will throw the syntax error. So except for the content of the syntax error,
+    // there are no difference.
+
     // Map localName -> ImportEntry.
-    ImportMap m_importEntries;
+    ImportEntries m_importEntries;
 
     // Map exportName -> ExportEntry.
-    ExportMap m_exportEntries;
+    ExportEntries m_exportEntries;
 
-    IdentifierSet m_starExportEntries;
+    // Save the occurrence order since resolveExport requires it.
+    OrderedIdentifierSet m_starExportEntries;
 
     // Save the occurrence order since the module loader loads and runs the modules in this order.
     // http://www.ecma-international.org/ecma-262/6.0/#sec-moduleevaluation
     OrderedIdentifierSet m_requestedModules;
 
-    WriteBarrier<ModuleProgramExecutable> m_moduleProgramExecutable;
     WriteBarrier<JSMap> m_dependenciesMap;
-};
 
-inline void JSModuleRecord::appendRequestedModule(const Identifier& moduleName)
-{
-    m_requestedModules.add(moduleName.impl());
-}
-
-inline void JSModuleRecord::addImportEntry(const ImportEntry& entry)
-{
-    m_importEntries.add(entry.localName.impl(), entry);
-}
-
-inline void JSModuleRecord::addExportEntry(const ExportEntry& entry)
-{
-    m_exportEntries.add(entry.exportName.impl(), entry);
-}
-
-inline void JSModuleRecord::addStarExportEntry(const Identifier& moduleName)
-{
-    m_starExportEntries.add(moduleName.impl());
-}
-
-inline auto JSModuleRecord::lookUpImportEntry(const RefPtr<UniquedStringImpl>& localName) -> const ImportEntry&
-{
-    return (*m_importEntries.find(localName)).value;
-}
+    WriteBarrier<ModuleProgramExecutable> m_moduleProgramExecutable;
+    WriteBarrier<JSModuleEnvironment> m_moduleEnvironment;
+};
 
 } // namespace JSC
 
index 128a602..1403616 100644 (file)
@@ -28,6 +28,8 @@
 
 #include "JSGlobalObject.h"
 #include "JSLexicalEnvironment.h"
+#include "JSModuleEnvironment.h"
+#include "JSModuleRecord.h"
 #include "JSWithScope.h"
 #include "JSCInlines.h"
 
@@ -65,6 +67,19 @@ static inline bool abstractAccess(ExecState* exec, JSScope* scope, const Identif
             return true;
         }
 
+        if (JSModuleEnvironment* moduleEnvironment = jsDynamicCast<JSModuleEnvironment*>(scope)) {
+            JSModuleRecord* moduleRecord = moduleEnvironment->moduleRecord();
+            JSModuleRecord::Resolution resolution = moduleRecord->resolveImport(exec, ident);
+            if (resolution.type == JSModuleRecord::Resolution::Type::Resolved) {
+                JSModuleRecord* importedRecord = resolution.moduleRecord;
+                JSModuleEnvironment* importedEnvironment = importedRecord->moduleEnvironment();
+                SymbolTableEntry entry = importedEnvironment->symbolTable()->get(resolution.localName.impl());
+                ASSERT(!entry.isNull());
+                op = ResolveOp(makeType(ModuleVar, needsVarInjectionChecks), depth, 0, importedEnvironment, entry.watchpointSet(), entry.scopeOffset().offset(), resolution.localName.impl());
+                return true;
+            }
+        }
+
         if (lexicalEnvironment->symbolTable()->usesNonStrictEval())
             needsVarInjectionChecks = true;
         return false;
@@ -210,6 +225,13 @@ void JSScope::collectVariablesUnderTDZ(JSScope* scope, VariableEnvironment& resu
     for (; scope; scope = scope->next()) {
         if (!scope->isLexicalScope() && !scope->isGlobalLexicalEnvironment())
             continue;
+
+        if (scope->isModuleScope()) {
+            JSModuleRecord* moduleRecord = jsCast<JSModuleEnvironment*>(scope)->moduleRecord();
+            for (const auto& pair : moduleRecord->importEntries())
+                result.add(pair.key);
+        }
+
         SymbolTable* symbolTable = jsCast<JSSymbolTableObject*>(scope)->symbolTable();
         ASSERT(symbolTable->scopeType() == SymbolTable::ScopeType::LexicalScope || symbolTable->scopeType() == SymbolTable::ScopeType::GlobalLexicalScope);
         ConcurrentJITLocker locker(symbolTable->m_lock);
@@ -238,6 +260,11 @@ bool JSScope::isLexicalScope()
     return isScopeType<JSLexicalEnvironment, SymbolTable::ScopeType::LexicalScope>(this);
 }
 
+bool JSScope::isModuleScope()
+{
+    return isScopeType<JSModuleEnvironment, SymbolTable::ScopeType::LexicalScope>(this);
+}
+
 bool JSScope::isCatchScope()
 {
     return isScopeType<JSLexicalEnvironment, SymbolTable::ScopeType::CatchScope>(this);
index 06d9ef1..5594828 100644 (file)
@@ -57,6 +57,7 @@ public:
 
     bool isVarScope();
     bool isLexicalScope();
+    bool isModuleScope();
     bool isGlobalLexicalEnvironment();
     bool isCatchScope();
     bool isFunctionNameScopeObject();
index 28e0bad..6e6f9f9 100644 (file)
@@ -35,6 +35,7 @@
 #include "JSInternalPromise.h"
 #include "JSInternalPromiseDeferred.h"
 #include "JSMap.h"
+#include "JSModuleEnvironment.h"
 #include "JSModuleRecord.h"
 #include "ModuleAnalyzer.h"
 #include "Nodes.h"