Browse Source

Merge remote-tracking branch 'origin/master' into linux

Jianchun Xu 9 years ago
parent
commit
a0f9cff25d
97 changed files with 2684 additions and 572 deletions
  1. 4 0
      Build/Chakra.Build.props
  2. 2 1
      Build/scripts/init_build.ps1
  3. 0 51
      CODE_OF_CONDUCT.md
  4. 3 1
      README.md
  5. 6 0
      bin/ChakraCore/ChakraCore.def
  6. 215 0
      bin/NativeTests/CodexTests.cpp
  7. 1 0
      bin/NativeTests/NativeTests.vcxproj
  8. 5 1
      bin/ch/ChakraRtInterface.cpp
  9. 20 4
      bin/ch/ChakraRtInterface.h
  10. 210 27
      bin/ch/WScriptJsrt.cpp
  11. 26 1
      bin/ch/WScriptJsrt.h
  12. 7 0
      bin/ch/ch.cpp
  13. 4 2
      bin/ch/stdafx.h
  14. 51 4
      lib/Backend/BackwardPass.cpp
  15. 1 0
      lib/Backend/BackwardPass.h
  16. 6 3
      lib/Backend/GlobOpt.cpp
  17. 26 12
      lib/Backend/InterpreterThunkEmitter.cpp
  18. 3 3
      lib/Backend/InterpreterThunkEmitter.h
  19. 64 5
      lib/Common/Codex/Utf8Codex.cpp
  20. 47 0
      lib/Common/Codex/Utf8Codex.h
  21. 6 2
      lib/Common/Codex/Utf8Helper.h
  22. 1 1
      lib/Common/Common/Jobs.cpp
  23. 0 1
      lib/Common/Core/CodexAssert.cpp
  24. 5 1
      lib/Common/DataStructures/SparseBitVector.h
  25. 20 23
      lib/Jsrt/ChakraCommon.h
  26. 138 0
      lib/Jsrt/ChakraCore.h
  27. 4 3
      lib/Jsrt/Core/Chakra.Jsrt.Core.vcxproj
  28. 38 0
      lib/Jsrt/Core/JsrtContextCore.cpp
  29. 14 11
      lib/Jsrt/Core/JsrtContextCore.h
  30. 211 0
      lib/Jsrt/Core/JsrtCore.cpp
  31. 0 9
      lib/Jsrt/Jsrt.cpp
  32. 0 2
      lib/Jsrt/JsrtCommonExports.inc
  33. 64 0
      lib/Jsrt/JsrtInternal.h
  34. 1 1
      lib/Jsrt/JsrtSourceHolder.cpp
  35. 6 7
      lib/Runtime/Base/ScriptContext.cpp
  36. 3 1
      lib/Runtime/ByteCode/ByteCodeEmitter.cpp
  37. 2 2
      lib/Runtime/Debug/TTActionEvents.cpp
  38. 1 3
      lib/Runtime/Language/JavascriptConversion.cpp
  39. 1 1
      lib/Runtime/Language/JavascriptConversion.h
  40. 11 37
      lib/Runtime/Language/JavascriptOperators.cpp
  41. 5 5
      lib/Runtime/Language/JavascriptOperators.h
  42. 24 7
      lib/Runtime/Language/SourceTextModuleRecord.cpp
  43. 18 0
      lib/Runtime/Language/SourceTextModuleRecord.h
  44. 26 3
      lib/Runtime/Library/ArrayBuffer.cpp
  45. 1 11
      lib/Runtime/Library/JSON.cpp
  46. 58 34
      lib/Runtime/Library/JavascriptArray.cpp
  47. 1 1
      lib/Runtime/Library/JavascriptArray.h
  48. 1 0
      lib/Runtime/Library/JavascriptBuiltInFunctionList.h
  49. 42 96
      lib/Runtime/Library/JavascriptExternalFunction.cpp
  50. 5 4
      lib/Runtime/Library/JavascriptExternalFunction.h
  51. 31 11
      lib/Runtime/Library/JavascriptLibrary.cpp
  52. 3 1
      lib/Runtime/Library/JavascriptLibrary.h
  53. 15 53
      lib/Runtime/Library/JavascriptObject.cpp
  54. 6 6
      lib/Runtime/Library/JavascriptObject.h
  55. 7 29
      lib/Runtime/Library/JavascriptProxy.cpp
  56. 1 1
      lib/Runtime/Library/JavascriptProxy.h
  57. 14 1
      lib/Runtime/Types/DictionaryTypeHandler.cpp
  58. 2 0
      lib/Runtime/Types/DictionaryTypeHandler.h
  59. 1 0
      lib/Runtime/Types/NullTypeHandler.cpp
  60. 0 8
      lib/Runtime/Types/RecyclableObject.cpp
  61. 0 1
      lib/Runtime/Types/RecyclableObject.h
  62. 239 0
      test/Array/Array_TypeConfusion_bugs.js
  63. 19 0
      test/Array/bug8159763.js
  64. 20 35
      test/Array/concat2.baseline
  65. 5 3
      test/Array/concat2.js
  66. 15 2
      test/Array/rlexe.xml
  67. 27 0
      test/Closures/delaycapture-loopbody.js
  68. 7 0
      test/Closures/rlexe.xml
  69. 4 0
      test/Function/StackArgsWithFormals.baseline
  70. 20 0
      test/Function/StackArgsWithFormals.js
  71. 1 1
      test/Function/rlexe.xml
  72. 14 0
      test/es6/ModuleCircularBar.js
  73. 15 0
      test/es6/ModuleCircularFoo.js
  74. 57 0
      test/es6/ModuleComplexExports.js
  75. 11 0
      test/es6/ModuleComplexReexports.js
  76. 7 0
      test/es6/ModuleDefaultExport1.js
  77. 6 0
      test/es6/ModuleDefaultExport2.js
  78. 8 0
      test/es6/ModuleDefaultExport3.js
  79. 8 0
      test/es6/ModuleDefaultExport4.js
  80. 7 0
      test/es6/ModuleDefaultReexport.js
  81. 6 0
      test/es6/ModuleReexportDefault.js
  82. 6 0
      test/es6/ModuleSimpleExport.js
  83. 6 0
      test/es6/ModuleSimpleReexport.js
  84. 7 0
      test/es6/ValidExportDefaultStatement1.js
  85. 6 0
      test/es6/ValidExportDefaultStatement2.js
  86. 50 0
      test/es6/ValidExportStatements.js
  87. 25 0
      test/es6/ValidImportStatements.js
  88. 12 0
      test/es6/ValidReExportStatements.js
  89. 3 37
      test/es6/es6toLength.js
  90. 6 0
      test/es6/exportmodule.js
  91. 296 0
      test/es6/module-functionality.js
  92. 149 0
      test/es6/module-syntax.js
  93. 42 0
      test/es6/module-syntax1.js
  94. 5 0
      test/es6/moduletest1.js
  95. 6 0
      test/es6/passmodule.js
  96. 43 1
      test/es6/rlexe.xml
  97. 38 1
      test/runtests.cmd

+ 4 - 0
Build/Chakra.Build.props

@@ -24,6 +24,10 @@
         %(PreprocessorDefinitions);
         DISABLE_JIT=1
       </PreprocessorDefinitions>
+      <PreprocessorDefinitions Condition="'$(ENABLE_CODECOVERAGE)'=='true'">
+        %(PreprocessorDefinitions);
+        BYTECODE_TESTING=1
+      </PreprocessorDefinitions>
       <!-- REVIEW: These are warning are introduced when moving to VS2015 tools, may want to clean these up -->
       <DisableSpecificWarnings>
         %(DisableSpecificWarnings);

+ 2 - 1
Build/scripts/init_build.ps1

@@ -112,6 +112,7 @@ if ($BranchPath.StartsWith("build")) {
 $BuildIdentifier = "${buildPushIdString}_${PushDate}_${Username}_${CommitHash}"
 $ComputedDropPathSegment = "${BranchPath}\${YearAndMonth}${BuildIdentifier}"
 $BinariesDirectory = "${Env:BUILD_SOURCESDIRECTORY}\Build\VcBuild"
+$ObjectDirectory = "${BinariesDirectory}\obj\${BuildPlatform}_${BuildConfiguration}"
 
 # Create a sentinel file for each build flavor to track whether the build is complete.
 # * ${arch}_${flavor}.incomplete       # will be deleted when the build of this flavor completes
@@ -178,7 +179,7 @@ set TF_BUILD_SOURCEGETVERSION=LG:${branch}:${CommitHash}
 set TF_BUILD_DROPLOCATION=${BinariesDirectory}
 
 set TF_BUILD_SOURCESDIRECTORY=${Env:BUILD_SOURCESDIRECTORY}
-REM set TF_BUILD_BUILDDIRECTORY=${Env:AGENT_BUILDDIRECTORY}\b  # note: inferred location works
+set TF_BUILD_BUILDDIRECTORY=${ObjectDirectory}
 set TF_BUILD_BINARIESDIRECTORY=${BinariesDirectory}
 
 set TF_BUILD_BUILDDEFINITIONNAME=${Env:BUILD_DEFINITIONNAME}

+ 0 - 51
CODE_OF_CONDUCT.md

@@ -1,51 +0,0 @@
-# Contributor Code of Conduct
-
-As contributors and maintainers of this project, and in the interest of
-fostering an open and welcoming community, we pledge to respect all people who
-contribute through reporting issues, posting feature requests, updating
-documentation, submitting pull requests or patches, and other activities.
-
-We are committed to making participation in this project a harassment-free
-experience for everyone, regardless of level of experience, gender, gender
-identity and expression, sexual orientation, disability, personal appearance,
-body size, race, ethnicity, age, religion, or nationality. We will
-prioritize marginalized people's safety over privileged people's comfort.
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery
-* Personal attacks
-* Trolling or insulting/derogatory comments
-* Public or private harassment
-* Publishing other's private information, such as physical or electronic
-  addresses, without explicit permission
-* Other unethical or unprofessional conduct
-
-Project maintainers have the right _and responsibility_ to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
-
-By adopting this Code of Conduct, project maintainers commit themselves to
-fairly and consistently applying these principles to every aspect of managing
-this project. Project maintainers who do not follow or enforce the Code of
-Conduct may be permanently removed from the project team.
-
-This Code of Conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community.
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting us at [[email protected]][email]. All
-complaints will be reviewed and investigated and will result in a response that
-is deemed necessary and appropriate to the circumstances. Maintainers are
-obligated to maintain confidentiality with regard to the reporter of an
-incident.
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage] version 1.3.0
-and [Open Code of Conduct][opencoc].
-
-[email]: mailto:[email protected]
-[homepage]: http://contributor-covenant.org
-[version]: http://contributor-covenant.org/version/1/3/0/
-[opencoc]: http://todogroup.org/opencodeofconduct/

+ 3 - 1
README.md

@@ -97,7 +97,9 @@ Contributions to ChakraCore are welcome.  Here is how you can contribute to Chak
 * [Submit pull requests](https://github.com/Microsoft/ChakraCore/pulls) for bug fixes and features and discuss existing proposals
 * Chat about [@ChakraCore](https://twitter.com/ChakraCore) on Twitter
 
-Please refer to [Contribution guidelines](CONTRIBUTING.md) and the [Code of Conduct](CODE_OF_CONDUCT.md) for more details.
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [[email protected]](mailto:[email protected]) with any additional questions or comments.
+
+Please refer to [Contribution guidelines](CONTRIBUTING.md) for more details.
 
 ### License
 

+ 6 - 0
bin/ChakraCore/ChakraCore.def

@@ -38,3 +38,9 @@ JsTTDNotifyYield
 JsTTDPrepContextsForTopLevelEventMove
 JsTTDMoveToTopLevelEvent
 JsTTDReplayExecution
+
+JsInitializeModuleRecord
+JsParseModuleSource
+JsModuleEvaluation
+JsSetModuleHostInfo
+JsGetModuleHostInfo

+ 215 - 0
bin/NativeTests/CodexTests.cpp

@@ -0,0 +1,215 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#include "stdafx.h"
+#include "catch.hpp"
+#include <process.h>
+#include "Codex\Utf8Codex.h"
+
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:6387) // suppressing preFAST which raises warning for passing null to the JsRT APIs
+#pragma warning(disable:6262) // CATCH is using stack variables to report errors, suppressing the preFAST warning.
+
+namespace CodexTest
+{
+    ///
+    /// The following test verifies that for invalid characters, we replace them
+    /// with the unicode replacement character
+    ///
+
+    // Verify single utf8-encoded codepoint
+    void CheckIsUnicodeReplacementChar(const utf8char_t* encodedBuffer)
+    {
+        CHECK(encodedBuffer[0] == 0xEF);
+        CHECK(encodedBuffer[1] == 0xBF);
+        CHECK(encodedBuffer[2] == 0xBD);
+    }
+
+    //
+    // Following test cases are based on the Utf-8 decoder tests 
+    // suggested by Markus Kuhn at https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+    //
+    TEST_CASE("CodexTest_EncodeTrueUtf8_SingleSurrogates", "[CodexTest]")
+    {
+        const charcount_t charCount = 1;
+        utf8char_t encodedBuffer[(charCount + 1) * 3]; // +1 since the buffer will be null-terminated
+
+        char16 testValues[] = { 0xD800, 0xDB7F, 0xDB80, 0xDBFF, 0xDC00, 0xDF80, 0xDFFF };
+        const int numTestCases = _countof(testValues);
+
+        for (int i = 0; i < numTestCases; i++)
+        {
+            size_t numEncodedBytes = utf8::EncodeTrueUtf8IntoAndNullTerminate(encodedBuffer, &testValues[i], charCount);
+            CHECK(numEncodedBytes == 3);
+            CheckIsUnicodeReplacementChar(encodedBuffer);
+        }
+    }
+
+    // 
+    // Test encoding of given utf16-encoded strings into another encoding
+    //
+    // In the expected encoded string, extra bytes are represented as 0
+    //
+
+    template <typename TTestCase, typename TEncodingFunc>
+    void RunUtf8EncodingTestCase(const TTestCase &testCases, const TEncodingFunc func)
+    {
+        const int numTestCases = _countof(testCases);
+        const charcount_t charCount = _countof(testCases[0].surrogatePair);
+        const charcount_t maxEncodedByteCount = _countof(testCases[0].utf8Encoding);
+        utf8char_t encodedBuffer[maxEncodedByteCount + 1]; // +1 in case a null-terminating func is passed in
+
+        for (int i = 0; i < numTestCases; i++)
+        {
+            size_t numEncodedBytes = func(encodedBuffer, testCases[i].surrogatePair, charCount);
+            CHECK(numEncodedBytes <= maxEncodedByteCount);
+            for (size_t j = 0; j < numEncodedBytes; j++)
+            {
+                CHECK(encodedBuffer[j] == testCases[i].utf8Encoding[j]);
+            }
+
+            // Check and make sure there were no other bytes expected in the encoded string
+            if (numEncodedBytes < maxEncodedByteCount)
+            {
+                for (size_t j = numEncodedBytes; j < maxEncodedByteCount; j++)
+                {
+                    CHECK(testCases[i].utf8Encoding[j] == 0);
+                }
+            }
+        }
+    }
+
+    TEST_CASE("CodexTest_EncodeCesu_PairedSurrogates", "[CodexTest]")
+    {
+        // Each of these test cases verifies the encoding 
+        // of a single surrogate pair into a 6 byte CESU string
+        // Each surrogate-pair unit is encoded seperately into utf8
+        struct TestCase
+        {
+            char16     surrogatePair[2];
+            utf8char_t utf8Encoding[6];
+        };
+
+        TestCase testCases[] = {
+            { { 0xD800, 0xDC00 }, { 0xED, 0xA0, 0x80, 0xED, 0xB0, 0x80 } }, //  U+010000 LINEAR B SYLLABLE B008 A character
+            { { 0xD800, 0xDFFF }, { 0xED, 0xA0, 0x80, 0xED, 0xBF, 0xBF } }, //  U+0103FF
+            { { 0xDB7F, 0xDC00 }, { 0xED, 0xAD, 0xBF, 0xED, 0xB0, 0x80 } }, //  U+0EFC00
+            { { 0xDB7F, 0xDFFF }, { 0xED, 0xAD, 0xBF, 0xED, 0xBF, 0xBF } }, //  U+0EFFFF
+            { { 0xDB80, 0xDC00 }, { 0xED, 0xAE, 0x80, 0xED, 0xB0, 0x80 } }, //  U+0F0000 Plane 15 Private Use First
+            { { 0xDB80, 0xDFFF }, { 0xED, 0xAE, 0x80, 0xED, 0xBF, 0xBF } }, //  U+0F03FF
+            { { 0xDBFF, 0xDC00 }, { 0xED, 0xAF, 0xBF, 0xED, 0xB0, 0x80 } }, //  U+10FC00
+            { { 0xDBFF, 0xDFFF }, { 0xED, 0xAF, 0xBF, 0xED, 0xBF, 0xBF } }  //  U+10FFFF
+        };
+
+        RunUtf8EncodingTestCase(testCases, static_cast<size_t (*)(utf8char_t*, const char16*, charcount_t)>(utf8::EncodeInto));
+    }
+
+    TEST_CASE("CodexTest_EncodeUtf8_PairedSurrogates", "[CodexTest]")
+    {
+        // Each of these test cases verifies the encoding 
+        // of a single surrogate pair into a 4 byte utf8 string
+        // Each surrogate-pair unit is decoded to its original codepoint
+        // and then encoded into utf8
+        struct TestCase
+        {
+            char16     surrogatePair[2];
+            utf8char_t utf8Encoding[4];
+        };
+
+        TestCase testCases[] = {
+            { { 0xD800, 0xDC00 }, { 0xF0, 0x90, 0x80, 0x80 } }, //  U+010000 LINEAR B SYLLABLE B008 A character
+            { { 0xD800, 0xDFFF }, { 0xF0, 0x90, 0x8F, 0xBF } }, //  U+0103FF
+            { { 0xDB7F, 0xDC00 }, { 0xF3, 0xAF, 0xB0, 0x80 } }, //  U+0EFC00
+            { { 0xDB7F, 0xDFFF }, { 0xF3, 0xAF, 0xBF, 0xBF } }, //  U+0EFFFF
+            { { 0xDB80, 0xDC00 }, { 0xF3, 0xB0, 0x80, 0x80 } }, //  U+0F0000 Plane 15 Private Use First
+            { { 0xDB80, 0xDFFF }, { 0xF3, 0xB0, 0x8F, 0xBF } }, //  U+0F03FF
+            { { 0xDBFF, 0xDC00 }, { 0xF4, 0x8F, 0xB0, 0x80 } }, //  U+10FC00
+            { { 0xDBFF, 0xDFFF }, { 0xF4, 0x8F, 0xBF, 0xBF } }  //  U+10FFFF
+        };
+
+        RunUtf8EncodingTestCase(testCases, utf8::EncodeTrueUtf8IntoAndNullTerminate);
+    }
+
+    TEST_CASE("CodexTest_EncodeUtf8_NonCharacters", "[CodexTest]")
+    {
+        // Each of these test cases verifies the encoding 
+        // of certain problematic codepoints that do not represent
+        // characters
+        struct TestCase
+        {
+            char16     surrogatePair[1];
+            utf8char_t utf8Encoding[3];
+        };
+
+        TestCase testCases[] = {
+            { { 0xFFFE }, { 0xEF, 0xBF, 0xBE } }, //  U+FFFE
+            { { 0xFFFF }, { 0xEF, 0xBF, 0xBF } }  //  U+FFFF
+        };
+
+        RunUtf8EncodingTestCase(testCases, utf8::EncodeTrueUtf8IntoAndNullTerminate);
+    }
+
+    TEST_CASE("CodexTest_EncodeUtf8_BoundaryChars", "[CodexTest]")
+    {
+        // Each of these test cases verifies the encoding 
+        // of boundary conditions
+        struct SingleChar16TestCase
+        {
+            char16     surrogatePair[1];
+            utf8char_t utf8Encoding[3];
+        };
+
+        SingleChar16TestCase testCases[] = {
+            { { 0xD7FF }, { 0xED, 0x9F, 0xBF } }, //  U+D7FF
+            { { 0xE000 }, { 0xEE, 0x80, 0x80 } }, //  U+E000
+            { { 0xFFFD }, { 0xEF, 0xBF, 0xBD } }  //  U+FFFD
+        };
+
+        struct TwoChar16TestCase
+        {
+            char16     surrogatePair[2];
+            utf8char_t utf8Encoding[4];
+        };
+
+        TwoChar16TestCase testCases2[] = {
+            { { 0xDBFF, 0xDFFF }, { 0xF4, 0x8F, 0xBF, 0xBF } } //  U+10FFFF
+        };
+
+        RunUtf8EncodingTestCase(testCases, utf8::EncodeTrueUtf8IntoAndNullTerminate);
+        RunUtf8EncodingTestCase(testCases2, utf8::EncodeTrueUtf8IntoAndNullTerminate);
+    }
+
+    TEST_CASE("CodexTest_EncodeUtf8_SimpleCharacters", "[CodexTest]")
+    {
+        // Each of these test cases verifies the encoding 
+        // of certain problematic codepoints that do not represent
+        // characters
+        struct TestCase
+        {
+            char16     surrogatePair[1];
+            utf8char_t utf8Encoding[3];
+        };
+
+        TestCase testCases[] = {
+            { { 0x0024 }, { 0x24 } },              //  U+0024 - Dollar Symbol
+            { { 0x00A2 }, { 0xC2, 0xA2 } },        //  U+00A2 - Cent symbol
+            { { 0x20AC }, { 0xE2, 0x82, 0xAC } }   //  U+20AC - Euro symbol
+        };
+
+        RunUtf8EncodingTestCase(testCases, utf8::EncodeTrueUtf8IntoAndNullTerminate);
+    }
+
+    TEST_CASE("CodexTest_EncodeTrueUtf8_SimpleString", "[CodexTest]")
+    {
+        const charcount_t charCount = 3;
+        utf8char_t encodedBuffer[(charCount + 1) * 3]; // +1 since the buffer will be null terminated
+        char16* sourceBuffer = L"abc";
+        size_t numEncodedBytes = utf8::EncodeTrueUtf8IntoAndNullTerminate(encodedBuffer, sourceBuffer, charCount);
+        CHECK(numEncodedBytes == charCount);
+        for (int i = 0; i < charCount; i++)
+        {
+            CHECK(sourceBuffer[i] == (char16)encodedBuffer[i]);
+        }
+    }
+};

+ 1 - 0
bin/NativeTests/NativeTests.vcxproj

@@ -42,6 +42,7 @@
     <ClInclude Include="stdafx.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="CodexTests.cpp" />
     <ClCompile Include="FileLoadHelpers.cpp" />
     <ClCompile Include="CodexAssert.cpp" />
     <ClCompile Include="JsRTApiTest.cpp" />

+ 5 - 1
bin/ch/ChakraRtInterface.cpp

@@ -115,6 +115,11 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary)
     m_jsApiHooks.pfJsrtSetPromiseContinuationCallback = (JsAPIHooks::JsrtSetPromiseContinuationCallbackPtr)GetChakraCoreSymbol(library, "JsSetPromiseContinuationCallback");
     m_jsApiHooks.pfJsrtGetContextOfObject = (JsAPIHooks::JsrtGetContextOfObject)GetChakraCoreSymbol(library, "JsGetContextOfObject");
     m_jsApiHooks.pfJsrtParseScriptWithAttributesUtf8 = (JsAPIHooks::JsrtParseScriptWithAttributesUtf8)GetChakraCoreSymbol(library, "JsParseScriptWithAttributesUtf8");
+    m_jsApiHooks.pfJsrtInitializeModuleRecord = (JsAPIHooks::JsInitializeModuleRecordPtr)GetChakraCoreSymbol(library, "JsInitializeModuleRecord");
+    m_jsApiHooks.pfJsrtParseModuleSource = (JsAPIHooks::JsParseModuleSourcePtr)GetChakraCoreSymbol(library, "JsParseModuleSource");
+    m_jsApiHooks.pfJsrtSetModuleHostInfo = (JsAPIHooks::JsSetModuleHostInfoPtr)GetChakraCoreSymbol(library, "JsSetModuleHostInfo");
+    m_jsApiHooks.pfJsrtGetModuleHostInfo = (JsAPIHooks::JsGetModuleHostInfoPtr)GetChakraCoreSymbol(library, "JsGetModuleHostInfo");
+    m_jsApiHooks.pfJsrtModuleEvaluation = (JsAPIHooks::JsModuleEvaluationPtr)GetChakraCoreSymbol(library, "JsModuleEvaluation");
     m_jsApiHooks.pfJsrtDiagStartDebugging = (JsAPIHooks::JsrtDiagStartDebugging)GetChakraCoreSymbol(library, "JsDiagStartDebugging");
     m_jsApiHooks.pfJsrtDiagStopDebugging = (JsAPIHooks::JsrtDiagStopDebugging)GetChakraCoreSymbol(library, "JsDiagStopDebugging");
     m_jsApiHooks.pfJsrtDiagGetSource = (JsAPIHooks::JsrtDiagGetSource)GetChakraCoreSymbol(library, "JsDiagGetSource");
@@ -135,7 +140,6 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary)
     m_jsApiHooks.pfJsrtRunScriptUtf8 = (JsAPIHooks::JsrtRunScriptUtf8)GetChakraCoreSymbol(library, "JsRunScriptUtf8");
     m_jsApiHooks.pfJsrtSerializeScriptUtf8 = (JsAPIHooks::JsrtSerializeScriptUtf8)GetChakraCoreSymbol(library, "JsSerializeScriptUtf8");
     m_jsApiHooks.pfJsrtRunSerializedScriptUtf8 = (JsAPIHooks::JsrtRunSerializedScriptUtf8)GetChakraCoreSymbol(library, "JsRunSerializedScriptUtf8");
-    m_jsApiHooks.pfJsrtExperimentalApiRunModuleUtf8 = (JsAPIHooks::JsrtRunModuleUtf8Ptr)GetChakraCoreSymbol(library, "JsExperimentalApiRunModuleUtf8");
     m_jsApiHooks.pfJsrtGetPropertyIdFromNameUtf8 = (JsAPIHooks::JsrtGetPropertyIdFromNameUtf8Ptr)GetChakraCoreSymbol(library, "JsGetPropertyIdFromNameUtf8");
     m_jsApiHooks.pfJsrtStringFree = (JsAPIHooks::JsrtStringFreePtr)GetChakraCoreSymbol(library, "JsStringFree");
 

+ 20 - 4
bin/ch/ChakraRtInterface.h

@@ -28,6 +28,11 @@ struct JsAPIHooks
     typedef JsErrorCode (WINAPI *JsrtGetPropertyIdFromNameUtf8Ptr)(const char *name, JsPropertyIdRef *propertyId);
     typedef JsErrorCode (WINAPI *JsrtGetPropertyPtr)(JsValueRef object, JsPropertyIdRef property, JsValueRef* value);
     typedef JsErrorCode (WINAPI *JsrtHasPropertyPtr)(JsValueRef object, JsPropertyIdRef property, bool *hasProperty);
+    typedef JsErrorCode (WINAPI *JsInitializeModuleRecordPtr)(JsModuleRecord referencingModule, JsValueRef normalizedSpecifier, JsModuleRecord* moduleRecord);
+    typedef JsErrorCode (WINAPI *JsParseModuleSourcePtr)(JsModuleRecord requestModule, JsSourceContext sourceContext, byte* sourceText, unsigned int sourceLength, JsParseModuleSourceFlags sourceFlag, JsValueRef* exceptionValueRef);
+    typedef JsErrorCode (WINAPI *JsModuleEvaluationPtr)(JsModuleRecord requestModule, JsValueRef* result);
+    typedef JsErrorCode (WINAPI *JsSetModuleHostInfoPtr)(JsModuleRecord requestModule, JsModuleHostInfoKind moduleHostInfo, void* hostInfo);
+    typedef JsErrorCode (WINAPI *JsGetModuleHostInfoPtr)(JsModuleRecord requestModule, JsModuleHostInfoKind moduleHostInfo, void** hostInfo);
     typedef JsErrorCode (WINAPI *JsrtCallFunctionPtr)(JsValueRef function, JsValueRef* arguments, unsigned short argumentCount, JsValueRef *result);
     typedef JsErrorCode (WINAPI *JsrtNumberToDoublePtr)(JsValueRef value, double *doubleValue);
     typedef JsErrorCode (WINAPI *JsrtNumberToIntPtr)(JsValueRef value, int *intValue);
@@ -35,7 +40,7 @@ struct JsAPIHooks
     typedef JsErrorCode (WINAPI *JsrtGetExternalDataPtr)(JsValueRef object, void **data);
     typedef JsErrorCode (WINAPI *JsrtCreateArrayPtr)(unsigned int length, JsValueRef *result);
     typedef JsErrorCode (WINAPI *JsrtCreateErrorPtr)(JsValueRef message, JsValueRef *error);
-    typedef JsErrorCode(WINAPI *JsrtHasExceptionPtr)(bool *hasException);
+    typedef JsErrorCode (WINAPI *JsrtHasExceptionPtr)(bool *hasException);
     typedef JsErrorCode (WINAPI *JsrtSetExceptionPtr)(JsValueRef exception);
     typedef JsErrorCode (WINAPI *JsrtGetAndClearExceptiopnPtr)(JsValueRef* exception);
     typedef JsErrorCode (WINAPI *JsrtGetRuntimePtr)(JsContextRef context, JsRuntimeHandle *runtime);
@@ -68,7 +73,6 @@ struct JsAPIHooks
     typedef JsErrorCode(WINAPI *JsrtRunScriptUtf8)(const char *script, JsSourceContext sourceContext, const char *sourceUrl, JsValueRef *result);
     typedef JsErrorCode(WINAPI *JsrtSerializeScriptUtf8)(const char *script, ChakraBytePtr buffer, unsigned int *bufferSize);
     typedef JsErrorCode(WINAPI *JsrtRunSerializedScriptUtf8)(JsSerializedScriptLoadUtf8SourceCallback scriptLoadCallback, JsSerializedScriptUnloadCallback scriptUnloadCallback, ChakraBytePtr buffer, JsSourceContext sourceContext, const char *sourceUrl, JsValueRef * result);
-    typedef JsErrorCode(WINAPI *JsrtRunModuleUtf8Ptr)(const char *script, JsSourceContext sourceContext, const char *sourceUrl, JsValueRef *result);
     typedef JsErrorCode(WINAPI *JsrtStringFreePtr)(const char *stringValue);
 
     typedef JsErrorCode(WINAPI *JsrtTTDCreateRecordRuntimePtr)(JsRuntimeAttributes attributes, char* infoUri, size_t infoUriCount, UINT32 snapInterval, UINT32 snapHistoryLength, JsThreadServiceCallback threadService, JsRuntimeHandle *runtime);
@@ -117,6 +121,11 @@ struct JsAPIHooks
     JsrtGetPropertyIdFromNameUtf8Ptr pfJsrtGetPropertyIdFromNameUtf8;
     JsrtGetPropertyPtr pfJsrtGetProperty;
     JsrtHasPropertyPtr pfJsrtHasProperty;
+    JsParseModuleSourcePtr pfJsrtParseModuleSource;
+    JsInitializeModuleRecordPtr pfJsrtInitializeModuleRecord;
+    JsModuleEvaluationPtr pfJsrtModuleEvaluation;
+    JsSetModuleHostInfoPtr pfJsrtSetModuleHostInfo;
+    JsGetModuleHostInfoPtr pfJsrtGetModuleHostInfo;
     JsrtCallFunctionPtr pfJsrtCallFunction;
     JsrtNumberToDoublePtr pfJsrtNumberToDouble;
     JsrtNumberToIntPtr pfJsrtNumberToInt;
@@ -156,7 +165,6 @@ struct JsAPIHooks
     JsrtRunScriptUtf8 pfJsrtRunScriptUtf8;
     JsrtSerializeScriptUtf8 pfJsrtSerializeScriptUtf8;
     JsrtRunSerializedScriptUtf8 pfJsrtRunSerializedScriptUtf8;
-    JsrtRunModuleUtf8Ptr pfJsrtExperimentalApiRunModuleUtf8;
     JsrtStringFreePtr pfJsrtStringFree;
 
     JsrtTTDCreateRecordRuntimePtr pfJsrtTTDCreateRecordRuntime;
@@ -317,6 +325,15 @@ public:
     static JsErrorCode WINAPI JsDiagGetProperties(unsigned int objectHandle, unsigned int fromCount, unsigned int totalCount, JsValueRef * propertiesObject) { return HOOK_JS_API(DiagGetProperties(objectHandle, fromCount, totalCount, propertiesObject)); }
     static JsErrorCode WINAPI JsDiagGetObjectFromHandle(unsigned int handle, JsValueRef * handleObject) { return HOOK_JS_API(DiagGetObjectFromHandle(handle, handleObject)); }
     static JsErrorCode WINAPI JsDiagEvaluateUtf8(const char * expression, unsigned int stackFrameIndex, JsValueRef * evalResult) { return HOOK_JS_API(DiagEvaluateUtf8(expression, stackFrameIndex, evalResult)); }
+    static JsErrorCode WINAPI JsParseModuleSource(JsModuleRecord requestModule, JsSourceContext sourceContext, byte* sourceText, unsigned int sourceLength, JsParseModuleSourceFlags sourceFlag, JsValueRef* exceptionValueRef) {
+        return m_jsApiHooks.pfJsrtParseModuleSource(requestModule, sourceContext, sourceText, sourceLength, sourceFlag, exceptionValueRef);
+    }
+    static JsErrorCode WINAPI JsModuleEvaluation(JsModuleRecord requestModule, JsValueRef* result) { return m_jsApiHooks.pfJsrtModuleEvaluation(requestModule, result); }
+    static JsErrorCode WINAPI JsInitializeModuleRecord(JsModuleRecord referencingModule, JsValueRef normalizedSpecifier, JsModuleRecord* moduleRecord) {
+        return m_jsApiHooks.pfJsrtInitializeModuleRecord(referencingModule, normalizedSpecifier, moduleRecord);
+    }
+    static JsErrorCode WINAPI JsSetModuleHostInfo(JsModuleRecord requestModule, JsModuleHostInfoKind moduleHostInfo, void* hostInfo) { return m_jsApiHooks.pfJsrtSetModuleHostInfo(requestModule, moduleHostInfo, hostInfo); }
+    static JsErrorCode WINAPI JsGetModuleHostInfo(JsModuleRecord requestModule, JsModuleHostInfoKind moduleHostInfo, void** hostInfo) { return m_jsApiHooks.pfJsrtGetModuleHostInfo(requestModule, moduleHostInfo, hostInfo); }
 
     static JsErrorCode WINAPI JsValueToCharCopy(JsValueRef value, char **stringValue, size_t *length)
     {
@@ -329,7 +346,6 @@ public:
     static JsErrorCode WINAPI JsRunScriptUtf8(const char *script, JsSourceContext sourceContext, const char *sourceUrl, JsValueRef *result) { return HOOK_JS_API(RunScriptUtf8(script, sourceContext, sourceUrl, result)); }
     static JsErrorCode WINAPI JsSerializeScriptUtf8(const char *script, ChakraBytePtr buffer, unsigned int *bufferSize) { return HOOK_JS_API(SerializeScriptUtf8(script, buffer, bufferSize)); }
     static JsErrorCode WINAPI JsRunSerializedScriptUtf8(JsSerializedScriptLoadUtf8SourceCallback scriptLoadCallback, JsSerializedScriptUnloadCallback scriptUnloadCallback, ChakraBytePtr buffer, JsSourceContext sourceContext, const char *sourceUrl, JsValueRef * result) { return HOOK_JS_API(RunSerializedScriptUtf8(scriptLoadCallback, scriptUnloadCallback, buffer, sourceContext, sourceUrl, result)); }
-    static JsErrorCode WINAPI JsRunModuleUtf8(const char *script, JsSourceContext sourceContext, const char *sourceUrl, JsValueRef *result) { return HOOK_JS_API(ExperimentalApiRunModuleUtf8(script, sourceContext, sourceUrl, result)); }
     static JsErrorCode WINAPI JsPointerToStringUtf8(const char *stringValue, size_t length, JsValueRef *value) { return HOOK_JS_API(PointerToStringUtf8(stringValue, length, value)); }
     static JsErrorCode WINAPI JsStringFree(char *stringValue) { return HOOK_JS_API(StringFree(stringValue)); }
     static JsErrorCode WINAPI JsTTDCreateRecordRuntime(JsRuntimeAttributes attributes, char* infoUri, size_t infoUriCount, UINT32 snapInterval, UINT32 snapHistoryLength, JsThreadServiceCallback threadService, JsRuntimeHandle *runtime) { return HOOK_JS_API(TTDCreateRecordRuntime(attributes, infoUri, infoUriCount, snapInterval, snapHistoryLength, threadService, runtime)); }

+ 210 - 27
bin/ch/WScriptJsrt.cpp

@@ -3,8 +3,10 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #include "stdafx.h"
+#include <string>
 
 MessageQueue* WScriptJsrt::messageQueue = nullptr;
+std::map<std::string, JsModuleRecord>  WScriptJsrt::moduleRecordMap;
 DWORD_PTR WScriptJsrt::sourceContext = 0;
 
 #define ERROR_MESSAGE_TO_STRING(errorCode, errorMessage, errorMessageString)        \
@@ -125,11 +127,6 @@ JsValueRef __stdcall WScriptJsrt::QuitCallback(JsValueRef callee, bool isConstru
     ExitProcess(exitCode);
 }
 
-JsValueRef __stdcall WScriptJsrt::LoadModuleFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
-{
-    return LoadScriptFileHelper(callee, arguments, argumentCount, true);
-}
-
 JsValueRef __stdcall WScriptJsrt::LoadScriptFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
 {
     return LoadScriptFileHelper(callee, arguments, argumentCount, false);
@@ -220,10 +217,12 @@ JsValueRef WScriptJsrt::LoadScriptHelper(JsValueRef callee, bool isConstructCall
     else
     {
         AutoString fileContent;
-        AutoString fileName;
+        char* fileNameNarrow = nullptr;
+        bool freeFileName = false;
         AutoString scriptInjectType;
         size_t fileContentLength;
         size_t scriptInjectTypeLength;
+        char fileNameBuffer[MAX_PATH];
 
         IfJsrtErrorSetGo(ChakraRTInterface::JsStringToPointerUtf8Copy(arguments[1], &fileContent, &fileContentLength));
 
@@ -234,15 +233,26 @@ JsValueRef WScriptJsrt::LoadScriptHelper(JsValueRef callee, bool isConstructCall
             if (argumentCount > 3)
             {
                 size_t unused;
-                IfJsrtErrorSetGo(ChakraRTInterface::JsStringToPointerUtf8Copy(arguments[3], &fileName, &unused));
+                IfJsrtErrorSetGo(ChakraRTInterface::JsStringToPointerUtf8Copy(arguments[3], &fileNameNarrow, &unused));
+                freeFileName = true;
             }
         }
 
+        if (!freeFileName && isSourceModule)
+        {
+            sprintf_s(fileNameBuffer, MAX_PATH, "moduleScript%i.js", (int)sourceContext);
+            fileNameNarrow = fileNameBuffer;
+        }
+
         if (*fileContent)
         {
             // TODO: This is CESU-8. How to tell the engine?
             // TODO: How to handle this source (script) life time?
-            returnValue = LoadScript(callee, *fileName, *fileContent, *scriptInjectType ? *scriptInjectType : "self", isSourceModule);
+            returnValue = LoadScript(callee, fileNameNarrow, *fileContent, *scriptInjectType ? *scriptInjectType : "self", isSourceModule);
+            if (freeFileName)
+            {
+                ChakraRTInterface::JsStringFree(fileNameNarrow);
+            }
         }
     }
 
@@ -265,6 +275,64 @@ Error:
     return returnValue;
 }
 
+JsErrorCode WScriptJsrt::InitializeModuleInfo(JsValueRef specifier, JsModuleRecord moduleRecord)
+{
+    JsErrorCode errorCode = JsNoError;
+    errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_FetchImportedModuleCallback, (void*)WScriptJsrt::FetchImportedModule);
+    if (errorCode == JsNoError)
+    {
+        errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_NotifyModuleReadyCallback, (void*)WScriptJsrt::NotifyModuleReadyCallback);
+    }
+    if (errorCode == JsNoError)
+    {
+        errorCode = ChakraRTInterface::JsSetModuleHostInfo(moduleRecord, JsModuleHostInfo_HostDefined, specifier);
+    }
+    IfJsrtErrorFailLogAndRetErrorCode(errorCode);
+    return JsNoError;
+}
+
+JsErrorCode WScriptJsrt::LoadModuleFromString(LPCSTR fileName, LPCSTR fileContent)
+{
+    DWORD_PTR dwSourceCookie = WScriptJsrt::GetNextSourceContext();
+    JsModuleRecord requestModule = JS_INVALID_REFERENCE;
+    auto moduleRecordEntry = moduleRecordMap.find(std::string(fileName));
+    JsErrorCode errorCode = JsNoError;
+    // we need to create a new moduleRecord if the specifier (fileName) is not found; otherwise we'll use the old one.
+    if (moduleRecordEntry == moduleRecordMap.end())
+    {
+        JsValueRef specifier;
+        errorCode = ChakraRTInterface::JsPointerToStringUtf8(fileName, strlen(fileName), &specifier);
+        if (errorCode == JsNoError)
+        {
+            errorCode = ChakraRTInterface::JsInitializeModuleRecord(nullptr, specifier, &requestModule);
+        }
+        if (errorCode == JsNoError)
+        {
+            errorCode = InitializeModuleInfo(specifier, requestModule);
+        }
+        if (errorCode == JsNoError)
+        {
+            moduleRecordMap[std::string(fileName)] = requestModule;
+        }
+    }
+    else
+    {
+        requestModule = moduleRecordEntry->second;
+    }
+    IfJsrtErrorFailLogAndRetErrorCode(errorCode);
+    JsValueRef errorObject = JS_INVALID_REFERENCE;
+
+    // ParseModuleSource is sync, while additional fetch & evaluation are async.
+    errorCode = ChakraRTInterface::JsParseModuleSource(requestModule, dwSourceCookie, (LPBYTE)fileContent, 
+        (unsigned int)strlen(fileContent), JsParseModuleSourceFlags_DataIsUTF8, &errorObject);
+    if ((errorCode != JsNoError) && errorObject != JS_INVALID_REFERENCE)
+    {
+        ChakraRTInterface::JsSetException(errorObject);
+        return errorCode;
+    }
+    return JsNoError;
+}
+
 JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCSTR fileName, LPCSTR fileContent, LPCSTR scriptInjectType, bool isSourceModule)
 {
     HRESULT hr = E_FAIL;
@@ -301,25 +369,24 @@ JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCSTR fileName, LPCSTR fi
         strcpy_s(fullPathNarrow, "script.js");
     }
 
-    if (strcmp(scriptInjectType, "self") == 0)
+    // this is called with LoadModuleCallback method as well where caller pass in a string that should be
+    // treated as a module source text instead of opening a new file.
+    if (isSourceModule || (strcmp(scriptInjectType, "module") == 0))
+    {
+        errorCode = LoadModuleFromString(fileName, fileContent);
+    }
+    else if (strcmp(scriptInjectType, "self") == 0)
     {
         JsContextRef calleeContext;
         IfJsrtErrorSetGo(ChakraRTInterface::JsGetContextOfObject(callee, &calleeContext));
 
         IfJsrtErrorSetGo(ChakraRTInterface::JsSetCurrentContext(calleeContext));
 
-        if (isSourceModule)
-        {
-            errorCode = ChakraRTInterface::JsRunModuleUtf8(fileContent, GetNextSourceContext(), fullPathNarrow, &returnValue);
-        }
-        else
-        {
 #if ENABLE_TTD
-            errorCode = ChakraRTInterface::JsTTDRunScript(-1, fileContent, GetNextSourceContext(), fullPathNarrow, &returnValue);
+        errorCode = ChakraRTInterface::JsTTDRunScript(-1, fileContent, GetNextSourceContext(), fullPathNarrow, &returnValue);
 #else
-            errorCode = ChakraRTInterface::JsRunScriptUtf8(fileContent, GetNextSourceContext(), fullPathNarrow, &returnValue);
+        errorCode = ChakraRTInterface::JsRunScriptUtf8(fileContent, GetNextSourceContext(), fullPathNarrow, &returnValue);
 #endif
-        }
 
         if (errorCode == JsNoError)
         {
@@ -339,19 +406,11 @@ JsValueRef WScriptJsrt::LoadScript(JsValueRef callee, LPCSTR fileName, LPCSTR fi
         // Initialize the host objects
         Initialize();
 
-
-        if (isSourceModule)
-        {
-            errorCode = ChakraRTInterface::JsRunModuleUtf8(fileContent, GetNextSourceContext(), fullPathNarrow, &returnValue);
-        }
-        else
-        {
 #if ENABLE_TTD
             errorCode = ChakraRTInterface::JsTTDRunScript(-1, fileContent, GetNextSourceContext(), fullPathNarrow, &returnValue);
 #else
             errorCode = ChakraRTInterface::JsRunScriptUtf8(fileContent, GetNextSourceContext(), fullPathNarrow, &returnValue);
 #endif
-        }
 
         if (errorCode == JsNoError)
         {
@@ -654,7 +713,6 @@ bool WScriptJsrt::Initialize()
     IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Echo", EchoCallback));
     IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "Quit", QuitCallback));
     IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadScriptFile", LoadScriptFileCallback));
-    IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadModuleFile", LoadModuleFileCallback));
     IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadScript", LoadScriptCallback));
     IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "LoadModule", LoadModuleCallback));
     IfFalseGo(WScriptJsrt::InstallObjectsOnObject(wscript, "SetTimeout", SetTimeoutCallback));
@@ -864,3 +922,128 @@ HRESULT WScriptJsrt::CallbackMessage::CallFunction(LPCSTR fileName)
 Error:
     return hr;
 }
+
+WScriptJsrt::ModuleMessage::ModuleMessage(JsModuleRecord module, JsValueRef specifier)
+    : MessageBase(0), moduleRecord(module), specifier(specifier)
+{
+    ChakraRTInterface::JsAddRef(module, nullptr);
+    if (specifier != nullptr)
+    {
+        // nullptr specifier means a Promise to execute; non-nullptr means a "fetch" operation.
+        ChakraRTInterface::JsAddRef(specifier, nullptr);
+    }
+}
+
+WScriptJsrt::ModuleMessage::~ModuleMessage()
+{
+    ChakraRTInterface::JsRelease(moduleRecord, nullptr);
+    if (specifier != nullptr)
+    {
+        ChakraRTInterface::JsRelease(specifier, nullptr);
+    }
+}
+
+HRESULT WScriptJsrt::ModuleMessage::Call(LPCSTR fileName)
+{
+    JsErrorCode errorCode;
+    JsValueRef result = JS_INVALID_REFERENCE;
+    HRESULT hr;
+    if (specifier == nullptr)
+    {
+        errorCode = ChakraRTInterface::JsModuleEvaluation(moduleRecord, &result);
+        if (errorCode != JsNoError)
+        {
+            PrintException(fileName, errorCode);
+        }
+    }
+    else
+    {
+        LPCSTR fileContent = nullptr;
+        char* specifierStr = nullptr;
+        size_t length;
+        errorCode = ChakraRTInterface::JsStringToPointerUtf8Copy(specifier, &specifierStr, &length);
+        if (errorCode == JsNoError)
+        {
+            hr = Helpers::LoadScriptFromFile(specifierStr, fileContent);
+            if (FAILED(hr))
+            {
+                fprintf(stderr, "Couldn't load file.\n");
+            }
+            else
+            {
+                LoadScript(nullptr, specifierStr, fileContent, "module", true);
+            }
+        }
+    }
+    return errorCode;
+}
+
+// Callback from chakracore to fetch dependent module. In the test harness, we are not doing any translation, just treat the specifier as fileName.
+// While this call will come back directly from ParseModuleSource, the additional task are treated as Promise that will be executed later.
+JsErrorCode WScriptJsrt::FetchImportedModule(_In_ JsModuleRecord referencingModule, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord)
+{
+    JsModuleRecord moduleRecord = JS_INVALID_REFERENCE;
+    AutoString specifierStr;
+    size_t length;
+
+    JsErrorCode errorCode = ChakraRTInterface::JsStringToPointerUtf8Copy(specifier, &specifierStr, &length);
+    if (errorCode != JsNoError)
+    {
+        return errorCode;
+    }
+    auto moduleEntry = moduleRecordMap.find(std::string(*specifierStr));
+    if (moduleEntry != moduleRecordMap.end())
+    {
+        *dependentModuleRecord = moduleEntry->second;
+        return JsNoError;
+    }
+
+    if (errorCode == JsNoError)
+    {
+        errorCode = ChakraRTInterface::JsInitializeModuleRecord(referencingModule, specifier, &moduleRecord);
+    }
+    if (errorCode == JsNoError)
+    {
+        InitializeModuleInfo(specifier, moduleRecord);
+        moduleRecordMap[std::string(*specifierStr)] = moduleRecord;
+        ModuleMessage* moduleMessage =
+            WScriptJsrt::ModuleMessage::Create(referencingModule, specifier);
+        if (moduleMessage == nullptr)
+        {
+            return JsErrorOutOfMemory;
+        }
+        WScriptJsrt::PushMessage(moduleMessage);
+        *dependentModuleRecord = moduleRecord;
+    }
+    return errorCode;
+}
+
+// Callback from chakraCore when the module resolution is finished, either successfuly or unsuccessfully. 
+JsErrorCode WScriptJsrt::NotifyModuleReadyCallback(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar)
+{
+    if (exceptionVar != nullptr)
+    {
+        ChakraRTInterface::JsSetException(exceptionVar);
+        JsValueRef specifier = JS_INVALID_REFERENCE;
+        ChakraRTInterface::JsGetModuleHostInfo(referencingModule, JsModuleHostInfo_HostDefined, &specifier);
+        AutoString fileName;
+        size_t length;
+        if (specifier != JS_INVALID_REFERENCE)
+        {
+            ChakraRTInterface::JsStringToPointerUtf8Copy(specifier, &fileName, &length);
+        }
+        PrintException(*fileName, JsErrorScriptException);
+    }
+    else
+    {
+        WScriptJsrt::ModuleMessage* moduleMessage =
+            WScriptJsrt::ModuleMessage::Create(referencingModule, nullptr);
+        if (moduleMessage == nullptr)
+        {
+            return JsErrorOutOfMemory;
+        }
+        WScriptJsrt::PushMessage(moduleMessage);
+    }
+    return JsNoError;
+}
+

+ 26 - 1
bin/ch/WScriptJsrt.h

@@ -28,9 +28,32 @@ public:
         }
     };
 
+    class ModuleMessage : public MessageBase
+    {
+    private:
+        JsModuleRecord moduleRecord;
+        JsValueRef specifier;
+
+        ModuleMessage(JsModuleRecord module, JsValueRef specifier);
+
+    public:
+        ~ModuleMessage();
+
+        virtual HRESULT Call(LPCSTR fileName) override;
+
+        static ModuleMessage* Create(JsModuleRecord module, JsValueRef specifier)
+        {
+            return new ModuleMessage(module, specifier);
+        }
+
+    };
+
     static void AddMessageQueue(MessageQueue *messageQueue);
     static void PushMessage(MessageBase *message) { messageQueue->InsertSorted(message); }
 
+    static JsErrorCode FetchImportedModule(_In_ JsModuleRecord referencingModule, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord);
+    static JsErrorCode NotifyModuleReadyCallback(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar);
+
     static LPCWSTR ConvertErrorCodeToMessage(JsErrorCode errorCode)
     {
         switch (errorCode)
@@ -68,7 +91,6 @@ private:
     static bool CreateNamedFunction(const char*, JsNativeFunction callback, JsValueRef* functionVar);
     static JsValueRef __stdcall EchoCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
     static JsValueRef __stdcall QuitCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
-    static JsValueRef __stdcall LoadModuleFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
     static JsValueRef __stdcall LoadScriptFileCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
     static JsValueRef __stdcall LoadScriptCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
     static JsValueRef __stdcall LoadModuleCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
@@ -80,7 +102,10 @@ private:
     static JsValueRef __stdcall RequestAsyncBreakCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
 
     static JsValueRef __stdcall EmptyCallback(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
+    static JsErrorCode __stdcall LoadModuleFromString(LPCSTR fileName, LPCSTR fileContent);
+    static JsErrorCode __stdcall InitializeModuleInfo(JsValueRef specifier, JsModuleRecord moduleRecord);
 
     static MessageQueue *messageQueue;
     static DWORD_PTR sourceContext;
+    static std::map<std::string, JsModuleRecord> moduleRecordMap;
 };

+ 7 - 0
bin/ch/ch.cpp

@@ -344,6 +344,13 @@ Error:
     if (messageQueue != nullptr)
     {
         messageQueue->RemoveAll();
+        // clean up possible pinned exception object on exit to avoid potential leak
+        bool hasException;
+        if (ChakraRTInterface::JsHasException(&hasException) == JsNoError && hasException)
+        {
+            JsValueRef exception = JS_INVALID_REFERENCE;
+            ChakraRTInterface::JsGetAndClearException(&exception);
+        }
         delete messageQueue;
     }
     return hr;

+ 4 - 2
bin/ch/stdafx.h

@@ -25,6 +25,7 @@
 
 #define ENABLE_TEST_HOOKS 1
 #include "CommonDefines.h"
+#include <map>
 
 #ifdef _WIN32
 #include <windows.h>
@@ -63,7 +64,9 @@
 #if defined(DBG)
 
 #define _STRINGIZE_(x) #x
+#if !defined(_STRINGIZE)
 #define _STRINGIZE(x) _STRINGIZE_(x)
+#endif
 
 #define AssertMsg(exp, comment)   \
 do { \
@@ -80,10 +83,9 @@ if (!(exp)) \
 
 #define Assert(exp)             AssertMsg(exp, #exp)
 #define _JSRT_
-#include "ChakraCommon.h"
+#include "ChakraCore.h"
 #include "Core/CommonTypedefs.h"
 #include "TestHooksRt.h"
-#include "ChakraDebug.h"
 
 typedef void * Var;
 

+ 51 - 4
lib/Backend/BackwardPass.cpp

@@ -300,7 +300,9 @@ BackwardPass::ProcessBailOnStackArgsOutOfActualsRange()
 {
     IR::Instr * instr = this->currentInstr;
 
-    if (tag == Js::DeadStorePhase && instr->m_opcode == Js::OpCode::LdElemI_A && instr->HasBailOutInfo() && !IsPrePass())
+    if (tag == Js::DeadStorePhase && 
+        (instr->m_opcode == Js::OpCode::LdElemI_A || instr->m_opcode == Js::OpCode::TypeofElem) && 
+        instr->HasBailOutInfo() && !IsPrePass())
     {
         if (instr->DoStackArgsOpt(this->func))
         {
@@ -308,7 +310,7 @@ BackwardPass::ProcessBailOnStackArgsOutOfActualsRange()
             if (instr->GetBailOutKind() & ~IR::BailOnStackArgsOutOfActualsRange)
             {
                 Assert(instr->GetBailOutKind() == (IR::BailOnStackArgsOutOfActualsRange | IR::BailOutOnImplicitCallsPreOp));
-                //We are sure at this point, that we will not have any implicit calls as we don't have any call to helper.
+                //We are sure at this point, that we will not have any implicit calls as we wouldn't have done this optimization in the first place.
                 instr->SetBailOutKind(IR::BailOnStackArgsOutOfActualsRange);
             }
         }
@@ -2515,6 +2517,13 @@ BackwardPass::ProcessBlock(BasicBlock * block)
 
         this->currentInstr = instr;
         this->currentRegion = this->currentBlock->GetFirstInstr()->AsLabelInstr()->GetRegion();
+        
+        IR::Instr * insertedInstr = TryChangeInstrForStackArgOpt();
+        if (insertedInstr != nullptr)
+        {
+            instrPrev = insertedInstr;
+            continue;
+        }
 
         ProcessBailOnStackArgsOutOfActualsRange();
         
@@ -2929,8 +2938,9 @@ BackwardPass::DeadStoreOrChangeInstrForScopeObjRemoval()
 
                         Assert(currFunc->HasStackSymForFormal(value));
                         StackSym * paramStackSym = currFunc->GetStackSymForFormal(value);
-                        instr->ReplaceSrc1(IR::RegOpnd::New(paramStackSym, TyVar, currFunc));
-                        this->currentBlock->upwardExposedUses->Set(paramStackSym->m_id);
+                        IR::RegOpnd * srcOpnd = IR::RegOpnd::New(paramStackSym, TyVar, currFunc);
+                        instr->ReplaceSrc1(srcOpnd);
+                        this->ProcessSymUse(paramStackSym, true, true);
 
                         if (PHASE_VERBOSE_TRACE1(Js::StackArgFormalsOptPhase))
                         {
@@ -2965,6 +2975,43 @@ BackwardPass::DeadStoreOrChangeInstrForScopeObjRemoval()
     return false;
 }
 
+IR::Instr *
+BackwardPass::TryChangeInstrForStackArgOpt()
+{
+    IR::Instr * instr = this->currentInstr;
+    if (instr->DoStackArgsOpt(this->func))
+    {
+        switch (instr->m_opcode)
+        {
+            case Js::OpCode::TypeofElem:
+            {
+                /*
+                    Before:
+                        dst = TypeOfElem arguments[i] <(BailOnStackArgsOutOfActualsRange)>
+
+                    After:
+                        tmpdst = LdElemI_A arguments[i] <(BailOnStackArgsOutOfActualsRange)>
+                        dst = TypeOf tmpdst
+                */
+
+                AssertMsg(instr->HasBailOutInfo() && (instr->GetBailOutKind() & IR::BailOutKind::BailOnStackArgsOutOfActualsRange), "Why is the bailout kind not set, when it is StackArgOptimized?");
+
+                instr->m_opcode = Js::OpCode::LdElemI_A;
+                IR::Opnd * dstOpnd = instr->UnlinkDst();
+
+                IR::RegOpnd * elementOpnd = IR::RegOpnd::New(StackSym::New(instr->m_func), IRType::TyVar, instr->m_func);
+                instr->SetDst(elementOpnd);
+
+                IR::Instr * typeOfInstr = IR::Instr::New(Js::OpCode::Typeof, dstOpnd, elementOpnd, instr->m_func);
+                instr->InsertAfter(typeOfInstr);
+
+                return typeOfInstr;
+            }
+        }
+    }
+    return nullptr;
+}
+
 void
 BackwardPass::TraceDeadStoreOfInstrsForScopeObjectRemoval()
 {

+ 1 - 0
lib/Backend/BackwardPass.h

@@ -27,6 +27,7 @@ private:
     bool IsFormalParamSym(Func * func, Sym * sym) const;
     bool CanDeadStoreInstrForScopeObjRemoval(Sym *sym = nullptr) const;
     void TraceDeadStoreOfInstrsForScopeObjectRemoval();
+    IR::Instr * TryChangeInstrForStackArgOpt();
     void InsertArgInsForFormals();
     void ProcessBailOnStackArgsOutOfActualsRange();
     bool DeadStoreOrChangeInstrForScopeObjRemoval();

+ 6 - 3
lib/Backend/GlobOpt.cpp

@@ -3880,8 +3880,9 @@ GlobOpt::TrackInstrsForScopeObjectRemoval(IR::Instr * instr)
             //So we don't want to track the stack sym for this argout.- Skipping it here.
             if (instr->m_func->IsInlinedConstructor())
             {
-                Assert(argOutInstr->GetSrc1()->GetStackSym()->GetInstrDef() != nullptr);
-                Assert(argOutInstr->GetSrc1()->GetStackSym()->GetInstrDef()->m_opcode == Js::OpCode::NewScObjectNoCtor);
+                //PRE might introduce a second defintion for the Src1. So assert for the opcode only when it has single definition.
+                Assert(argOutInstr->GetSrc1()->GetStackSym()->GetInstrDef() == nullptr ||
+                    argOutInstr->GetSrc1()->GetStackSym()->GetInstrDef()->m_opcode == Js::OpCode::NewScObjectNoCtor);
                 argOutInstr = argOutInstr->GetSrc2()->GetStackSym()->GetInstrDef();
             }
             if (formalsCount < actualsCount)
@@ -4023,6 +4024,7 @@ GlobOpt::OptArguments(IR::Instr *instr)
     switch(instr->m_opcode)
     {
     case Js::OpCode::LdElemI_A:
+    case Js::OpCode::TypeofElem:
     {
         Assert(src1->IsIndirOpnd());
         IR::RegOpnd *indexOpnd = src1->AsIndirOpnd()->GetIndexOpnd();
@@ -4800,7 +4802,8 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
     this->OptArguments(instr);
 
     //StackArguments Optimization - We bail out if the index is out of range of actuals.
-    if (instr->m_opcode == Js::OpCode::LdElemI_A && instr->DoStackArgsOpt(this->func) && !this->IsLoopPrePass())
+    if ((instr->m_opcode == Js::OpCode::LdElemI_A || instr->m_opcode == Js::OpCode::TypeofElem) && 
+        instr->DoStackArgsOpt(this->func) && !this->IsLoopPrePass())
     {
         GenerateBailAtOperation(&instr, IR::BailOnStackArgsOutOfActualsRange);
     }

+ 26 - 12
lib/Backend/InterpreterThunkEmitter.cpp

@@ -189,13 +189,13 @@ const BYTE InterpreterThunkEmitter::HeaderSize = sizeof(InterpreterThunk);
 const BYTE InterpreterThunkEmitter::ThunkSize = sizeof(Call);
 const uint InterpreterThunkEmitter::ThunksPerBlock = (BlockSize - HeaderSize) / ThunkSize;
 
-InterpreterThunkEmitter::InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, void * interpreterThunk) :
+InterpreterThunkEmitter::InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk) :
     emitBufferManager(allocator, codePageAllocators, /*scriptContext*/ nullptr, _u("Interpreter thunk buffer")),
     allocation(nullptr),
     allocator(allocator),
     thunkCount(0),
     thunkBuffer(nullptr),
-    interpreterThunk(interpreterThunk)
+    isAsmInterpreterThunk(isAsmInterpreterThunk)
 {
 }
 
@@ -253,6 +253,20 @@ void InterpreterThunkEmitter::NewThunkBlock()
     DWORD bufferSize = BlockSize;
     DWORD thunkCount = 0;
 
+    void * interpreterThunk = nullptr;
+
+    // the static interpreter thunk invoked by the dynamic emitted thunk
+#ifdef ASMJS_PLAT
+    if (isAsmInterpreterThunk)
+    {
+        interpreterThunk = Js::InterpreterStackFrame::InterpreterAsmThunk;
+    }
+    else
+#endif
+    {
+        interpreterThunk = Js::InterpreterStackFrame::InterpreterThunk;
+    }
+
     allocation = emitBufferManager.AllocateBuffer(bufferSize, &buffer);
     if (!emitBufferManager.ProtectBufferWithExecuteReadWriteForInterpreter(allocation))
     {
@@ -280,7 +294,7 @@ void InterpreterThunkEmitter::NewThunkBlock()
 
     // Copy the thunk buffer and modify it.
     js_memcpy_s(currentBuffer, bytesRemaining, InterpreterThunk, HeaderSize);
-    EncodeInterpreterThunk(currentBuffer, buffer, HeaderSize, epilogStart, epilogSize);
+    EncodeInterpreterThunk(currentBuffer, buffer, HeaderSize, epilogStart, epilogSize, interpreterThunk);
     currentBuffer += HeaderSize;
     bytesRemaining -= HeaderSize;
 
@@ -359,16 +373,16 @@ void InterpreterThunkEmitter::NewThunkBlock()
 
 
 #if _M_ARM
-void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize)
+void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk)
 {
     _Analysis_assume_(thunkSize == HeaderSize);
     // Encode MOVW
-    DWORD lowerThunkBits = (uint32)this->interpreterThunk & 0x0000FFFF;
+    DWORD lowerThunkBits = (uint32)interpreterThunk & 0x0000FFFF;
     DWORD movW = EncodeMove(/*Opcode*/ 0x0000F240, /*register*/1, lowerThunkBits);
     Emit(thunkBuffer,ThunkAddressOffset, movW);
 
     // Encode MOVT
-    DWORD higherThunkBits = ((uint32)this->interpreterThunk & 0xFFFF0000) >> 16;
+    DWORD higherThunkBits = ((uint32)interpreterThunk & 0xFFFF0000) >> 16;
     DWORD movT = EncodeMove(/*Opcode*/ 0x0000F2C0, /*register*/1, higherThunkBits);
     Emit(thunkBuffer, ThunkAddressOffset + sizeof(movW), movT);
 
@@ -424,7 +438,7 @@ void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ co
 }
 
 #elif _M_ARM64
-void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize)
+void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk)
 {
     int addrOffset = ThunkAddressOffset;
 
@@ -434,28 +448,28 @@ void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE
     // Following 4 MOV Instrs are to move the 64-bit address of the InterpreterThunk address into register x1.
 
     // Encode MOVZ (movz        x1, #<interpreterThunk 16-0 bits>)
-    DWORD lowerThunkBits = (uint64)this->interpreterThunk & 0x0000FFFF;
+    DWORD lowerThunkBits = (uint64)interpreterThunk & 0x0000FFFF;
     DWORD movZ = EncodeMove(/*Opcode*/ 0xD2800000, /*register x1*/1, lowerThunkBits); // no shift; hw = 00
     Emit(thunkBuffer,addrOffset, movZ);
     AssertMsg(sizeof(movZ) == 4, "movZ has to be 32-bit encoded");
     addrOffset+= sizeof(movZ);
 
     // Encode MOVK (movk        x1, #<interpreterThunk 32-16 bits>, lsl #16)
-    DWORD higherThunkBits = ((uint64)this->interpreterThunk & 0xFFFF0000) >> 16;
+    DWORD higherThunkBits = ((uint64)interpreterThunk & 0xFFFF0000) >> 16;
     DWORD movK = EncodeMove(/*Opcode*/ 0xF2A00000, /*register x1*/1, higherThunkBits); // left shift 16 bits; hw = 01
     Emit(thunkBuffer, addrOffset, movK);
     AssertMsg(sizeof(movK) == 4, "movK has to be 32-bit encoded");
     addrOffset+= sizeof(movK);
 
     // Encode MOVK (movk        x1, #<interpreterThunk 48-32 bits>, lsl #16)
-    higherThunkBits = ((uint64)this->interpreterThunk & 0xFFFF00000000) >> 32;
+    higherThunkBits = ((uint64)interpreterThunk & 0xFFFF00000000) >> 32;
     movK = EncodeMove(/*Opcode*/ 0xF2C00000, /*register x1*/1, higherThunkBits); // left shift 32 bits; hw = 02
     Emit(thunkBuffer, addrOffset, movK);
     AssertMsg(sizeof(movK) == 4, "movK has to be 32-bit encoded");
     addrOffset += sizeof(movK);
 
     // Encode MOVK (movk        x1, #<interpreterThunk 64-48 bits>, lsl #16)
-    higherThunkBits = ((uint64)this->interpreterThunk & 0xFFFF000000000000) >> 48;
+    higherThunkBits = ((uint64)interpreterThunk & 0xFFFF000000000000) >> 48;
     movK = EncodeMove(/*Opcode*/ 0xF2E00000, /*register x1*/1, higherThunkBits); // left shift 48 bits; hw = 03
     AssertMsg(sizeof(movK) == 4, "movK has to be 32-bit encoded");
     Emit(thunkBuffer, addrOffset, movK);
@@ -498,7 +512,7 @@ void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ co
     function->FrameSize = 5;                    // the number of bytes of stack that is allocated for this function divided by 16
 }
 #else
-void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize)
+void InterpreterThunkEmitter::EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk)
 {
     _Analysis_assume_(thunkSize == HeaderSize);
     Emit(thunkBuffer, ThunkAddressOffset, (uintptr_t)interpreterThunk);

+ 3 - 3
lib/Backend/InterpreterThunkEmitter.h

@@ -61,7 +61,7 @@ private:
     EmitBufferAllocation *allocation;
     SListBase<ThunkBlock> thunkBlocks;
     SListBase<ThunkBlock> freeListedThunkBlocks;
-    void * interpreterThunk; // the static interpreter thunk invoked by the dynamic emitted thunk
+    bool isAsmInterpreterThunk; // To emit address of InterpreterAsmThunk or InterpreterThunk
     BYTE*                thunkBuffer;
     ArenaAllocator*      allocator;
     DWORD thunkCount;                      // Count of thunks available in the current thunk block
@@ -94,7 +94,7 @@ private:
 
     /* ------private helpers -----------*/
     void NewThunkBlock();
-    void EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize);
+    void EncodeInterpreterThunk(__in_bcount(thunkSize) BYTE* thunkBuffer, __in_bcount(thunkSize) BYTE* thunkBufferStartAddress, __in const DWORD thunkSize, __in_bcount(epilogSize) BYTE* epilogStart, __in const DWORD epilogSize, __in void * const interpreterThunk);
 #if defined(_M_ARM32_OR_ARM64)
     DWORD EncodeMove(DWORD opCode, int reg, DWORD imm16);
     void GeneratePdata(_In_ const BYTE* entryPoint, _In_ const DWORD functionSize, _Out_ RUNTIME_FUNCTION* function);
@@ -116,7 +116,7 @@ public:
     static const uint BlockSize;
     static void* ConvertToEntryPoint(PVOID dynamicInterpreterThunk);
 
-    InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, void * interpreterThunk);
+    InterpreterThunkEmitter(ArenaAllocator* allocator, CustomHeap::CodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk = false);
 
     BYTE* GetNextThunk(PVOID* ppDynamicInterpreterThunk);
 

+ 64 - 5
lib/Common/Codex/Utf8Codex.cpp

@@ -313,6 +313,37 @@ LFourByte:
         return ptr;
     }
 
+    LPUTF8 EncodeSurrogatePair(char16 surrogateHigh, char16 surrogateLow, __out_ecount(3) LPUTF8 ptr)
+    {
+        // A unicode codepoint is encoded into a surrogate pair by doing the following:
+        //  subtract 0x10000 from the codepoint
+        //  Split the resulting value into the high-ten bits and low-ten bits
+        //  Add 0xD800 to the high ten bits, and 0xDC00 to the low ten bits
+        // Below, we want to decode the surrogate pair to its original codepoint
+        // So we do the above process in reverse
+        uint32 highTen = (surrogateHigh - 0xD800);
+        uint32 lowTen  = (surrogateLow - 0xDC00);
+        uint32 codepoint = 0x10000 + ((highTen << 10) | lowTen);
+
+        // This is the maximum valid unicode codepoint
+        // This should be ensured anyway since you can't encode a value higher 
+        // than this as a surrogate pair, so we assert this here
+        CodexAssert(codepoint <= 0x10FFFF);
+
+        // Now we need to encode the code point into utf-8
+        // Codepoints in the range that gets encoded into a surrogate pair
+        // gets encoded into 4 bytes under utf8
+        // Since the codepoint can be represented by 21 bits, the encoding 
+        // does the following: first 3 bits in the first byte, the next 6 in the
+        // second, the next six in the third, and the last six in the 4th byte
+        *ptr++ = static_cast<utf8char_t>(codepoint >> 18) | 0xF0;
+        *ptr++ = static_cast<utf8char_t>((codepoint >> 12) & 0x3F) | 0x80;
+        *ptr++ = static_cast<utf8char_t>((codepoint >> 6) & 0x3F) | 0x80;
+        *ptr++ = static_cast<utf8char_t>(codepoint & 0x3F) | 0x80;
+
+        return ptr;
+    }
+
     LPCUTF8 NextCharFull(LPCUTF8 ptr)
     {
         return ptr + EncodedBytes(*ptr);
@@ -430,8 +461,9 @@ LSlowPath:
         return true;
     }
 
+    template <bool cesu8Encoding>
     __range(0, cch * 3)
-    size_t EncodeInto(__out_ecount(cch * 3) LPUTF8 buffer, __in_ecount(cch) const char16 *source, charcount_t cch)
+    size_t EncodeIntoImpl(__out_ecount(cch * 3) LPUTF8 buffer, __in_ecount(cch) const char16 *source, charcount_t cch)
     {
         LPUTF8 dest = buffer;
 
@@ -451,15 +483,36 @@ LFastPath:
         }
 
 LSlowPath:
-        while( cch-- > 0 )
+        if (cesu8Encoding)
         {
-            dest = Encode(*source++, dest);
-            if (ShouldFastPath(dest, source)) goto LFastPath;
+            while (cch-- > 0)
+            {
+                dest = Encode(*source++, dest);
+                if (ShouldFastPath(dest, source)) goto LFastPath;
+            }
+        }
+        else
+        {
+            while (cch-- > 0)
+            {
+                // We increment the source pointer here since at least one utf16 code unit is read here
+                // If the code unit turns out to be the high surrogate in a surrogate pair, then 
+                // EncodeTrueUtf8 will consume the low surrogate code unit too by decrementing cch 
+                // and incrementing source
+                dest = EncodeTrueUtf8(*source++, &source, &cch, dest);
+                if (ShouldFastPath(dest, source)) goto LFastPath;
+            }
         }
 
         return dest - buffer;
     }
 
+    __range(0, cch * 3)
+        size_t EncodeInto(__out_ecount(cch * 3) LPUTF8 buffer, __in_ecount(cch) const char16 *source, charcount_t cch)
+    {
+        return EncodeIntoImpl<true>(buffer, source, cch);
+    }
+
     __range(0, cch * 3)
     size_t EncodeIntoAndNullTerminate(__out_ecount(cch * 3 + 1) utf8char_t *buffer, __in_ecount(cch) const char16 *source, charcount_t cch)
     {
@@ -468,7 +521,13 @@ LSlowPath:
         return result;
     }
 
-
+    __range(0, cch * 3)
+        size_t EncodeTrueUtf8IntoAndNullTerminate(__out_ecount(cch * 3 + 1) utf8char_t *buffer, __in_ecount(cch) const char16 *source, charcount_t cch)
+    {
+        size_t result = EncodeIntoImpl<false>(buffer, source, cch);
+        buffer[result] = 0;
+        return result;
+    }
 
     // Convert the character index into a byte index.
     size_t CharacterIndexToByteIndex(__in_ecount(cbLength) LPCUTF8 pch, size_t cbLength, charcount_t cchIndex, DecodeOptions options)

+ 47 - 0
lib/Common/Codex/Utf8Codex.h

@@ -161,6 +161,9 @@ namespace utf8
     // special cases ASCII to avoid a call the most common characters.
     LPUTF8 EncodeFull(char16 ch, __out_ecount(3) LPUTF8 ptr);
 
+    // Encode a surrogate pair into a utf8 sequence 
+    LPUTF8 EncodeSurrogatePair(char16 surrogateHigh, char16 surrogateLow, __out_ecount(3) LPUTF8 ptr);
+
     // Encode ch into a UTF8 sequence ignoring surrogate pairs (which are encoded as two
     // separate code points).
     inline LPUTF8 Encode(char16 ch, __out_ecount(3) LPUTF8 ptr)
@@ -173,6 +176,46 @@ namespace utf8
         return EncodeFull(ch, ptr);
     }
 
+    // Encode ch into a UTF8 sequence while being aware of surrogate pairs.
+    inline LPUTF8 EncodeTrueUtf8(char16 ch, const char16** source, charcount_t* cch, __out_ecount(3) LPUTF8 ptr)
+    {
+        if (ch < 0x80)
+        {
+            *ptr = static_cast<utf8char_t>(ch);
+            return ptr + 1;
+        }
+        else if (ch < 0xD800 || (ch >= 0xE000 && ch <= 0xFFFF))
+        {
+            return EncodeFull(ch, ptr);
+        } 
+
+        // We're now decoding a surrogate pair. If the input is malformed (eg. low surrogate is absent)
+        // we'll instead encode the unicode replacement character as utf8
+        if (*cch > 0)
+        {
+            char16 surrogateHigh = ch;
+            char16 surrogateLow = **source;
+
+            // Validate that the surrogate code units are within the appropriate 
+            // ranges for high and low surrogates
+            if ((surrogateHigh >= 0xD800 && surrogateHigh <= 0xDBFF) &&
+                (surrogateLow >= 0xDC00 && surrogateLow <= 0xDFFF))
+            {
+                // Consume the low surrogate
+                *source = *source + 1;
+                *cch = *cch - 1;
+
+                return EncodeSurrogatePair(surrogateHigh, surrogateLow, ptr);
+            }
+        }
+
+        // Invalid input: insert the unicode replacement character instead
+        ptr[0] = 0xEF;
+        ptr[1] = 0xBF;
+        ptr[2] = 0xBD;
+        return ptr + 3;
+    }
+
     // Return true if ch is a lead byte of a UTF8 multi-unit sequence.
     inline bool IsLeadByte(utf8char_t ch)
     {
@@ -263,6 +306,10 @@ namespace utf8
     __range(0, cch * 3)
     size_t EncodeIntoAndNullTerminate(__out_ecount(cch * 3 + 1) utf8char_t *buffer, __in_ecount(cch) const char16 *source, charcount_t cch);
 
+    // Like EncodeInto but ensures that buffer[return value] == 0.
+    __range(0, cch * 3)
+    size_t EncodeTrueUtf8IntoAndNullTerminate(__out_ecount(cch * 3 + 1) utf8char_t *buffer, __in_ecount(cch) const char16 *source, charcount_t cch);
+
     // Returns true if the pch refers to a UTF-16LE encoding of the given UTF-8 encoding bch.
     bool CharsAreEqual(__in_ecount(cch) LPCOLESTR pch, LPCUTF8 bch, size_t cch, DecodeOptions options = doDefault);
 

+ 6 - 2
lib/Common/Codex/Utf8Helper.h

@@ -39,7 +39,7 @@ namespace utf8
             return E_OUTOFMEMORY;
         }
 
-        size_t cbEncoded = utf8::EncodeIntoAndNullTerminate(destString, sourceString, (charcount_t) cchSourceString);
+        size_t cbEncoded = utf8::EncodeTrueUtf8IntoAndNullTerminate(destString, sourceString, (charcount_t) cchSourceString);
         Assert(cbEncoded <= cbDestString);
         static_assert(sizeof(utf8char_t) == sizeof(char), "Needs to be valid for cast");
         *destStringPtr = (char*)destString;
@@ -74,7 +74,11 @@ namespace utf8
             return E_OUTOFMEMORY;
         }
 
-        utf8::DecodeIntoAndNullTerminate(destString, (LPCUTF8) sourceString, cchDestString);
+        // Some node tests depend on the utf8 decoder not swallowing invalid unicode characters
+        // instead of replacing them with the "replacement" chracter. Pass a flag to our 
+        // decoder to require such behavior
+        utf8::DecodeIntoAndNullTerminate(destString, (LPCUTF8) sourceString, cchDestString,
+            DecodeOptions::doAllowInvalidWCHARs);
         Assert(destString[cchDestString] == 0);
         static_assert(sizeof(utf8char_t) == sizeof(char), "Needs to be valid for cast");
         *destStringPtr = destString;

+ 1 - 1
lib/Common/Common/Jobs.cpp

@@ -1402,7 +1402,7 @@ namespace JsUtil
         _u("BackgroundJobProcessor thread 5"),
         _u("BackgroundJobProcessor thread 6"),
         _u("BackgroundJobProcessor thread 7"),
-        _u("BackgroundJobProcessor thread 8")
+        _u("BackgroundJobProcessor thread 8"),
         _u("BackgroundJobProcessor thread 9"),
         _u("BackgroundJobProcessor thread 10"),
         _u("BackgroundJobProcessor thread 11"),

+ 0 - 1
lib/Common/Core/CodexAssert.cpp

@@ -10,4 +10,3 @@ void CodexAssert(bool condition)
 {
     Assert(condition);
 }
-

+ 5 - 1
lib/Common/DataStructures/SparseBitVector.h

@@ -474,7 +474,11 @@ BOOLEAN
 BVSparse<TAllocator>::TestAndClear(BVIndex i)
 {
     BVSparseNode ** prevNextField;
-    BVSparseNode * current = this->NodeFromIndex(i, &prevNextField);
+    BVSparseNode * current = this->NodeFromIndex(i, &prevNextField, false /* create */);
+    if (current == nullptr)
+    {
+        return false;
+    }
     BVIndex bvIndex = SparseBVUnit::Offset(i);
     BOOLEAN bit = current->data.Test(bvIndex);
     current->data.Clear(bvIndex);

+ 20 - 23
lib/Jsrt/ChakraCommon.h

@@ -186,7 +186,22 @@ typedef unsigned char* ChakraBytePtr;
         ///     The error code is returned by existing JsGetPropertyNamefromId if the function is called with non-string property id.
         /// </summary>
         JsErrorPropertyNotString,
-
+        /// <summary>
+        ///     Module evaulation is called in wrong context.
+        /// </summary>
+        JsErrorInvalidContext,
+        /// <summary>
+        ///     Module evaulation is called in wrong context.
+        /// </summary>
+        JsInvalidModuleHostInfoKind,
+        /// <summary>
+        ///     Module was parsed already when JsParseModuleSource is called.
+        /// </summary>
+        JsErrorModuleParsed,
+        /// <summary>
+        ///     Module was evaluated already when JsModuleEvaluation is called.
+        /// </summary>
+        JsErrorModuleEvaluated,
         /// <summary>
         ///     Category of errors that relates to errors occurring within the engine itself.
         /// </summary>
@@ -195,6 +210,10 @@ typedef unsigned char* ChakraBytePtr;
         ///     The Chakra engine has run out of memory.
         /// </summary>
         JsErrorOutOfMemory,
+        /// <summary>
+        ///     The Chakra engine failed to set the Floating Point Unit state.
+        /// </summary>
+        JsErrorBadFPUState,
 
         /// <summary>
         ///     Category of errors that relates to errors in a script.
@@ -1073,28 +1092,6 @@ typedef unsigned char* ChakraBytePtr;
             _In_z_ const char *sourceUrl,
             _Out_ JsValueRef *result);
 
-    /// <summary>
-    ///     Executes a module.
-    /// </summary>
-    /// <remarks>
-    ///     Requires an active script context.
-    /// </remarks>
-    /// <param name="script">The module script to parse and execute, encoded as utf8.</param>
-    /// <param name="sourceContext">
-    ///     A cookie identifying the script that can be used by debuggable script contexts.
-    /// </param>
-    /// <param name="sourceUrl">The location the module script came from, encoded as utf8.</param>
-    /// <param name="result">The result of executing the module script, if any. This parameter can be null.</param>
-    /// <returns>
-    ///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
-    /// </returns>
-    CHAKRA_API
-        JsExperimentalApiRunModuleUtf8(
-            _In_z_ const char *script,
-            _In_ JsSourceContext sourceContext,
-            _In_z_ const char *sourceUrl,
-            _Out_ JsValueRef *result);
-
     /// <summary>
     ///     Serializes a parsed script to a buffer than can be reused.
     /// </summary>

+ 138 - 0
lib/Jsrt/ChakraCore.h

@@ -25,4 +25,142 @@
 
 #include "ChakraDebug.h"
 
+typedef void* JsModuleRecord;
+
+typedef enum JsParseModuleSourceFlags
+{
+    JsParseModuleSourceFlags_DataIsUTF16LE = 0x00000000,
+    JsParseModuleSourceFlags_DataIsUTF8 = 0x00000001
+} JsParseModuleSourceFlags;
+
+typedef enum JsModuleHostInfoKind
+{
+    JsModuleHostInfo_Exception = 0x01,
+    JsModuleHostInfo_HostDefined = 0x02,
+    JsModuleHostInfo_NotifyModuleReadyCallback = 0x3,
+    JsModuleHostInfo_FetchImportedModuleCallback = 0x4
+} JsModuleHostInfoKind;
+
+/// <summary>
+///     User implemented callback to fetch additional imported modules.
+/// </summary>
+/// <remarks>
+/// Notify the host to fetch the dependent module. This is the "import" part before HostResolveImportedModule in ES6 spec.
+/// This notifies the host that the referencing module has the specified module dependency, and the host need to retrieve the module back.
+/// </remarks>
+/// <param name="referencingModule">The referencing module that is requesting the dependency modules.</param>
+/// <param name="specifier">The specifier coming from the module source code.</param>
+/// <param name="dependentModuleRecord">The ModuleRecord of the dependent module. If the module was requested before from other source, return the
+///                           existing ModuleRecord, otherwise return a newly created ModuleRecord.</param>
+/// <returns>
+///     true if the operation succeeded, false otherwise.
+/// </returns>
+typedef JsErrorCode(CHAKRA_CALLBACK * FetchImportedModuleCallBack)(_In_ JsModuleRecord referencingModule, _In_ JsValueRef specifier, _Outptr_result_maybenull_ JsModuleRecord* dependentModuleRecord);
+
+/// <summary>
+///     User implemented callback to get notification when the module is ready.
+/// </summary>
+/// <remarks>
+/// Notify the host after ModuleDeclarationInstantiation step (15.2.1.1.6.4) is finished. If there was error in the process, exceptionVar
+/// holds the exception. Otherwise the referencingModule is ready and the host should schedule execution afterwards.
+/// </remarks>
+/// <param name="referencingModule</param>The referencing module that have finished running ModuleDeclarationInstantiation step.
+/// <param name="exceptionVar">If nullptr, the module is successfully initialized and host should queue the execution job
+///                           otherwise it's the exception object.</param>
+/// <returns>
+///     true if the operation succeeded, false otherwise.
+/// </returns>
+typedef JsErrorCode(CHAKRA_CALLBACK * NotifyModuleReadyCallback)(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar);
+
+/// <summary>
+///     Initialize a ModuleRecord from host
+/// </summary>
+/// <remarks>
+///     Bootstrap the module loading process by creating a new module record.
+/// </remarks>
+/// <param name="referencingModule">The referencingModule as in HostResolveImportedModule (15.2.1.17). nullptr if this is the top level module.</param>
+/// <param name="normalizedSpecifier">The host normalized specifier. This is the key to a unique ModuleRecord.</param>
+/// <param name="moduleRecord">The new ModuleRecord created. The host should not try to call this API twice with the same normalizedSpecifier.
+///                           chakra will return an existing ModuleRecord if the specifier was passed in before.</param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+JsInitializeModuleRecord(
+    _In_opt_ JsModuleRecord referencingModule,
+    _In_ JsValueRef normalizedSpecifier,
+    _Outptr_result_maybenull_ JsModuleRecord* moduleRecord);
+
+/// <summary>
+///     Parse the module source
+/// </summary>
+/// <remarks>
+/// This is basically ParseModule operation in ES6 spec. It is slightly different in that the ModuleRecord was initialized earlier, and passed in as an argument.
+/// </remarks>
+/// <param name="requestModule">The ModuleRecord that holds the parse tree of the source code.</param>
+/// <param name="sourceContext">A cookie identifying the script that can be used by debuggable script contexts.</param>
+/// <param name="script">The source script to be parsed, but not executed in this code.</param>
+/// <param name="scriptLength">The source length of sourceText. The input might contain embedded null.</param>
+/// <param name="sourceFlag">The type of the source code passed in. It could be UNICODE or utf8 at this time.</param>
+/// <param name="exceptionValueRef">The error object if there is parse error.</param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+JsParseModuleSource(
+    _In_ JsModuleRecord requestModule,
+    _In_ JsSourceContext sourceContext,
+    _In_ byte* script,
+    _In_ unsigned int scriptLength,
+    _In_ JsParseModuleSourceFlags sourceFlag,
+    _Outptr_result_maybenull_ JsValueRef* exceptionValueRef);
+
+/// <summary>
+///     Execute module code.
+/// </summary>
+/// <remarks>
+///     This method implements 15.2.1.1.6.5, "ModuleEvaluation" concrete method.
+///     When this methid is called, the chakra engine should have notified the host that the module and all its dependent are ready to be executed.
+///     One moduleRecord will be executed only once. Additional execution call on the same moduleRecord will fail.
+/// </remarks>
+/// <param name="requestModule">The module to be executed.</param>
+/// <param name="result">The return value of the module.</param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+JsModuleEvaluation(
+    _In_ JsModuleRecord requestModule,
+    _Outptr_result_maybenull_ JsValueRef* result);
+
+/// <summary>
+///     Set the host info for the specified module.
+/// </summary>
+/// <param name="requestModule">The request module.</param>
+/// <param name="moduleHostInfo">The type of host info to be set.</param>
+/// <param name="hostInfo">The host info to be set.</param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+JsSetModuleHostInfo(
+    _In_ JsModuleRecord requestModule,
+    _In_ JsModuleHostInfoKind moduleHostInfo,
+    _In_ void* hostInfo);
+
+/// <summary>
+///     Retrieve the host info for the specified module.
+/// </summary>
+/// <param name="requestModule">The request module.</param>
+/// <param name="moduleHostInfo">The type of host info to get.</param>
+/// <param name="hostInfo">The host info to be retrieved.</param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+JsGetModuleHostInfo(
+    _In_  JsModuleRecord requestModule,
+    _In_ JsModuleHostInfoKind moduleHostInfo,
+    _Outptr_result_maybenull_ void** hostInfo);
+
 #endif // _CHAKRACORE_H_

+ 4 - 3
lib/Jsrt/Core/Chakra.Jsrt.Core.vcxproj

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Condition="'$(ChakraBuildPathImported)'!='true'" Project="$(SolutionDir)Chakra.Build.Paths.props"/>
+  <Import Condition="'$(ChakraBuildPathImported)'!='true'" Project="$(SolutionDir)Chakra.Build.Paths.props" />
   <Import Project="$(BuildConfigPropsPath)Chakra.Build.ProjectConfiguration.props" />
   <PropertyGroup Label="Globals">
     <TargetName>Chakra.Jsrt.Core</TargetName>
@@ -34,10 +34,11 @@
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="$(MSBuildThisFileDirectory)JsrtContextCore.cpp" />
+    <ClCompile Include="JsrtCore.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="JsrtContextCore.h" />
   </ItemGroup>
-  <Import Project="$(BuildConfigPropsPath)Chakra.Build.targets" Condition="exists('$(BuildConfigPropsPath)Chakra.Build.targets')"/>
+  <Import Project="$(BuildConfigPropsPath)Chakra.Build.targets" Condition="exists('$(BuildConfigPropsPath)Chakra.Build.targets')" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-</Project>
+</Project>

+ 38 - 0
lib/Jsrt/Core/JsrtContextCore.cpp

@@ -94,3 +94,41 @@ void JsrtContextCore::OnScriptLoad(Js::JavascriptFunction * scriptFunction, Js::
         jsrtDebugManager->ReportScriptCompile(scriptFunction, utf8SourceInfo, compileException);
     }
 }
+
+HRESULT ChakraCoreHostScriptContext::FetchImportedModule(Js::ModuleRecordBase* referencingModule, LPCOLESTR specifier, Js::ModuleRecordBase** dependentModuleRecord)
+{
+    if (fetchImportedModuleCallback == nullptr)
+    {
+        return E_INVALIDARG;
+    }
+    Js::JavascriptString* specifierVar = Js::JavascriptString::NewCopySz(specifier, GetScriptContext());
+    JsModuleRecord dependentRecord = JS_INVALID_REFERENCE;
+    {
+        AUTO_NO_EXCEPTION_REGION;
+        JsErrorCode errorCode = fetchImportedModuleCallback(referencingModule, specifierVar, &dependentRecord);
+        if (errorCode == JsNoError)
+        {
+            *dependentModuleRecord = static_cast<Js::ModuleRecordBase*>(dependentRecord);
+            return NOERROR;
+        }
+    }
+    return E_INVALIDARG;
+}
+
+HRESULT ChakraCoreHostScriptContext::NotifyHostAboutModuleReady(Js::ModuleRecordBase* referencingModule, Js::Var exceptionVar)
+{
+    if (notifyModuleReadyCallback == nullptr)
+    {
+        return E_INVALIDARG;
+    }
+    {
+        AUTO_NO_EXCEPTION_REGION;
+        JsErrorCode errorCode = notifyModuleReadyCallback(referencingModule, exceptionVar);
+        if (errorCode == JsNoError)
+        {
+            return NOERROR;
+        }
+    }
+    return E_INVALIDARG;
+}
+

+ 14 - 11
lib/Jsrt/Core/JsrtContextCore.h

@@ -12,6 +12,7 @@ class JsrtContextCore sealed : public JsrtContext
 public:
     static JsrtContextCore *New(JsrtRuntime * runtime);
     virtual void Dispose(bool isShutdown) override;
+    ChakraCoreHostScriptContext* GetHostScriptContext() const { return hostContext; }
 
     void OnScriptLoad(Js::JavascriptFunction * scriptFunction, Js::Utf8SourceInfo* utf8SourceInfo, CompileScriptException* compileException);
 private:
@@ -25,7 +26,9 @@ class ChakraCoreHostScriptContext sealed : public HostScriptContext
 {
 public:
     ChakraCoreHostScriptContext(Js::ScriptContext* scriptContext)
-        : HostScriptContext(scriptContext)
+        : HostScriptContext(scriptContext),
+        notifyModuleReadyCallback(nullptr),
+        fetchImportedModuleCallback(nullptr)
     {
     }
     ~ChakraCoreHostScriptContext()
@@ -162,18 +165,15 @@ public:
         return E_NOTIMPL;
     }
 
-    HRESULT FetchImportedModule(Js::ModuleRecordBase* referencingModule, LPCOLESTR specifier, Js::ModuleRecordBase** dependentModuleRecord) override
-    {
-        AssertMsg(false, "not implemented");
-        return S_FALSE;
-    }
+    HRESULT FetchImportedModule(Js::ModuleRecordBase* referencingModule, LPCOLESTR specifier, Js::ModuleRecordBase** dependentModuleRecord) override;
 
-    HRESULT NotifyHostAboutModuleReady(Js::ModuleRecordBase* referencingModule, Js::Var exceptionVar) override
-    {
-        AssertMsg(false, "not implemented");
-        return S_FALSE;
-    }
+    HRESULT NotifyHostAboutModuleReady(Js::ModuleRecordBase* referencingModule, Js::Var exceptionVar) override;
 
+    void SetNotifyModuleReadyCallback(NotifyModuleReadyCallback notifyCallback) { this->notifyModuleReadyCallback = notifyCallback; }
+    NotifyModuleReadyCallback GetNotifyModuleReadyCallback() const { return this->notifyModuleReadyCallback; }
+
+    void SetFetchImportedModuleCallback(FetchImportedModuleCallBack fetchCallback) { this->fetchImportedModuleCallback = fetchCallback ; }
+    FetchImportedModuleCallBack GetFetchImportedModuleCallback() const { return this->fetchImportedModuleCallback; }
 
 #if DBG_DUMP || defined(PROFILE_EXEC) || defined(PROFILE_MEM)
     void EnsureParentInfo(Js::ScriptContext* scriptContext = NULL) override
@@ -183,4 +183,7 @@ public:
     }
 #endif
 
+private:
+    FetchImportedModuleCallBack fetchImportedModuleCallback;
+    NotifyModuleReadyCallback notifyModuleReadyCallback;
 };

+ 211 - 0
lib/Jsrt/Core/JsrtCore.cpp

@@ -0,0 +1,211 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#include "JsrtPch.h"
+#include "JsrtInternal.h"
+#include "jsrtHelper.h"
+#include "JsrtContextCore.h"
+#include "chakracore.h"
+
+CHAKRA_API
+JsInitializeModuleRecord(
+    _In_opt_ JsModuleRecord referencingModule,
+    _In_ JsValueRef normalizedSpecifier,
+    _Outptr_result_maybenull_ JsModuleRecord* moduleRecord)
+{
+    PARAM_NOT_NULL(moduleRecord);
+
+    Js::SourceTextModuleRecord* childModuleRecord = nullptr;
+
+    JsErrorCode errorCode = ContextAPIWrapper<true>([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
+        childModuleRecord = Js::SourceTextModuleRecord::Create(scriptContext);
+        if (referencingModule == nullptr)
+        {
+            childModuleRecord->SetIsRootModule();
+        }
+        if (normalizedSpecifier != JS_INVALID_REFERENCE)
+        {
+            childModuleRecord->SetSpecifier(normalizedSpecifier);
+        }
+        return JsNoError;
+    });
+    if (errorCode == JsNoError)
+    {
+        *moduleRecord = childModuleRecord;
+    }
+    else
+    {
+        *moduleRecord = JS_INVALID_REFERENCE;
+    }
+    return errorCode;
+}
+
+CHAKRA_API
+JsParseModuleSource(
+    _In_ JsModuleRecord requestModule,
+    _In_ JsSourceContext sourceContext,
+    _In_ byte* sourceText,
+    _In_ unsigned int sourceLength,
+    _In_ JsParseModuleSourceFlags sourceFlag,
+    _Outptr_result_maybenull_ JsValueRef* exceptionValueRef)
+{
+    PARAM_NOT_NULL(requestModule);
+    PARAM_NOT_NULL(exceptionValueRef);
+    if (sourceFlag > JsParseModuleSourceFlags_DataIsUTF8)
+    {
+        return JsErrorInvalidArgument;
+    }
+
+    *exceptionValueRef = JS_INVALID_REFERENCE;
+    HRESULT hr;
+    if (!Js::SourceTextModuleRecord::Is(requestModule))
+    {
+        return JsErrorInvalidArgument;
+    }
+    Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
+    if (moduleRecord->WasParsed())
+    {
+        return JsErrorModuleParsed;
+    }
+    Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
+    JsErrorCode errorCode = GlobalAPIWrapper([&]() -> JsErrorCode {
+        SourceContextInfo* sourceContextInfo = scriptContext->GetSourceContextInfo(sourceContext, nullptr);
+        if (sourceContextInfo == nullptr)
+        {
+            sourceContextInfo = scriptContext->CreateSourceContextInfo(sourceContext, nullptr, 0, nullptr, nullptr, 0);
+        }
+        SRCINFO si = {
+            /* sourceContextInfo   */ sourceContextInfo,
+            /* dlnHost             */ 0,
+            /* ulColumnHost        */ 0,
+            /* lnMinHost           */ 0,
+            /* ichMinHost          */ 0,
+            /* ichLimHost          */ static_cast<ULONG>(sourceLength),
+            /* ulCharOffset        */ 0,
+            /* mod                 */ 0,
+            /* grfsi               */ 0
+        };
+        hr = moduleRecord->ParseSource(sourceText, sourceLength, &si, exceptionValueRef, sourceFlag == JsParseModuleSourceFlags_DataIsUTF8 ? true : false);
+        if (FAILED(hr))
+        {
+            return JsErrorScriptCompile;
+        }
+        return JsNoError;
+    });
+    return errorCode;
+}
+
+CHAKRA_API
+JsModuleEvaluation(
+    _In_ JsModuleRecord requestModule,
+    _Outptr_result_maybenull_ JsValueRef* result)
+{
+    if (!Js::SourceTextModuleRecord::Is(requestModule))
+    {
+        return JsErrorInvalidArgument;
+    }
+    Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
+    if (moduleRecord->WasEvaluated())
+    {
+        return JsErrorModuleEvaluated;
+    }
+    if (result != nullptr)
+    {
+        *result = JS_INVALID_REFERENCE;
+    }
+    Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
+    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetPinnedJsrtContextObject();
+    JsErrorCode errorCode = SetContextAPIWrapper(jsrtContext, [&](Js::ScriptContext *scriptContext) -> JsErrorCode {
+        SmartFPUControl smartFpuControl;
+        if (smartFpuControl.HasErr())
+        {
+            return JsErrorBadFPUState;
+        }
+        JsValueRef returnRef = moduleRecord->ModuleEvaluation();
+        if (result != nullptr)
+        {
+            *result = returnRef;
+        }
+        return JsNoError;
+    });
+    return errorCode;
+}
+
+CHAKRA_API
+JsSetModuleHostInfo(
+    _In_ JsModuleRecord requestModule,
+    _In_ JsModuleHostInfoKind moduleHostInfo,
+    _In_ void* hostInfo)
+{
+    if (!Js::SourceTextModuleRecord::Is(requestModule))
+    {
+        return JsErrorInvalidArgument;
+    }
+    Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
+    Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
+    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetPinnedJsrtContextObject();
+    JsErrorCode errorCode = SetContextAPIWrapper(jsrtContext, [&](Js::ScriptContext *scriptContext) -> JsErrorCode {
+        JsrtContextCore* currentContext = static_cast<JsrtContextCore*>(JsrtContextCore::GetCurrent());
+        switch (moduleHostInfo)
+        {
+        case JsModuleHostInfo_Exception:
+            moduleRecord->OnHostException(hostInfo);
+            break;
+        case JsModuleHostInfo_HostDefined:
+            moduleRecord->SetHostDefined(hostInfo);
+            break;
+        case JsModuleHostInfo_FetchImportedModuleCallback:
+            currentContext->GetHostScriptContext()->SetFetchImportedModuleCallback(static_cast<FetchImportedModuleCallBack>(hostInfo));
+            break;
+        case JsModuleHostInfo_NotifyModuleReadyCallback:
+            currentContext->GetHostScriptContext()->SetNotifyModuleReadyCallback(static_cast<NotifyModuleReadyCallback>(hostInfo));
+            break;
+        default:
+            return JsInvalidModuleHostInfoKind;
+        };
+        return JsNoError;
+    });
+    return errorCode;
+}
+
+CHAKRA_API
+JsGetModuleHostInfo(
+    _In_  JsModuleRecord requestModule,
+    _In_ JsModuleHostInfoKind moduleHostInfo,
+    _Outptr_result_maybenull_ void** hostInfo)
+{
+    if (!Js::SourceTextModuleRecord::Is(requestModule) || (hostInfo == nullptr))
+    {
+        return JsErrorInvalidArgument;
+    }
+    *hostInfo = nullptr;
+    Js::SourceTextModuleRecord* moduleRecord = Js::SourceTextModuleRecord::FromHost(requestModule);
+    Js::ScriptContext* scriptContext = moduleRecord->GetScriptContext();
+    JsrtContext* jsrtContext = (JsrtContext*)scriptContext->GetLibrary()->GetPinnedJsrtContextObject();
+    JsErrorCode errorCode = SetContextAPIWrapper(jsrtContext, [&](Js::ScriptContext *scriptContext) -> JsErrorCode {
+        JsrtContextCore* currentContext = static_cast<JsrtContextCore*>(JsrtContextCore::GetCurrent());
+        switch (moduleHostInfo)
+        {
+        case JsModuleHostInfo_Exception:
+            if (moduleRecord->GetErrorObject() != nullptr)
+            {
+                *hostInfo = moduleRecord->GetErrorObject();
+            }
+            break;
+        case JsModuleHostInfo_HostDefined:
+            *hostInfo = moduleRecord->GetHostDefined();
+            break;
+        case JsModuleHostInfo_FetchImportedModuleCallback:
+            *hostInfo = currentContext->GetHostScriptContext()->GetFetchImportedModuleCallback();
+            break;
+        case JsModuleHostInfo_NotifyModuleReadyCallback:
+            *hostInfo = currentContext->GetHostScriptContext()->GetNotifyModuleReadyCallback();
+            break;
+        default:
+            return JsInvalidModuleHostInfoKind;
+        };
+        return JsNoError;
+    });
+    return errorCode;
+}

+ 0 - 9
lib/Jsrt/Jsrt.cpp

@@ -3420,15 +3420,6 @@ CHAKRA_API JsRunSerializedScriptUtf8(
         buffer, sourceContext, url, false, result);
 }
 
-CHAKRA_API JsExperimentalApiRunModuleUtf8(
-    _In_z_ const char *script,
-    _In_ JsSourceContext sourceContext,
-    _In_z_ const char *sourceUrl,
-    _Out_ JsValueRef *result)
-{
-    return RunScriptCore(-1, script, sourceContext, sourceUrl, false, JsParseScriptAttributeNone, true, result);
-}
-
 CHAKRA_API JsStringFree(_In_ char* stringValue)
 {
     if (stringValue == nullptr)

+ 0 - 2
lib/Jsrt/JsrtCommonExports.inc

@@ -11,7 +11,6 @@
     JsGetContextData
     JsSetContextData
     JsRunScript
-    JsExperimentalApiRunModule
     JsGetUndefinedValue
     JsGetNullValue
     JsGetTrueValue
@@ -107,7 +106,6 @@
     JsRunScriptUtf8
     JsSerializeScriptUtf8
     JsRunSerializedScriptUtf8
-    JsExperimentalApiRunModuleUtf8
     JsGetPropertyIdFromNameUtf8
     JsStringFree
     JsDiagEvaluateUtf8

+ 64 - 0
lib/Jsrt/JsrtInternal.h

@@ -228,6 +228,70 @@ JsErrorCode ContextAPINoScriptWrapper(Fn fn, bool allowInObjectBeforeCollectCall
     return errCode;
 }
 
+template <class Fn>
+JsErrorCode SetContextAPIWrapper(JsrtContext* newContext, Fn fn)
+{
+    JsrtContext* oldContext = JsrtContext::GetCurrent();
+    Js::ScriptContext* scriptContext = newContext->GetScriptContext();
+
+    JsErrorCode errorCode = JsNoError;
+    try
+    {
+        // For now, treat this like an out of memory; consider if we should do something else here.
+
+        AUTO_NESTED_HANDLED_EXCEPTION_TYPE((ExceptionType)(ExceptionType_OutOfMemory | ExceptionType_StackOverflow | ExceptionType_JavascriptException));
+        if (JsrtContext::TrySetCurrent(newContext))
+        {
+            // Enter script
+            BEGIN_ENTER_SCRIPT(scriptContext, true, true, true)
+            {
+                errorCode = fn(scriptContext);
+            }
+            END_ENTER_SCRIPT
+        }
+        else
+        {
+            errorCode = JsErrorWrongThread;
+        }
+
+        // These are error codes that should only be produced by the wrappers and should never
+        // be produced by the internal calls.
+        Assert(errorCode != JsErrorFatal &&
+            errorCode != JsErrorNoCurrentContext &&
+            errorCode != JsErrorInExceptionState &&
+            errorCode != JsErrorInDisabledState &&
+            errorCode != JsErrorOutOfMemory &&
+            errorCode != JsErrorScriptException &&
+            errorCode != JsErrorScriptTerminated);
+    }
+    catch (Js::OutOfMemoryException)
+    {
+        errorCode = JsErrorOutOfMemory;
+    }
+    catch (Js::JavascriptExceptionObject *  exceptionObject)
+    {
+        scriptContext->GetThreadContext()->SetRecordedException(exceptionObject);
+        errorCode = JsErrorScriptException;
+    }
+    catch (Js::ScriptAbortException)
+    {
+        Assert(scriptContext->GetThreadContext()->GetRecordedException() == nullptr);
+        scriptContext->GetThreadContext()->SetRecordedException(scriptContext->GetThreadContext()->GetPendingTerminatedErrorObject());
+        errorCode = JsErrorScriptTerminated;
+    }
+    catch (Js::EvalDisabledException)
+    {
+        errorCode = JsErrorScriptEvalDisabled;
+    }
+    catch (Js::StackOverflowException)
+    {
+        return JsErrorOutOfMemory;
+    }
+    CATCH_OTHER_EXCEPTIONS
+    JsrtContext::TrySetCurrent(oldContext);
+    return errorCode;
+}
+
 void HandleScriptCompileError(Js::ScriptContext * scriptContext, CompileScriptException * se);
 
 #if DBG

+ 1 - 1
lib/Jsrt/JsrtSourceHolder.cpp

@@ -64,7 +64,7 @@ namespace Js
                 *utf8Script = HeapNewArray(utf8char_t, cbUtf8Buffer);
             }
 
-            *utf8Length = utf8::EncodeIntoAndNullTerminate(*utf8Script, script, static_cast<charcount_t>(length));
+            *utf8Length = utf8::EncodeTrueUtf8IntoAndNullTerminate(*utf8Script, script, static_cast<charcount_t>(length));
             *scriptLength = length;
 
             if (utf8AllocLength != nullptr)

+ 6 - 7
lib/Runtime/Base/ScriptContext.cpp

@@ -375,7 +375,7 @@ namespace Js
 
     ScriptContext::~ScriptContext()
     {
-        Assert(isFinalized);
+        Assert(isFinalized || !isInitialized);
         // Take etw rundown lock on this thread context. We are going to change/destroy this scriptContext.
         AutoCriticalSection autocs(GetThreadContext()->GetEtwRundownCriticalSection());
 
@@ -642,7 +642,7 @@ namespace Js
         // Stop profiling if present
         DeRegisterProfileProbe(S_OK, nullptr);
 #endif
-        
+
         if (this->diagnosticArena != nullptr)
         {
             HeapDelete(this->diagnosticArena);
@@ -1189,13 +1189,12 @@ if (!sourceList)
         }
 
 #if DYNAMIC_INTERPRETER_THUNK
-        interpreterThunkEmitter = HeapNew(InterpreterThunkEmitter, SourceCodeAllocator(), this->GetThreadContext()->GetThunkPageAllocators(),
-            Js::InterpreterStackFrame::InterpreterThunk);
+        interpreterThunkEmitter = HeapNew(InterpreterThunkEmitter, SourceCodeAllocator(), this->GetThreadContext()->GetThunkPageAllocators());
 #endif
 
 #ifdef ASMJS_PLAT
         asmJsInterpreterThunkEmitter = HeapNew(InterpreterThunkEmitter, SourceCodeAllocator(), this->GetThreadContext()->GetThunkPageAllocators(),
-            Js::InterpreterStackFrame::InterpreterAsmThunk);
+            true);
 #endif
 
         JS_ETW(EtwTrace::LogScriptContextLoadEvent(this));
@@ -3344,11 +3343,11 @@ if (!sourceList)
                     || entryPoint == DefaultDeferredDeserializeThunk || entryPoint == ProfileDeferredDeserializeThunk
                     || entryPoint == CrossSite::DefaultThunk || entryPoint == CrossSite::ProfileThunk);
 #else
-                Assert(entryPoint == DefaultDeferredParsingThunk 
+                Assert(entryPoint == DefaultDeferredParsingThunk
                     || entryPoint == DefaultDeferredDeserializeThunk
                     || entryPoint == CrossSite::DefaultThunk);
 #endif
-                
+
                 Assert(!proxy->IsDeferred());
                 Assert(proxy->GetFunctionBody()->GetProfileSession() == proxy->GetScriptContext()->GetProfileSession());
 

+ 3 - 1
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -1387,7 +1387,9 @@ void ByteCodeGenerator::DefineUserVars(FuncInfo *funcInfo)
                         }
                     }
 
-                    if ((!sym->GetHasInit() && !sym->IsInSlot(funcInfo)) ||
+                    // Undef-initialize the home location if it is a register (not closure-captured, or else capture
+                    // is delayed) or a property of an object.
+                    if ((!sym->GetHasInit() && (!sym->NeedsSlotAlloc(funcInfo) || sym->GetHasNonCommittedReference())) ||
                         (funcInfo->bodyScope->GetIsObject() && !funcInfo->GetHasCachedScope()))
                     {
                         Js::RegSlot reg = sym->GetLocation();

+ 2 - 2
lib/Runtime/Debug/TTActionEvents.cpp

@@ -338,7 +338,7 @@ namespace TTD
             const JsRTVarsArgumentAction* action = GetInlineEventDataAs<JsRTVarsArgumentAction, EventKind::GetOwnPropertyNamesInfoActionTag>(evt);
             Js::Var var = InflateVarInReplay(ctx, action->Var1);
 
-            Js::Var res = Js::JavascriptOperators::GetOwnPropertyNames(var, ctx);
+            Js::JavascriptArray* res = Js::JavascriptOperators::GetOwnPropertyNames(var, ctx);
 
             JsRTActionHandleResultForReplay<JsRTVarsArgumentAction, EventKind::GetOwnPropertyNamesInfoActionTag>(ctx, evt, res);
         }
@@ -348,7 +348,7 @@ namespace TTD
             const JsRTVarsArgumentAction* action = GetInlineEventDataAs<JsRTVarsArgumentAction, EventKind::GetOwnPropertySymbolsInfoActionTag>(evt);
             Js::Var var = InflateVarInReplay(ctx, action->Var1);
 
-            Js::Var res = Js::JavascriptOperators::GetOwnPropertySymbols(var, ctx);
+            Js::JavascriptArray* res = Js::JavascriptOperators::GetOwnPropertySymbols(var, ctx);
 
             JsRTActionHandleResultForReplay<JsRTVarsArgumentAction, EventKind::GetOwnPropertySymbolsInfoActionTag>(ctx, evt, res);
         }

+ 1 - 3
lib/Runtime/Language/JavascriptConversion.cpp

@@ -295,7 +295,7 @@ CommonNumber:
     // ToPropertyKey() takes a value and converts it to a property key
     // Implementation of ES6 7.1.14
     //----------------------------------------------------------------------------
-    BOOL JavascriptConversion::ToPropertyKey(Var argument, ScriptContext* scriptContext, const PropertyRecord** propertyRecord)
+    void JavascriptConversion::ToPropertyKey(Var argument, ScriptContext* scriptContext, const PropertyRecord** propertyRecord)
     {
         Var key = JavascriptConversion::ToPrimitive(argument, JavascriptHint::HintString, scriptContext);
 
@@ -319,8 +319,6 @@ CommonNumber:
                 scriptContext->GetOrAddPropertyRecord(propName->GetString(), propName->GetLength(), propertyRecord);
             }
         }
-
-        return TRUE;
     }
 
     //----------------------------------------------------------------------------

+ 1 - 1
lib/Runtime/Language/JavascriptConversion.h

@@ -14,7 +14,7 @@ namespace Js {
         static Var ToPrimitive(Var aValue, JavascriptHint hint, ScriptContext * scriptContext);
         static BOOL CanonicalNumericIndexString(Var aValue, double *indexValue, ScriptContext * scriptContext);
 
-        static BOOL ToPropertyKey(Var argument, ScriptContext* scriptContext, const PropertyRecord** propertyRecord);
+        static void ToPropertyKey(Var argument, ScriptContext* scriptContext, const PropertyRecord** propertyRecord);
 
         static JavascriptString* ToString(Var aValue, ScriptContext* scriptContext);
         static JavascriptString* ToLocaleString(Var aValue, ScriptContext* scriptContext);

+ 11 - 37
lib/Runtime/Language/JavascriptOperators.cpp

@@ -1112,7 +1112,7 @@ CommonNumber:
         return result;
     }
 
-    Var JavascriptOperators::GetOwnPropertyNames(Var instance, ScriptContext *scriptContext)
+    JavascriptArray* JavascriptOperators::GetOwnPropertyNames(Var instance, ScriptContext *scriptContext)
     {
         RecyclableObject *object = RecyclableObject::FromVar(ToObject(instance, scriptContext));
 
@@ -1125,7 +1125,7 @@ CommonNumber:
         return JavascriptObject::CreateOwnStringPropertiesHelper(object, scriptContext);
     }
 
-    Var JavascriptOperators::GetOwnPropertySymbols(Var instance, ScriptContext *scriptContext)
+    JavascriptArray* JavascriptOperators::GetOwnPropertySymbols(Var instance, ScriptContext *scriptContext)
     {
         RecyclableObject *object = RecyclableObject::FromVar(ToObject(instance, scriptContext));
         CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(GetOwnPropertySymbolsCount);
@@ -1139,7 +1139,7 @@ CommonNumber:
         return JavascriptObject::CreateOwnSymbolPropertiesHelper(object, scriptContext);
     }
 
-    Var JavascriptOperators::GetOwnPropertyKeys(Var instance, ScriptContext* scriptContext)
+    JavascriptArray* JavascriptOperators::GetOwnPropertyKeys(Var instance, ScriptContext* scriptContext)
     {
         RecyclableObject *object = RecyclableObject::FromVar(ToObject(instance, scriptContext));
 
@@ -1152,26 +1152,15 @@ CommonNumber:
         return JavascriptObject::CreateOwnStringSymbolPropertiesHelper(object, scriptContext);
     }
 
-    Var JavascriptOperators::GetOwnEnumerablePropertyNames(Var instance, ScriptContext* scriptContext)
+    JavascriptArray* JavascriptOperators::GetOwnEnumerablePropertyNames(Var instance, ScriptContext* scriptContext)
     {
         RecyclableObject *object = RecyclableObject::FromVar(ToObject(instance, scriptContext));
 
         if (JavascriptProxy::Is(instance))
         {
             JavascriptProxy* proxy = JavascriptProxy::FromVar(instance);
-            Var result = proxy->PropertyKeysTrap(JavascriptProxy::KeysTrapKind::GetOwnPropertyNamesKind);
-
-            AssertMsg(JavascriptArray::Is(result), "PropertyKeysTrap should return JavascriptArray.");
-            JavascriptArray* proxyResult;
+            JavascriptArray* proxyResult = proxy->PropertyKeysTrap(JavascriptProxy::KeysTrapKind::GetOwnPropertyNamesKind);
             JavascriptArray* proxyResultToReturn = scriptContext->GetLibrary()->CreateArray(0);
-            if (JavascriptArray::Is(result))
-            {
-                proxyResult = JavascriptArray::FromVar(result);
-            }
-            else
-            {
-                return proxyResultToReturn;
-            }
 
             // filter enumerable keys
             uint32 resultLength = proxyResult->GetLength();
@@ -1199,7 +1188,7 @@ CommonNumber:
         return JavascriptObject::CreateOwnEnumerableStringPropertiesHelper(object, scriptContext);
     }
 
-    Var JavascriptOperators::GetOwnEnumerablePropertyNamesSymbols(Var instance, ScriptContext* scriptContext)
+    JavascriptArray* JavascriptOperators::GetOwnEnumerablePropertyNamesSymbols(Var instance, ScriptContext* scriptContext)
     {
         RecyclableObject *object = RecyclableObject::FromVar(ToObject(instance, scriptContext));
 
@@ -4599,7 +4588,7 @@ CommonNumber:
     {
         if (length <= 0)
         {
-            return true;
+            return false;
         }
 
         TypeId instanceType = JavascriptOperators::GetTypeId(srcInstance);
@@ -4706,7 +4695,7 @@ CommonNumber:
     {
         if (length <= 0)
         {
-            return true;
+            return false;
         }
         TypeId instanceType = JavascriptOperators::GetTypeId(instance);
         BOOL  returnValue = false;
@@ -6893,7 +6882,7 @@ CommonNumber:
     HeapArgumentsObject *JavascriptOperators::CreateHeapArguments(JavascriptFunction *funcCallee, uint32 actualsCount, uint32 formalsCount, Var frameObj, ScriptContext* scriptContext)
     {
         JavascriptLibrary *library = scriptContext->GetLibrary();
-        HeapArgumentsObject *argsObj = library->CreateHeapArguments(frameObj, formalsCount);
+        HeapArgumentsObject *argsObj = library->CreateHeapArguments(frameObj, formalsCount, !!funcCallee->IsStrictMode());
 
         //
         // Set the number of arguments of Arguments Object
@@ -6904,27 +6893,12 @@ CommonNumber:
         JavascriptOperators::SetProperty(argsObj, argsObj, PropertyIds::_symbolIterator, library->GetArrayPrototypeValuesFunction(), scriptContext);
         if (funcCallee->IsStrictMode())
         {
-            PropertyDescriptor propertyDescriptorCaller;
-            JavascriptFunction* callerAccessor = library->GetThrowTypeErrorCallerAccessorFunction();
-
-            propertyDescriptorCaller.SetGetter(callerAccessor);
-            propertyDescriptorCaller.SetSetter(callerAccessor);
-            propertyDescriptorCaller.SetEnumerable(false);
-            propertyDescriptorCaller.SetConfigurable(false);
-
+            JavascriptFunction* callerAccessor = library->GetThrowTypeErrorCallerAccessorFunction();            
             argsObj->SetAccessors(PropertyIds::caller, callerAccessor, callerAccessor, PropertyOperation_NonFixedValue);
-            JavascriptOperators::SetAttributes(argsObj, PropertyIds::caller, propertyDescriptorCaller, false);
 
-            PropertyDescriptor propertyDescriptorCallee;
             JavascriptFunction* calleeAccessor = library->GetThrowTypeErrorCalleeAccessorFunction();
-
-            propertyDescriptorCallee.SetGetter(calleeAccessor);
-            propertyDescriptorCallee.SetSetter(calleeAccessor);
-            propertyDescriptorCallee.SetEnumerable(false);
-            propertyDescriptorCallee.SetConfigurable(false);
-
             argsObj->SetAccessors(PropertyIds::callee, calleeAccessor, calleeAccessor, PropertyOperation_NonFixedValue);
-            JavascriptOperators::SetAttributes(argsObj, PropertyIds::callee, propertyDescriptorCallee, false);
+
         }
         else
         {

+ 5 - 5
lib/Runtime/Language/JavascriptOperators.h

@@ -151,13 +151,13 @@ namespace Js
         static void OP_EnsureNoRootProperty(Var instance, PropertyId propertyId);
         static void OP_EnsureNoRootRedeclProperty(Var instance, PropertyId propertyId);
         static void OP_ScopedEnsureNoRedeclProperty(FrameDisplay *pDisplay, PropertyId propertyId, Var instanceDefault);
-        static Var  GetOwnPropertyNames(Var instance, ScriptContext *scriptContext);
-        static Var  GetOwnPropertySymbols(Var instance, ScriptContext *scriptContext);
-        static Var  GetOwnPropertyKeys(Var instance, ScriptContext *scriptContext);
+        static JavascriptArray*  GetOwnPropertyNames(Var instance, ScriptContext *scriptContext);
+        static JavascriptArray*  GetOwnPropertySymbols(Var instance, ScriptContext *scriptContext);
+        static JavascriptArray*  GetOwnPropertyKeys(Var instance, ScriptContext *scriptContext);
 
 
-        static Var  GetOwnEnumerablePropertyNames(Var instance, ScriptContext *scriptContext);
-        static Var  GetOwnEnumerablePropertyNamesSymbols(Var instance, ScriptContext *scriptContext);
+        static JavascriptArray*  GetOwnEnumerablePropertyNames(Var instance, ScriptContext *scriptContext);
+        static JavascriptArray*  GetOwnEnumerablePropertyNamesSymbols(Var instance, ScriptContext *scriptContext);
 
         static BOOL GetOwnPropertyDescriptor(RecyclableObject* obj, PropertyId propertyId, ScriptContext* scriptContext, PropertyDescriptor* propertyDescriptor);
         static BOOL GetOwnPropertyDescriptor(RecyclableObject* obj, JavascriptString* propertyKey, ScriptContext* scriptContext, PropertyDescriptor* propertyDescriptor);

+ 24 - 7
lib/Runtime/Language/SourceTextModuleRecord.cpp

@@ -23,6 +23,7 @@ namespace Js
         localExportMapByExportName(nullptr),
         localExportMapByLocalName(nullptr),
         localExportIndexList(nullptr),
+        normalizedSpecifier(nullptr),
         errorObject(nullptr),
         hostDefined(nullptr),
         exportedNames(nullptr),
@@ -30,6 +31,7 @@ namespace Js
         wasParsed(false),
         wasDeclarationInitialized(false),
         isRootModule(false),
+        hadNotifyHostReady(false),
         localExportSlots(nullptr),
         numUnParsedChildrenModule(0),
         moduleId(InvalidModuleIndex),
@@ -199,7 +201,11 @@ namespace Js
                 Assert(this->errorObject == nullptr);
 
                 ModuleDeclarationInstantiation();
-                hr = scriptContext->GetHostScriptContext()->NotifyHostAboutModuleReady(this, this->errorObject);
+                if (!hadNotifyHostReady)
+                {
+                    hr = scriptContext->GetHostScriptContext()->NotifyHostAboutModuleReady(this, this->errorObject);
+                    hadNotifyHostReady = true;
+                }
             }
         }
         return hr;
@@ -208,11 +214,6 @@ namespace Js
     HRESULT SourceTextModuleRecord::OnChildModuleReady(SourceTextModuleRecord* childModule, Var childException)
     {
         HRESULT hr = NOERROR;
-        if (numUnParsedChildrenModule == 0)
-        {
-            return NOERROR; // this is only in case of recursive module reference. Let the higher stack frame handle this module.
-        }
-        numUnParsedChildrenModule--;
         if (childException != nullptr)
         {
             // propagate the error up as needed.
@@ -221,9 +222,20 @@ namespace Js
                 this->errorObject = childException;
             }
             NotifyParentsAsNeeded();
+            if (isRootModule && !hadNotifyHostReady)
+            {
+                hr = scriptContext->GetHostScriptContext()->NotifyHostAboutModuleReady(this, this->errorObject);
+                hadNotifyHostReady = true;
+            }
         }
         else
         {
+            if (numUnParsedChildrenModule == 0)
+            {
+                return NOERROR; // this is only in case of recursive module reference. Let the higher stack frame handle this module.
+            }
+            numUnParsedChildrenModule--;
+
             hr = PrepareForModuleDeclarationInitialization();
         }
         return hr;
@@ -642,8 +654,13 @@ namespace Js
         {
             return nullptr;
         }
-        SetWasEvaluated();
         Assert(this->errorObject == nullptr);
+        if (this->errorObject != nullptr)
+        {
+            JavascriptExceptionOperators::Throw(errorObject, scriptContext);
+        }
+        Assert(!WasEvaluated());
+        SetWasEvaluated();
         // we shouldn't evaluate if there are existing failure. This is defense in depth as the host shouldn't 
         // call into evaluation if there was previous fialure on the module.
         if (this->errorObject)

+ 18 - 0
lib/Runtime/Language/SourceTextModuleRecord.h

@@ -43,6 +43,11 @@ namespace Js
         void* GetHostDefined() const { return hostDefined; }
         void SetHostDefined(void* hostObj) { hostDefined = hostObj; }
 
+        void SetSpecifier(Var specifier) { this->normalizedSpecifier = specifier; }
+        Var GetSpecifier() const { return normalizedSpecifier; }
+
+        Var GetErrorObject() const { return errorObject; }
+
         bool WasParsed() const { return wasParsed; }
         void SetWasParsed() { wasParsed = true; }
         bool WasDeclarationInitialized() const { return wasDeclarationInitialized; }
@@ -65,6 +70,17 @@ namespace Js
             Assert((moduleRecord == nullptr) || (moduleRecord->magicNumber == moduleRecord->ModuleMagicNumber));
             return moduleRecord;
         }
+
+        static bool Is(void* hostModuleRecord)
+        {
+            SourceTextModuleRecord* moduleRecord = static_cast<SourceTextModuleRecord*>(hostModuleRecord);
+            if (moduleRecord != nullptr && (moduleRecord->magicNumber == moduleRecord->ModuleMagicNumber))
+            {
+                return true;
+            }
+            return false;
+        }
+
         static SourceTextModuleRecord* Create(ScriptContext* scriptContext);
 
         uint GetLocalExportSlotIndexByExportName(PropertyId exportNameId);
@@ -89,6 +105,7 @@ namespace Js
         bool wasParsed;
         bool wasDeclarationInitialized;
         bool isRootModule;
+        bool hadNotifyHostReady;
         ParseNodePtr parseTree;
         Utf8SourceInfo* pSourceInfo;
         uint sourceIndex;
@@ -110,6 +127,7 @@ namespace Js
 
         Js::JavascriptFunction* rootFunction;
         void* hostDefined;
+        Var normalizedSpecifier;
         Var errorObject;
         Var* localExportSlots;
 

+ 26 - 3
lib/Runtime/Library/ArrayBuffer.cpp

@@ -909,6 +909,29 @@ namespace Js
         /* See JavascriptArrayBuffer::Finalize */
     }
 
+    // Copy memory from src to dst, truncate if dst smaller, zero extra memory
+    // if dst larger
+    static void MemCpyZero(__bcount(dstSize) BYTE* dst, size_t dstSize,
+                           __in_bcount(count) const BYTE* src, size_t count)
+    {
+        js_memcpy_s(dst, dstSize, src, min(dstSize, count));
+        if (dstSize > count)
+        {
+            ZeroMemory(dst + count, dstSize - count);
+        }
+    }
+
+    // Same as realloc but zero newly allocated portion if newSize > oldSize
+    static BYTE* ReallocZero(BYTE* ptr, size_t oldSize, size_t newSize)
+    {
+        BYTE* ptrNew = (BYTE*)realloc(ptr, newSize);
+        if (ptrNew && newSize > oldSize)
+        {
+            ZeroMemory(ptrNew + oldSize, newSize - oldSize);
+        }
+        return ptrNew;
+    }
+
     ArrayBuffer * JavascriptArrayBuffer::TransferInternal(uint32 newBufferLength)
     {
         ArrayBuffer* newArrayBuffer;
@@ -975,7 +998,7 @@ namespace Js
                         recycler->ReportExternalMemoryFailure(newBufferLength);
                         JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
                     }
-                    js_memcpy_s(newBuffer, newBufferLength, this->buffer, newBufferLength);
+                    MemCpyZero(newBuffer, newBufferLength, this->buffer, this->bufferLength);
                 }
             }
             else
@@ -984,12 +1007,12 @@ namespace Js
                 {
                     // we are transferring from an unoptimized buffer, but new length can be optimized, so move to that
                     newBuffer = (BYTE*)JavascriptArrayBuffer::AllocWrapper(newBufferLength);
-                    js_memcpy_s(newBuffer, newBufferLength, this->buffer, newBufferLength);
+                    MemCpyZero(newBuffer, newBufferLength, this->buffer, this->bufferLength);
                 }
                 else if (newBufferLength != this->bufferLength)
                 {
                     // both sides will just be regular ArrayBuffer, so realloc
-                    newBuffer = (BYTE*)realloc(this->buffer, newBufferLength);
+                    newBuffer = ReallocZero(this->buffer, this->bufferLength, newBufferLength);
                     if (!newBuffer)
                     {
                         recycler->ReportExternalMemoryFailure(newBufferLength);

+ 1 - 11
lib/Runtime/Library/JSON.cpp

@@ -603,17 +603,7 @@ namespace JSON
             if (JavascriptProxy::Is(object))
             {
                 JavascriptProxy* proxyObject = JavascriptProxy::FromVar(object);
-                Var propertyKeysTrapResult = proxyObject->PropertyKeysTrap(JavascriptProxy::KeysTrapKind::GetOwnPropertyNamesKind);
-
-                AssertMsg(JavascriptArray::Is(propertyKeysTrapResult), "PropertyKeysTrap should return JavascriptArray.");
-                JavascriptArray* proxyResult;
-                if (JavascriptArray::Is(propertyKeysTrapResult))
-                {
-                    proxyResult = JavascriptArray::FromVar(propertyKeysTrapResult);
-                } else
-                {
-                    proxyResult = scriptContext->GetLibrary()->CreateArray(0);
-                }
+                JavascriptArray* proxyResult = proxyObject->PropertyKeysTrap(JavascriptProxy::KeysTrapKind::GetOwnPropertyNamesKind);
 
                 // filter enumerable keys
                 uint32 resultLength = proxyResult->GetLength();

+ 58 - 34
lib/Runtime/Library/JavascriptArray.cpp

@@ -2918,7 +2918,8 @@ namespace Js
                 }
             }
 
-            if (pDestArray && JavascriptArray::IsDirectAccessArray(aItem) && JavascriptArray::IsDirectAccessArray(pDestArray)) // Fast path
+            if (pDestArray && JavascriptArray::IsDirectAccessArray(aItem) && JavascriptArray::IsDirectAccessArray(pDestArray)
+                && BigIndex(idxDest + JavascriptArray::FromVar(aItem)->length).IsSmallIndex()) // Fast path
             {
                 if (JavascriptNativeIntArray::Is(aItem))
                 {
@@ -5716,6 +5717,7 @@ Case0:
         bool isIntArray = false;
         bool isFloatArray = false;
         bool isTypedArrayEntryPoint = typedArrayBase != nullptr;
+        bool isBuiltinArrayCtor = true;
         T startT = 0;
         T newLenT = length;
         T endT = length;
@@ -5768,10 +5770,11 @@ Case0:
         if (isTypedArrayEntryPoint)
         {
             Var constructor = JavascriptOperators::SpeciesConstructor(typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext);
+            isBuiltinArrayCtor = (constructor == library->GetArrayConstructor());
 
             // If we have an array source object, we need to make sure to do the right thing if it's a native array.
             // The helpers below which do the element copying require the source and destination arrays to have the same native type.
-            if (pArr && constructor == library->GetArrayConstructor())
+            if (pArr && isBuiltinArrayCtor)
             {
                 if (newLenT > JavascriptArray::MaxArrayLength)
                 {
@@ -5805,13 +5808,13 @@ Case0:
 
         else if (pArr != nullptr)
         {
-            newObj = ArraySpeciesCreate(pArr, newLenT, scriptContext, &isIntArray, &isFloatArray);
+            newObj = ArraySpeciesCreate(pArr, newLenT, scriptContext, &isIntArray, &isFloatArray, &isBuiltinArrayCtor);
         }
 
         // skip the typed array and "pure" array case, we still need to handle special arrays like es5array, remote array, and proxy of array.
         else
         {
-            newObj = ArraySpeciesCreate(obj, newLenT, scriptContext);
+            newObj = ArraySpeciesCreate(obj, newLenT, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor);
         }
 
         // If we didn't create a new object above we will create a new array here.
@@ -5855,7 +5858,7 @@ Case0:
         if (pArr)
         {
             // If we constructed a new Array object, we have some nice helpers here
-            if (newArr)
+            if (newArr && isBuiltinArrayCtor)
             {
                 if (JavascriptArray::IsDirectAccessArray(newArr))
                 {
@@ -5901,7 +5904,7 @@ Case0:
                             continue;
                         }
 
-                        newArr->DirectSetItemAt(i, element);
+                        newArr->SetItem(i, element, PropertyOperation_None);
                     }
                 }
             }
@@ -5967,7 +5970,7 @@ Case0:
                     Var element = JavascriptOperators::GetItem(obj, i + start, scriptContext);
                     if (newArr != nullptr)
                     {
-                        newArr->DirectSetItemAt(i, element);
+                        newArr->SetItem(i, element, PropertyOperation_None);
                     }
                     else
                     {
@@ -6254,21 +6257,22 @@ Case0:
                 {
                     countUndefined++;
                 }
-                orig[i] = SparseArraySegment<Var>::GetMissingItem();
             }
         }
 
-        if (count == 0)
+        if (count > 0)
         {
-            *len = 0; // set the length to zero
-            return countUndefined;
-        }
+            SortElements(elements, 0, count - 1);
 
-        SortElements(elements, 0, count - 1);
+            for (uint32 i = 0; i < count; ++i)
+            {
+                orig[i] = elements[i].Value;
+            }
+        }
 
-        for (uint32 i = 0; i < count; ++i)
+        for (uint32 i = count + countUndefined; i < *len; ++i)
         {
-            orig[i] = elements[i].Value;
+            orig[i] = SparseArraySegment<Var>::GetMissingItem();
         }
 
         *len = count; // set the correct length
@@ -6599,6 +6603,7 @@ Case0:
 
             bool isIntArray = false;
             bool isFloatArray = false;
+            bool isBuiltinArrayCtor = true;
             JavascriptArray *newArr = nullptr;
 
             // Just dump the segment map on splice (before any possible allocation and throw)
@@ -6606,7 +6611,7 @@ Case0:
 
             // If the source object is an Array exotic object (Array.isArray) we should try to load the constructor property
             // and use it to construct the return object.
-            newObj = ArraySpeciesCreate(pArr, deleteLen, scriptContext);
+            newObj = ArraySpeciesCreate(pArr, deleteLen, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor);
             if (newObj != nullptr)
             {
                 pArr = EnsureNonNativeArray(pArr);
@@ -6624,7 +6629,7 @@ Case0:
             }
 
             // If return object is a JavascriptArray, we can use all the array splice helpers
-            if (newArr)
+            if (newArr && isBuiltinArrayCtor)
             {
 
                 // Array has a single segment (need not start at 0) and splice start lies in the range
@@ -6703,6 +6708,15 @@ Case0:
                     pArr->length = newLen;
                 }
 
+                if (newArr->length != deleteLen)
+                {
+                    newArr->SetLength(deleteLen);
+                }
+                else
+                {
+                    newArr->length = deleteLen;
+                }
+
                 newArr->InvalidateLastUsedSegment();
 
 #ifdef VALIDATE_ARRAY
@@ -7119,7 +7133,7 @@ Case0:
                    Var element = JavascriptOperators::GetItem(pObj, start + i, scriptContext);
                    if (pnewArr)
                    {
-                       pnewArr->DirectSetItemAt(i, element);
+                       pnewArr->SetItem(i, element, PropertyOperation_None);
                    }
                    else
                    {
@@ -7180,6 +7194,7 @@ Case0:
         // Set up new length
         indexT newLen = indexT(len - deleteLen) + insertLen;
         h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pObj, pObj, PropertyIds::length, IndexTrace<indexT>::ToNumber(newLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
+        h.ThrowTypeErrorOnFailure(JavascriptOperators::SetProperty(pNewObj, pNewObj, PropertyIds::length, IndexTrace<indexT>::ToNumber(deleteLen, scriptContext), scriptContext, PropertyOperation_ThrowIfNotExtensible));
 #ifdef VALIDATE_ARRAY
         if (pnewArr)
         {
@@ -8809,6 +8824,7 @@ Case0:
         RecyclableObject* newObj = nullptr;
         JavascriptArray* newArr = nullptr;
         bool isTypedArrayEntryPoint = typedArrayBase != nullptr;
+        bool isBuiltinArrayCtor = true;
 
         if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
         {
@@ -8846,6 +8862,7 @@ Case0:
         {
             Var constructor = JavascriptOperators::SpeciesConstructor(
                 typedArrayBase, TypedArrayBase::GetDefaultConstructor(args[0], scriptContext), scriptContext);
+            isBuiltinArrayCtor = (constructor == scriptContext->GetLibrary()->GetArrayConstructor());
 
             if (JavascriptOperators::IsConstructor(constructor))
             {
@@ -8862,7 +8879,7 @@ Case0:
         // skip the typed array and "pure" array case, we still need to handle special arrays like es5array, remote array, and proxy of array.
         else if (pArr == nullptr || scriptContext->GetConfig()->IsES6SpeciesEnabled())
         {
-            newObj = ArraySpeciesCreate(obj, length, scriptContext);
+            newObj = ArraySpeciesCreate(obj, length, scriptContext, nullptr, nullptr, &isBuiltinArrayCtor);
         }
 
         if (newObj == nullptr)
@@ -8910,7 +8927,7 @@ Case0:
                     pArr);
 
                 // If newArr is a valid pointer, then we constructed an array to return. Otherwise we need to do generic object operations
-                if (newArr)
+                if (newArr && isBuiltinArrayCtor)
                 {
                     newArr->DirectSetItemAt(k, mappedValue);
                 }
@@ -9681,7 +9698,7 @@ Case0:
 
                 if (newArr)
                 {
-                    newArr->DirectSetItemAt(k, nextValue);
+                    newArr->SetItem(k, nextValue, PropertyOperation_None);
                 }
                 else
                 {
@@ -9750,7 +9767,7 @@ Case0:
 
                 if (newArr)
                 {
-                    newArr->DirectSetItemAt(k, kValue);
+                    newArr->SetItem(k, kValue, PropertyOperation_None);
                 }
                 else
                 {
@@ -9800,10 +9817,12 @@ Case0:
         Var newObj = nullptr;
         JavascriptArray* newArr = nullptr;
         TypedArrayBase* newTypedArray = nullptr;
+        bool isBuiltinArrayCtor = true;
 
         if (JavascriptOperators::IsConstructor(args[0]))
         {
             RecyclableObject* constructor = RecyclableObject::FromVar(args[0]);
+            isBuiltinArrayCtor = (constructor == scriptContext->GetLibrary()->GetArrayConstructor());
 
             Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(len, scriptContext) };
             Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
@@ -9837,7 +9856,7 @@ Case0:
         // At least we have a new object of some kind
         Assert(newObj);
 
-        if (newArr)
+        if (isBuiltinArrayCtor)
         {
             for (uint32 k = 0; k < len; k++)
             {
@@ -10018,11 +10037,11 @@ Case0:
                     if (index < startIndex) continue;
                     else if (index >= limitIndex) break;
 
-                    Var value;
-                    BOOL success = JavascriptOperators::GetOwnItem(es5Array, index, &value, scriptContext);
-                    Assert(success);
-
-                    fn(index, value);
+                    Var value = nullptr;
+                    if (JavascriptOperators::GetOwnItem(es5Array, index, &value, scriptContext))
+                    {
+                        fn(index, value);
+                    }
                 }
             }
         }
@@ -10085,11 +10104,11 @@ Case0:
                         T n = destIndex + (index - startIndex);
                         if (destArray == nullptr || !destArray->DirectGetItemAt(n, &oldValue))
                         {
-                            Var value;
-                            BOOL success = JavascriptOperators::GetOwnItem(es5Array, index, &value, scriptContext);
-                            Assert(success);
-
-                            fn(index, value);
+                            Var value = nullptr;
+                            if (JavascriptOperators::GetOwnItem(es5Array, index, &value, scriptContext))
+                            {
+                                fn(index, value);
+                            }
                         }
                     }
                 }
@@ -11470,7 +11489,7 @@ Case0:
 
     template<typename T>
     RecyclableObject*
-    JavascriptArray::ArraySpeciesCreate(Var originalArray, T length, ScriptContext* scriptContext, bool* pIsIntArray, bool* pIsFloatArray)
+    JavascriptArray::ArraySpeciesCreate(Var originalArray, T length, ScriptContext* scriptContext, bool *pIsIntArray, bool *pIsFloatArray, bool *pIsBuiltinArrayCtor)
     {
         if (originalArray == nullptr || !scriptContext->GetConfig()->IsES6SpeciesEnabled())
         {
@@ -11544,6 +11563,11 @@ Case0:
             JavascriptError::ThrowTypeError(scriptContext, JSERR_NotAConstructor, _u("constructor[Symbol.species]"));
         }
 
+        if (pIsBuiltinArrayCtor != nullptr)
+        {
+            *pIsBuiltinArrayCtor = false;
+        }
+
         Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(length, scriptContext) };
         Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
 

+ 1 - 1
lib/Runtime/Library/JavascriptArray.h

@@ -766,7 +766,7 @@ namespace Js
         static JavascriptArray* ConcatFloatArgs(JavascriptNativeFloatArray* pDestArray, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext);
     private:
         template<typename T=uint32>
-        static RecyclableObject* ArraySpeciesCreate(Var pThisArray, T length, ScriptContext* scriptContext, bool* pIsIntArray = nullptr, bool* pIsFloatArray = nullptr);
+        static RecyclableObject* ArraySpeciesCreate(Var pThisArray, T length, ScriptContext* scriptContext, bool *pIsIntArray = nullptr, bool *pIsFloatArray = nullptr, bool *pIsBuiltinArrayCtor = nullptr);
         template <typename T, typename R> static R ConvertToIndex(T idxDest, ScriptContext* scriptContext) { Throw::InternalError(); return 0; }
         static BOOL SetArrayLikeObjects(RecyclableObject* pDestObj, uint32 idxDest, Var aItem);
         static BOOL SetArrayLikeObjects(RecyclableObject* pDestObj, BigIndex idxDest, Var aItem);

+ 1 - 0
lib/Runtime/Library/JavascriptBuiltInFunctionList.h

@@ -139,6 +139,7 @@ BUILTIN(JavascriptError, ToString, EntryToString, FunctionInfo::ErrorOnNew | Fun
 BUILTIN(JavascriptExternalFunction, ExternalFunctionThunk, ExternalFunctionThunk, FunctionInfo::None)
 BUILTIN(JavascriptExternalFunction, WrappedFunctionThunk, WrappedFunctionThunk, FunctionInfo::None)
 BUILTIN(JavascriptExternalFunction, StdCallExternalFunctionThunk, StdCallExternalFunctionThunk, FunctionInfo::None)
+BUILTIN(JavascriptExternalFunction, DefaultExternalFunctionThunk, DefaultExternalFunctionThunk, FunctionInfo::None)
 BUILTIN(JavascriptFunction, NewInstance, NewInstance, FunctionInfo::SkipDefaultNewObject)
 BUILTIN(JavascriptFunction, PrototypeEntryPoint, PrototypeEntryPoint, FunctionInfo::DoNotProfile | FunctionInfo::ErrorOnNew)
 BUILTIN(JavascriptFunction, Apply, EntryApply, FunctionInfo::ErrorOnNew)

+ 42 - 96
lib/Runtime/Library/JavascriptExternalFunction.cpp

@@ -15,35 +15,43 @@ namespace Js
 
     JavascriptExternalFunction::JavascriptExternalFunction(ExternalMethod entryPoint, DynamicType* type)
         : RuntimeFunction(type, &EntryInfo::ExternalFunctionThunk), nativeMethod(entryPoint), signature(nullptr), callbackState(nullptr), initMethod(nullptr),
-        oneBit(1), typeSlots(0), hasAccessors(0), callCount(0), prototypeTypeId(-1), flags(0)
+        oneBit(1), typeSlots(0), hasAccessors(0), prototypeTypeId(-1), flags(0)
     {
         DebugOnly(VerifyEntryPoint());
     }
 
     JavascriptExternalFunction::JavascriptExternalFunction(ExternalMethod entryPoint, DynamicType* type, InitializeMethod method, unsigned short deferredSlotCount, bool accessors)
         : RuntimeFunction(type, &EntryInfo::ExternalFunctionThunk), nativeMethod(entryPoint), signature(nullptr), callbackState(nullptr), initMethod(method),
-        oneBit(1), typeSlots(deferredSlotCount), hasAccessors(accessors), callCount(0), prototypeTypeId(-1), flags(0)
+        oneBit(1), typeSlots(deferredSlotCount), hasAccessors(accessors),prototypeTypeId(-1), flags(0)
     {
         DebugOnly(VerifyEntryPoint());
     }
 
+    JavascriptExternalFunction::JavascriptExternalFunction(DynamicType* type, InitializeMethod method, unsigned short deferredSlotCount, bool accessors)
+        : RuntimeFunction(type, &EntryInfo::DefaultExternalFunctionThunk), nativeMethod(nullptr), signature(nullptr), callbackState(nullptr), initMethod(method),
+        oneBit(1), typeSlots(deferredSlotCount), hasAccessors(accessors), prototypeTypeId(-1), flags(0)
+    {
+        DebugOnly(VerifyEntryPoint());
+    }
+
+
     JavascriptExternalFunction::JavascriptExternalFunction(JavascriptExternalFunction* entryPoint, DynamicType* type)
         : RuntimeFunction(type, &EntryInfo::WrappedFunctionThunk), wrappedMethod(entryPoint), callbackState(nullptr), initMethod(nullptr),
-        oneBit(1), typeSlots(0), hasAccessors(0), callCount(0), prototypeTypeId(-1), flags(0)
+        oneBit(1), typeSlots(0), hasAccessors(0), prototypeTypeId(-1), flags(0)
     {
         DebugOnly(VerifyEntryPoint());
     }
 
     JavascriptExternalFunction::JavascriptExternalFunction(StdCallJavascriptMethod entryPoint, DynamicType* type)
         : RuntimeFunction(type, &EntryInfo::StdCallExternalFunctionThunk), stdCallNativeMethod(entryPoint), signature(nullptr), callbackState(nullptr), initMethod(nullptr),
-        oneBit(1), typeSlots(0), hasAccessors(0), callCount(0), prototypeTypeId(-1), flags(0)
+        oneBit(1), typeSlots(0), hasAccessors(0), prototypeTypeId(-1), flags(0)
     {
         DebugOnly(VerifyEntryPoint());
     }
 
     JavascriptExternalFunction::JavascriptExternalFunction(DynamicType *type)
         : RuntimeFunction(type, &EntryInfo::ExternalFunctionThunk), nativeMethod(nullptr), signature(nullptr), callbackState(nullptr), initMethod(nullptr),
-        oneBit(1), typeSlots(0), hasAccessors(0), callCount(0), prototypeTypeId(-1), flags(0)
+        oneBit(1), typeSlots(0), hasAccessors(0), prototypeTypeId(-1), flags(0)
     {
         DebugOnly(VerifyEntryPoint());
     }
@@ -112,69 +120,6 @@ namespace Js
 
         Js::TypeId typeId = Js::JavascriptOperators::GetTypeId(thisVar);
 
-        this->callCount++;
-        if (IS_JS_ETW(EventEnabledJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START()))
-        {
-            JavascriptFunction* caller = nullptr;
-
-            // Lot the caller function if the call count of the external function pass certain threshold (randomly pick 256)
-            // we don't want to call stackwalk too often. The threshold can be adjusted as needed.
-            if (callCount >= ETW_MIN_COUNT_FOR_CALLER && ((callCount % ETW_MIN_COUNT_FOR_CALLER) == 0))
-            {
-                Js::JavascriptStackWalker stackWalker(scriptContext);
-                bool foundScriptCaller = false;
-                while(stackWalker.GetCaller(&caller))
-                {
-                    if(caller != nullptr && Js::ScriptFunction::Is(caller))
-                    {
-                        foundScriptCaller = true;
-                        break;
-                    }
-                }
-                if(foundScriptCaller)
-                {
-                    Var sourceString = caller->EnsureSourceString();
-                    Assert(JavascriptString::Is(sourceString));
-                    const char16* callerString = Js::JavascriptString::FromVar(sourceString)->GetSz();
-                    char16* outString = (char16*)callerString;
-                    int length = 0;
-                    if (wcschr(callerString, _u('\n')) != NULL || wcschr(callerString, _u('\n')) != NULL)
-                    {
-                        length = Js::JavascriptString::FromVar(sourceString)->GetLength();
-                        outString = HeapNewArray(char16, length+1);
-                        int j = 0;
-                        for (int i = 0; i < length; i++)
-                        {
-                            if (callerString[i] != _u('\n') && callerString[i] != _u('\r'))
-                            {
-                                outString[j++] = callerString[i];
-                            }
-                        }
-                        outString[j] = _u('\0');
-                    }
-                    JS_ETW(EventWriteJSCRIPT_HOSTING_CALLER_TO_EXTERNAL(scriptContext, this, typeId, outString, callCount));
-                    if (outString != callerString)
-                    {
-                        HeapDeleteArray(length+1, outString);
-                    }
-#if DBG_DUMP
-                    if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase))
-                    {
-                        Output::Print(_u("Large number of Call to trampoline: methodAddr= %p, Object typeid= %d, caller method= %s, callcount= %d\n"),
-                            this, typeId, callerString, callCount);
-                    }
-#endif
-                }
-            }
-            JS_ETW(EventWriteJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_START(scriptContext, this, typeId));
-#if DBG_DUMP
-            if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::HostPhase))
-            {
-                Output::Print(_u("Call to trampoline: methodAddr= %p, Object typeid= %d\n"), this, typeId);
-            }
-#endif
-        }
-
         Js::RecyclableObject* directHostObject = nullptr;
         switch(typeId)
         {
@@ -229,37 +174,11 @@ namespace Js
         }
     }
 
-    Var JavascriptExternalFunction::FinishExternalCall(Var result)
-    {
-        ScriptContext * scriptContext = this->type->GetScriptContext();
-
-        if ( NULL == result )
-        {
-            result = scriptContext->GetLibrary()->GetUndefined();
-        }
-        else
-        {
-            result = CrossSite::MarshalVar(scriptContext, result);
-        }
-        if (IS_JS_ETW(EventEnabledJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_STOP()))
-        {
-            JS_ETW(EventWriteJSCRIPT_HOSTING_EXTERNAL_FUNCTION_CALL_STOP(scriptContext, this, 0));
-        }
-        return result;
-    }
-
     Var JavascriptExternalFunction::ExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
     {
         RUNTIME_ARGUMENTS(args, callInfo);
         JavascriptExternalFunction* externalFunction = static_cast<JavascriptExternalFunction*>(function);
 
-        // Deferred constructors which are not callable fall back to using the RecyclableObject::DefaultEntryPoint. In order to call
-        // this function we have to be inside script so all this and return before doing any of the external call preparation.
-        if (externalFunction->nativeMethod == Js::RecyclableObject::DefaultExternalEntryPoint)
-        {
-            return Js::RecyclableObject::DefaultExternalEntryPoint(function, callInfo, args.Values);
-        }
-
         ScriptContext * scriptContext = externalFunction->type->GetScriptContext();
 
 #ifdef ENABLE_DIRECTCALL_TELEMETRY
@@ -325,8 +244,19 @@ namespace Js
         }
         END_LEAVE_SCRIPT_WITH_EXCEPTION(scriptContext);
 #endif
+        if (result == nullptr)
+        {
+#pragma warning(push)
+#pragma warning(disable:6011) // scriptContext cannot be null here
+            result = scriptContext->GetLibrary()->GetUndefined();
+#pragma warning(pop)
+        }
+        else
+        {
+            result = CrossSite::MarshalVar(scriptContext, result);
+        }
 
-        return externalFunction->FinishExternalCall(result);
+        return result;
     }
 
     Var JavascriptExternalFunction::WrappedFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
@@ -346,6 +276,13 @@ namespace Js
         return JavascriptFunction::CallFunction<true>(externalFunction->wrappedMethod, externalFunction->wrappedMethod->GetEntryPoint(), args);
     }
 
+    Var JavascriptExternalFunction::DefaultExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
+    {
+        TypeId typeId = function->GetTypeId();
+        rtErrors err = typeId == TypeIds_Undefined || typeId == TypeIds_Null ? JSERR_NeedObject : JSERR_NeedFunction;
+        JavascriptError::ThrowTypeError(function->GetScriptContext(), err);
+    }
+
     Var JavascriptExternalFunction::StdCallExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...)
     {
         RUNTIME_ARGUMENTS(args, callInfo);
@@ -430,7 +367,16 @@ namespace Js
             }
         }
 
-        return externalFunction->FinishExternalCall(result);
+        if (result == nullptr)
+        {
+            result = scriptContext->GetLibrary()->GetUndefined();
+        }
+        else
+        {
+            result = CrossSite::MarshalVar(scriptContext, result);
+        }
+
+        return result;
     }
 
     BOOL JavascriptExternalFunction::SetLengthProperty(Var length)

+ 5 - 4
lib/Runtime/Library/JavascriptExternalFunction.h

@@ -21,6 +21,8 @@ namespace Js
     public:
         JavascriptExternalFunction(ExternalMethod nativeMethod, DynamicType* type);
         JavascriptExternalFunction(ExternalMethod entryPoint, DynamicType* type, InitializeMethod method, unsigned short deferredSlotCount, bool accessors);
+        // this is default external function for DOM constructor that cannot be new'ed.
+        JavascriptExternalFunction(DynamicType* type, InitializeMethod method, unsigned short deferredSlotCount, bool accessors);
         JavascriptExternalFunction(JavascriptExternalFunction* wrappedMethod, DynamicType* type);
         JavascriptExternalFunction(StdCallJavascriptMethod nativeMethod, DynamicType* type);
 
@@ -37,6 +39,7 @@ namespace Js
             static FunctionInfo ExternalFunctionThunk;
             static FunctionInfo WrappedFunctionThunk;
             static FunctionInfo StdCallExternalFunctionThunk;
+            static FunctionInfo DefaultExternalFunctionThunk;
         };
 
         ExternalMethod GetNativeMethod() { return nativeMethod; }
@@ -62,9 +65,7 @@ namespace Js
         unsigned int typeSlots:15;
         // Determines if we need are a dictionary type
         unsigned int hasAccessors:1;
-        // This is used for etw tracking for now; potentially we can use this as heuristic
-        // to determine if when to JIT the method.
-        unsigned int callCount:15;
+        unsigned int unused:15;
 
         JavascriptTypeId prototypeTypeId;
         UINT64 flags;
@@ -72,10 +73,10 @@ namespace Js
         static Var ExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...);
         static Var WrappedFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...);
         static Var StdCallExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...);
+        static Var DefaultExternalFunctionThunk(RecyclableObject* function, CallInfo callInfo, ...);
         static void __cdecl DeferredInitializer(DynamicObject* instance, DeferredTypeHandlerBase* typeHandler, DeferredInitializeMode mode);
 
         void PrepareExternalCall(Arguments * args);
-        Var FinishExternalCall(Var result);
 
 #if ENABLE_TTD
     public:

+ 31 - 11
lib/Runtime/Library/JavascriptLibrary.cpp

@@ -530,6 +530,11 @@ namespace Js
         // Initialize Array/Argument types
         heapArgumentsType = DynamicType::New(scriptContext, TypeIds_Arguments, objectPrototype, nullptr,
             SimpleDictionaryTypeHandler::New(scriptContext, HeapArgumentsPropertyDescriptors, _countof(HeapArgumentsPropertyDescriptors), 0, 0, true, true), true, true);
+
+        DictionaryTypeHandler * dictTypeHandlerForArgumentsInStrictMode = DictionaryTypeHandler::CreateTypeHandlerForArgumentsInStrictMode(recycler, scriptContext);
+        heapArgumentsTypeStrictMode = DynamicType::New(scriptContext, TypeIds_Arguments, objectPrototype, nullptr,
+            dictTypeHandlerForArgumentsInStrictMode, false, false);
+
         activationObjectType = DynamicType::New(scriptContext, TypeIds_ActivationObject, nullValue, nullptr,
             SimplePathTypeHandler::New(scriptContext, this->GetRootPath(), 0, 0, 0, true, true), true, true);
         arrayType = DynamicType::New(scriptContext, TypeIds_Array, arrayPrototype, nullptr,
@@ -665,6 +670,8 @@ namespace Js
             &SharedIdMappedFunctionWithPrototypeTypeHandler, true, true);
         externalConstructorFunctionWithDeferredPrototypeType = DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, JavascriptExternalFunction::ExternalFunctionThunk,
             Js::DeferredTypeHandler<Js::JavascriptExternalFunction::DeferredInitializer>::GetDefaultInstance(), true, true);
+        defaultExternalConstructorFunctionWithDeferredPrototypeType = DynamicType::New(scriptContext, TypeIds_Function, functionPrototype, JavascriptExternalFunction::DefaultExternalFunctionThunk,
+            Js::DeferredTypeHandler<Js::JavascriptExternalFunction::DeferredInitializer>::GetDefaultInstance(), true, true);
 
         if (config->IsES6FunctionNameEnabled())
         {
@@ -4443,16 +4450,18 @@ namespace Js
     JavascriptExternalFunction* JavascriptLibrary::CreateExternalConstructor(Js::ExternalMethod entryPoint, PropertyId nameId, InitializeMethod method, unsigned short deferredTypeSlots, bool hasAccessors)
     {
         Assert(nameId >= Js::InternalPropertyIds::Count && scriptContext->IsTrackedPropertyId(nameId));
-
-        // Make sure the actual entry point is never null.
-        if (entryPoint == nullptr)
+        
+        JavascriptExternalFunction* function = nullptr;
+        if (entryPoint != nullptr)
         {
-            entryPoint = Js::RecyclableObject::DefaultExternalEntryPoint;
+             function = RecyclerNewEnumClass(this->GetRecycler(), EnumFunctionClass, JavascriptExternalFunction, entryPoint,
+                externalConstructorFunctionWithDeferredPrototypeType, method, deferredTypeSlots, hasAccessors);
+        }
+        else
+        {
+            function = RecyclerNewEnumClass(this->GetRecycler(), EnumFunctionClass, JavascriptExternalFunction, 
+                defaultExternalConstructorFunctionWithDeferredPrototypeType, method, deferredTypeSlots, hasAccessors);
         }
-
-        JavascriptExternalFunction* function = RecyclerNewEnumClass(this->GetRecycler(), EnumFunctionClass, JavascriptExternalFunction, entryPoint,
-            externalConstructorFunctionWithDeferredPrototypeType, method, deferredTypeSlots, hasAccessors);
-
         function->SetFunctionNameId(TaggedInt::ToVarUnchecked(nameId));
 
         return function;
@@ -5589,15 +5598,26 @@ namespace Js
                     ) / InlineSlotCountIncrement];
     }
 
-    HeapArgumentsObject* JavascriptLibrary::CreateHeapArguments(Var frameObj, uint32 formalCount)
+    HeapArgumentsObject* JavascriptLibrary::CreateHeapArguments(Var frameObj, uint32 formalCount, bool isStrictMode)
     {
-        AssertMsg(heapArgumentsType, "Where's heapArgumentsType?");
+        AssertMsg(heapArgumentsType && heapArgumentsTypeStrictMode, "Where's heapArgumentsType?");
 
         Recycler *recycler = this->GetRecycler();
 
         EnsureArrayPrototypeValuesFunction(); //InitializeArrayPrototype can be delay loaded, which could prevent us from access to array.prototype.values
+        
+        DynamicType * argumentsType = nullptr;
+
+        if (isStrictMode)
+        {
+            argumentsType = heapArgumentsTypeStrictMode;
+        }
+        else
+        {
+            argumentsType = heapArgumentsType;
+        }
 
-        return RecyclerNew(recycler, HeapArgumentsObject, recycler, (ActivationObject*)frameObj, formalCount, heapArgumentsType);
+        return RecyclerNew(recycler, HeapArgumentsObject, recycler, (ActivationObject*)frameObj, formalCount, argumentsType);
     }
 
     JavascriptArray* JavascriptLibrary::CreateArray()

+ 3 - 1
lib/Runtime/Library/JavascriptLibrary.h

@@ -185,6 +185,7 @@ namespace Js
         DynamicType * generatorConstructorPrototypeObjectType;
         DynamicType * constructorPrototypeObjectType;
         DynamicType * heapArgumentsType;
+        DynamicType * heapArgumentsTypeStrictMode;
         DynamicType * activationObjectType;
         DynamicType * arrayType;
         DynamicType * nativeIntArrayType;
@@ -242,6 +243,7 @@ namespace Js
         DynamicType * stdCallFunctionWithDeferredPrototypeType;
         DynamicType * idMappedFunctionWithPrototypeType;
         DynamicType * externalConstructorFunctionWithDeferredPrototypeType;
+        DynamicType * defaultExternalConstructorFunctionWithDeferredPrototypeType;
         DynamicType * boundFunctionType;
         DynamicType * regexConstructorType;
         DynamicType * crossSiteDeferredPrototypeFunctionType;
@@ -836,7 +838,7 @@ namespace Js
         FinalizableObject* GetPinnedJsrtContextObject();
         void EnqueueTask(Var taskVar);
 
-        HeapArgumentsObject* CreateHeapArguments(Var frameObj, uint formalCount);
+        HeapArgumentsObject* CreateHeapArguments(Var frameObj, uint formalCount, bool isStrictMode = false);
         JavascriptArray* CreateArray();
         JavascriptArray* CreateArray(uint32 length);
         JavascriptArray *CreateArrayOnStack(void *const stackAllocationPointer);

+ 15 - 53
lib/Runtime/Library/JavascriptObject.cpp

@@ -868,25 +868,15 @@ namespace Js
             }
         }
 
-        Var ownPropertyKeys = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
-        Assert(JavascriptArray::Is(ownPropertyKeys));
-
-        if (!JavascriptArray::Is(ownPropertyKeys))
-        {
-            ownPropertyKeys = scriptContext->GetLibrary()->CreateArray(0);
-        }
-
-
-        JavascriptArray *ownPropsArray = JavascriptArray::FromVar(ownPropertyKeys);
-
-        RecyclableObject* resultObj = scriptContext->GetLibrary()->CreateObject(true, (Js::PropertyIndex) ownPropsArray->GetLength());
+        JavascriptArray* ownPropertyKeys = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
+        RecyclableObject* resultObj = scriptContext->GetLibrary()->CreateObject(true, (Js::PropertyIndex) ownPropertyKeys->GetLength());
         
         PropertyDescriptor propDesc;
         Var propKey = nullptr;
 
-        for (uint i = 0; i < ownPropsArray->GetLength(); i++)
+        for (uint i = 0; i < ownPropertyKeys->GetLength(); i++)
         {
-            BOOL getPropResult = ownPropsArray->DirectGetItemAt(i, &propKey);
+            BOOL getPropResult = ownPropertyKeys->DirectGetItemAt(i, &propKey);
             Assert(getPropResult);
 
             if (!getPropResult)
@@ -1242,17 +1232,7 @@ namespace Js
         Assert(scriptContext != nullptr);
         JavascriptArray* valuesArray = scriptContext->GetLibrary()->CreateArray(0);
 
-        Var ownKeysVar = JavascriptOperators::GetOwnPropertyNames(object, scriptContext);
-        JavascriptArray* ownKeysResult = nullptr;
-        if (JavascriptArray::Is(ownKeysVar))
-        {
-            ownKeysResult = JavascriptArray::FromVar(ownKeysVar);
-        }
-        else
-        {
-            return valuesArray;
-        }
-
+        JavascriptArray* ownKeysResult = JavascriptOperators::GetOwnPropertyNames(object, scriptContext);
         uint32 length = ownKeysResult->GetLength();
 
         Var nextKey;
@@ -1265,10 +1245,10 @@ namespace Js
 
             PropertyDescriptor propertyDescriptor;
 
-            BOOL propertyKeyResult = JavascriptConversion::ToPropertyKey(nextKey, scriptContext, &propertyRecord);
-            Assert(propertyKeyResult);
+            JavascriptConversion::ToPropertyKey(nextKey, scriptContext, &propertyRecord);
             propertyId = propertyRecord->GetPropertyId();
             Assert(propertyId != Constants::NoProperty);
+
             if (JavascriptOperators::GetOwnPropertyDescriptor(object, propertyId, scriptContext, &propertyDescriptor))
             {
                 if (propertyDescriptor.IsEnumerable())
@@ -1322,33 +1302,33 @@ namespace Js
         return GetValuesOrEntries(object, false /*valuesToReturn*/, scriptContext);
     }
 
-    Var JavascriptObject::CreateOwnSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
+    JavascriptArray* JavascriptObject::CreateOwnSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
     {
         return CreateKeysHelper(object, scriptContext, TRUE, true /*includeSymbolsOnly */, false, true /*includeSpecialProperties*/);
     }
 
-    Var JavascriptObject::CreateOwnStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
+    JavascriptArray* JavascriptObject::CreateOwnStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
     {
         return CreateKeysHelper(object, scriptContext, TRUE, false, true /*includeStringsOnly*/, true /*includeSpecialProperties*/);
     }
 
-    Var JavascriptObject::CreateOwnStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
+    JavascriptArray* JavascriptObject::CreateOwnStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
     {
         return CreateKeysHelper(object, scriptContext, TRUE, true/*includeSymbolsOnly*/, true /*includeStringsOnly*/, true /*includeSpecialProperties*/);
     }
 
-    Var JavascriptObject::CreateOwnEnumerableStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
+    JavascriptArray* JavascriptObject::CreateOwnEnumerableStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
     {
         return CreateKeysHelper(object, scriptContext, FALSE, false, true/*includeStringsOnly*/, false);
     }
 
-    Var JavascriptObject::CreateOwnEnumerableStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
+    JavascriptArray* JavascriptObject::CreateOwnEnumerableStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext)
     {
         return CreateKeysHelper(object, scriptContext, FALSE, true/*includeSymbolsOnly*/, true/*includeStringsOnly*/, false);
     }
 
     // 9.1.12 [[OwnPropertyKeys]] () in RC#4 dated April 3rd 2015.
-    Var JavascriptObject::CreateKeysHelper(RecyclableObject* object, ScriptContext* scriptContext, BOOL includeNonEnumerable, bool includeSymbolProperties, bool includeStringProperties, bool includeSpecialProperties)
+    JavascriptArray* JavascriptObject::CreateKeysHelper(RecyclableObject* object, ScriptContext* scriptContext, BOOL includeNonEnumerable, bool includeSymbolProperties, bool includeStringProperties, bool includeSpecialProperties)
     {
         //1. Let keys be a new empty List.
         //2. For each own property key P of O that is an integer index, in ascending numeric index order
@@ -1786,17 +1766,8 @@ namespace Js
 
     void JavascriptObject::AssignForProxyObjects(RecyclableObject* from, RecyclableObject* to, ScriptContext* scriptContext)
     {
-        Var keysResult = JavascriptOperators::GetOwnEnumerablePropertyNamesSymbols(from, scriptContext);
-        JavascriptArray *keys;
+         JavascriptArray *keys = JavascriptOperators::GetOwnEnumerablePropertyNamesSymbols(from, scriptContext);
 
-        if (JavascriptArray::Is(keysResult))
-        {
-            keys = JavascriptArray::FromVar(keysResult);
-        }
-        else
-        {
-            return;
-        }
         //      c. Repeat for each element nextKey of keys in List order,
         //          i. Let desc be from.[[GetOwnProperty]](nextKey).
         //          ii. ReturnIfAbrupt(desc).
@@ -2010,16 +1981,7 @@ namespace Js
         //3.  Let keys be props.[[OwnPropertyKeys]]().
         //4.  ReturnIfAbrupt(keys).
         //5.  Let descriptors be an empty List.
-        JavascriptArray* keys;
-        Var ownKeysResult = JavascriptOperators::GetOwnEnumerablePropertyNamesSymbols(props, scriptContext);
-        if (JavascriptArray::Is(ownKeysResult))
-        {
-            keys = JavascriptArray::FromVar(ownKeysResult);
-        }
-        else
-        {
-            return object;
-        }
+        JavascriptArray* keys = JavascriptOperators::GetOwnEnumerablePropertyNamesSymbols(props, scriptContext);
         uint32 length = keys->GetLength();
 
         ENTER_PINNED_SCOPE(DescriptorMap, descriptors);

+ 6 - 6
lib/Runtime/Library/JavascriptObject.h

@@ -87,11 +87,11 @@ namespace Js
         static Var GetPrototypeOf(RecyclableObject* obj, ScriptContext* scriptContext);
         static BOOL ChangePrototype(RecyclableObject* object, RecyclableObject* newPrototype, bool validate, ScriptContext* scriptContext);
 
-        static Var CreateOwnSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
-        static Var CreateOwnStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
-        static Var CreateOwnStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
-        static Var CreateOwnEnumerableStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
-        static Var CreateOwnEnumerableStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
+        static JavascriptArray* CreateOwnSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
+        static JavascriptArray* CreateOwnStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
+        static JavascriptArray* CreateOwnStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
+        static JavascriptArray* CreateOwnEnumerableStringPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
+        static JavascriptArray* CreateOwnEnumerableStringSymbolPropertiesHelper(RecyclableObject* object, ScriptContext* scriptContext);
 
         static Var GetOwnPropertyDescriptorHelper(RecyclableObject* obj, Var propertyKey, ScriptContext* scriptContext);
         static BOOL GetOwnPropertyDescriptorHelper(RecyclableObject* obj, PropertyId propertyId, ScriptContext* scriptContext, PropertyDescriptor& propertyDescriptor);
@@ -113,7 +113,7 @@ namespace Js
     private:
         static void AssignForGenericObjects(RecyclableObject* from, RecyclableObject* to, ScriptContext* scriptContext);
         static void AssignForProxyObjects(RecyclableObject* from, RecyclableObject* to, ScriptContext* scriptContext);
-        static Var CreateKeysHelper(RecyclableObject* object, ScriptContext* scriptContext, BOOL enumNonEnumerable, bool includeSymbolProperties, bool includeStringProperties, bool includeSpecialProperties);
+        static JavascriptArray* CreateKeysHelper(RecyclableObject* object, ScriptContext* scriptContext, BOOL enumNonEnumerable, bool includeSymbolProperties, bool includeStringProperties, bool includeSpecialProperties);
 
         static void ModifyGetterSetterFuncName(const PropertyRecord * propertyRecord, const PropertyDescriptor& descriptor, ScriptContext* scriptContext);
         static char16 * ConstructName(const PropertyRecord * propertyRecord, const char16 * getOrSetStr, ScriptContext* scriptContext);

+ 7 - 29
lib/Runtime/Library/JavascriptProxy.cpp

@@ -1142,8 +1142,7 @@ namespace Js
         //7. Let keys be O.[[OwnPropertyKeys]]().
         //8. ReturnIfAbrupt(keys).
         Assert(JavascriptProxy::Is(obj));
-        Var resultVar = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
-        Assert(JavascriptArray::Is(resultVar));
+        JavascriptArray* resultArray = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
 
         //9. Repeat for each element k of keys,
         //      a. Let currentDesc be O.[[GetOwnProperty]](k).
@@ -1152,7 +1151,6 @@ namespace Js
         //          i. If currentDesc.[[Configurable]] is true, return false.
         //          ii. If level is "frozen" and IsDataDescriptor(currentDesc) is true, then
         //              1. If currentDesc.[[Writable]] is true, return false.
-        JavascriptArray* resultArray = JavascriptArray::FromVar(resultVar);
         Var itemVar;
         bool writable = false;
         bool configurable = false;
@@ -1199,10 +1197,7 @@ namespace Js
 
         //6. Let keys be O.[[OwnPropertyKeys]]().
         //7. ReturnIfAbrupt(keys).
-        Var resultVar = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
-        Assert(JavascriptArray::Is(resultVar));
-
-        JavascriptArray* resultArray = JavascriptArray::FromVar(resultVar);
+        JavascriptArray* resultArray = JavascriptOperators::GetOwnPropertyKeys(obj, scriptContext);
 
         const PropertyRecord* propertyRecord;        
         if (integrityLevel == IntegrityLevel::IntegrityLevel_sealed)
@@ -2061,7 +2056,7 @@ namespace Js
         return trapResult;
     }
 
-    Var JavascriptProxy::PropertyKeysTrap(KeysTrapKind keysTrapKind)
+    JavascriptArray* JavascriptProxy::PropertyKeysTrap(KeysTrapKind keysTrapKind)
     {
         PROBE_STACK(GetScriptContext(), Js::Constants::MinStackDefault);
 
@@ -2094,33 +2089,24 @@ namespace Js
         Assert(!GetScriptContext()->IsHeapEnumInProgress());
 
         JavascriptArray *targetKeys;
-        Var targetResult;
 
         if (nullptr == ownKeysMethod)
         {
             switch (keysTrapKind)
             {
                 case GetOwnPropertyNamesKind:
-                    targetResult = JavascriptOperators::GetOwnPropertyNames(this->target, scriptContext);
+                    targetKeys = JavascriptOperators::GetOwnPropertyNames(this->target, scriptContext);
                     break;
                 case GetOwnPropertySymbolKind:
-                    targetResult = JavascriptOperators::GetOwnPropertySymbols(this->target, scriptContext);
+                    targetKeys = JavascriptOperators::GetOwnPropertySymbols(this->target, scriptContext);
                     break;
                 case KeysKind:
-                    targetResult = JavascriptOperators::GetOwnPropertyKeys(this->target, scriptContext);
+                    targetKeys = JavascriptOperators::GetOwnPropertyKeys(this->target, scriptContext);
                     break;
                 default:
                     AssertMsg(false, "Invalid KeysTrapKind.");
                     return scriptContext->GetLibrary()->CreateArray(0);
             }
-            if (JavascriptArray::Is(targetResult))
-            {
-                targetKeys = JavascriptArray::FromVar(targetResult);
-            }
-            else
-            {
-                targetKeys = scriptContext->GetLibrary()->CreateArray(0);
-            }
             return targetKeys;
         }
 
@@ -2149,15 +2135,7 @@ namespace Js
 
         BOOL isTargetExtensible = target->IsExtensible();
 
-        targetResult = JavascriptOperators::GetOwnPropertyKeys(this->target, scriptContext);
-        if (JavascriptArray::Is(targetResult))
-        {
-            targetKeys = JavascriptArray::FromVar(targetResult);
-        }
-        else
-        {
-            targetKeys = scriptContext->GetLibrary()->CreateArray(0);
-        }
+        targetKeys = JavascriptOperators::GetOwnPropertyKeys(this->target, scriptContext);
 
         //15. Assert: targetKeys is a List containing only String and Symbol values.
         //16. Let targetConfigurableKeys be an empty List.

+ 1 - 1
lib/Runtime/Library/JavascriptProxy.h

@@ -155,7 +155,7 @@ namespace Js
 
         void PropertyIdFromInt(uint32 index, PropertyRecord const** propertyRecord);
 
-        Var PropertyKeysTrap(KeysTrapKind keysTrapKind);
+        JavascriptArray* PropertyKeysTrap(KeysTrapKind keysTrapKind);
 
         template <class Fn>
         void GetOwnPropertyKeysHelper(ScriptContext* scriptContext, RecyclableObject* trapResultArray, uint32 len, JavascriptArray* trapResult,

+ 14 - 1
lib/Runtime/Types/DictionaryTypeHandler.cpp

@@ -12,6 +12,19 @@ namespace Js
         return NewTypeHandler<DictionaryTypeHandlerBase>(recycler, initialCapacity, inlineSlotCapacity, offsetOfInlineSlots);
     }
 
+    template <typename T>
+    DictionaryTypeHandlerBase<T>* DictionaryTypeHandlerBase<T>::CreateTypeHandlerForArgumentsInStrictMode(Recycler * recycler, ScriptContext * scriptContext)
+    {
+        DictionaryTypeHandlerBase<T> * dictTypeHandler = New(recycler, 8, 0, 0);
+        
+        dictTypeHandler->Add(scriptContext->GetPropertyName(Js::PropertyIds::caller), PropertyWritable, scriptContext);
+        dictTypeHandler->Add(scriptContext->GetPropertyName(Js::PropertyIds::callee), PropertyWritable, scriptContext);
+        dictTypeHandler->Add(scriptContext->GetPropertyName(Js::PropertyIds::length), PropertyBuiltInMethodDefaults, scriptContext);
+        dictTypeHandler->Add(scriptContext->GetPropertyName(Js::PropertyIds::_symbolIterator), PropertyBuiltInMethodDefaults, scriptContext);
+
+        return dictTypeHandler;
+    }
+
     template <typename T>
     DictionaryTypeHandlerBase<T>::DictionaryTypeHandlerBase(Recycler* recycler) :
         DynamicTypeHandler(1),
@@ -354,7 +367,6 @@ namespace Js
 
     template <typename T>
     void DictionaryTypeHandlerBase<T>::Add(
-
         const PropertyRecord* propertyId,
         PropertyAttributes attributes,
         bool isInitialized, bool isFixed, bool usedAsFixed,
@@ -369,6 +381,7 @@ namespace Js
         descriptor.IsInitialized = isInitialized;
         descriptor.IsFixed = isFixed;
         descriptor.UsedAsFixed = usedAsFixed;
+
         propertyMap->Add(propertyId, descriptor);
 
         if (!(attributes & PropertyWritable))

+ 2 - 0
lib/Runtime/Types/DictionaryTypeHandler.h

@@ -60,6 +60,8 @@ namespace Js
         // Create a new type handler for a future DynamicObject. This is for public usage. "initialCapacity" indicates desired slotCapacity, subject to alignment round up.
         static DictionaryTypeHandlerBase* New(Recycler * recycler, int initialCapacity, uint16 inlineSlotCapacity, uint16 offsetOfInlineSlots);
 
+        static DictionaryTypeHandlerBase* CreateTypeHandlerForArgumentsInStrictMode(Recycler * recycler, ScriptContext * scriptContext);
+
         BOOL IsBigDictionaryTypeHandler();
 
         virtual BOOL IsLockable() const override { return false; }

+ 1 - 0
lib/Runtime/Types/NullTypeHandler.cpp

@@ -58,6 +58,7 @@ namespace Js
             const EquivalentPropertyEntry* refInfo = &properties[pi];
             if (!this->NullTypeHandlerBase::IsObjTypeSpecEquivalent(type, refInfo))
             {
+                failedPropertyIndex = pi;
                 return false;
             }
         }

+ 0 - 8
lib/Runtime/Types/RecyclableObject.cpp

@@ -302,14 +302,6 @@ namespace Js
             /* TODO-ERROR: args.Info.Count > 0? args[0] : nullptr); */);
     }
 
-    Var RecyclableObject::DefaultExternalEntryPoint(RecyclableObject* function, CallInfo callInfo, Var* arguments)
-    {
-        TypeId typeId = function->GetTypeId();
-        rtErrors err = typeId == TypeIds_Undefined || typeId == TypeIds_Null ? JSERR_NeedObject : JSERR_NeedFunction;
-        JavascriptError::ThrowTypeError(function->GetScriptContext(), err
-            /* TODO-ERROR: args.Info.Count > 0? args[0] : nullptr); */);
-    }
-
     BOOL RecyclableObject::HasProperty(PropertyId propertyId)
     {
         return false;

+ 0 - 1
lib/Runtime/Types/RecyclableObject.h

@@ -240,7 +240,6 @@ namespace Js {
         // function - however, if it can't be called like a function, it's set to DefaultEntryPoint
         // which will emit an error.
         static Var DefaultEntryPoint(RecyclableObject* function, CallInfo callInfo, ...);
-        static Var DefaultExternalEntryPoint(RecyclableObject* function, CallInfo callInfo, Var* arguments);
 
         virtual PropertyId GetPropertyId(PropertyIndex index) { return Constants::NoProperty; }
         virtual PropertyId GetPropertyId(BigPropertyIndex index) { return Constants::NoProperty; }

+ 239 - 0
test/Array/Array_TypeConfusion_bugs.js

@@ -0,0 +1,239 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+//Note: see function  ArraySpliceHelper of JavascriptArray.cpp
+
+if (this.WScript && this.WScript.LoadScriptFile) { // Check for running in ch
+    this.WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+}
+
+var tests = [
+    {
+         name: "OS7342663:OOB writes using type confusion in InternalCopyArrayElements",
+         body: function ()
+         {
+             function test() {
+                 var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe];
+
+                 class MyArray extends Uint32Array { }
+                 Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } });
+
+                 var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
+                 var test = [float_val, float_val, float_val, float_val];
+                 test.length = 0x1000;
+                 test.__proto__ = new MyArray(0);
+
+                 var res = Array.prototype.slice.apply(test, []);  // OOB write
+                 assert.areEqual(0x1000, res.length, "res.length == 0x1000");
+                 assert.areEqual(float_val, res[0], "res[0] == float_val");
+                 assert.areEqual(float_val, res[1], "res[1] == float_val");
+                 assert.areEqual(float_val, res[2], "res[2] == float_val");
+                 assert.areEqual(float_val, res[3], "res[3] == float_val");
+                 assert.areEqual(undefined, res[4], "res[4] == float_val");
+                 assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined");
+             }
+             test();
+             test();
+             test();
+         }
+     },
+     {
+         name: "OS7342689:OOB writes using type confusion in InternalFillFromPrototypes",
+         body: function ()
+         {
+             function test() {
+                 var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead,
+                 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe,
+                 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead,
+                 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe];
+
+                 class MyArray extends Uint32Array { }
+                 Object.defineProperty(MyArray, Symbol.species, { value: function() { return arr1; } });
+
+                 var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
+                 var test = [{}];
+                 delete test[0];
+                 test.length = 0x1000;
+                 var src = [float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
+                 float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
+                 float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
+                 float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val,
+                 float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val, float_val];
+                 test.__proto__ = src;
+                 test.__proto__.__proto__ = new MyArray(0);
+
+                //this will write 0xfffc0daddeadbabe to [arr1] + 0x1D8
+                 var res = Array.prototype.slice.apply(test, [])
+                 assert.areEqual(0x1000, res.length, "res.length == 0x1000");
+                 assert.areEqual(float_val, res[0], "res[0] == float_val");
+                 assert.areEqual(float_val, res[1], "res[1] == float_val");
+                 assert.areEqual(float_val, res[2], "res[2] == float_val");
+                 assert.areEqual(float_val, res[src.length-1], "res[src.length-1] == float_val");
+                 assert.areEqual(undefined, res[src.length], "res[src] == undefined");
+                 assert.areEqual(undefined, res[0xfff], "res[0xfff] == undefined");
+             }
+             test();
+             test();
+             test();
+         }
+     },
+     {
+         name: "OS7307908:type confusion in Array.prototype.slice",
+         body: function ()
+         {
+             function test() {
+                 var arr = [1, 2]
+
+                //Our species function will get called during chakra!Js::JavascriptArray::SliceHelper<unsigned int>
+                 Object.defineProperty(
+                     arr.constructor,
+                     Symbol.species,
+                     {
+                         value : function()
+                         {
+                            //change 'arr' from TypeIds_NativeIntArray to TypeIds_Array
+                             arr[0] = WScript;
+
+                            //return a TypeIds_NativeIntArray so we can read back out the 64 bit pointer as two 32bit ints.
+                             return [];
+                         }
+                     }
+                 );
+
+                //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer.
+                 var brr = arr.slice();
+
+                 assert.areEqual(2, brr.length, "brr.length == 2");
+                 assert.areEqual(WScript, brr[0], "brr[0] == WScript");
+                 assert.areEqual(2, brr[1], "brr[0] == WScript");
+             }
+             test();
+             test();
+             test();
+         }
+     },
+     {
+         name: "OS7342791:type confusion in Array.from",
+         body: function ()
+         {
+             function test() {
+                 var arr1 = [0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe, 0xdead, 0xbabe];
+
+                 var float_val = 0xdaddeadbabe * 4.9406564584124654E-324;
+                 var test = [float_val, float_val, float_val, float_val];
+                 delete test[0];
+                 delete test[1];
+                 delete test[2];
+
+                 var res = Array.from.apply(function(){return arr1}, [test]);
+                 assert.areEqual(4, res.length, "res.length == 4");
+                 assert.areEqual(undefined, res[0], "res[0] == undefined");
+                 assert.areEqual(undefined, res[1], "res[1] == undefined");
+                 assert.areEqual(undefined, res[2], "res[2] == undefined");
+                 assert.areEqual(float_val, res[3], "res[3] == float_val");
+
+                 assert.areEqual(['1','2','3'], Array.from.apply(()=>new Array(), ["123"]), "Array.from on iterable");
+                 assert.areEqual([1,2,3], Array.from.apply(()=>new Array(), [{"0":1, "1":2, "2":3, "length":3}]), "Array.from on non-iterable");
+             }
+             test();
+             test();
+             test();
+         }
+     },
+     {
+         name: "OS7342844:type confusion in Array.of",
+         body: function ()
+         {
+             function test() {
+                 var brr = Array.of.call(()=>[ 1, 2, 3, 4 ],
+                     WScript, // supply 2 copies of target so the brr array will have a length of 2 and we can read the 64bit pointer.
+                     WScript
+                 );
+
+                 assert.areEqual(2, brr.length, "brr.length == 2");
+                 assert.areEqual(WScript, brr[0], "res[0] == WScript");
+                 assert.areEqual(WScript, brr[1], "res[1] == WScript");
+                 assert.areEqual(undefined, brr[2], "res[2] == undefined");
+                 assert.areEqual(undefined, brr[3], "res[3] == undefined");
+             }
+             test();
+             test();
+             test();
+         }
+     },
+     {
+         name: "OS7342907:type confusion in Array.prototype.map",
+         body: function ()
+         {
+             function test() {
+                 var arr = [ 1, 2 ];
+
+                 Object.defineProperty(
+                     arr.constructor,
+                     Symbol.species,
+                     {
+                         value : function()
+                         {
+                             return [];
+                         }
+                     }
+                 );
+
+                //The value returned from our callback is directly set into the array whose type we create via the species.
+                 var brr = arr.map( function( v )
+                     {
+                         if( v == 1 )
+                             return WScript;
+                     }
+                 );
+
+                 assert.areEqual(2, brr.length, "brr.length == 2");
+                 assert.areEqual(WScript, brr[0], "brr[0] == WScript");
+                 assert.areEqual(undefined, brr[1], "brr[1] == undefined");
+             }
+             test();
+             test();
+             test();
+         }
+     },
+     {
+         name: "OS7342965:type confusion in Array.prototype.splice",
+         body: function ()
+         {
+             function test() {
+                //create a TypeIds_Array holding two 64 bit values (The same amount of space for four 32 bit values).
+                 var arr = [ WScript, WScript ];
+
+                //Our species function will get called during chakra!Js::JavascriptArray::EntrySplice
+                 Object.defineProperty(
+                     arr.constructor,
+                     Symbol.species,
+                     {
+                         value : function()
+                         {
+                            //return a TypeIds_NativeIntArray so we can read back out a 64 bit pointer as two 32bit ints.
+                             return [ 1, 2, 3, 4 ];
+                         }
+                     }
+                 );
+
+                //trigger the bug and retrieve a TypeIds_NativeIntArray array containing a pointer. The helper
+                //method ArraySegmentSpliceHelper<Var> will directly copy over the TypeIds_Array segment data
+                //into the TypeIds_NativeIntArray segment.
+                 var brr = arr.splice( 0, 2 );
+
+                 assert.areEqual(2, brr.length, "brr.length == 2");
+                 assert.areEqual(WScript, brr[0], "brr[0] == WScript");
+                 assert.areEqual(WScript, brr[1], "brr[1] == WScript");
+                 assert.areEqual(undefined, brr[2], "brr[2] == undefined");
+                 assert.areEqual(undefined, brr[3], "brr[3] == undefined");
+             }
+             test();
+             test();
+             test();
+         }
+     },
+];
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 19 - 0
test/Array/bug8159763.js

@@ -0,0 +1,19 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function test0() {
+  var IntArr0 = [];
+  v5 = IntArr0.length;
+  for (var i = 10; i < v5; i++) {
+    IntArr0[i] = 0.5;
+  }
+  return i;
+}
+test0();
+if (test0() !== 10) {
+  print("FAILED");
+} else {
+  print("PASSED");
+}

+ 20 - 35
test/Array/concat2.baseline

@@ -1,41 +1,26 @@
 -------concat Small-------------
 - concat 101, 102, 103, 104, 105
-length: 2147483653
-  2147483647: 100
-  2147483648: 101
-  2147483649: 102
-  2147483650: 103
-  2147483651: 104
-  2147483652: 105
+length: 2147483647
+  2147483641: 100
+  2147483642: 101
+  2147483643: 102
+  2147483644: 103
+  2147483645: 104
+  2147483646: 105
 - arr.concat(arr)
-length: 4294967295
-  2147483647: 100
-  2147483648: 101
-  2147483649: 102
-  2147483650: 103
-  2147483651: 104
-  2147483652: 105
-  4294967300: 100
-  4294967301: 101
-  4294967302: 102
-  4294967303: 103
-  4294967304: 104
-  4294967305: 105
--------concat Large-------------
-- concat 101, 102, 103, 104, 105
-length: 4294967295
-  4294967293: 100
-  4294967294: 101
-  4294967295: 102
-  4294967296: 103
-  4294967297: 104
-  4294967298: 105
-- arr.concat(arr)
-length: 4294967295
-  4294967293: 100
-  4294967294: 101
-  8589934588: 100
-  8589934589: 101
+length: 4294967294
+  2147483641: 100
+  2147483642: 101
+  2147483643: 102
+  2147483644: 103
+  2147483645: 104
+  2147483646: 105
+  4294967288: 100
+  4294967289: 101
+  4294967290: 102
+  4294967291: 103
+  4294967292: 104
+  4294967293: 105
 -------test prototype lookup-------------
 a: 200,101,202,203,204,105,106,207
 r: 200,101,202,203,204,105,106,207,300,301,302,303,304

+ 5 - 3
test/Array/concat2.js

@@ -38,10 +38,12 @@ function test_concat(size) {
 }
 
 echo("-------concat Small-------------");
-test_concat(2147483648);
+test_concat(2147483642);
 
-echo("-------concat Large-------------");
-test_concat(4294967294);
+// Fix for MSRC 33319 changes concat to skip a fast path if the index we're copying into is a BigIndex.
+// Disable the large portion of this test since this change makes such a test run for hours.
+//echo("-------concat Large-------------");
+//test_concat(4294967294);
 
 echo("-------test prototype lookup-------------");
 for (var i = 0; i < 10; i++) {

+ 15 - 2
test/Array/rlexe.xml

@@ -639,13 +639,13 @@
         <compile-flags>-mic:1 -off:simplejit -mmoc:0 -off:JITLoopBody</compile-flags>
      </default>
   </test>
-  <!-- 
+  <!--
    <test>
      <default>
         <files>memset_simd.js</files>
         <compile-flags>-mic:1 -off:simplejit -mmoc:0 -off:JITLoopBody -simdjs -simd128typespec</compile-flags>
      </default>
-  </test>  
+  </test>
 -->
   <test>
      <default>
@@ -708,4 +708,17 @@
         <compile-flags>-mic:1 -off:simplejit</compile-flags>
      </default>
   </test>
+  <test>
+     <default>
+        <files>bug8159763.js</files>
+        <compile-flags>-mic:1 -off:simplejit -mmoc:0 -off:bailonnoprofile</compile-flags>
+     </default>
+  </test>
+  <test>
+    <default>
+      <files>Array_TypeConfusion_bugs.js</files>
+      <compile-flags>-args summary -endargs</compile-flags>
+      <tags>BugFix</tags>
+    </default>
+  </test>
 </regress-exe>

+ 27 - 0
test/Closures/delaycapture-loopbody.js

@@ -0,0 +1,27 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function foo() {
+    var a = [1, 0, 0, 0, 1, 1, 1, 1, 0, 0];
+    var i;
+    for (var k = 0; k < 10; k++) {
+        switch (a[k]) {
+            case 1:
+                break;
+            case 2:
+                {
+                    if (i) {
+                        function bar() {
+                            i;
+                        }
+                    }
+                }
+        }
+    }
+}
+
+foo();
+
+WScript.Echo('pass');

+ 7 - 0
test/Closures/rlexe.xml

@@ -67,6 +67,13 @@
       <compile-flags>-Intl-</compile-flags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>delaycapture-loopbody.js</files>
+      <compile-flags>-lic:1 -off:simplejit -bgjit- -off:stackfunc -InitializeInterpreterSlotsWithInvalidStackVar</compile-flags>
+      <tags>exclude_fre</tags>
+    </default>
+  </test>
   <test>
     <default>
       <files>initcachedscope.js</files>

+ 4 - 0
test/Function/StackArgsWithFormals.baseline

@@ -8,4 +8,8 @@ StackArgFormals : inner_test4 (8) :Removing Heap Arguments object creation in Lo
 StackArgFormals : test5 (9) :Removing Heap Arguments object creation in Lowerer. 
 StackArgFormals : test6 (10) :Removing Heap Arguments object creation in Lowerer. 
 StackArgFormals : test7 (11) :Removing Heap Arguments object creation in Lowerer. 
+StackArgFormals : test12_1 (17) :Removing Scope object creation in Deadstore pass. 
+StackArgFormals : test12_1 (17) :Removing Heap Arguments object creation in Lowerer. 
+StackArgFormals : test13 (19) :Removing Scope object creation in Deadstore pass. 
+StackArgFormals : test13 (19) :Removing Heap Arguments object creation in Lowerer. 
 PASSED

+ 20 - 0
test/Function/StackArgsWithFormals.js

@@ -195,6 +195,26 @@ test11(1,2);
 test11('x','y');
 verify([20,40,20,40], "TEST 11");
 
+
+var obj12 = { method1: function () {} };
+function test12_1(arg1) {
+  this.prop1 = arg1;
+  obj12.method1.apply(obj12, arguments);
+}
+function test12() {
+  new test12_1(-{});
+}
+test12();
+test12();
+verify([], "TEST 12");
+
+function test13(a) {
+    actuals.push(typeof arguments[1]);
+}
+test13(1,2);
+test13({}, {});
+verify(["number", "object"], "TEST 13");
+
 if(hasAllPassed)
 {
     print("PASSED");

+ 1 - 1
test/Function/rlexe.xml

@@ -325,7 +325,7 @@
       <files>FuncBody.bug231397.js</files>
       <baseline>FuncBody.bug231397.baseline</baseline>
       <compile-flags>-dump:bytecode</compile-flags>
-      <tags>exclude_fre,require_backend</tags>
+      <tags>exclude_bytecodelayout,exclude_fre,require_backend</tags>
     </default>
   </test>
   <test>

+ 14 - 0
test/es6/ModuleCircularBar.js

@@ -0,0 +1,14 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+import { circular_foo } from "ModuleCircularFoo.js"
+export function circular_bar() {
+    increment();
+    return circular_foo();
+}
+export function increment() { 
+    counter++;
+}
+export var counter = 0;

+ 15 - 0
test/es6/ModuleCircularFoo.js

@@ -0,0 +1,15 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+import { circular_bar, increment, counter } from "ModuleCircularBar.js"
+export function circular_foo() {
+    if (counter == 0) {
+        return circular_bar();
+    } else {
+        increment();
+        return counter;
+    }
+}
+export { circular_bar as rexportbar } from "ModuleCircularBar.js"

+ 57 - 0
test/es6/ModuleComplexExports.js

@@ -0,0 +1,57 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+export function foo() { return 'foo'; };
+export { foo as foo2 };
+
+function bar() { return 'bar'; };
+export { bar, bar as bar2 };
+
+export let let2 = 'let2';
+export const const2 = 'const2';
+export var var2 = 'var2';
+export { let2 as let4, const2 as const4, var2 as var4 };
+
+let let3 = 'let3';
+const const3 = 'const3';
+var var3 = 'var3';
+export { let3, let3 as let5, const3, const3 as const5, var3, var3 as var5 };
+
+export class class2 { 
+    member() { return 'class2'; } 
+    static static_member() { return 'class2'; } 
+};
+export { class2 as class3 };
+
+class class4 { 
+    member() { return 'class4'; } 
+    static static_member() { return 'class4'; } 
+};
+export { class4, class4 as class5 };
+
+export async function asyncfoo() { };
+async function asyncbar() { };
+export { asyncfoo as asyncfoo2, asyncbar, asyncbar as asyncbar2 };
+
+export function* genfoo() { };
+function* genbar() { };
+export { genfoo as genfoo2, genbar, genbar as genbar2 };
+
+export default function () { return 'default'; };
+
+var mutatingExportTarget = function() { return 'before'; };
+function changeMutatingExportTarget() {
+    mutatingExportTarget = function() { return 'after'; };
+    return 'ok';
+}
+
+export { mutatingExportTarget as target, changeMutatingExportTarget as changeTarget };
+
+var exportedAsKeyword = 'ModuleComplexExports';
+export { exportedAsKeyword as export };
+export { exportedAsKeyword as function };
+
+var as = function() { return 'as'; };
+export { as as as };

+ 11 - 0
test/es6/ModuleComplexReexports.js

@@ -0,0 +1,11 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+export { bar2 as ModuleComplexReexports_foo } from 'ModuleComplexExports.js';
+
+export { function as switch } from 'ModuleComplexExports.js';
+
+import { foo, foo2, foo as localfoo, foo2 as localfoo2 } from "ModuleComplexExports.js";
+export { foo, foo2 as baz, localfoo, localfoo as bar, localfoo2, localfoo2 as bar2 };

+ 7 - 0
test/es6/ModuleDefaultExport1.js

@@ -0,0 +1,7 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function ModuleDefaultExport1_foo() { return 'ModuleDefaultExport1'; }
+export { ModuleDefaultExport1_foo as default };

+ 6 - 0
test/es6/ModuleDefaultExport2.js

@@ -0,0 +1,6 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+export default function() { return 'ModuleDefaultExport2'; }

+ 8 - 0
test/es6/ModuleDefaultExport3.js

@@ -0,0 +1,8 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+let export3 = 1;
+export { export3 as default };
+export3 = 2;

+ 8 - 0
test/es6/ModuleDefaultExport4.js

@@ -0,0 +1,8 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+let export4 = 1;
+export default export4;
+export4 = 2;

+ 7 - 0
test/es6/ModuleDefaultReexport.js

@@ -0,0 +1,7 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+export { default } from 'ModuleDefaultExport1.js'
+export { default as not_default } from 'ModuleDefaultExport2.js'

+ 6 - 0
test/es6/ModuleReexportDefault.js

@@ -0,0 +1,6 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+export { default } from 'ModuleDefaultExport1.js'

+ 6 - 0
test/es6/ModuleSimpleExport.js

@@ -0,0 +1,6 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+export function ModuleSimpleExport_foo() { return 'ModuleSimpleExport'; };

+ 6 - 0
test/es6/ModuleSimpleReexport.js

@@ -0,0 +1,6 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+export { ModuleSimpleExport_foo } from 'ModuleSimpleExport.js';

+ 7 - 0
test/es6/ValidExportDefaultStatement1.js

@@ -0,0 +1,7 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+var x;
+export default x;
+ 

+ 6 - 0
test/es6/ValidExportDefaultStatement2.js

@@ -0,0 +1,6 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+export default function() {return "hello world";}
+ 

+ 50 - 0
test/es6/ValidExportStatements.js

@@ -0,0 +1,50 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function foo() { }
+class bar { }
+function* baz() { }
+function foobar() { }
+
+// Export function expressions
+export function fn1 () { };
+export function fn2 () { }
+
+// Export generator expressions
+export function* gn1 () { };
+export function* gn2 () { }
+
+// Export class expressions
+export class cl1 { };
+export class cl2 { }
+
+// Export let decls
+export let let1;
+export let let2 = 2;
+export let let3, let4, let5;
+export let let6 = { }
+export let let7 = [ ]
+
+// Export const decls
+export const const2 = 'str';
+export const const3 = 3, const4 = 4;
+export const const5 = { }
+export const const6 = [ ]
+
+// Export with export clauses
+export {};
+export { foo };
+export { bar, };
+export { foo as foo2, baz }
+export { foo as foo3, baz as baz2, }
+export { foo as foo4, bar as bar2, foobar }
+
+// Export var decls
+export var var1 = 'string';
+export var var2;
+export var var3 = 5, var4
+export var var5, var6, var7
+
+export default 'default';

+ 25 - 0
test/es6/ValidImportStatements.js

@@ -0,0 +1,25 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+// Empty import statement is legal - does nothing but execute the module
+import "ValidExportStatements.js";
+import "ValidExportStatements.js"
+
+// Import default binding
+import ns1 from "ValidExportStatements.js";
+import ns2 from "ValidExportStatements.js"
+
+// Named import list
+import { foo } from "ValidExportStatements.js";
+import { foo as foo22, bar, } from "ValidExportStatements.js"
+
+// Namespace import statement
+//import * as ns3 from "ValidExportStatements.js";
+//import * as ns8 from "ValidExportStatements.js"
+
+// Import statement with default binding and a second clause
+//import ns4, * as ns5 from "ValidExportStatements.js"
+import ns6, { baz } from "ValidExportStatements.js";
+import ns7, { foo as foo23, foobar, } from "ValidExportStatements.js"

+ 12 - 0
test/es6/ValidReExportStatements.js

@@ -0,0 +1,12 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+// Re-export statements
+//export * from 'ValidExportDefaultStatement1.js';
+export {} from 'ValidExportDefaultStatement2.js';
+export { foo } from 'ValidExportStatements.js';
+export { bar, } from 'ValidExportStatements.js';
+export { foo as foo2, baz } from 'ValidExportStatements.js'
+export { foo as foo3, bar as bar2, } from 'ValidExportStatements.js'

+ 3 - 37
test/es6/es6toLength.js

@@ -12,46 +12,12 @@ var tests = [
        {
             var c = [];
             c[0] = 1;
-            c[4294967294] = 2;
-            var obj = {length : 3, 0 : 3, 1 : 4, 2: 5, [Symbol.isConcatSpreadable] : true}
-            c = c.concat(obj);
-            assert.areEqual(1, c[0], "confirm indices of array concated to did not change")
-            assert.areEqual(2, c[4294967294], "confirm indices of array concated to did not change");
-            assert.areEqual(3, c[4294967295], "confirm obj is spread as properties beyond array length bound");
-            assert.areEqual(4, c[4294967296], "confirm obj is spread as properties beyond array length bound");
-            assert.areEqual(5, c[4294967297], "confirm obj is spread as properties beyond array length bound");
-            assert.areEqual(4294967295, c.length, "length maxes out at 4294967295");
-
-            var c = [];
-            c[0] = 1;
-            c[4294967294] = 2;
-            c = c.concat(3,4,5);
-            assert.areEqual(1, c[0], "confirm indices of array concated to did not change")
-            assert.areEqual(2, c[4294967294], "confirm indices of array concated to did not change");
-            assert.areEqual(3, c[4294967295], "confirm integers are spread as properties beyond array length bound");
-            assert.areEqual(4, c[4294967296], "confirm integers are spread as properties beyond array length bound");
-            assert.areEqual(5, c[4294967297], "confirm integers are spread as properties beyond array length bound");
-            assert.areEqual(4294967295, c.length, "length maxes out at 4294967295");
-
-            var c = [];
-            c[0] = 1;
-            c[4294967294] = 2;
-            c = c.concat([3,4,5]);
-            assert.areEqual(1, c[0], "confirm indices of array concated to did not change")
-            assert.areEqual(2, c[4294967294], "confirm indices of array concated to did not change");
-            assert.areEqual(3, c[4294967295], "confirm array is spread as properties beyond array length bound");
-            assert.areEqual(4, c[4294967296], "confirm array is spread as properties beyond array length bound");
-            assert.areEqual(5, c[4294967297], "confirm array is spread as properties beyond array length bound");
-            assert.areEqual(4294967295, c.length, "length maxes out at 4294967295");
-
-            var c = [];
-            c[0] = 1;
-            c[4294967294] = 2;
+            c[4294967293] = 2;
             var oNeg = { length : -1, 0 : 3, 1: 4, [Symbol.isConcatSpreadable] : true};
             c = c.concat(oNeg);
             assert.areEqual(1, c[0], "confirm indices of array concated to did not change")
-            assert.areEqual(2, c[4294967294], "confirm indices of array concated to did not change");
-            assert.areEqual(undefined, c[4294967295], "Length of oNeg is coerced to 0 nothing is concated here");
+            assert.areEqual(2, c[4294967293], "confirm indices of array concated to did not change");
+            assert.areEqual(undefined, c[4294967294], "Length of oNeg is coerced to 0 nothing is concated here");
        }
    },
    {

+ 6 - 0
test/es6/exportmodule.js

@@ -0,0 +1,6 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+export var x;
+x = 'Pass';

+ 296 - 0
test/es6/module-functionality.js

@@ -0,0 +1,296 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+// ES6 Module functionality tests -- verifies functionality of import and export statements
+
+WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+
+function testModuleScript(source, message, shouldFail) {
+    let testfunc = () => WScript.LoadModule(source, 'samethread');
+
+    if (shouldFail) {
+        let caught = false;
+
+        // We can't use assert.throws here because the SyntaxError used to construct the thrown error
+        // is from a different context so it won't be strictly equal to our SyntaxError.
+        try {
+            testfunc();
+        } catch(e) {
+            caught = true;
+
+            // Compare toString output of SyntaxError and other context SyntaxError constructor.
+            assert.areEqual(e.constructor.toString(), SyntaxError.toString(), message);
+        }
+
+        assert.isTrue(caught, `Expected error not thrown: ${message}`);
+    } else {
+        assert.doesNotThrow(testfunc, message);
+    }
+}
+
+var tests = [
+    {
+        name: "Validate a simple module export",
+        body: function () {
+            let functionBody = 
+                `import { ModuleSimpleExport_foo } from 'ModuleSimpleExport.js';
+                assert.areEqual('ModuleSimpleExport', ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');`;
+            testModuleScript(functionBody, "Test importing a simple exported function", false);
+        }
+    },
+    {
+        name: "Validate importing from multiple modules",
+        body: function () {
+            let functionBody = 
+                `import { ModuleSimpleExport_foo } from 'ModuleSimpleExport.js';
+                assert.areEqual('ModuleSimpleExport', ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');
+                import { foo2 } from 'ModuleComplexExports.js';
+                assert.areEqual('foo', foo2(), 'Failed to import foo2 from ModuleComplexExports.js');`;
+            testModuleScript(functionBody, "Test importing from multiple modules", false);
+        }
+    },
+    {
+        name: "Validate a variety of more complex exports",
+        body: function () {
+            let functionBody = 
+                `import { foo, foo2 } from 'ModuleComplexExports.js';
+                assert.areEqual('foo', foo(), 'Failed to import foo from ModuleComplexExports.js');
+                assert.areEqual('foo', foo2(), 'Failed to import foo2 from ModuleComplexExports.js');
+                import { bar, bar2 } from 'ModuleComplexExports.js';
+                assert.areEqual('bar', bar(), 'Failed to import bar from ModuleComplexExports.js');
+                assert.areEqual('bar', bar2(), 'Failed to import bar2 from ModuleComplexExports.js');
+                import { let2, let3, let4, let5 } from 'ModuleComplexExports.js';
+                assert.areEqual('let2', let2, 'Failed to import let2 from ModuleComplexExports.js');
+                assert.areEqual('let3', let3, 'Failed to import let3 from ModuleComplexExports.js');
+                assert.areEqual('let2', let4, 'Failed to import let4 from ModuleComplexExports.js');
+                assert.areEqual('let3', let5, 'Failed to import let5 from ModuleComplexExports.js');
+                import { const2, const3, const4, const5 } from 'ModuleComplexExports.js';
+                assert.areEqual('const2', const2, 'Failed to import const2 from ModuleComplexExports.js');
+                assert.areEqual('const3', const3, 'Failed to import const3 from ModuleComplexExports.js');
+                assert.areEqual('const2', const4, 'Failed to import const4 from ModuleComplexExports.js');
+                assert.areEqual('const3', const5, 'Failed to import const5 from ModuleComplexExports.js');
+                import { var2, var3, var4, var5 } from 'ModuleComplexExports.js';
+                assert.areEqual('var2', var2, 'Failed to import var2 from ModuleComplexExports.js');
+                assert.areEqual('var3', var3, 'Failed to import var3 from ModuleComplexExports.js');
+                assert.areEqual('var2', var4, 'Failed to import var4 from ModuleComplexExports.js');
+                assert.areEqual('var3', var5, 'Failed to import var5 from ModuleComplexExports.js');
+                import { class2, class3, class4, class5 } from 'ModuleComplexExports.js';
+                assert.areEqual('class2', class2.static_member(), 'Failed to import class2 from ModuleComplexExports.js');
+                assert.areEqual('class2', new class2().member(), 'Failed to create intance of class2 from ModuleComplexExports.js');
+                assert.areEqual('class2', class3.static_member(), 'Failed to import class3 from ModuleComplexExports.js');
+                assert.areEqual('class2', new class3().member(), 'Failed to create intance of class3 from ModuleComplexExports.js');
+                assert.areEqual('class4', class4.static_member(), 'Failed to import class4 from ModuleComplexExports.js');
+                assert.areEqual('class4', new class4().member(), 'Failed to create intance of class4 from ModuleComplexExports.js');
+                assert.areEqual('class4', class5.static_member(), 'Failed to import class4 from ModuleComplexExports.js');
+                assert.areEqual('class4', new class5().member(), 'Failed to create intance of class4 from ModuleComplexExports.js');
+                import _default from 'ModuleComplexExports.js';
+                assert.areEqual('default', _default(), 'Failed to import default from ModuleComplexExports.js');
+                `;
+            testModuleScript(functionBody, "Test importing a variety of exports", false);
+        }
+    },
+    {
+        name: "Import an export as a different binding identifier",
+        body: function () {
+            let functionBody = 
+                `import { ModuleSimpleExport_foo as foo3 } from 'ModuleSimpleExport.js';
+                assert.areEqual('ModuleSimpleExport', foo3(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');
+                import { foo2 as foo4 } from 'ModuleComplexExports.js';
+                assert.areEqual('foo', foo4(), 'Failed to import foo4 from ModuleComplexExports.js');`;
+            testModuleScript(functionBody, "Test importing as different binding identifiers", false);
+        }
+    },
+    {
+        name: "Import the same export under multiple local binding identifiers",
+        body: function () {
+            let functionBody = 
+                `import { ModuleSimpleExport_foo as foo3, ModuleSimpleExport_foo as foo4 } from 'ModuleSimpleExport.js';
+                assert.areEqual('ModuleSimpleExport', foo3(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');
+                assert.areEqual('ModuleSimpleExport', foo4(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleExport.js');
+                assert.isTrue(foo3 === foo4, 'Export has the same value even if rebound');`;
+            testModuleScript(functionBody, "Test importing the same export under multiple binding identifier", false);
+        }
+    },
+    {
+        name: "Exporting module changes exported value",
+        body: function () {
+            let functionBody = 
+                `import { target, changeTarget } from 'ModuleComplexExports.js';
+                assert.areEqual('before', target(), 'Failed to import target from ModuleComplexExports.js');
+                assert.areEqual('ok', changeTarget(), 'Failed to import changeTarget from ModuleComplexExports.js');
+                assert.areEqual('after', target(), 'changeTarget failed to change export value');`;
+            testModuleScript(functionBody, "Changing exported value", false);
+        }
+    },
+    {
+        name: "Simple re-export forwards import to correct slot",
+        body: function () {
+            let functionBody = 
+                `import { ModuleSimpleExport_foo } from 'ModuleSimpleReexport.js';
+                assert.areEqual('ModuleSimpleExport', ModuleSimpleExport_foo(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleReexport.js');`;
+            testModuleScript(functionBody, "Simple re-export from one module to another", false);
+        }
+    },
+    {
+        name: "Import of renamed re-export forwards import to correct slot",
+        body: function () {
+            let functionBody = 
+                `import { ModuleSimpleExport_foo as ModuleSimpleExport_baz } from 'ModuleSimpleReexport.js';
+                assert.areEqual('ModuleSimpleExport', ModuleSimpleExport_baz(), 'Failed to import ModuleSimpleExport_foo from ModuleSimpleReexport.js');`;
+            testModuleScript(functionBody, "Rename simple re-export", false);
+        }
+    },
+    {
+        name: "Renamed re-export and renamed import",
+        body: function () {
+            let functionBody = 
+                `import { ModuleComplexReexports_foo as ModuleComplexReexports_baz } from 'ModuleComplexReexports.js';
+                assert.areEqual('bar', ModuleComplexReexports_baz(), 'Failed to import ModuleComplexReexports_foo from ModuleComplexReexports.js');`;
+            testModuleScript(functionBody, "Rename already renamed re-export", false);
+        }
+    },
+    {
+        name: "Explicit export/import to default binding",
+        body: function () {
+            let functionBody = 
+                `import { default as baz } from 'ModuleDefaultExport1.js';
+                assert.areEqual('ModuleDefaultExport1', baz(), 'Failed to import default from ModuleDefaultExport1.js');`;
+            testModuleScript(functionBody, "Explicitly export and import a local name to the default binding", false);
+        }
+    },
+    {
+        name: "Explicit import of default binding",
+        body: function () {
+            let functionBody = 
+                `import { default as baz } from 'ModuleDefaultExport2.js';
+                assert.areEqual('ModuleDefaultExport2', baz(), 'Failed to import default from ModuleDefaultExport2.js');`;
+            testModuleScript(functionBody, "Explicitly import the default export binding", false);
+        }
+    },
+    {
+        name: "Implicitly re-export default export",
+        body: function () {
+            let functionBody = 
+                `import baz from 'ModuleDefaultReexport.js';
+                assert.areEqual('ModuleDefaultExport1', baz(), 'Failed to import default from ModuleDefaultReexport.js');`;
+            testModuleScript(functionBody, "Implicitly re-export the default export binding", false);
+        }
+    },
+    {
+        name: "Implicitly re-export default export and rename the imported binding",
+        body: function () {
+            let functionBody = 
+                `import { default as baz } from 'ModuleDefaultReexport.js';
+                assert.areEqual('ModuleDefaultExport1', baz(), 'Failed to import default from ModuleDefaultReexport.js');
+                import { not_default as bat } from 'ModuleDefaultReexport.js';
+                assert.areEqual('ModuleDefaultExport2', bat(), 'Failed to import not_default from ModuleDefaultReexport.js');`;
+            testModuleScript(functionBody, "Implicitly re-export the default export binding and rename the import binding", false);
+        }
+    },
+    {
+        name: "Exporting module changes value of default export",
+        body: function () {
+            let functionBody = 
+                `import ModuleDefaultExport3_default from 'ModuleDefaultExport3.js';
+                assert.areEqual(2, ModuleDefaultExport3_default, 'Failed to import default from ModuleDefaultExport3.js');
+                import ModuleDefaultExport4_default from 'ModuleDefaultExport4.js';
+                assert.areEqual(1, ModuleDefaultExport4_default, 'Failed to import not_default from ModuleDefaultExport4.js');`;
+            testModuleScript(functionBody, "Exported value incorrectly bound", false);
+        }
+    },
+    {
+        name: "Import bindings used in a nested function",
+        body: function () {
+            let functionBody = 
+                `function test() {
+                    assert.areEqual('ModuleDefaultExport2', foo(), 'Failed to import default from ModuleDefaultExport2.js');
+                }
+                test();
+                import foo from 'ModuleDefaultExport2.js';
+                test();`;
+            testModuleScript(functionBody, "Failed to find imported name correctly in nested function", false);
+        }
+    },
+    {
+        name: "Exported name may be any keyword",
+        body: function () {
+            let functionBody = 
+                `import { export as baz } from 'ModuleComplexExports.js';
+                assert.areEqual('ModuleComplexExports', baz, 'Failed to import export from ModuleDefaultExport2.js');
+                import { function as bat } from 'ModuleComplexExports.js';
+                assert.areEqual('ModuleComplexExports', bat, 'Failed to import function from ModuleDefaultExport2.js');`;
+            testModuleScript(functionBody, "Exported name may be a keyword (import binding must be binding identifier)", false);
+        }
+    },
+    {
+        name: "Import binding of a keyword-named export may not be a keyword unless it is bound to a different binding identifier",
+        body: function () {
+            let functionBody = `import { export } from 'ModuleComplexExports.js';`;
+            testModuleScript(functionBody, "Import binding must be binding identifier even if export name is not (export)", true);
+            functionBody = `import { function } from 'ModuleComplexExports.js';`;
+            testModuleScript(functionBody, "Import binding must be binding identifier even if export name is not (function)", true);
+            functionBody = `import { switch } from 'ModuleComplexReexports.js';`;
+            testModuleScript(functionBody, "Import binding must be binding identifier even if re-export name is not (switch)", true);
+        }
+    },
+    {
+        name: "Exported name may be any keyword testing re-exports",
+        body: function () {
+            let functionBody = 
+                `import { switch as baz } from 'ModuleComplexReexports.js';
+                assert.areEqual('ModuleComplexExports', baz, 'Failed to import switch from ModuleComplexReexports.js');`;
+            testModuleScript(functionBody, "Exported name may be a keyword including re-epxort chains", false);
+        }
+    },
+    {
+        name: "Odd case of 'export { as as as }; import { as as as };'",
+        body: function () {
+            let functionBody = 
+                `import { as as as } from 'ModuleComplexExports.js';
+                assert.areEqual('as', as(), 'String "as" is not reserved word');`;
+            testModuleScript(functionBody, "Test 'import { as as as}'", false);
+        }
+    },
+    {
+        name: "Typeof a module export",
+        body: function () {
+            let functionBody = 
+                `import _default from 'ModuleDefaultExport2.js';
+                assert.areEqual('function', typeof _default, 'typeof default export from ModuleDefaultExport2.js is function');`;
+                
+            WScript.LoadModule(functionBody, 'samethread');
+        }
+    },
+    {
+        name: "Circular module dependency",
+        body: function () {
+            let functionBody = 
+                `import { circular_foo } from 'ModuleCircularFoo.js';
+                assert.areEqual(2, circular_foo(), 'This function calls between both modules in the circular dependency incrementing a counter in each');
+                import { circular_bar } from 'ModuleCircularBar.js';
+                assert.areEqual(4, circular_bar(), 'Second call originates in the other module but still increments the counter twice');`;
+                
+            WScript.LoadModule(functionBody, 'samethread');
+        }
+    },
+    {
+        name: "Implicitly re-exporting an import binding (import { foo } from ''; export { foo };)",
+        body: function () {
+            let functionBody = 
+                `import { foo, baz, localfoo, bar, localfoo2, bar2, bar2 as bar3 } from 'ModuleComplexReexports.js';
+                assert.areEqual('foo', foo(), 'Simple implicit re-export');
+                assert.areEqual('foo', baz(), 'Renamed export imported and renamed during implicit re-export');
+                assert.areEqual('foo', localfoo(), 'Export renamed as import and implicitly re-exported');
+                assert.areEqual('foo', bar(), 'Renamed export renamed as import and renamed again during implicit re-exported');
+                assert.areEqual('foo', localfoo2(), 'Renamed export renamed as import and implicitly re-exported');
+                assert.areEqual('foo', bar2(), 'Renamed export renamed as import and renamed again during implicit re-export');
+                assert.areEqual('foo', bar3(), 'Renamed export renamed as import renamed during implicit re-export and renamed in final import');`;
+                
+            WScript.LoadModule(functionBody, 'samethread');
+        }
+    },
+];
+
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 149 - 0
test/es6/module-syntax.js

@@ -0,0 +1,149 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+// ES6 Module syntax tests -- verifies syntax of import and export statements
+
+WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+
+function testModuleScript(source, message, shouldFail) {
+    let testfunc = () => WScript.LoadModule(source, 'samethread');
+
+    if (shouldFail) {
+        let caught = false;
+
+        // We can't use assert.throws here because the SyntaxError used to construct the thrown error
+        // is from a different context so it won't be strictly equal to our SyntaxError.
+        try {
+            testfunc();
+        } catch(e) {
+            caught = true;
+
+            // Compare toString output of SyntaxError and other context SyntaxError constructor.
+            assert.areEqual(e.constructor.toString(), SyntaxError.toString(), message);
+        }
+
+        assert.isTrue(caught, `Expected error not thrown: ${message}`);
+    } else {
+        assert.doesNotThrow(testfunc, message);
+    }
+}
+
+var tests = [
+    {
+        name: "All valid (non-default) export statements",
+        body: function () {
+            assert.doesNotThrow(function () { WScript.LoadModuleFile('ValidExportStatements.js', 'samethread'); }, "Valid export statements");
+        }
+    },
+    {
+        name: "Valid default export statements",
+        body: function () {
+            testModuleScript('export default function () { };', 'Unnamed function expression default export');
+            testModuleScript('export default function _fn2 () { }', 'Named function expression default export');
+            testModuleScript('export default function* () { };', 'Unnamed generator function expression default export');
+            testModuleScript('export default function* _gn2 () { }', 'Named generator function expression default export');
+            testModuleScript('export default class { };', 'Unnamed class expression default export');
+            testModuleScript('export default class _cl2 { }', 'Named class default expression export');
+            testModuleScript('export default 1;', 'Primitive type default export');
+            testModuleScript('var a; export default a = 10;', 'Variable in assignment expression default export');
+            testModuleScript('export default () => 3', 'Simple default lambda expression export statement');
+            testModuleScript('function _default() { }; export default _default', 'Named function statement default export');
+            testModuleScript('function* g() { }; export default g', 'Named generator function statement default export');
+            testModuleScript('class c { }; export default c', 'Named class statement default export');
+            testModuleScript("var _ = { method: function() { return 'method_result'; }, method2: function() { return 'method2_result'; } }; export default _", 'Export object with methods - framework model');
+        }
+    },
+    {
+        name: "Syntax error export statements",
+        body: function () {
+            testModuleScript('export const const1;', 'Syntax error if const decl is missing initializer', true);
+            testModuleScript('function foo() { }; export foo;', "Syntax error if we're trying to export an identifier without default or curly braces", true);
+            testModuleScript('export function () { }', 'Syntax error if function declaration is missing binding identifier', true);
+            testModuleScript('export function* () { }', 'Syntax error if generator declaration is missing binding identifier', true);
+            testModuleScript('export class { }', 'Syntax error if class declaration is missing binding identifier', true);
+            testModuleScript('function foo() { }; export [ foo ];', 'Syntax error if we use brackets instead of curly braces in export statement', true);
+            testModuleScript('function foo() { export default function() { } }', 'Syntax error if export statement is in a nested function', true);
+            testModuleScript('function foo() { }; export { , foo };', 'Syntax error if named export list contains an empty element', true);
+            testModuleScript('function foo() { }; () => { export { foo }; }', 'Syntax error if export statement is in arrow function', true);
+            testModuleScript('function foo() { }; try { export { foo }; } catch(e) { }', 'Syntax error if export statement is in try catch statement', true);
+            testModuleScript('function foo() { }; { export { foo }; }', 'Syntax error if export statement is in any block', true);
+            testModuleScript('export default 1, 2, 3;', "Export default takes an assignment expression which doesn't allow comma expressions", true);
+            testModuleScript('export 12;', 'Syntax error if export is followed by non-identifier', true);
+            testModuleScript("export 'string_constant';", 'Syntax error if export is followed by string constant', true);
+            testModuleScript('export ', 'Syntax error if export is followed by EOF', true);
+            testModuleScript('function foo() { }; export { foo as 100 };', 'Syntax error in named export clause if trying to export as numeric constant', true);
+        }
+    },
+    {
+        name: "Syntax error import statements",
+        body: function () {
+            testModuleScript('function foo() { import foo from "ValidExportStatements.js"; }', 'Syntax error if import statement is in nested function', true);
+            testModuleScript('import foo, bar from "ValidExportStatements.js";', 'Syntax error if import statement has multiple default bindings', true);
+            testModuleScript('import { foo, foo } from "ValidExportStatements.js";', 'Redeclaration error if multiple imports have the same local name', true);
+            testModuleScript('import { foo, bar as foo } from "ValidExportStatements.js";', 'Redeclaration error if multiple imports have the same local name', true);
+            testModuleScript('const foo = 12; import { foo } from "ValidExportStatements.js";', 'Syntax error if module body has a const declaration bound to the same name as a module import', true);
+            testModuleScript('function foo() { }; import { foo } from "ValidExportStatements.js";', 'Syntax error if module body has a function declaration bound to the same name as a module import', true);
+            testModuleScript('import foo;', 'Syntax error if import statement is missing from clause', true);
+            testModuleScript('import * as foo, from "ValidExportStatements.js";', 'Syntax error if import statement has comma after namespace import', true);
+            testModuleScript('import * as foo, bar from "ValidExportStatements.js";', 'Syntax error if import statement has default binding after namespace import', true);
+            testModuleScript('import * as foo, { bar } from "ValidExportStatements.js";', 'Syntax error if import statement has named import list after namespace import', true);
+            testModuleScript('import { foo }, from "ValidExportStatements.js";', 'Syntax error if import statement has comma after named import list', true);
+            testModuleScript('import { foo }, bar from "ValidExportStatements.js";', 'Syntax error if import statement has default binding after named import list', true);
+            testModuleScript('import { foo }, * as ns1 from "ValidExportStatements.js";', 'Syntax error if import statement has namespace import after named import list', true);
+            testModuleScript('import { foo }', 'Syntax error if import statement is missing from clause', true);
+            testModuleScript('import [ foo ] from "ValidExportStatements.js";', 'Syntax error if named import clause uses brackets', true);
+            testModuleScript('import * foo from "ValidExportStatements.js";', 'Syntax error if namespace import is missing "as" keyword', true);
+            testModuleScript('import * as "foo" from "ValidExportStatements.js";', 'Syntax error if namespace imported binding name is not identifier', true);
+            testModuleScript('import { , foo } from "ValidExportStatements.js";', 'Syntax error if named import list contains an empty element', true);
+            testModuleScript('import foo from "ValidExportStatements.js"; import foo from "ValidExportStatements.js";', 'Default import cannot be bound to the same symbol', true);
+            testModuleScript('import { foo } from "ValidExportStatements.js"; import { foo } from "ValidExportStatements.js";', 'Multiple named imports cannot be bound to the same symbol', true);
+            testModuleScript('import * as foo from "ValidExportStatements.js"; import * as foo from "ValidExportStatements.js";', 'Multiple namespace imports cannot be bound to the same symbol', true);
+            testModuleScript('import { foo as bar, bar } from "ValidExportStatements.js";', 'Named import clause may not contain multiple binding identifiers with the same name', true);
+            testModuleScript('import foo from "ValidExportStatements.js"; import * as foo from "ValidExportStatements.js";', 'Imported bindings cannot be overwritten by later imports', true);
+            testModuleScript('() => { import arrow from ""; }', 'Syntax error if import statement is in arrow function', true);
+            testModuleScript('try { import _try from ""; } catch(e) { }', 'Syntax error if import statement is in try catch statement', true);
+            testModuleScript('{ import in_block from ""; }', 'Syntax error if import statement is in any block', true);
+            testModuleScript('import {', 'Named import clause which has EOF after left curly', true);
+            testModuleScript('import { foo', 'Named import clause which has EOF after identifier', true);
+            testModuleScript('import { foo as ', 'Named import clause which has EOF after identifier as', true);
+            testModuleScript('import { foo as bar ', 'Named import clause which has EOF after identifier as identifier', true);
+            testModuleScript('import { foo as bar, ', 'Named import clause which has EOF after identifier as identifier comma', true);
+            testModuleScript('import { switch } from "module";', 'Named import clause which has non-identifier token as the first token', true);
+            testModuleScript('import { foo bar } from "module";', 'Named import clause missing "as" token', true);
+            testModuleScript('import { foo as switch } from "module";', 'Named import clause with non-identifier token after "as"', true);
+            testModuleScript('import { foo, , } from "module";', 'Named import clause with too many trailing commas', true);
+        }
+    },
+    {
+        name: "Runtime error import statements",
+        body: function () {
+            testModuleScript('import foo from "ValidExportStatements.js"; assert.throws(()=>{ foo =12; }, TypeError, "assignment to const");', 'Imported default bindings are constant bindings', false);
+            testModuleScript('import { foo } from "ValidExportStatements.js"; assert.throws(()=>{ foo = 12; }, TypeError, "assignment to const");', 'Imported named bindings are constant bindings', false);
+
+            // 'import *' is not yet implemented
+            //testModuleScript('import * as foo from "ValidExportStatements.js"; assert.throws(()=>{ foo = 12; }, TypeError, "assignment to const");', 'Namespace import bindings are constant bindings', false);
+
+            testModuleScript('import { foo as foo22 } from "ValidExportStatements.js"; assert.throws(()=>{ foo22 = 12; }, TypeError, "assignment to const");', 'Renamed import bindings are constant bindings', false);
+        }
+    },
+    {
+        name: "All valid re-export statements",
+        body: function () {
+            assert.doesNotThrow(function () { WScript.LoadModuleFile('ValidReExportStatements.js', 'samethread'); }, "Valid re-export statements");
+        }
+    },
+    {
+        name: "HTML comments do not parse in module code",
+        body: function () {
+            testModuleScript("<!--\n",     "HTML open comment does not parse in module code",  true);
+            testModuleScript("\n-->",      "HTML close comment does not parse in module code", true);
+            testModuleScript("<!-- -->",   "HTML comment does not parse in module code",       true);
+            testModuleScript("/* */ -->",  "HTML comment after delimited comment does not parse in module code", true);
+            testModuleScript("/* */\n-->", "HTML comment after delimited comment does not parse in module code", true);
+        }
+    }
+];
+
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 42 - 0
test/es6/module-syntax1.js

@@ -0,0 +1,42 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+// ES6 Module syntax tests -- verifies syntax of import and export statements
+
+WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
+
+function testModuleScript(source, message, shouldFail) {
+    let testfunc = () => WScript.LoadModule(source, 'samethread');
+    
+    if (shouldFail) {
+        let caught = false;
+        
+        // We can't use assert.throws here because the SyntaxError used to construct the thrown error
+        // is from a different context so it won't be strictly equal to our SyntaxError.
+        try {
+            testfunc();
+        } catch(e) {
+            caught = true;
+            
+            // Compare toString output of SyntaxError and other context SyntaxError constructor.
+            assert.areEqual(e.constructor.toString(), SyntaxError.toString(), message);
+        }
+        
+        assert.isTrue(caught, `Expected error not thrown: ${message}`);
+    } else {
+        assert.doesNotThrow(testfunc, message);
+    }
+}
+
+var tests = [
+    {
+        name: "All valid import statements",
+        body: function () {
+            assert.doesNotThrow(function () { WScript.LoadModuleFile('ValidImportStatements.js', 'samethread'); }, "Valid import statements");
+        }
+    },
+];
+
+testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });

+ 5 - 0
test/es6/moduletest1.js

@@ -0,0 +1,5 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+WScript.LoadScriptFile("passmodule.js", "module");

+ 6 - 0
test/es6/passmodule.js

@@ -0,0 +1,6 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+import { x } from 'exportmodule.js';
+WScript.Echo(x);

+ 43 - 1
test/es6/rlexe.xml

@@ -1243,8 +1243,50 @@
   <default>
     <files>await-futreserved-only-in-modules.js</files>
     <compile-flags>-ES6Module</compile-flags>
-    <tags>exclude_dynapogo</tags>
+    <tags>exclude_dynapogo, exclude_xplat</tags>
   </default>
+</test>
+  <test>
+    <default>
+      <files>moduletest1.js</files>
+      <compile-flags>-ES6Module</compile-flags>
+      <tags>exclude_dynapogo, exclude_xplat</tags>
+    </default>
+  </test>
+<test>
+    <default>
+        <files>module-syntax.js</files>
+        <compile-flags>-ES6Module -args summary -endargs</compile-flags>
+        <tags>exclude_xplat</tags>
+    </default>
+</test>
+<test>
+    <default>
+        <files>module-syntax1.js</files>
+        <compile-flags>-ES6Module -args summary -endargs</compile-flags>
+        <tags>exclude_xplat</tags>
+    </default>
+</test>
+<test>
+    <default>
+        <files>module-functionality.js</files>
+        <compile-flags>-ES6Module  -ES7AsyncAwait -args summary -endargs</compile-flags>
+        <tags>exclude_xplat</tags>
+    </default>
+</test>
+<test>
+    <default>
+        <files>module-syntax.js</files>
+        <compile-flags>-ES6Module -force:deferparse -args summary -endargs</compile-flags>
+        <tags>exclude_xplat</tags>
+    </default>
+</test>
+<test>
+    <default>
+        <files>module-syntax1.js</files>
+        <compile-flags>-ES6Module -force:deferparse -args summary -endargs</compile-flags>
+        <tags>exclude_xplat</tags>
+    </default>
 </test>
 <test>
   <default>

+ 38 - 1
test/runtests.cmd

@@ -163,7 +163,6 @@ goto :main
   if /i "%1" == "-quiet"            set _quiet=-quiet&                                          goto :ArgOk
   :: TODO Consider removing -drt and exclude_drt in some reasonable manner
   if /i "%1" == "-drt"              set _drt=1& set _NOTTAGS=%_NOTTAGS% -nottags:exclude_drt&   goto :ArgOk
-  if /i "%1" == "-nightly"          set _nightly=1&                                             goto :ArgOk
   if /i "%1" == "-rebase"           set _rebase=-rebase&                                        goto :ArgOk
   if /i "%1" == "-rundebug"         set _RUNDEBUG=1&                                            goto :ArgOk
   :: TODO Figure out best way to specify build arch for tests that are excluded to specific archs
@@ -212,6 +211,16 @@ goto :main
     set _Variants=disable_jit
     goto :ArgOk
   )
+  
+  if /i "%1" == "-nightly" (
+    set _nightly=1
+    if "%_ExtraVariants%" == "" (
+      set _ExtraVariants=mediumlayout,largelayout,forceserialized
+    ) else (
+      set _ExtraVariants=%_ExtraVariants%,mediumlayout,largelayout,forceserialized
+    )
+    goto :ArgOk
+  )
 
   if not "%1" == "" echo Unknown argument: %1 & set fShowGetHelp=1
 
@@ -344,6 +353,24 @@ goto :main
 :: ============================================================================
 :RunOneVariant
 
+  if "%_BuildType%" == "test" (
+    rem bytecode layout switches not available in test build
+    if "%_TESTCONFIG%"=="largelayout" (
+      if not exist %_logsRoot%\%_BuildArch%_%_BuildType%\%_TESTCONFIG% (
+        mkdir %_logsRoot%\%_BuildArch%_%_BuildType%\%_TESTCONFIG%
+      )
+      echo. > %_logsRoot%\%_BuildArch%_%_BuildType%\%_TESTCONFIG%\rl.log
+      goto :eof
+    )
+    if "%_TESTCONFIG%"=="mediumlayout" (
+      if not exist %_logsRoot%\%_BuildArch%_%_BuildType%\%_TESTCONFIG% (
+        mkdir %_logsRoot%\%_BuildArch%_%_BuildType%\%_TESTCONFIG%
+      )
+      echo. > %_logsRoot%\%_BuildArch%_%_BuildType%\%_TESTCONFIG%\rl.log
+      goto :eof
+    )
+  )
+
   set _OLD_CC_FLAGS=%EXTRA_CC_FLAGS%
   set EXTRA_RL_FLAGS=-appendtestnametoextraccflags
   set _exclude_serialized=
@@ -407,6 +434,16 @@ goto :main
     set EXTRA_RL_FLAGS=
     set _exclude_serialized=-nottags:exclude_serialized
   )
+  if "%_TESTCONFIG%"=="mediumlayout" (
+    set EXTRA_CC_FLAGS=%EXTRA_CC_FLAGS% -MediumByteCodeLayout -forceserialized
+    set EXTRA_RL_FLAGS=-nottags:exclude_bytecodelayout
+    set _exclude_serialized=-nottags:exclude_serialized
+  )
+  if "%_TESTCONFIG%"=="largelayout" (
+    set EXTRA_CC_FLAGS=%EXTRA_CC_FLAGS% -LargeByteCodeLayout -forceserialized
+    set EXTRA_RL_FLAGS=-nottags:exclude_bytecodelayout
+    set _exclude_serialized=-nottags:exclude_serialized
+  )
 
   echo.
   echo ############# Starting %_TESTCONFIG% variant #############