소스 검색

[1.11>master] [1.10>1.11] [MERGE #5787 @Cellule] Merge changes from Windows 1809 October Update

Merge pull request #5787 from Cellule:1809_october
Michael Ferris 7 년 전
부모
커밋
878e8cb5ba
100개의 변경된 파일2080개의 추가작업 그리고 857개의 파일을 삭제
  1. 1 0
      Build/Common.Build.Default.props
  2. 3 10
      bin/GCStress/StubExternalApi.cpp
  3. 16 13
      bin/NativeTests/CodexTests.cpp
  4. 2 1
      bin/rl/rl.vcxproj
  5. 359 160
      lib/Backend/BackwardPass.cpp
  6. 10 3
      lib/Backend/BackwardPass.h
  7. 35 8
      lib/Backend/EmitBuffer.cpp
  8. 1 0
      lib/Backend/EmitBuffer.h
  9. 3 0
      lib/Backend/FlowGraph.cpp
  10. 11 1
      lib/Backend/Func.cpp
  11. 9 0
      lib/Backend/Func.h
  12. 25 4
      lib/Backend/GlobOpt.cpp
  13. 8 8
      lib/Backend/GlobOpt.h
  14. 15 8
      lib/Backend/IRBuilderAsmJs.cpp
  15. 2 1
      lib/Backend/IRBuilderAsmJs.h
  16. 10 2
      lib/Backend/Inline.cpp
  17. 9 3
      lib/Backend/JITOutput.cpp
  18. 28 2
      lib/Backend/JITThunkEmitter.cpp
  19. 11 0
      lib/Backend/JnHelperMethod.cpp
  20. 2 0
      lib/Backend/JnHelperMethod.h
  21. 19 18
      lib/Backend/JnHelperMethodList.h
  22. 74 66
      lib/Backend/Lower.cpp
  23. 78 0
      lib/Backend/LowerMDShared.cpp
  24. 1 0
      lib/Backend/LowerMDShared.h
  25. 9 1
      lib/Backend/TempTracker.cpp
  26. 1 1
      lib/Backend/arm64/LowerMD.h
  27. 4 1
      lib/Common/Codex/Chakra.Common.Codex.vcxproj
  28. 65 32
      lib/Common/Codex/Utf8Codex.cpp
  29. 36 23
      lib/Common/Codex/Utf8Codex.h
  30. 27 7
      lib/Common/Codex/Utf8Helper.h
  31. 4 2
      lib/Common/Common.h
  32. 4 0
      lib/Common/Common/CommonCommonPch.h
  33. 12 8
      lib/Common/ConfigFlagsList.h
  34. 1 1
      lib/Common/Core/Chakra.Common.Core.vcxproj
  35. 4 0
      lib/Common/Core/CommonCorePch.h
  36. 2 1
      lib/Common/Core/JitHelperUtils.h
  37. 1 1
      lib/Common/DataStructures/SparseBitVector.h
  38. 6 10
      lib/Common/Memory/AllocationPolicyManager.h
  39. 5 0
      lib/Common/Memory/AutoPtr.h
  40. 52 51
      lib/Common/Memory/CollectionFlags.h
  41. 4 0
      lib/Common/Memory/CommonMemoryPch.h
  42. 5 1
      lib/Common/Memory/CustomHeap.h
  43. 0 6
      lib/Common/Memory/PageAllocator.cpp
  44. 10 6
      lib/Common/Memory/Recycler.cpp
  45. 2 0
      lib/Common/Memory/Recycler.h
  46. 26 0
      lib/Common/Memory/SectionAllocWrapper.cpp
  47. 2 0
      lib/Common/Memory/SectionAllocWrapper.h
  48. 3 4
      lib/Common/Memory/VirtualAllocWrapper.cpp
  49. 2 0
      lib/Common/Memory/VirtualAllocWrapper.h
  50. 9 6
      lib/JITClient/JITManager.cpp
  51. 1 1
      lib/JITClient/JITManager.h
  52. 1 5
      lib/JITServer/JITServer.cpp
  53. 1 1
      lib/Jsrt/Jsrt.cpp
  54. 1 1
      lib/Jsrt/JsrtDebugUtils.cpp
  55. 22 6
      lib/Jsrt/JsrtExternalArrayBuffer.cpp
  56. 4 2
      lib/Jsrt/JsrtExternalArrayBuffer.h
  57. 8 3
      lib/Jsrt/JsrtSourceHolder.cpp
  58. 3 1
      lib/Jsrt/JsrtSourceHolder.h
  59. 11 11
      lib/Parser/Parse.h
  60. 34 14
      lib/Runtime/Base/CrossSite.cpp
  61. 43 0
      lib/Runtime/Base/DelayLoadLibrary.cpp
  62. 13 1
      lib/Runtime/Base/DelayLoadLibrary.h
  63. 6 4
      lib/Runtime/Base/FunctionBody.cpp
  64. 16 2
      lib/Runtime/Base/ScriptContext.cpp
  65. 13 1
      lib/Runtime/Base/ScriptContext.h
  66. 22 0
      lib/Runtime/Base/SourceHolder.cpp
  67. 22 2
      lib/Runtime/Base/SourceHolder.h
  68. 16 0
      lib/Runtime/Base/ThreadContext.cpp
  69. 16 3
      lib/Runtime/Base/ThreadContext.h
  70. 66 12
      lib/Runtime/Base/ThreadContextInfo.cpp
  71. 8 0
      lib/Runtime/Base/ThreadContextInfo.h
  72. 0 16
      lib/Runtime/Base/Utf8SourceInfo.cpp
  73. 0 5
      lib/Runtime/Base/Utf8SourceInfo.h
  74. 17 13
      lib/Runtime/Base/VTuneChakraProfile.cpp
  75. 2 2
      lib/Runtime/ByteCode/ByteCodeEmitter.cpp
  76. 0 1
      lib/Runtime/ByteCode/ByteCodeGenerator.h
  77. 1 1
      lib/Runtime/ByteCode/OpCodes.h
  78. 3 3
      lib/Runtime/Debug/DebugContext.cpp
  79. 1 1
      lib/Runtime/Debug/DebugContext.h
  80. 3 3
      lib/Runtime/Debug/DiagObjectModel.cpp
  81. 3 2
      lib/Runtime/Debug/TTEventLog.cpp
  82. 19 19
      lib/Runtime/Debug/TTSnapValues.h
  83. 37 2
      lib/Runtime/DetachedStateBase.h
  84. 3 2
      lib/Runtime/Language/DynamicProfileInfo.cpp
  85. 4 3
      lib/Runtime/Language/DynamicProfileStorage.cpp
  86. 11 4
      lib/Runtime/Language/InlineCache.h
  87. 1 1
      lib/Runtime/Language/InterpreterHandler.inl
  88. 1 1
      lib/Runtime/Language/InterpreterHandlerAsmJs.inl
  89. 1 1
      lib/Runtime/Language/InterpreterStackFrame.cpp
  90. 27 15
      lib/Runtime/Language/JavascriptOperators.cpp
  91. 3 6
      lib/Runtime/Language/JavascriptOperators.h
  92. 0 1
      lib/Runtime/Language/JavascriptStackWalker.cpp
  93. 252 59
      lib/Runtime/Library/ArrayBuffer.cpp
  94. 59 18
      lib/Runtime/Library/ArrayBuffer.h
  95. 5 2
      lib/Runtime/Library/BoundFunction.cpp
  96. 137 136
      lib/Runtime/Library/CMakeLists.txt
  97. 2 0
      lib/Runtime/Library/Chakra.Runtime.Library.vcxproj
  98. 2 0
      lib/Runtime/Library/Chakra.Runtime.Library.vcxproj.filters
  99. 4 0
      lib/Runtime/Library/DataView.cpp
  100. 127 0
      lib/Runtime/Library/DelayFreeArrayBufferHelper.cpp

+ 1 - 0
Build/Common.Build.Default.props

@@ -6,6 +6,7 @@
     <NtTargetVersion_Win8 >0x602</NtTargetVersion_Win8>
     <NtTargetVersion_WinBlue>0x603</NtTargetVersion_WinBlue>
     <NtTargetVersion_Win10>0xA00</NtTargetVersion_Win10>
+    <NtTargetVersion_Latest>$(NtTargetVersion_Win10)</NtTargetVersion_Latest>
   </PropertyGroup>
 
   <!-- Auto tool set selection -->

+ 3 - 10
bin/GCStress/StubExternalApi.cpp

@@ -82,16 +82,9 @@ bool GetDeviceFamilyInfo(
 void
 ChakraBinaryAutoSystemInfoInit(AutoSystemInfo * autoSystemInfo)
 {
-    ULONGLONG UAPInfo;
-    ULONG DeviceFamily;
-    ULONG DeviceForm;
-    if (GetDeviceFamilyInfo(&UAPInfo, &DeviceFamily, &DeviceForm))
-    {
-        bool isMobile = (DeviceFamily == 0x00000004 /*DEVICEFAMILYINFOENUM_MOBILE*/);
-        autoSystemInfo->shouldQCMoreFrequently = isMobile;
-        autoSystemInfo->supportsOnlyMultiThreadedCOM = isMobile;  //TODO: pick some other platform to the list
-        autoSystemInfo->isLowMemoryDevice = isMobile;  //TODO: pick some other platform to the list
-    }
+    autoSystemInfo->shouldQCMoreFrequently = false;
+    autoSystemInfo->supportsOnlyMultiThreadedCOM = false;  //TODO: pick some other platform to the list
+    autoSystemInfo->isLowMemoryDevice = false;  //TODO: pick some other platform to the list
 }
 
 enum MemProtectHeapCollectFlags {};

+ 16 - 13
bin/NativeTests/CodexTests.cpp

@@ -36,15 +36,16 @@ namespace CodexTest
     //
     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
+        const size_t charCount = 1;
+        constexpr size_t cbEncodedBuffer = charCount * 3 + 1; // +1 since the buffer will be null-terminated
+        utf8char_t encodedBuffer[cbEncodedBuffer];
 
         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);
+            size_t numEncodedBytes = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>(encodedBuffer, cbEncodedBuffer, &testValues[i], charCount);
             CHECK(numEncodedBytes == 3);
             CheckIsUnicodeReplacementChar(encodedBuffer);
         }
@@ -62,11 +63,12 @@ namespace CodexTest
         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
+        const size_t encodedBufferSize = maxEncodedByteCount + 1; // +1 in case a null-terminating func is passed in
+        utf8char_t encodedBuffer[encodedBufferSize];
 
         for (int i = 0; i < numTestCases; i++)
         {
-            size_t numEncodedBytes = func(encodedBuffer, testCases[i].surrogatePair, charCount);
+            size_t numEncodedBytes = func(encodedBuffer, encodedBufferSize, testCases[i].surrogatePair, charCount);
             CHECK(numEncodedBytes <= maxEncodedByteCount);
             for (size_t j = 0; j < numEncodedBytes; j++)
             {
@@ -106,7 +108,7 @@ namespace CodexTest
             { { 0xDBFF, 0xDFFF }, { 0xED, 0xAF, 0xBF, 0xED, 0xBF, 0xBF } }  //  U+10FFFF
         };
 
-        RunUtf8EncodingTestCase(testCases, static_cast<size_t (*)(utf8char_t*, const char16*, charcount_t)>(utf8::EncodeInto));
+        RunUtf8EncodingTestCase(testCases, static_cast<size_t (*)(utf8char_t*, size_t, const char16*, charcount_t)>(utf8::EncodeInto<utf8::Utf8EncodingKind::Cesu8>));
     }
 
     TEST_CASE("CodexTest_EncodeUtf8_PairedSurrogates", "[CodexTest]")
@@ -132,7 +134,7 @@ namespace CodexTest
             { { 0xDBFF, 0xDFFF }, { 0xF4, 0x8F, 0xBF, 0xBF } }  //  U+10FFFF
         };
 
-        RunUtf8EncodingTestCase(testCases, utf8::EncodeTrueUtf8IntoAndNullTerminate);
+        RunUtf8EncodingTestCase(testCases, utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>);
     }
 
     TEST_CASE("CodexTest_EncodeUtf8_NonCharacters", "[CodexTest]")
@@ -151,7 +153,7 @@ namespace CodexTest
             { { 0xFFFF }, { 0xEF, 0xBF, 0xBF } }  //  U+FFFF
         };
 
-        RunUtf8EncodingTestCase(testCases, utf8::EncodeTrueUtf8IntoAndNullTerminate);
+        RunUtf8EncodingTestCase(testCases, utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>);
     }
 
     TEST_CASE("CodexTest_EncodeUtf8_BoundaryChars", "[CodexTest]")
@@ -180,8 +182,8 @@ namespace CodexTest
             { { 0xDBFF, 0xDFFF }, { 0xF4, 0x8F, 0xBF, 0xBF } } //  U+10FFFF
         };
 
-        RunUtf8EncodingTestCase(testCases, utf8::EncodeTrueUtf8IntoAndNullTerminate);
-        RunUtf8EncodingTestCase(testCases2, utf8::EncodeTrueUtf8IntoAndNullTerminate);
+        RunUtf8EncodingTestCase(testCases, utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>);
+        RunUtf8EncodingTestCase(testCases2, utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>);
     }
 
     TEST_CASE("CodexTest_EncodeUtf8_SimpleCharacters", "[CodexTest]")
@@ -201,15 +203,16 @@ namespace CodexTest
             { { 0x20AC }, { 0xE2, 0x82, 0xAC } }   //  U+20AC - Euro symbol
         };
 
-        RunUtf8EncodingTestCase(testCases, utf8::EncodeTrueUtf8IntoAndNullTerminate);
+        RunUtf8EncodingTestCase(testCases, utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>);
     }
 
     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
+        constexpr size_t cbEncodedBuffer = charCount * 3 + 1; // +1 since the buffer will be null terminated
+        utf8char_t encodedBuffer[cbEncodedBuffer];
         const char16* sourceBuffer = L"abc";
-        size_t numEncodedBytes = utf8::EncodeTrueUtf8IntoAndNullTerminate(encodedBuffer, sourceBuffer, charCount);
+        size_t numEncodedBytes = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>(encodedBuffer, cbEncodedBuffer, sourceBuffer, charCount);
         CHECK(numEncodedBytes == charCount);
         for (int i = 0; i < charCount; i++)
         {

+ 2 - 1
bin/rl/rl.vcxproj

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <Import Condition="'$(ChakraBuildPathImported)'!='true'" Project="$(SolutionDir)Chakra.Build.Paths.props" />
   <Import Project="$(BuildConfigPropsPath)Chakra.Build.ProjectConfiguration.props" />
@@ -9,6 +9,7 @@
   <PropertyGroup Label="Configuration">
     <TargetName>rl</TargetName>
     <ConfigurationType>Application</ConfigurationType>
+    <UseUniCrt>false</UseUniCrt>
   </PropertyGroup>
   <Import Project="$(BuildConfigPropsPath)Chakra.Build.Default.props" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />

+ 359 - 160
lib/Backend/BackwardPass.cpp

@@ -15,9 +15,13 @@ BackwardPass::BackwardPass(Func * func, GlobOpt * globOpt, Js::Phase tag)
     collectionPassSubPhase(CollectionPassSubPhase::None),
     isLoopPrepass(false)
 {
-    // Those are the only two phase dead store will be used currently
-    Assert(tag == Js::BackwardPhase || tag == Js::DeadStorePhase);
+    // Those are the only three phases BackwardPass will use currently
+    Assert(tag == Js::BackwardPhase || tag == Js::DeadStorePhase || tag == Js::CaptureByteCodeRegUsePhase);
 
+#if DBG
+    // The CaptureByteCodeRegUse phase is just a collection phase, no mutations should occur
+    this->isCollectionPass = tag == Js::CaptureByteCodeRegUsePhase;
+#endif
     this->implicitCallBailouts = 0;
     this->fieldOpts = 0;
 
@@ -49,15 +53,26 @@ BackwardPass::DoSetDead(IR::Opnd * opnd, bool isDead) const
 bool
 BackwardPass::DoByteCodeUpwardExposedUsed() const
 {
-    return
-        !this->func->GetJITFunctionBody()->IsAsmJsMode() &&
-        (
-            (this->tag == Js::DeadStorePhase && this->func->hasBailout) ||
-            (this->func->HasTry() && this->func->DoOptimizeTry() && this->tag == Js::BackwardPhase)
+    return (
+        (this->tag == Js::DeadStorePhase && this->func->hasBailout) ||
+        (this->tag == Js::BackwardPhase && this->func->HasTry() && this->func->DoOptimizeTry())
+#if DBG
+        || tag == Js::CaptureByteCodeRegUsePhase
+#endif
+    );
+}
+
+bool BackwardPass::DoCaptureByteCodeUpwardExposedUsed() const
+{
 #if DBG
-            || this->tag == Js::BackwardPhase
+    return (this->tag == Js::CaptureByteCodeRegUsePhase || this->tag == Js::DeadStorePhase) &&
+        this->DoByteCodeUpwardExposedUsed() &&
+        !func->IsJitInDebugMode() &&
+        !this->func->GetJITFunctionBody()->IsAsmJsMode() &&
+        this->func->DoGlobOpt();
+#else
+    return false;
 #endif
-        );
 }
 
 bool
@@ -297,10 +312,6 @@ BackwardPass::MarkScopeObjSymUseForStackArgOpt()
     {
         if (instr->DoStackArgsOpt() && instr->m_func->GetScopeObjSym() != nullptr && this->DoByteCodeUpwardExposedUsed())
         {
-            if (this->currentBlock->byteCodeUpwardExposedUsed == nullptr)
-            {
-                this->currentBlock->byteCodeUpwardExposedUsed = JitAnew(this->tempAlloc, BVSparse<JitArenaAllocator>, this->tempAlloc);
-            }
             this->currentBlock->byteCodeUpwardExposedUsed->Set(instr->m_func->GetScopeObjSym()->m_id);
         }
     }
@@ -351,6 +362,11 @@ BackwardPass::Optimize()
         return;
     }
 
+    if (tag == Js::CaptureByteCodeRegUsePhase && (!PHASE_ENABLED(CaptureByteCodeRegUsePhase, this->func) || !DoCaptureByteCodeUpwardExposedUsed()))
+    {
+        return;
+    }
+
     if (tag == Js::DeadStorePhase)
     {
         if (!this->func->DoLoopFastPaths() || !this->func->DoFastPaths())
@@ -1687,69 +1703,6 @@ BackwardPass::OptBlock(BasicBlock * block)
             intOverflowDoesNotMatterInRangeBySymId->ClearAll();
         }
     }
-
-#if DBG
-    if (this->DoByteCodeUpwardExposedUsed())
-    {
-        if (this->tag == Js::BackwardPhase)
-        {
-            // Keep track of all the bytecode upward exposed after Backward's pass
-            BVSparse<JitArenaAllocator>* byteCodeUpwardExposedUsed = nullptr;
-            const auto EnsureBV = [&] { if (!byteCodeUpwardExposedUsed) byteCodeUpwardExposedUsed = JitAnew(this->globOpt->alloc, BVSparse<JitArenaAllocator>, this->globOpt->alloc); };
-            Assert(block->byteCodeUpwardExposedUsed != nullptr);
-            FOREACH_BITSET_IN_SPARSEBV(symID, block->byteCodeUpwardExposedUsed)
-            {
-                Sym* sym = this->func->m_symTable->Find(symID);
-                if (sym != nullptr && sym->IsStackSym())
-                {
-                    StackSym* stackSym = sym->AsStackSym();
-                    if (stackSym->HasByteCodeRegSlot())
-                    {
-                        Js::RegSlot bytecode = stackSym->GetByteCodeRegSlot();
-                        EnsureBV();
-                        byteCodeUpwardExposedUsed->Set(bytecode);
-                    }
-                }
-            }
-            NEXT_BITSET_IN_SPARSEBV;
-            if (byteCodeUpwardExposedUsed)
-            {
-                // Exclude unwanted syms
-                byteCodeUpwardExposedUsed->Minus(block->excludeByteCodeUpwardExposedTracking);
-                Assert(block->trackingByteCodeUpwardExposedUsed == nullptr);
-                block->trackingByteCodeUpwardExposedUsed = byteCodeUpwardExposedUsed;
-            }
-        }
-        else
-        {
-            // The calculated bytecode upward exposed should be the same between Backward and DeadStore passes
-            Assert(this->tag == Js::DeadStorePhase);
-            if (block->trackingByteCodeUpwardExposedUsed)
-            {
-                // We don't need to track bytecodeUpwardExposeUses if we don't have bailout
-                // We've collected the Backward bytecodeUpwardExposeUses for nothing, oh well.
-                if (this->func->hasBailout)
-                {
-                    Assert(block->byteCodeUpwardExposedUsed);
-                    BVSparse<JitArenaAllocator>* byteCodeUpwardExposedUsed = JitAnew(this->globOpt->tempAlloc, BVSparse<JitArenaAllocator>, this->globOpt->tempAlloc);
-                    FOREACH_BITSET_IN_SPARSEBV(symID, block->byteCodeUpwardExposedUsed)
-                    {
-                        Sym* sym = this->func->m_symTable->Find(symID);
-                        Js::RegSlot bytecode = sym->AsStackSym()->GetByteCodeRegSlot();
-                        byteCodeUpwardExposedUsed->Set(bytecode);
-                    }
-                    NEXT_BITSET_IN_SPARSEBV;
-                    // Exclude unwanted syms
-                    byteCodeUpwardExposedUsed->Minus(block->excludeByteCodeUpwardExposedTracking);
-                    Assert(block->trackingByteCodeUpwardExposedUsed->Equal(byteCodeUpwardExposedUsed));
-                    JitAdelete(this->globOpt->tempAlloc, byteCodeUpwardExposedUsed);
-                }
-                JitAdelete(this->globOpt->alloc, block->trackingByteCodeUpwardExposedUsed);
-                block->trackingByteCodeUpwardExposedUsed = nullptr;
-            }
-        }
-    }
-#endif
 }
 
 void
@@ -2063,6 +2016,13 @@ BackwardPass::ProcessByteCodeUsesInstr(IR::Instr * instr)
             this->currentBlock->upwardExposedUses->Or(byteCodeUpwardExposedUsed);
         }
     }
+#if DBG
+    else if (tag == Js::CaptureByteCodeRegUsePhase)
+    {
+        ProcessByteCodeUsesDst(byteCodeUsesInstr);
+        ProcessByteCodeUsesSrcs(byteCodeUsesInstr);
+    }
+#endif
     else
     {
         Assert(tag == Js::DeadStorePhase);
@@ -2411,6 +2371,7 @@ BackwardPass::DeadStoreImplicitCallBailOut(IR::Instr * instr, bool hasLiveFields
     }
 }
 
+
 bool
 BackwardPass::NeedBailOutOnImplicitCallsForTypedArrayStore(IR::Instr* instr)
 {
@@ -2524,7 +2485,52 @@ BackwardPass::ProcessBailOutInfo(IR::Instr * instr, BailOutInfo * bailOutInfo)
         }
     */
     BasicBlock * block = this->currentBlock;
-    BVSparse<JitArenaAllocator> * byteCodeUpwardExposedUsed = block->byteCodeUpwardExposedUsed;
+    BVSparse<JitArenaAllocator> * byteCodeUpwardExposedUsed = nullptr;
+
+#if DBG
+    if (DoCaptureByteCodeUpwardExposedUsed() &&
+        !IsPrePass() &&
+        bailOutInfo->bailOutFunc->HasByteCodeOffset() &&
+        bailOutInfo->bailOutFunc->byteCodeRegisterUses)
+    {
+        uint32 offset = bailOutInfo->bailOutOffset;
+        Assert(offset != Js::Constants::NoByteCodeOffset);
+        BVSparse<JitArenaAllocator>* trackingByteCodeUpwardExposedUsed = bailOutInfo->bailOutFunc->GetByteCodeOffsetUses(offset);
+        if (trackingByteCodeUpwardExposedUsed)
+        {
+            BVSparse<JitArenaAllocator>* tmpBv = nullptr;
+            if (instr->IsBranchInstr())
+            {
+                IR::LabelInstr* target = instr->AsBranchInstr()->GetTarget();
+                uint32 targetOffset = target->GetByteCodeOffset();
+                if (targetOffset == instr->GetByteCodeOffset())
+                {
+                    // This can happen if the target is an break or airlock block
+                    Assert(
+                        target->GetBasicBlock()->isAirLockBlock ||
+                        target->GetBasicBlock()->isAirLockCompensationBlock ||
+                        target->GetBasicBlock()->isBreakBlock ||
+                        target->GetBasicBlock()->isBreakCompensationBlockAtSink ||
+                        target->GetBasicBlock()->isBreakCompensationBlockAtSource
+                    );
+                    targetOffset = target->GetNextByteCodeInstr()->GetByteCodeOffset();
+                }
+                BVSparse<JitArenaAllocator>* branchTargetUpdwardExposed = target->m_func->GetByteCodeOffsetUses(targetOffset);
+                if (branchTargetUpdwardExposed)
+                {
+                    // The bailout should restore both the bailout destination and the branch target since we don't know where we'll end up
+                    trackingByteCodeUpwardExposedUsed = tmpBv = trackingByteCodeUpwardExposedUsed->OrNew(branchTargetUpdwardExposed);
+                }
+            }
+            Assert(trackingByteCodeUpwardExposedUsed);
+            VerifyByteCodeUpwardExposed(block, bailOutInfo->bailOutFunc, trackingByteCodeUpwardExposedUsed, instr, offset);
+            if (tmpBv)
+            {
+                JitAdelete(tmpBv->GetAllocator(), tmpBv);
+            }
+        }
+    }
+#endif
 
     Assert(bailOutInfo->bailOutInstr == instr);
 
@@ -2537,13 +2543,13 @@ BackwardPass::ProcessBailOutInfo(IR::Instr * instr, BailOutInfo * bailOutInfo)
     if (!this->IsPrePass())
     {
         // Create the BV of symbols that need to be restored in the BailOutRecord
-        byteCodeUpwardExposedUsed = byteCodeUpwardExposedUsed->CopyNew(this->func->m_alloc);
+        byteCodeUpwardExposedUsed = block->byteCodeUpwardExposedUsed->CopyNew(this->func->m_alloc);
         bailOutInfo->byteCodeUpwardExposedUsed = byteCodeUpwardExposedUsed;
     }
     else
     {
         // Create a temporary byteCodeUpwardExposedUsed
-        byteCodeUpwardExposedUsed = byteCodeUpwardExposedUsed->CopyNew(this->tempAlloc);
+        byteCodeUpwardExposedUsed = block->byteCodeUpwardExposedUsed->CopyNew(this->tempAlloc);
     }
 
     // All the register-based argument syms need to be tracked. They are either:
@@ -2705,55 +2711,92 @@ BackwardPass::ProcessBlock(BasicBlock * block)
 {
     this->currentBlock = block;
     this->MergeSuccBlocksInfo(block);
-#if DBG_DUMP
-    if (this->IsTraceEnabled())
-    {
-        Output::Print(_u("******************************* Before Process Block *******************************n"));
-        DumpBlockData(block);
-    }
-#endif
-    FOREACH_INSTR_BACKWARD_IN_BLOCK_EDITING(instr, instrPrev, block)
+#if DBG
+    struct ByteCodeRegisterUsesTracker
     {
-#if DBG_DUMP
-        if (!IsCollectionPass() && IsTraceEnabled() && Js::Configuration::Global.flags.Verbose)
+        Js::OpCode opcode = Js::OpCode::Nop;
+        uint32 offset = Js::Constants::NoByteCodeOffset;
+        Func* func = nullptr;
+        bool active = false;
+
+        void Capture(BackwardPass* backwardPass, BasicBlock* block)
         {
-            Output::Print(_u(">>>>>>>>>>>>>>>>>>>>>> %s: Instr Start\n"), tag == Js::BackwardPhase? _u("BACKWARD") : _u("DEADSTORE"));
-            instr->Dump();
-            if (block->upwardExposedUses)
+            if (offset != Js::Constants::NoByteCodeOffset)
             {
-                Output::SkipToColumn(10);
-                Output::Print(_u("   Exposed Use: "));
-                block->upwardExposedUses->Dump();
+                backwardPass->CaptureByteCodeUpwardExposed(block, func, opcode, offset);
+                offset = Js::Constants::NoByteCodeOffset;
             }
-            if (block->upwardExposedFields)
+        }
+        static bool IsValidByteCodeOffset(IR::Instr* instr)
+        {
+            return instr->m_func->HasByteCodeOffset() &&
+                instr->GetByteCodeOffset() != Js::Constants::NoByteCodeOffset;
+        };
+        static bool IsInstrOffsetBoundary(IR::Instr* instr)
+        {
+            if (IsValidByteCodeOffset(instr))
             {
-                Output::SkipToColumn(10);
-                Output::Print(_u("Exposed Fields: "));
-                block->upwardExposedFields->Dump();
+                if (instr->m_opcode == Js::OpCode::Leave)
+                {
+                    // Leave is a special case, capture now and ignore other instrs at that offset
+                    return true;
+                }
+                else
+                {
+                    uint32 bytecodeOffset = instr->GetByteCodeOffset();
+                    IR::Instr* prev = instr->m_prev;
+                    while (prev && !IsValidByteCodeOffset(prev))
+                    {
+                        prev = prev->m_prev;
+                    }
+                    return !prev || prev->GetByteCodeOffset() != bytecodeOffset;
+                }
             }
-            if (block->byteCodeUpwardExposedUsed)
+            return false;
+        }
+        void CheckInstrIsOffsetBoundary(IR::Instr* instr)
+        {
+            if (active && IsInstrOffsetBoundary(instr))
             {
-                Output::SkipToColumn(10);
-                Output::Print(_u(" Byte Code Use: "));
-                block->byteCodeUpwardExposedUsed->Dump();
+                // This is the last occurence of that bytecode offset
+                // We need to process this instr before we capture and there are too many `continue`
+                // to safely do this check at the end of the loop
+                // Save the info and Capture the ByteCodeUpwardExposedUsed on next loop iteration
+                opcode = instr->m_opcode;
+                offset = instr->GetByteCodeOffset();
+                func = instr->m_func;
             }
-            Output::Print(_u("--------------------\n"));
         }
+    };
+    ByteCodeRegisterUsesTracker tracker;
+    tracker.active = tag == Js::CaptureByteCodeRegUsePhase && DoCaptureByteCodeUpwardExposedUsed();
+#endif
+#if DBG_DUMP
+    TraceBlockUses(block, true);
 #endif
 
+    FOREACH_INSTR_BACKWARD_IN_BLOCK_EDITING(instr, instrPrev, block)
+    {
+#if DBG_DUMP
+        TraceInstrUses(block, instr, true);
+#endif
 #if DBG
-        // Track Symbol with weird lifetime to exclude them from the ByteCodeUpwardExpose verification
-        if (DoByteCodeUpwardExposedUsed() && instr->m_func->GetScopeObjSym())
+        if (tracker.active)
         {
-            StackSym* sym = instr->m_func->GetScopeObjSym();
-            if (sym->HasByteCodeRegSlot())
+            // Track Symbol with weird lifetime to exclude them from the ByteCodeUpwardExpose verification
+            if (instr->m_func->GetScopeObjSym())
             {
-                block->excludeByteCodeUpwardExposedTracking->Set(sym->GetByteCodeRegSlot());
+                StackSym* sym = instr->m_func->GetScopeObjSym();
+                if (sym->HasByteCodeRegSlot())
+                {
+                    block->excludeByteCodeUpwardExposedTracking->Set(sym->GetByteCodeRegSlot());
+                }
             }
+            tracker.Capture(this, block);
+            tracker.CheckInstrIsOffsetBoundary(instr);
         }
 #endif
 
-
         AssertOrFailFastMsg(!instr->IsLowered(), "Lowered instruction detected in pre-lower context!");
 
         this->currentInstr = instr;
@@ -3241,6 +3284,9 @@ BackwardPass::ProcessBlock(BasicBlock * block)
             }
 #endif
             // Continue normal CollectionPass behavior
+#if DBG_DUMP
+            TraceInstrUses(block, instr, false);
+#endif
             continue;
         }
 
@@ -3592,34 +3638,19 @@ BackwardPass::ProcessBlock(BasicBlock * block)
         instrPrev = ProcessPendingPreOpBailOutInfo(instr);
 
 #if DBG_DUMP
-        if (!IsCollectionPass() && IsTraceEnabled() && Js::Configuration::Global.flags.Verbose)
-        {
-            Output::Print(_u("-------------------\n"));
-            instr->Dump();
-            if (block->upwardExposedUses)
-            {
-                Output::SkipToColumn(10);
-                Output::Print(_u("   Exposed Use: "));
-                block->upwardExposedUses->Dump();
-            }
-            if (block->upwardExposedFields)
-            {
-                Output::SkipToColumn(10);
-                Output::Print(_u("Exposed Fields: "));
-                block->upwardExposedFields->Dump();
-            }
-            if (block->byteCodeUpwardExposedUsed)
-            {
-                Output::SkipToColumn(10);
-                Output::Print(_u(" Byte Code Use: "));
-                block->byteCodeUpwardExposedUsed->Dump();
-            }
-            Output::Print(_u("<<<<<<<<<<<<<<<<<<<<<< %s: Instr End\n"), tag == Js::BackwardPhase? _u("BACKWARD") : _u("DEADSTORE"));
-        }
+        TraceInstrUses(block, instr, false);
 #endif
     }
     NEXT_INSTR_BACKWARD_IN_BLOCK_EDITING;
 
+#if DBG
+    tracker.Capture(this, block);
+    if (tag == Js::CaptureByteCodeRegUsePhase)
+    {
+        return;
+    }
+#endif
+
 #ifndef _M_ARM
     if (
            this->tag == Js::DeadStorePhase
@@ -3732,11 +3763,7 @@ BackwardPass::ProcessBlock(BasicBlock * block)
     Assert(!considerSymAsRealUseInNoImplicitCallUses);
 
 #if DBG_DUMP
-    if (this->IsTraceEnabled())
-    {
-        Output::Print(_u("******************************* After Process Block *******************************n"));
-        DumpBlockData(block);
-    }
+    TraceBlockUses(block, false);
 #endif
 }
 
@@ -4011,42 +4038,114 @@ BackwardPass::IsFormalParamSym(Func * func, Sym * sym) const
 }
 
 #if DBG_DUMP
+struct BvToDump
+{
+    const BVSparse<JitArenaAllocator>* bv;
+    const char16* tag;
+    size_t tagLen;
+    BvToDump(const BVSparse<JitArenaAllocator>* bv, const char16* tag) :
+        bv(bv),
+        tag(tag),
+        tagLen(bv ? wcslen(tag) : 0)
+    {}
+};
+
 void
-BackwardPass::DumpBlockData(BasicBlock * block)
+BackwardPass::DumpBlockData(BasicBlock * block, IR::Instr* instr)
 {
-    block->DumpHeader();
-    if (block->upwardExposedUses) // may be null for dead blocks
+    const int skip = 8;
+    BVSparse<JitArenaAllocator>* byteCodeRegisterUpwardExposed = nullptr;
+    if (instr)
+    {
+        // Instr specific bv to dump
+        byteCodeRegisterUpwardExposed = GetByteCodeRegisterUpwardExposed(block, instr->m_func, this->tempAlloc);
+    }
+    BvToDump bvToDumps[] = {
+        { block->upwardExposedUses, _u("Exposed Use") },
+        { block->typesNeedingKnownObjectLayout, _u("Needs Known Object Layout") },
+        { block->upwardExposedFields, _u("Exposed Fields") },
+        { block->byteCodeUpwardExposedUsed, _u("Byte Code Use") },
+        { byteCodeRegisterUpwardExposed, _u("Byte Code Reg Use") },
+        { !this->IsCollectionPass() && !block->isDead && this->DoDeadStoreSlots() ? block->slotDeadStoreCandidates : nullptr, _u("Slot deadStore candidates") },
+    };
+
+    size_t maxTagLen = 0;
+    for (int i = 0; i < sizeof(bvToDumps) / sizeof(BvToDump); ++i)
     {
-        Output::Print(_u("             Exposed Uses: "));
-        block->upwardExposedUses->Dump();
+        if (bvToDumps[i].tagLen > maxTagLen)
+        {
+            maxTagLen = bvToDumps[i].tagLen;
+        }
     }
 
-    if (block->typesNeedingKnownObjectLayout)
+    for (int i = 0; i < sizeof(bvToDumps) / sizeof(BvToDump); ++i)
+    {
+        if (bvToDumps[i].bv)
+        {
+            Output::Print((int)(maxTagLen + skip - bvToDumps[i].tagLen), _u("%s: "), bvToDumps[i].tag);
+            bvToDumps[i].bv->Dump();
+        }
+    }
+    if (byteCodeRegisterUpwardExposed)
     {
-        Output::Print(_u("            Needs Known Object Layout: "));
-        block->typesNeedingKnownObjectLayout->Dump();
+        JitAdelete(this->tempAlloc, byteCodeRegisterUpwardExposed);
     }
+}
 
-    if (block->byteCodeUpwardExposedUsed)
+void
+BackwardPass::TraceInstrUses(BasicBlock * block, IR::Instr* instr, bool isStart)
+{
+    if ((!IsCollectionPass() || tag == Js::CaptureByteCodeRegUsePhase) && IsTraceEnabled() && Js::Configuration::Global.flags.Verbose)
     {
-        Output::Print(_u("   Byte Code Exposed Uses: "));
-        block->byteCodeUpwardExposedUsed->Dump();
+        const char16* tagName = 
+            tag == Js::CaptureByteCodeRegUsePhase ? _u("CAPTURE BYTECODE REGISTER") : (
+            tag == Js::BackwardPhase ? _u("BACKWARD") : (
+            tag == Js::DeadStorePhase ? _u("DEADSTORE") :
+            _u("UNKNOWN")
+        ));
+        if (isStart)
+        {
+            Output::Print(_u(">>>>>>>>>>>>>>>>>>>>>> %s: Instr Start\n"), tagName);
+        }
+        else
+        {
+            Output::Print(_u("---------------------------------------\n"));
+        }
+        instr->Dump();
+        DumpBlockData(block, instr);
+        if (isStart)
+        {
+            Output::Print(_u("----------------------------------------\n"));
+        }
+        else
+        {
+            Output::Print(_u("<<<<<<<<<<<<<<<<<<<<<< %s: Instr End\n"), tagName);
+        }
     }
+}
 
-    if (!this->IsCollectionPass())
+void
+BackwardPass::TraceBlockUses(BasicBlock * block, bool isStart)
+{
+    if (this->IsTraceEnabled())
     {
-        if (!block->isDead)
+        if (isStart)
+        {
+            Output::Print(_u("******************************* Before Process Block *******************************\n"));
+        }
+        else
+        {
+            Output::Print(_u("******************************* After Process Block *******************************n"));
+        }
+        block->DumpHeader();
+        DumpBlockData(block);
+        if (!this->IsCollectionPass() && !block->isDead)
         {
-            if (this->DoDeadStoreSlots())
-            {
-                Output::Print(_u("Slot deadStore candidates: "));
-                block->slotDeadStoreCandidates->Dump();
-            }
             DumpMarkTemp();
         }
     }
-    Output::Flush();
 }
+
 #endif
 
 bool
@@ -7369,12 +7468,14 @@ BackwardPass::ProcessDef(IR::Opnd * opnd)
     Assert(!instr->IsByteCodeUsesInstr());
     if (sym->IsPropertySym())
     {
+        PropertySym *propertySym = sym->AsPropertySym();
+        ProcessStackSymUse(propertySym->m_stackSym, isJITOptimizedReg);
+
         if(IsCollectionPass())
         {
             return false;
         }
 
-        PropertySym *propertySym = sym->AsPropertySym();
         if (this->DoDeadStoreSlots())
         {
             if (propertySym->m_fieldKind == PropertyKindLocalSlots || propertySym->m_fieldKind == PropertyKindSlots)
@@ -7388,7 +7489,6 @@ BackwardPass::ProcessDef(IR::Opnd * opnd)
 
         this->DoSetDead(opnd, !block->upwardExposedFields->TestAndClear(propertySym->m_id));
 
-        ProcessStackSymUse(propertySym->m_stackSym, isJITOptimizedReg);
         if (tag == Js::BackwardPhase)
         {
             if (opnd->AsSymOpnd()->IsPropertySymOpnd())
@@ -8435,6 +8535,105 @@ BackwardPass::CheckWriteThroughSymInRegion(Region* region, StackSym* sym)
     return selfOrFirstTryAncestor->writeThroughSymbolsSet && selfOrFirstTryAncestor->writeThroughSymbolsSet->Test(sym->m_id);
 }
 
+#if DBG
+void
+BackwardPass::VerifyByteCodeUpwardExposed(BasicBlock* block, Func* func, BVSparse<JitArenaAllocator>* trackingByteCodeUpwardExposedUsed, IR::Instr* instr, uint32 bytecodeOffset)
+{
+    Assert(instr);
+    Assert(bytecodeOffset != Js::Constants::NoByteCodeOffset);
+    Assert(this->tag == Js::DeadStorePhase);
+
+    // The calculated bytecode upward exposed should be the same between Backward and DeadStore passes
+    if (trackingByteCodeUpwardExposedUsed && !trackingByteCodeUpwardExposedUsed->IsEmpty())
+    {
+        // We don't need to track bytecodeUpwardExposeUses if we don't have bailout
+        // We've collected the Backward bytecodeUpwardExposeUses for nothing, oh well.
+        if (this->func->hasBailout)
+        {
+            BVSparse<JitArenaAllocator>* byteCodeUpwardExposedUsed = GetByteCodeRegisterUpwardExposed(block, func, this->tempAlloc);
+            BVSparse<JitArenaAllocator>* notInDeadStore = trackingByteCodeUpwardExposedUsed->MinusNew(byteCodeUpwardExposedUsed, this->tempAlloc);
+
+            if (!notInDeadStore->IsEmpty())
+            {
+                Output::Print(_u("\n\nByteCode Updward Exposed mismatch after DeadStore\n"));
+                Output::Print(_u("Mismatch Instr:\n"));
+                instr->Dump();
+                Output::Print(_u("  ByteCode Register list present before Backward pass missing in DeadStore pass:\n"));
+                FOREACH_BITSET_IN_SPARSEBV(bytecodeReg, notInDeadStore)
+                {
+                    Output::Print(_u("    R%u\n"), bytecodeReg);
+                }
+                NEXT_BITSET_IN_SPARSEBV;
+                AssertMsg(false, "ByteCode Updward Exposed Used Mismatch");
+            }
+            JitAdelete(this->tempAlloc, notInDeadStore);
+            JitAdelete(this->tempAlloc, byteCodeUpwardExposedUsed);
+        }
+    }
+}
+
+void
+BackwardPass::CaptureByteCodeUpwardExposed(BasicBlock* block, Func* func, Js::OpCode opcode, uint32 offset)
+{
+    Assert(this->DoCaptureByteCodeUpwardExposedUsed());
+    // Keep track of all the bytecode upward exposed after Backward's pass
+    BVSparse<JitArenaAllocator>* byteCodeUpwardExposedUsed = GetByteCodeRegisterUpwardExposed(block, func, this->globOpt->alloc);
+    byteCodeUpwardExposedUsed->Minus(block->excludeByteCodeUpwardExposedTracking);
+    if (func->GetJITFunctionBody()->GetEnvReg() != Js::Constants::NoByteCodeOffset)
+    {
+        // No need to restore the environment so don't track it
+        byteCodeUpwardExposedUsed->Clear(func->GetJITFunctionBody()->GetEnvReg());
+    }
+    if (!func->byteCodeRegisterUses)
+    {
+        func->byteCodeRegisterUses = JitAnew(this->globOpt->alloc, Func::ByteCodeRegisterUses, this->globOpt->alloc);
+    }
+
+    Func::InstrByteCodeRegisterUses instrUses;
+    if (func->byteCodeRegisterUses->TryGetValueAndRemove(offset, &instrUses))
+    {
+        if (instrUses.capturingOpCode == Js::OpCode::Leave)
+        {
+            // Do not overwrite in the case of Leave
+            JitAdelete(this->globOpt->alloc, byteCodeUpwardExposedUsed);
+            func->byteCodeRegisterUses->Add(offset, instrUses);
+            return;
+        }
+        byteCodeUpwardExposedUsed->Or(instrUses.bv);
+        JitAdelete(this->globOpt->alloc, instrUses.bv);
+    }
+
+    instrUses.capturingOpCode = opcode;
+    instrUses.bv = byteCodeUpwardExposedUsed;
+    func->byteCodeRegisterUses->Add(offset, instrUses);
+}
+
+BVSparse<JitArenaAllocator>*
+BackwardPass::GetByteCodeRegisterUpwardExposed(BasicBlock* block, Func* func, JitArenaAllocator* alloc)
+{
+    BVSparse<JitArenaAllocator>* byteCodeRegisterUpwardExposed = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
+    // Convert the sym to the corresponding bytecode register
+    FOREACH_BITSET_IN_SPARSEBV(symID, block->byteCodeUpwardExposedUsed)
+    {
+        Sym* sym = func->m_symTable->Find(symID);
+        if (sym && sym->IsStackSym())
+        {
+            StackSym* stackSym = sym->AsStackSym();
+            // Make sure we only look at bytecode from the func we're interested in
+            if (stackSym->GetByteCodeFunc() == func && stackSym->HasByteCodeRegSlot())
+            {
+                Js::RegSlot bytecode = stackSym->GetByteCodeRegSlot();
+                byteCodeRegisterUpwardExposed->Set(bytecode);
+            }
+        }
+    }
+    NEXT_BITSET_IN_SPARSEBV;
+
+    return byteCodeRegisterUpwardExposed;
+}
+
+#endif
+
 bool
 BackwardPass::DoDeadStoreLdStForMemop(IR::Instr *instr)
 {

+ 10 - 3
lib/Backend/BackwardPass.h

@@ -67,7 +67,9 @@ private:
     void CollectCloneStrCandidate(IR::Opnd *opnd);
     void InvalidateCloneStrCandidate(IR::Opnd *opnd);
 #if DBG_DUMP
-    void DumpBlockData(BasicBlock * block);
+    void DumpBlockData(BasicBlock * block, IR::Instr* instr = nullptr);
+    void TraceInstrUses(BasicBlock * block, IR::Instr* instr, bool isStart);
+    void TraceBlockUses(BasicBlock * block, bool isStart);
     void DumpMarkTemp();
 #endif
 
@@ -108,6 +110,7 @@ private:
     bool ProcessBailOnNoProfile(IR::Instr *instr, BasicBlock *block);
 
     bool DoByteCodeUpwardExposedUsed() const;
+    bool DoCaptureByteCodeUpwardExposedUsed() const;
     void DoSetDead(IR::Opnd * opnd, bool isDead) const;
     bool DoMarkTempObjects() const;
     bool DoMarkTempNumbers() const;
@@ -150,7 +153,11 @@ private:
     bool FoldCmBool(IR::Instr *instr);
     void SetWriteThroughSymbolsSetForRegion(BasicBlock * catchBlock, Region * tryRegion);
     bool CheckWriteThroughSymInRegion(Region * region, StackSym * sym);
-
+#if DBG
+    void VerifyByteCodeUpwardExposed(BasicBlock* block, Func* func, BVSparse<JitArenaAllocator>* trackingByteCodeUpwardExposedUsed, IR::Instr* instr, uint32 bytecodeOffset);
+    void CaptureByteCodeUpwardExposed(BasicBlock* block, Func* func, Js::OpCode opcode, uint32 offset);
+    BVSparse<JitArenaAllocator>* GetByteCodeRegisterUpwardExposed(BasicBlock* block, Func* func, JitArenaAllocator* alloc);
+#endif
 private:
     // Javascript number values (64-bit floats) have 53 bits excluding the sign bit to precisely represent integers. If we have
     // compounded uses in add/sub, such as:
@@ -233,7 +240,7 @@ private:
     };
 
     typedef JsUtil::BaseDictionary<SymID, FloatSymEquivalenceClass *, JitArenaAllocator> FloatSymEquivalenceMap;
-    FloatSymEquivalenceMap *floatSymEquivalenceMap;
+    FloatSymEquivalenceMap *floatSymEquivalenceMap = nullptr;
 
     // Use by numberTemp to keep track of the property sym  that is used to represent a property, since we don't trace aliasing
     typedef JsUtil::BaseDictionary<Js::PropertyId, SymID, JitArenaAllocator> NumberTempRepresentativePropertySymMap;

+ 35 - 8
lib/Backend/EmitBuffer.cpp

@@ -193,6 +193,40 @@ EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::NewAllocation(size_t b
     return allocation;
 }
 
+template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
+void
+EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::SetValidCallTarget(TEmitBufferAllocation* allocation, void* callTarget, bool isValid)
+{
+#if _M_ARM
+    callTarget = (void*)((uintptr_t)callTarget | 0x1); // add the thumb bit back, so we CFG-unregister the actual call target
+#endif
+    if (!JITManager::GetJITManager()->IsJITServer())
+    {
+        this->threadContext->SetValidCallTargetForCFG(callTarget, isValid);
+    }
+#if ENABLE_OOP_NATIVE_CODEGEN
+    else if (CONFIG_FLAG(OOPCFGRegistration))
+    {
+        void* segment = allocation->allocation->IsLargeAllocation()
+            ? allocation->allocation->largeObjectAllocation.segment
+            : allocation->allocation->page->segment;
+        HANDLE fileHandle = nullptr;
+        PVOID baseAddress = nullptr;
+        bool found = false;
+        if (this->allocationHeap.IsPreReservedSegment(segment))
+        {
+            found = ((SegmentBase<TPreReservedAlloc>*)segment)->GetAllocator()->GetVirtualAllocator()->GetFileInfo(callTarget, &fileHandle, &baseAddress);
+        }
+        else
+        {
+            found = ((SegmentBase<TAlloc>*)segment)->GetAllocator()->GetVirtualAllocator()->GetFileInfo(callTarget, &fileHandle, &baseAddress);
+        }
+        AssertOrFailFast(found);
+        this->threadContext->SetValidCallTargetFile(callTarget, fileHandle, baseAddress, isValid);
+    }
+#endif
+}
+
 template <typename TAlloc, typename TPreReservedAlloc, class SyncObject>
 bool
 EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocation(void* address)
@@ -241,14 +275,7 @@ EmitBufferManager<TAlloc, TPreReservedAlloc, SyncObject>::FreeAllocation(void* a
             else
 #endif
             {
-                if (!JITManager::GetJITManager()->IsJITServer() || CONFIG_FLAG(OOPCFGRegistration))
-                {
-                    void* callTarget = address;
-#if _M_ARM
-                    callTarget = (void*)((uintptr_t)callTarget | 0x1); // add the thumb bit back, so we CFG-unregister the actual call target
-#endif
-                    threadContext->SetValidCallTargetForCFG(callTarget, false);
-                }
+                SetValidCallTarget(allocation, address, false);
             }
             VerboseHeapTrace(_u("Freeing 0x%p, allocation: 0x%p\n"), address, allocation->allocation->address);
 

+ 1 - 0
lib/Backend/EmitBuffer.h

@@ -47,6 +47,7 @@ public:
     bool CommitBufferForInterpreter(TEmitBufferAllocation* allocation, _In_reads_bytes_(bufferSize) BYTE* pBuffer, _In_ size_t bufferSize);
     void CompletePreviousAllocation(TEmitBufferAllocation* allocation);
     bool FreeAllocation(void* address);
+    void SetValidCallTarget(TEmitBufferAllocation* allocation, void* callTarget, bool isValid);
     //Ends here
 
     bool IsInHeap(void* address);

+ 3 - 0
lib/Backend/FlowGraph.cpp

@@ -506,13 +506,16 @@ FlowGraph::Build(void)
                     {
                         Assert(exitLabel);
                         IR::Instr * bailOnEarlyExit = IR::BailOutInstr::New(Js::OpCode::BailOnEarlyExit, IR::BailOutOnEarlyExit, instr, instr->m_func);
+                        bailOnEarlyExit->SetByteCodeOffset(instr);
                         instr->InsertBefore(bailOnEarlyExit);
+
                         IR::LabelInstr *exceptFinallyLabel = this->finallyLabelStack->Top();
                         IR::LabelInstr *nonExceptFinallyLabel = exceptFinallyLabel->m_next->m_next->AsLabelInstr();
 
                         // It is possible for the finally region to have a non terminating loop, in which case the end of finally is eliminated
                         // We can skip adding edge from finally to early exit in this case
                         IR::Instr * leaveToFinally = IR::BranchInstr::New(Js::OpCode::Leave, exceptFinallyLabel, this->func);
+                        leaveToFinally->SetByteCodeOffset(instr);
                         instr->InsertBefore(leaveToFinally);
                         instr->Remove();
                         this->AddEdge(currentLabel->GetBasicBlock(), exceptFinallyLabel->GetBasicBlock());

+ 11 - 1
lib/Backend/Func.cpp

@@ -1228,6 +1228,17 @@ Func::NumberInstrs()
     NEXT_INSTR_IN_FUNC;
 }
 
+#if DBG
+BVSparse<JitArenaAllocator>* Func::GetByteCodeOffsetUses(uint offset) const
+{
+    InstrByteCodeRegisterUses uses;
+    if (byteCodeRegisterUses->TryGetValue(offset, &uses))
+    {
+        return uses.bv;
+    }
+    return nullptr;
+}
+
 ///----------------------------------------------------------------------------
 ///
 /// Func::IsInPhase
@@ -1235,7 +1246,6 @@ Func::NumberInstrs()
 /// Determines whether the function is currently in the provided phase
 ///
 ///----------------------------------------------------------------------------
-#if DBG
 bool
 Func::IsInPhase(Js::Phase tag)
 {

+ 9 - 0
lib/Backend/Func.h

@@ -755,6 +755,15 @@ public:
     bool                isPostLayout:1;
     bool                isPostFinalLower:1;
 
+    struct InstrByteCodeRegisterUses
+    {
+        Js::OpCode capturingOpCode;
+        BVSparse<JitArenaAllocator>* bv;
+    };
+    typedef JsUtil::BaseDictionary<uint32, InstrByteCodeRegisterUses, JitArenaAllocator> ByteCodeRegisterUses;
+    ByteCodeRegisterUses* byteCodeRegisterUses = nullptr;
+    BVSparse<JitArenaAllocator>* GetByteCodeOffsetUses(uint offset) const;
+
     typedef JsUtil::Stack<Js::Phase> CurrentPhasesStack;
     CurrentPhasesStack  currentPhases;
 

+ 25 - 4
lib/Backend/GlobOpt.cpp

@@ -198,6 +198,9 @@ GlobOpt::Optimize()
         // isn't available for some of the symbols created during the backward pass, or the forward pass.
         // Keep track of the last symbol for which we're guaranteed to have data.
         this->maxInitialSymID = this->func->m_symTable->GetMaxSymID();
+#if DBG
+        this->BackwardPass(Js::CaptureByteCodeRegUsePhase);
+#endif
         this->BackwardPass(Js::BackwardPhase);
         this->ForwardPass();
         this->BackwardPass(Js::DeadStorePhase);
@@ -1013,6 +1016,7 @@ BOOL GlobOpt::PRE::PreloadPRECandidate(Loop *loop, GlobHashBucket* candidate)
 
     IR::Instr * ldInstrInLoop = this->globOpt->prePassInstrMap->Lookup(propertySym->m_id, nullptr);
     Assert(ldInstrInLoop);
+    Assert(ldInstrInLoop->GetDst() == nullptr);
 
     // Create instr to put in landing pad for compensation
     Assert(IsPREInstrCandidateLoad(ldInstrInLoop->m_opcode));
@@ -2379,8 +2383,11 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
         CurrentBlockData()->KillStateForGeneratorYield();
     }
 
-    // Change LdLen on objects other than arrays, strings, and 'arguments' to LdFld.
-    this->TryReplaceLdLen(instr);
+    if (!IsLoopPrePass())
+    {
+        // Change LdLen on objects other than arrays, strings, and 'arguments' to LdFld.
+        this->TryReplaceLdLen(instr);
+    }
 
     // Consider: Do we ever get post-op bailout here, and if so is the FillBailOutInfo call in the right place?
     if (instr->HasBailOutInfo() && !this->IsLoopPrePass())
@@ -3804,6 +3811,7 @@ GlobOpt::CopyProp(IR::Opnd *opnd, IR::Instr *instr, Value *val, IR::IndirOpnd *p
     StackSym *copySym = CurrentBlockData()->GetCopyPropSym(opndSym, val);
     if (copySym != nullptr)
     {
+        Assert(!opndSym->IsStackSym() || copySym->GetSymSize() == opndSym->AsStackSym()->GetSymSize());
         // Copy prop.
         return CopyPropReplaceOpnd(instr, opnd, copySym, parentIndirOpnd);
     }
@@ -10509,11 +10517,17 @@ GlobOpt::TypeSpecializeLdLen(
                 Assert(lengthValue);
                 src1Value = lengthValue;
                 ValueInfo *const lengthValueInfo = lengthValue->GetValueInfo();
-                Assert(lengthValueInfo->GetSymStore() != lengthSym);
                 IntConstantBounds lengthConstantBounds;
                 AssertVerify(lengthValueInfo->TryGetIntConstantBounds(&lengthConstantBounds));
                 Assert(lengthConstantBounds.LowerBound() >= 0);
 
+                if (lengthValueInfo->GetSymStore() == lengthSym)
+                {
+                    // When type specializing the dst below, we will end up inserting lengthSym.u32 as symstore for a var
+                    // Clear the symstore here, so that we dont end up with problems with copyprop later on
+                    lengthValueInfo->SetSymStore(nullptr);
+                }
+
                 // Int-specialize, and transfer the value to the dst
                 TypeSpecializeIntDst(
                     instr,
@@ -12708,7 +12722,7 @@ GlobOpt::DoTrackNewValueForKills(Value *const value)
             return;
         }
 
-        if(valueInfo->HasNoMissingValues() && !DoArrayMissingValueCheckHoist())
+        if(isJsArray && valueInfo->HasNoMissingValues() && !DoArrayMissingValueCheckHoist())
         {
             valueInfo->Type() = valueInfo->Type().SetHasNoMissingValues(false);
         }
@@ -14739,6 +14753,8 @@ GlobOpt::OptHoistUpdateValueType(
                 // Replace above will free srcOpnd, so reassign it
                 *srcOpndPtr = srcOpnd = reinterpret_cast<IR::Opnd *>(strOpnd);
 
+                // We add ConvPrim_Str in the landingpad, and since this instruction doesn't go through the checks in OptInstr, the bailout is never added
+                // As we expand hoisting of instructions to new opcode, we need a better framework to handle such cases
                 if (IsImplicitCallBailOutCurrentlyNeeded(convPrimStrInstr, opndValueInLandingPad, nullptr, landingPad, landingPad->globOptData.liveFields->IsEmpty(), true, true))
                 {
                     EnsureBailTarget(loop);
@@ -15378,6 +15394,11 @@ InvariantBlockBackwardIterator::MoveNext()
             continue;
         }
 
+        if (!this->UpdatePredBlockBV())
+        {
+            continue;
+        }
+
         if(block->isDeleted)
         {
             continue;

+ 8 - 8
lib/Backend/GlobOpt.h

@@ -35,7 +35,7 @@ class GlobOpt;
         Output::Print(__VA_ARGS__); \
         Output::Print(_u("\n")); \
         Output::Flush(); \
-    } 
+    }
 #define GOPT_TRACE_OPND(opnd, ...) \
     if (PHASE_TRACE(Js::GlobOptPhase, this->func)) \
     { \
@@ -359,7 +359,7 @@ public:
 
         return
             killsAllArrays ||
-            (valueType.IsArrayOrObjectWithArray() && 
+            (valueType.IsArrayOrObjectWithArray() &&
              (
               (killsArraysWithNoMissingValues && valueType.HasNoMissingValues()) ||
               (killsNativeArrays && !valueType.HasVarElements())
@@ -429,7 +429,7 @@ private:
     SparseArray<Value>       *  byteCodeConstantValueArray;
     // Global bitvectors
     BVSparse<JitArenaAllocator> * byteCodeConstantValueNumbersBv;
-   
+
     // Global bitvectors
     IntConstantToStackSymMap *  intConstantToStackSymMap;
     IntConstantToValueMap*      intConstantToValueMap;
@@ -534,7 +534,7 @@ private:
     void                    OptLoops(Loop *loop);
     void                    TailDupPass();
     bool                    TryTailDup(IR::BranchInstr *tailBranch);
-    
+
     void                    FieldPRE(Loop *loop);
     void                    SetLoopFieldInitialValue(Loop *loop, IR::Instr *instr, PropertySym *propertySym, PropertySym *originalPropertySym);
     PRECandidates *         FindBackEdgePRECandidates(BasicBlock *block, JitArenaAllocator *alloc);
@@ -868,7 +868,7 @@ private:
     void                    EndTrackingOfArgObjSymsForInlinee();
     void                    FillBailOutInfo(BasicBlock *block, BailOutInfo *bailOutInfo);
     void                    FillBailOutInfo(BasicBlock *block, _In_ IR::Instr * instr);
-    
+
     static void             MarkNonByteCodeUsed(IR::Instr * instr);
     static void             MarkNonByteCodeUsed(IR::Opnd * opnd);
 
@@ -918,7 +918,7 @@ private:
 
 #if DBG
     bool                    IsPropertySymId(SymID symId) const;
-    
+
     static void             AssertCanCopyPropOrCSEFieldLoad(IR::Instr * instr);
     void                    EmitIntRangeChecks(IR::Instr* instr);
     void                    EmitIntRangeChecks(IR::Instr* instr, IR::Opnd* opnd);
@@ -938,10 +938,10 @@ private:
     bool                    ProcessPropOpInTypeCheckSeq(IR::Instr* instr, IR::PropertySymOpnd *opnd, BasicBlock* block, bool updateExistingValue, bool* emitsTypeCheckOut = nullptr, bool* changesTypeValueOut = nullptr, bool *isObjTypeChecked = nullptr);
     StackSym *              EnsureAuxSlotPtrSym(IR::PropertySymOpnd *opnd);
     void                    KillAuxSlotPtrSyms(IR::PropertySymOpnd *opnd, BasicBlock *block, bool isObjTypeSpecialized);
-    void                    KillObjectHeaderInlinedTypeSyms(BasicBlock *block, bool isObjTypeSpecialized, SymID symId = SymID_Invalid);
-    bool                    HasLiveObjectHeaderInlinedTypeSym(BasicBlock *block, bool isObjTypeSpecialized, SymID symId = SymID_Invalid);
     template<class Fn>
     bool                    MapObjectHeaderInlinedTypeSymsUntil(BasicBlock *block, bool isObjTypeSpecialized, SymID opndId, Fn fn);
+    void                    KillObjectHeaderInlinedTypeSyms(BasicBlock *block, bool isObjTypeSpecialized, SymID symId = SymID_Invalid);
+    bool                    HasLiveObjectHeaderInlinedTypeSym(BasicBlock *block, bool isObjTypeSpecialized, SymID symId = SymID_Invalid);
     void                    ValueNumberObjectType(IR::Opnd *dstOpnd, IR::Instr *instr);
     void                    SetSingleTypeOnObjectTypeValue(Value* value, const JITTypeHolder type);
     void                    SetTypeSetOnObjectTypeValue(Value* value, Js::EquivalentTypeSet* typeSet);

+ 15 - 8
lib/Backend/IRBuilderAsmJs.cpp

@@ -732,10 +732,15 @@ IRBuilderAsmJs::BuildHeapBufferReload(uint32 offset, bool isFirstLoad)
             // ArrayBuffer
             // GrowMemory can change the ArrayBuffer, we have to reload it
             AddLoadField(AsmJsRegSlots::ArrayReg, AsmJsRegSlots::WasmMemoryReg, Js::WebAssemblyMemory::GetOffsetOfArrayBuffer(), TyVar, DoReload);
-            // ArrayBuffer.buffer
+
             // The buffer doesn't change when using Fast Virtual buffer even if we grow the memory
             ShouldReload shouldReloadBufferPointer = m_func->GetJITFunctionBody()->UsesWAsmJsFastVirtualBuffer() ? DontReload : DoReload;
-            AddLoadField(AsmJsRegSlots::BufferReg, AsmJsRegSlots::ArrayReg, Js::ArrayBuffer::GetBufferOffset(), TyVar, shouldReloadBufferPointer);
+
+            // ArrayBuffer.bufferContent
+            AddLoadField(AsmJsRegSlots::RefCountedBuffer, AsmJsRegSlots::ArrayReg, Js::ArrayBuffer::GetBufferContentsOffset(), TyVar, DoReload);
+
+            // RefCountedBuffer.buffer
+            AddLoadField(AsmJsRegSlots::BufferReg, AsmJsRegSlots::RefCountedBuffer, Js::RefCountedBuffer::GetBufferOffset(), TyVar, shouldReloadBufferPointer);
             // ArrayBuffer.length
             AddLoadField(AsmJsRegSlots::LengthReg, AsmJsRegSlots::ArrayReg, Js::ArrayBuffer::GetByteLengthOffset(), TyUint32, DoReload);
         }
@@ -758,8 +763,10 @@ IRBuilderAsmJs::BuildHeapBufferReload(uint32 offset, bool isFirstLoad)
         // ArrayBuffer
         // The ArrayBuffer can be changed on the environment, if it is detached, we'll throw
         AddLoadField(AsmJsRegSlots::ArrayReg, AsmJsRegSlots::ModuleMemReg, (int32)Js::AsmJsModuleMemory::MemoryTableBeginOffset, TyVar, DontReload);
-        // ArrayBuffer.buffer
-        AddLoadField(AsmJsRegSlots::BufferReg, AsmJsRegSlots::ArrayReg, Js::ArrayBuffer::GetBufferOffset(), TyVar, DontReload);
+        // ArrayBuffer.bufferContent
+        AddLoadField(AsmJsRegSlots::RefCountedBuffer, AsmJsRegSlots::ArrayReg, Js::ArrayBuffer::GetBufferContentsOffset(), TyVar, DontReload);
+        // RefCountedBuffer.buffer
+        AddLoadField(AsmJsRegSlots::BufferReg, AsmJsRegSlots::RefCountedBuffer, Js::RefCountedBuffer::GetBufferOffset(), TyVar, DontReload);
         // ArrayBuffer.length
         AddLoadField(AsmJsRegSlots::LengthReg, AsmJsRegSlots::ArrayReg, Js::ArrayBuffer::GetByteLengthOffset(), TyUint32, DontReload);
     }
@@ -1446,7 +1453,7 @@ void
 IRBuilderAsmJs::InitializeMemAccessTypeInfo(Js::ArrayBufferView::ViewType viewType, _Out_ MemAccessTypeInfo * typeInfo)
 {
     AssertOrFailFast(typeInfo);
-    
+
     switch (viewType)
     {
 #define ARRAYBUFFER_VIEW(name, align, RegType, MemType, irSuffix) \
@@ -1873,9 +1880,9 @@ IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSl
 #ifdef ENABLE_WASM
     // heap buffer can change for wasm
     if (m_asmFuncInfo->UsesHeapBuffer() && m_func->GetJITFunctionBody()->IsWasmFunction())
-        {
-            BuildHeapBufferReload(offset);
-        }
+    {
+        BuildHeapBufferReload(offset);
+    }
 #endif
 }
 

+ 2 - 1
lib/Backend/IRBuilderAsmJs.h

@@ -17,6 +17,7 @@ namespace AsmJsRegSlots
         BufferReg,
         LengthReg,
         SharedContents,
+        RefCountedBuffer,
         RegCount
     };
 };
@@ -314,4 +315,4 @@ private:
 #undef Uint8x16Type
 };
 
-#endif
+#endif

+ 10 - 2
lib/Backend/Inline.cpp

@@ -2431,7 +2431,7 @@ IR::Instr* Inline::InsertInlineeBuiltInStartEndTags(IR::Instr* callInstr, uint a
     IR::Instr* inlineBuiltInEndInstr = IR::Instr::New(Js::OpCode::InlineBuiltInEnd, callInstr->m_func);
     inlineBuiltInEndInstr->SetSrc1(IR::IntConstOpnd::New(actualCount, TyInt32, callInstr->m_func));
     inlineBuiltInEndInstr->SetSrc2(callInstr->GetSrc2());
-    inlineBuiltInEndInstr->SetByteCodeOffset(callInstr->GetNextRealInstrOrLabel());
+    inlineBuiltInEndInstr->SetByteCodeOffset(callInstr);
     callInstr->InsertAfter(inlineBuiltInEndInstr);
     return inlineBuiltInEndInstr;
 }
@@ -2659,7 +2659,15 @@ IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, con
     }
     // Fixed function/function object checks for target built-in
     callInstr->ReplaceSrc1(applyTargetLdInstr->GetDst());
-    EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, builtInInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
+    {
+        IR::ByteCodeUsesInstr * useCallTargetInstr =
+            EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, builtInInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
+        if (useCallTargetInstr)
+        {
+            // The applyTarget dst already has a use in the argout, this bytecode use is not valid
+            useCallTargetInstr->Remove();
+        }
+    }
 
     // Fixed function/function object checks for .apply
     callInstr->m_opcode = originalCallOpcode;

+ 9 - 3
lib/Backend/JITOutput.cpp

@@ -287,10 +287,16 @@ JITOutput::FinalizeNativeCode()
     if (!allocation->thunkAddress && CONFIG_FLAG(OOPCFGRegistration))
     {
         PVOID callTarget = (PVOID)m_outputData->codeAddress;
-#ifdef _M_ARM
-        callTarget = (PVOID)((uintptr_t)callTarget | 0x1);
+#if ENABLE_OOP_NATIVE_CODEGEN
+        if (JITManager::GetJITManager()->IsJITServer())
+        {
+            m_func->GetOOPCodeGenAllocators()->emitBufferManager.SetValidCallTarget(m_oopAlloc, callTarget, true);
+        }
+        else
 #endif
-        m_func->GetThreadContextInfo()->SetValidCallTargetForCFG(callTarget);
+        {
+            m_func->GetInProcCodeGenAllocators()->emitBufferManager.SetValidCallTarget(m_inProcAlloc, callTarget, true);
+        }
     }
 }
 

+ 28 - 2
lib/Backend/JITThunkEmitter.cpp

@@ -114,7 +114,20 @@ JITThunkEmitter<TAlloc>::CreateThunk(uintptr_t entryPoint)
 
     if (CONFIG_FLAG(OOPCFGRegistration))
     {
-        this->threadContext->SetValidCallTargetForCFG((PVOID)thunkAddress);
+#if ENABLE_OOP_NATIVE_CODEGEN
+        if (JITManager::GetJITManager()->IsJITServer())
+        {
+            HANDLE fileHandle = nullptr;
+            PVOID baseAddress = nullptr;
+            bool found = this->codeAllocator->GetFileInfo((PVOID)thunkAddress, &fileHandle, &baseAddress);
+            AssertOrFailFast(found);
+            this->threadContext->SetValidCallTargetFile((PVOID)thunkAddress, fileHandle, baseAddress, true);
+        }
+        else
+#endif
+        {
+            this->threadContext->SetValidCallTargetForCFG((PVOID)thunkAddress);
+        }
     }
     this->firstBitToCheck = (thunkIndex + 1 < JITThunkEmitter<TAlloc>::TotalThunkCount) ? thunkIndex + 1 : 0;
     this->freeThunks.Clear(thunkIndex);
@@ -147,7 +160,20 @@ JITThunkEmitter<TAlloc>::FreeThunk(uintptr_t thunkAddress)
 
     if (CONFIG_FLAG(OOPCFGRegistration))
     {
-        this->threadContext->SetValidCallTargetForCFG((PVOID)thunkAddress, false);
+#if ENABLE_OOP_NATIVE_CODEGEN
+        if (JITManager::GetJITManager()->IsJITServer())
+        {
+            HANDLE fileHandle = nullptr;
+            PVOID baseAddress = nullptr;
+            bool found = this->codeAllocator->GetFileInfo((PVOID)thunkAddress, &fileHandle, &baseAddress);
+            AssertOrFailFast(found);
+            this->threadContext->SetValidCallTargetFile((PVOID)thunkAddress, fileHandle, baseAddress, false);
+        }
+        else
+#endif
+        {
+            this->threadContext->SetValidCallTargetForCFG((PVOID)thunkAddress, false);
+        }
     }
 
     uintptr_t pageStartAddress = GetThunkPageStart(thunkAddress);

+ 11 - 0
lib/Backend/JnHelperMethod.cpp

@@ -179,7 +179,13 @@ DECLSPEC_GUARDIGNORE  _NOINLINE intptr_t GetNonTableMethodAddress(ThreadContextI
         return ShiftAddr(context, (double(*)(double))InterlockedExchange64);
 
     case HelperMemoryBarrier:
+#ifdef _M_HYBRID_X86_ARM64
+        AssertOrFailFastMsg(false, "The usage below fails to build for CHPE, and HelperMemoryBarrier is only required "
+                                   "for WASM threads, which are currently disabled");
+        return 0;
+#else
         return ShiftAddr(context, (void(*)())MemoryBarrier);
+#endif // !_M_HYBRID_X86_ARM64
 #endif
 
     case HelperDirectMath_FloorDb:
@@ -480,6 +486,11 @@ bool CanBeReentrant(IR::JnHelperMethod helper)
     return (JnHelperMethodAttributes[helper] & AttrCanNotBeReentrant) == 0;
 }
 
+bool TempObjectProducing(IR::JnHelperMethod helper)
+{
+    return (JnHelperMethodAttributes[helper] & AttrTempObjectProducing) != 0;
+}
+
 #ifdef DBG_DUMP
 struct ValidateHelperHeaders
 {

+ 2 - 0
lib/Backend/JnHelperMethod.h

@@ -57,4 +57,6 @@ bool IsInVariant(IR::JnHelperMethod helper);
 
 bool CanBeReentrant(IR::JnHelperMethod helper);
 
+bool TempObjectProducing(IR::JnHelperMethod helper);
+
 } // namespace HelperMethodAttributes.

+ 19 - 18
lib/Backend/JnHelperMethodList.h

@@ -71,7 +71,7 @@ HELPERCALLCHK(Op_EnsureNoRedeclPropertyScoped, Js::JavascriptOperators::OP_Scope
 
 HELPERCALLCHK(Op_ToSpreadedFunctionArgument, Js::JavascriptOperators::OP_LdCustomSpreadIteratorList, AttrCanThrow)
 HELPERCALLCHK(Op_ConvObject, Js::JavascriptOperators::ToObject, AttrCanThrow | AttrCanNotBeReentrant)
-HELPERCALLCHK(Op_NewWithObject, Js::JavascriptOperators::ToWithObject, AttrCanThrow | AttrCanNotBeReentrant)
+HELPERCALLCHK(Op_NewUnscopablesWrapperObject, Js::JavascriptOperators::ToUnscopablesWrapperObject, AttrCanThrow | AttrCanNotBeReentrant)
 HELPERCALLCHK(SetComputedNameVar, Js::JavascriptOperators::OP_SetComputedNameVar, AttrCanNotBeReentrant)
 HELPERCALLCHK(Op_UnwrapWithObj, Js::JavascriptOperators::OP_UnwrapWithObj, AttrCanNotBeReentrant)
 HELPERCALLCHK(Op_ConvNumber_Full, Js::JavascriptOperators::ToNumber, AttrCanThrow)
@@ -385,7 +385,6 @@ HELPERCALL(ProbeCurrentStack2, ThreadContext::ProbeCurrentStack2, AttrCanNotBeRe
 
 HELPERCALLCHK(AdjustSlots, Js::DynamicTypeHandler::AdjustSlots_Jit, AttrCanNotBeReentrant)
 HELPERCALLCHK(InvalidateProtoCaches, Js::JavascriptOperators::OP_InvalidateProtoCaches, AttrCanNotBeReentrant)
-HELPERCALL(CheckProtoHasNonWritable, Js::JavascriptOperators::CheckIfPrototypeChainHasOnlyWritableDataProperties, 0)
 
 HELPERCALLCHK(GetStringForChar, (Js::JavascriptString * (*)(Js::CharStringCache *, char16))&Js::CharStringCache::GetStringForChar, AttrCanNotBeReentrant)
 HELPERCALLCHK(GetStringForCharCodePoint, (Js::JavascriptString * (*)(Js::CharStringCache *, codepoint_t))&Js::CharStringCache::GetStringForCharCodePoint, AttrCanNotBeReentrant)
@@ -473,8 +472,8 @@ HELPERCALL(String_IndexOf, Js::JavascriptString::EntryIndexOf, 0)
 HELPERCALL(String_LastIndexOf, Js::JavascriptString::EntryLastIndexOf, 0)
 HELPERCALL(String_Link, Js::JavascriptString::EntryLink, 0)
 HELPERCALL(String_LocaleCompare, Js::JavascriptString::EntryLocaleCompare, 0)
-HELPERCALL(String_Match, Js::JavascriptString::EntryMatch, 0)
-HELPERCALL(String_Replace, Js::JavascriptString::EntryReplace, 0)
+HELPERCALL(String_Match, Js::JavascriptString::EntryMatch, AttrTempObjectProducing)
+HELPERCALL(String_Replace, Js::JavascriptString::EntryReplace, AttrTempObjectProducing)
 HELPERCALL(String_Search, Js::JavascriptString::EntrySearch, 0)
 HELPERCALL(String_Slice, Js::JavascriptString::EntrySlice, 0)
 HELPERCALL(String_Split, Js::JavascriptString::EntrySplit, 0)
@@ -499,7 +498,7 @@ HELPERCALL(RegExp_SplitResultNotUsed, Js::RegexHelper::RegexSplitResultNotUsed,
 HELPERCALL(RegExp_MatchResultUsed, Js::RegexHelper::RegexMatchResultUsed, 0)
 HELPERCALL(RegExp_MatchResultUsedAndMayBeTemp, Js::RegexHelper::RegexMatchResultUsedAndMayBeTemp, 0)
 HELPERCALL(RegExp_MatchResultNotUsed, Js::RegexHelper::RegexMatchResultNotUsed, 0)
-HELPERCALL(RegExp_Exec, Js::JavascriptRegExp::EntryExec, 0)
+HELPERCALL(RegExp_Exec, Js::JavascriptRegExp::EntryExec, AttrTempObjectProducing)
 HELPERCALL(RegExp_ExecResultUsed, Js::RegexHelper::RegexExecResultUsed, 0)
 HELPERCALL(RegExp_ExecResultUsedAndMayBeTemp, Js::RegexHelper::RegexExecResultUsedAndMayBeTemp, 0)
 HELPERCALL(RegExp_ExecResultNotUsed, Js::RegexHelper::RegexExecResultNotUsed, 0)
@@ -521,10 +520,11 @@ HELPERCALL(StPropIdArrFromVar, Js::InterpreterStackFrame::OP_StPropIdArrFromVar,
 
 HELPERCALLCHK(LdHomeObj,           Js::JavascriptOperators::OP_LdHomeObj, AttrCanNotBeReentrant)
 HELPERCALLCHK(LdFuncObj,           Js::JavascriptOperators::OP_LdFuncObj, AttrCanNotBeReentrant)
+HELPERCALLCHK(SetHomeObj,          Js::JavascriptOperators::OP_SetHomeObj, AttrCanNotBeReentrant)
 HELPERCALLCHK(LdHomeObjProto,      Js::JavascriptOperators::OP_LdHomeObjProto, AttrCanNotBeReentrant)
 HELPERCALLCHK(LdFuncObjProto,      Js::JavascriptOperators::OP_LdFuncObjProto, AttrCanNotBeReentrant)
 
-HELPERCALLCHK(ImportCall,          Js::JavascriptOperators::OP_ImportCall, AttrCanNotBeReentrant)
+HELPERCALLCHK(ImportCall,          Js::JavascriptOperators::OP_ImportCall, 0)
 
 HELPERCALLCHK(ResumeYield,   Js::JavascriptOperators::OP_ResumeYield, AttrCanThrow)
 
@@ -566,20 +566,21 @@ HELPERCALL(DirectMath_NearestFlt, (float(*)(float)) Wasm::WasmMath::Nearest<floa
 HELPERCALL(PopCnt32, Math::PopCnt32, AttrCanNotBeReentrant)
 HELPERCALL(PopCnt64, (int64(*)(int64)) Wasm::WasmMath::PopCnt<int64>, AttrCanNotBeReentrant)
 
-HELPERCALL(F32ToI64, (int64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToI64<false /* saturating */>, AttrCanThrow | AttrCanNotBeReentrant)
-HELPERCALL(F32ToU64, (uint64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToU64<false /* saturating */>, AttrCanThrow | AttrCanNotBeReentrant)
-HELPERCALL(F64ToI64, (int64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToI64<false /* saturating */>, AttrCanThrow | AttrCanNotBeReentrant)
-HELPERCALL(F64ToU64, (uint64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToU64<false /* saturating */>, AttrCanThrow | AttrCanNotBeReentrant)
+HELPERCALL(F32ToI64, (int64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToI64<false /* saturating */>, AttrCanThrow|AttrCanNotBeReentrant)
+HELPERCALL(F32ToU64, (uint64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToU64<false /* saturating */>, AttrCanThrow|AttrCanNotBeReentrant)
+HELPERCALL(F64ToI64, (int64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToI64<false /* saturating */>, AttrCanThrow|AttrCanNotBeReentrant)
+HELPERCALL(F64ToU64, (uint64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToU64<false /* saturating */>, AttrCanThrow|AttrCanNotBeReentrant)
 
-HELPERCALL(F32ToI64Sat, (int64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToI64<true /* saturating */>, AttrCanThrow | AttrCanNotBeReentrant)
-HELPERCALL(F32ToU64Sat, (uint64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToU64<true /* saturating */>, AttrCanThrow | AttrCanNotBeReentrant)
-HELPERCALL(F64ToI64Sat, (int64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToI64<true /* saturating */>, AttrCanThrow | AttrCanNotBeReentrant)
-HELPERCALL(F64ToU64Sat, (uint64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToU64<true /* saturating */>, AttrCanThrow | AttrCanNotBeReentrant)
+HELPERCALL(F32ToI64Sat, (int64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToI64<true /* saturating */>, AttrCanThrow|AttrCanNotBeReentrant)
+HELPERCALL(F32ToU64Sat, (uint64(*)(float, Js::ScriptContext*)) Wasm::WasmMath::F32ToU64<true /* saturating */>, AttrCanThrow|AttrCanNotBeReentrant)
+HELPERCALL(F64ToI64Sat, (int64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToI64<true /* saturating */>, AttrCanThrow|AttrCanNotBeReentrant)
+HELPERCALL(F64ToU64Sat, (uint64(*)(double, Js::ScriptContext*)) Wasm::WasmMath::F64ToU64<true /* saturating */>, AttrCanThrow|AttrCanNotBeReentrant)
 
-HELPERCALL(I64TOF64,        Js::JavascriptConversion::LongToDouble,   AttrCanNotBeReentrant)
-HELPERCALL(UI64TOF64,       Js::JavascriptConversion::ULongToDouble,  AttrCanNotBeReentrant)
-HELPERCALL(I64TOF32,        Js::JavascriptConversion::LongToFloat,    AttrCanNotBeReentrant)
-HELPERCALL(UI64TOF32,       Js::JavascriptConversion::ULongToFloat,   AttrCanNotBeReentrant)
+
+HELPERCALL(I64TOF64,  Js::JavascriptConversion::LongToDouble, AttrCanNotBeReentrant)
+HELPERCALL(UI64TOF64, Js::JavascriptConversion::ULongToDouble, AttrCanNotBeReentrant)
+HELPERCALL(I64TOF32,  Js::JavascriptConversion::LongToFloat, AttrCanNotBeReentrant)
+HELPERCALL(UI64TOF32, Js::JavascriptConversion::ULongToFloat, AttrCanNotBeReentrant)
 
 HELPERCALLCRT(DirectMath_Acos, AttrCanNotBeReentrant)
 HELPERCALLCRT(DirectMath_Asin, AttrCanNotBeReentrant)

+ 74 - 66
lib/Backend/Lower.cpp

@@ -980,8 +980,8 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
             this->LowerUnaryHelperMem(instr, IR::HelperOp_ConvObject);
             break;
 
-        case Js::OpCode::NewWithObject:
-            this->LowerUnaryHelperMem(instr, IR::HelperOp_NewWithObject);
+        case Js::OpCode::NewUnscopablesWrapperObject:
+            this->LowerUnaryHelperMem(instr, IR::HelperOp_NewUnscopablesWrapperObject);
             break;
 
         case Js::OpCode::LdCustomSpreadIteratorList:
@@ -14815,7 +14815,6 @@ IR::RegOpnd *Lowerer::GenerateArrayTest(
     {
         // Only DynamicObject is allowed (DynamicObject vtable is ensured) because some object types have special handling for
         // index properties - arguments object, string object, external object, etc.
-        // If other object types are also allowed in the future, corresponding changes will have to made to
         // JavascriptArray::Jit_TryGetArrayForObjectWithArray as well.
         GenerateObjectTypeTest(baseOpnd, insertBeforeInstr, isNotObjectLabel);
         GenerateObjectHeaderInliningTest(baseOpnd, isNotArrayLabel, insertBeforeInstr);
@@ -15939,7 +15938,7 @@ Lowerer::GenerateDynamicLoadPolymorphicInlineCacheSlot(IR::Instr * instrInsert,
 
 // Test that the operand is a PropertyString, or bail to helper
 void
-Lowerer::GeneratePropertyStringTest(IR::RegOpnd *srcReg, IR::Instr *instrInsert, IR::LabelInstr *labelHelper, bool usePoison)
+Lowerer::GeneratePropertyStringTest(IR::RegOpnd *srcReg, IR::Instr *instrInsert, IR::LabelInstr *labelHelper, bool isStore)
 {
     // Generates:
     //      StringTest(srcReg, $helper)                ; verify index is string type
@@ -15955,7 +15954,9 @@ Lowerer::GeneratePropertyStringTest(IR::RegOpnd *srcReg, IR::Instr *instrInsert,
         LoadVTableValueOpnd(instrInsert, VTableValue::VtablePropertyString),
         Js::OpCode::BrNeq_A, notPropStrLabel, instrInsert);
 
-    InsertObjectPoison(srcReg, branchInstr, instrInsert, !usePoison);
+    InsertObjectPoison(srcReg, branchInstr, instrInsert, isStore);
+
+    InsertBranch(Js::OpCode::Br, propStrLoadedLabel, instrInsert);
 
     InsertBranch(Js::OpCode::Br, propStrLoadedLabel, instrInsert);
 
@@ -15966,7 +15967,7 @@ Lowerer::GeneratePropertyStringTest(IR::RegOpnd *srcReg, IR::Instr *instrInsert,
         LoadVTableValueOpnd(instrInsert, VTableValue::VtableLiteralStringWithPropertyStringPtr),
         Js::OpCode::BrNeq_A, labelHelper, instrInsert);
 
-    InsertObjectPoison(srcReg, branchInstr, instrInsert, !usePoison);
+    InsertObjectPoison(srcReg, branchInstr, instrInsert, isStore);
 
     IR::IndirOpnd * propStrOpnd = IR::IndirOpnd::New(srcReg, Js::LiteralStringWithPropertyStringPtr::GetOffsetOfPropertyString(), TyMachPtr, m_func);
     InsertCompareBranch(propStrOpnd, IR::IntConstOpnd::New(NULL, TyMachPtr, m_func), Js::OpCode::BrNeq_A, labelHelper, instrInsert);
@@ -15994,7 +15995,7 @@ Lowerer::GenerateFastElemIStringIndexCommon(
     //      PropertyStringTest(indexOpnd, $helper)                ; verify index is string type
     //      FastElemISymbolOrStringIndexCommon(indexOpnd, baseOpnd, $helper) ; shared code with JavascriptSymbol
 
-    GeneratePropertyStringTest(indexOpnd, elemInstr, labelHelper, !isStore /*usePoison*/);
+    GeneratePropertyStringTest(indexOpnd, elemInstr, labelHelper, isStore);
 
     const uint32 inlineCacheOffset = isStore ? Js::PropertyString::GetOffsetOfStElemInlineCache() : Js::PropertyString::GetOffsetOfLdElemInlineCache();
     const uint32 hitRateOffset = Js::PropertyString::GetOffsetOfHitRate();
@@ -16229,6 +16230,7 @@ Lowerer::GenerateLookUpInIndexCache(
 
     IR::IndirOpnd * hitRateOpnd = IR::IndirOpnd::New(indexOpnd, hitRateOffset, TyInt32, m_func);
     IR::IntConstOpnd * incOpnd = IR::IntConstOpnd::New(1, TyInt32, m_func);
+    // overflow check: not needed here, we don't allocate anything with hitrate
     InsertAdd(false, hitRateOpnd, hitRateOpnd, incOpnd, instrInsert);
 }
 
@@ -16841,6 +16843,11 @@ Lowerer::GenerateFastElemIIntIndexCommon(
                     // to the helper in any case where this would execute, it's a functional no-op.
 
                     // indexLessThanSize:
+                    // In speculative cases, we want to avoid a write to an array setting the length to something huge, which
+                    // would then allow subsequent reads to hit arbitrary memory (in the speculative path). This is done with
+                    // a mask generated from the difference between the index and the size. Since we should have already gone
+                    // to the helper in any case where this would execute, it's a functional no-op.
+
                     //     if(!index->IsConstOpnd()) {
                     //       sub  temp, index, [headSegment + offset(size)]
                     //       sar  temp, 31
@@ -17098,76 +17105,77 @@ Lowerer::GenerateFastElemIIntIndexCommon(
                     dst = tmpDst;
                 }
 
+                // Use a mask to prevent arbitrary speculative reads
+                // If you think this code looks highly similar to the code later in this function,
+                // you'd be right. Unfortunately, I wasn't able to find a way to reduce duplication
+                // here without significantly complicating the code structure.
+                if (!headSegmentLengthOpnd)
                 {
-                    // Use a mask to prevent arbitrary speculative reads
-                    if (!headSegmentLengthOpnd)
-                    {
-                        headSegmentLengthOpnd =
-                            IR::IndirOpnd::New(headSegmentOpnd, Js::SparseArraySegmentBase::GetOffsetOfLength(), TyUint32, m_func);
-                        autoReuseHeadSegmentLengthOpnd.Initialize(headSegmentLengthOpnd, m_func);
-                    }
-                    IR::RegOpnd* localMaskOpnd = nullptr;
+                    headSegmentLengthOpnd =
+                        IR::IndirOpnd::New(headSegmentOpnd, Js::SparseArraySegmentBase::GetOffsetOfLength(), TyUint32, m_func);
+                    autoReuseHeadSegmentLengthOpnd.Initialize(headSegmentLengthOpnd, m_func);
+                }
+                IR::RegOpnd* localMaskOpnd = nullptr;
 #if TARGET_64
-                    IR::Opnd* lengthOpnd = nullptr;
-                    AnalysisAssert(headSegmentLengthOpnd != nullptr);
-                    lengthOpnd = IR::RegOpnd::New(headSegmentLengthOpnd->GetType(), m_func);
-                    {
-                        IR::Instr * instrMov = IR::Instr::New(Js::OpCode::MOV_TRUNC, lengthOpnd, headSegmentLengthOpnd, m_func);
-                        instr->InsertBefore(instrMov);
-                        LowererMD::Legalize(instrMov);
-                    }
+                IR::Opnd* lengthOpnd = nullptr;
+                AnalysisAssert(headSegmentLengthOpnd != nullptr);
+                lengthOpnd = IR::RegOpnd::New(headSegmentLengthOpnd->GetType(), m_func);
+                {
+                    IR::Instr * instrMov = IR::Instr::New(Js::OpCode::MOV_TRUNC, lengthOpnd, headSegmentLengthOpnd, m_func);
+                    instr->InsertBefore(instrMov);
+                    LowererMD::Legalize(instrMov);
+                }
 
-                    if (lengthOpnd->GetSize() != MachPtr)
-                    {
-                        lengthOpnd = lengthOpnd->UseWithNewType(TyMachPtr, this->m_func)->AsRegOpnd();
-                    }
+                if (lengthOpnd->GetSize() != MachPtr)
+                {
+                    lengthOpnd = lengthOpnd->UseWithNewType(TyMachPtr, this->m_func)->AsRegOpnd();
+                }
 
-                    //  MOV r1, [opnd + offset(type)]
-                    IR::RegOpnd* indexValueRegOpnd = IR::RegOpnd::New(indexValueOpnd->GetType(), m_func);
+                //  MOV r1, [opnd + offset(type)]
+                IR::RegOpnd* indexValueRegOpnd = IR::RegOpnd::New(indexValueOpnd->GetType(), m_func);
 
-                    {
-                        IR::Instr * instrMov = IR::Instr::New(Js::OpCode::MOV_TRUNC, indexValueRegOpnd, indexValueOpnd, m_func);
-                        instr->InsertBefore(instrMov);
-                        LowererMD::Legalize(instrMov);
-                    }
+                {
+                    IR::Instr * instrMov = IR::Instr::New(Js::OpCode::MOV_TRUNC, indexValueRegOpnd, indexValueOpnd, m_func);
+                    instr->InsertBefore(instrMov);
+                    LowererMD::Legalize(instrMov);
+                }
 
-                    if (indexValueRegOpnd->GetSize() != MachPtr)
-                    {
-                        indexValueRegOpnd = indexValueRegOpnd->UseWithNewType(TyMachPtr, this->m_func)->AsRegOpnd();
-                    }
+                if (indexValueRegOpnd->GetSize() != MachPtr)
+                {
+                    indexValueRegOpnd = indexValueRegOpnd->UseWithNewType(TyMachPtr, this->m_func)->AsRegOpnd();
+                }
 
-                    localMaskOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
-                    InsertSub(false, localMaskOpnd, indexValueRegOpnd, lengthOpnd, instr);
-                    InsertShift(Js::OpCode::Shr_A, false, localMaskOpnd, localMaskOpnd, IR::IntConstOpnd::New(63, TyInt8, m_func), instr);
+                localMaskOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
+                InsertSub(false, localMaskOpnd, indexValueRegOpnd, lengthOpnd, instr);
+                InsertShift(Js::OpCode::Shr_A, false, localMaskOpnd, localMaskOpnd, IR::IntConstOpnd::New(63, TyInt8, m_func), instr);
 #else
-                    localMaskOpnd = IR::RegOpnd::New(TyInt32, m_func);
-                    InsertSub(false, localMaskOpnd, indexValueOpnd, headSegmentLengthOpnd, instr);
-                    InsertShift(Js::OpCode::Shr_A, false, localMaskOpnd, localMaskOpnd, IR::IntConstOpnd::New(31, TyInt8, m_func), instr);
+                localMaskOpnd = IR::RegOpnd::New(TyInt32, m_func);
+                InsertSub(false, localMaskOpnd, indexValueOpnd, headSegmentLengthOpnd, instr);
+                InsertShift(Js::OpCode::Shr_A, false, localMaskOpnd, localMaskOpnd, IR::IntConstOpnd::New(31, TyInt8, m_func), instr);
 #endif
 
-                    // for pop we always do the masking before the load in cases where we load a value
-                    IR::RegOpnd* loadAddr = IR::RegOpnd::New(TyMachPtr, m_func);
+                // for pop we always do the masking before the load in cases where we load a value
+                IR::RegOpnd* loadAddr = IR::RegOpnd::New(TyMachPtr, m_func);
 
 #if _M_ARM32_OR_ARM64
-                    if (indirOpnd->GetIndexOpnd() != nullptr && indirOpnd->GetScale() > 0)
+                if (indirOpnd->GetIndexOpnd() != nullptr && indirOpnd->GetScale() > 0)
+                {
+                    // We don't support encoding for LEA with scale on ARM/ARM64, so do the scale calculation as a separate instruction
+                    IR::RegOpnd* fullIndexOpnd = IR::RegOpnd::New(indirOpnd->GetIndexOpnd()->GetType(), m_func);
+                    InsertShift(Js::OpCode::Shl_A, false, fullIndexOpnd, indirOpnd->GetIndexOpnd(), IR::IntConstOpnd::New(indirOpnd->GetScale(), TyInt8, m_func), instr);
+                    IR::IndirOpnd* newIndir = IR::IndirOpnd::New(indirOpnd->GetBaseOpnd(), fullIndexOpnd, indirType, m_func);
+                    if (indirOpnd->GetOffset() != 0)
                     {
-                        // We don't support encoding for LEA with scale on ARM/ARM64, so do the scale calculation as a separate instruction
-                        IR::RegOpnd* fullIndexOpnd = IR::RegOpnd::New(indirOpnd->GetIndexOpnd()->GetType(), m_func);
-                        InsertShift(Js::OpCode::Shl_A, false, fullIndexOpnd, indirOpnd->GetIndexOpnd(), IR::IntConstOpnd::New(indirOpnd->GetScale(), TyInt8, m_func), instr);
-                        IR::IndirOpnd* newIndir = IR::IndirOpnd::New(indirOpnd->GetBaseOpnd(), fullIndexOpnd, indirType, m_func);
-                        if (indirOpnd->GetOffset() != 0)
-                        {
-                            newIndir->SetOffset(indirOpnd->GetOffset());
-                        }
-                        indirOpnd = newIndir;
+                        newIndir->SetOffset(indirOpnd->GetOffset());
                     }
+                    indirOpnd = newIndir;
+                }
 #endif
-                    IR::AutoReuseOpnd reuseIndir(indirOpnd, m_func);
+                IR::AutoReuseOpnd reuseIndir(indirOpnd, m_func);
 
-                    InsertLea(loadAddr, indirOpnd, instr);
-                    InsertAnd(loadAddr, loadAddr, localMaskOpnd, instr);
-                    indirOpnd = IR::IndirOpnd::New(loadAddr, 0, indirType, m_func);
-                }
+                InsertLea(loadAddr, indirOpnd, instr);
+                InsertAnd(loadAddr, loadAddr, localMaskOpnd, instr);
+                indirOpnd = IR::IndirOpnd::New(loadAddr, 0, indirType, m_func);
 
                 //  MOV dst, [head + offset]
                 InsertMove(dst, indirOpnd, instr);
@@ -17234,8 +17242,8 @@ Lowerer::GenerateFastElemIIntIndexCommon(
                     : ((indirType == TyVar && CONFIG_FLAG_RELEASE(PoisonVarArrayLoad))
                     || (IRType_IsNativeInt(indirType) && CONFIG_FLAG_RELEASE(PoisonIntArrayLoad))
                     || (IRType_IsFloat(indirType) && CONFIG_FLAG_RELEASE(PoisonFloatArrayLoad)))
-                    )
                 )
+            )
             ||
             (isStore &&
                 (baseValueType.IsLikelyTypedArray()
@@ -17243,10 +17251,10 @@ Lowerer::GenerateFastElemIIntIndexCommon(
                     : ((indirType == TyVar && CONFIG_FLAG_RELEASE(PoisonVarArrayStore))
                     || (IRType_IsNativeInt(indirType) && CONFIG_FLAG_RELEASE(PoisonIntArrayStore))
                     || (IRType_IsFloat(indirType) && CONFIG_FLAG_RELEASE(PoisonFloatArrayStore)))
-                    )
                 )
             )
-        ;
+        )
+    ;
 
     // We have two exit paths for this function in the store case when we might grow the head
     // segment, due to tracking for missing elements. This unfortunately means that we need a
@@ -20417,7 +20425,7 @@ void Lowerer::GenerateFastObjectIsIn(IR::Instr * instr)
 
     if (likelyStringIndex)
     {
-        GeneratePropertyStringTest(indexOpnd, instr, helperLabel, true /*usePoison*/);
+        GeneratePropertyStringTest(indexOpnd, instr, helperLabel, false /*isStore*/);
 
         const uint32 inlineCacheOffset = Js::PropertyString::GetOffsetOfLdElemInlineCache();
         const uint32 hitRateOffset = Js::PropertyString::GetOffsetOfHitRate();
@@ -21418,7 +21426,7 @@ Lowerer::GenerateArgOutForStackArgs(IR::Instr* callInstr, IR::Instr* stackArgsIn
 
     IR::RegOpnd* ldLenDstOpnd = IR::RegOpnd::New(TyMachReg, func);
     const IR::AutoReuseOpnd autoReuseLdLenDstOpnd(ldLenDstOpnd, func);
-    IR::Instr* ldLen = IR::Instr::New(Js::OpCode::LdLen_A, ldLenDstOpnd, stackArgs, func);
+    IR::Instr* ldLen = IR::Instr::New(Js::OpCode::LdLen_A, ldLenDstOpnd ,stackArgs, func);
     ldLenDstOpnd->SetValueType(ValueType::GetTaggedInt()); /*LdLen_A works only on stack arguments*/
     callInstr->InsertBefore(ldLen);
     GenerateFastRealStackArgumentsLdLen(ldLen);

+ 78 - 0
lib/Backend/LowerMDShared.cpp

@@ -3825,6 +3825,84 @@ LowererMD::GenerateLoadTaggedType(IR::Instr * instrLdSt, IR::RegOpnd * opndType,
     }
 }
 
+///----------------------------------------------------------------------------
+///
+/// LowererMD::GenerateFastLdMethodFromFlags
+///
+/// Make use of the helper to cache the type and slot index used to do a LdFld
+/// and do an inline load from the appropriate slot if the type hasn't changed
+/// since the last time this LdFld was executed.
+///
+///----------------------------------------------------------------------------
+
+bool
+LowererMD::GenerateFastLdMethodFromFlags(IR::Instr * instrLdFld)
+{
+    IR::LabelInstr *   labelFallThru;
+    IR::LabelInstr *   bailOutLabel;
+    IR::Opnd *         opndSrc;
+    IR::Opnd *         opndDst;
+    IR::RegOpnd *      opndBase;
+    IR::RegOpnd *      opndType;
+    IR::RegOpnd *      opndInlineCache;
+
+    opndSrc = instrLdFld->GetSrc1();
+
+    AssertMsg(opndSrc->IsSymOpnd() && opndSrc->AsSymOpnd()->IsPropertySymOpnd() && opndSrc->AsSymOpnd()->m_sym->IsPropertySym(),
+              "Expected property sym operand as src of LdFldFlags");
+
+    IR::PropertySymOpnd * propertySymOpnd = opndSrc->AsPropertySymOpnd();
+
+    Assert(!instrLdFld->DoStackArgsOpt());
+
+    if (propertySymOpnd->IsTypeCheckSeqCandidate())
+    {
+        AssertMsg(propertySymOpnd->HasObjectTypeSym(), "Type optimized property sym operand without a type sym?");
+        StackSym *typeSym = propertySymOpnd->GetObjectTypeSym();
+        opndType = IR::RegOpnd::New(typeSym, TyMachReg, this->m_func);
+    }
+    else
+    {
+        opndType = IR::RegOpnd::New(TyMachReg, this->m_func);
+    }
+
+    opndBase = propertySymOpnd->CreatePropertyOwnerOpnd(m_func);
+    opndDst = instrLdFld->GetDst();
+    opndInlineCache = IR::RegOpnd::New(TyMachPtr, this->m_func);
+
+    labelFallThru = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
+    // Label to jump to (or fall through to) when bailing out
+    bailOutLabel = IR::LabelInstr::New(Js::OpCode::Label, instrLdFld->m_func, true /* isOpHelper */);
+
+    instrLdFld->InsertBefore(IR::Instr::New(Js::OpCode::MOV, opndInlineCache, m_lowerer->LoadRuntimeInlineCacheOpnd(instrLdFld, propertySymOpnd), this->m_func));
+    IR::LabelInstr * labelFlagAux = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
+
+    // Check the flag cache with the untagged type
+    this->m_lowerer->GenerateObjectTestAndTypeLoad(instrLdFld, opndBase, opndType, bailOutLabel);
+    // Blindly do the check for getter flag first and then do the type check
+    // We avoid repeated check for getter flag when the function object may be in either
+    // inline slots or auxiliary slots
+    this->m_lowerer->GenerateFlagInlineCacheCheckForGetterSetter(instrLdFld, opndInlineCache, bailOutLabel);
+    this->m_lowerer->GenerateFlagInlineCacheCheck(instrLdFld, opndType, opndInlineCache, labelFlagAux);
+    this->m_lowerer->GenerateLdFldFromFlagInlineCache(instrLdFld, opndBase, opndDst, opndInlineCache, labelFallThru, true);
+
+    // Check the flag cache with the tagged type
+    instrLdFld->InsertBefore(labelFlagAux);
+    IR::RegOpnd * opndTaggedType = IR::RegOpnd::New(TyMachReg, this->m_func);
+    GenerateLoadTaggedType(instrLdFld, opndType, opndTaggedType);
+    this->m_lowerer->GenerateFlagInlineCacheCheck(instrLdFld, opndTaggedType, opndInlineCache, bailOutLabel);
+    this->m_lowerer->GenerateLdFldFromFlagInlineCache(instrLdFld, opndBase, opndDst, opndInlineCache, labelFallThru, false);
+
+    instrLdFld->InsertBefore(bailOutLabel);
+    instrLdFld->InsertAfter(labelFallThru);
+    // Generate the bailout helper call. 'instr' will be changed to the CALL into the bailout function, so it can't be used for
+    // ordering instructions anymore.
+    instrLdFld->UnlinkSrc1();
+    this->m_lowerer->GenerateBailOut(instrLdFld);
+
+    return true;
+}
+
 void
 LowererMD::GenerateLoadPolymorphicInlineCacheSlot(IR::Instr * instrLdSt, IR::RegOpnd * opndInlineCache, IR::RegOpnd * opndType, uint polymorphicInlineCacheSize)
 {

+ 1 - 0
lib/Backend/LowerMDShared.h

@@ -171,6 +171,7 @@ public:
             void            GenerateCheckForArgumentsLength(IR::Instr* ldElem, IR::LabelInstr* labelCreateHeapArgs, IR::Opnd* actualParamOpnd, IR::Opnd* valueOpnd, Js::OpCode);
             IR::RegOpnd *   LoadNonnegativeIndex(IR::RegOpnd *indexOpnd, const bool skipNegativeCheck, IR::LabelInstr *const notTaggedIntLabel, IR::LabelInstr *const negativeLabel, IR::Instr *const insertBeforeInstr);
             IR::RegOpnd *   GenerateUntagVar(IR::RegOpnd * opnd, IR::LabelInstr * labelFail, IR::Instr * insertBeforeInstr, bool generateTagCheck = true);
+            bool            GenerateFastLdMethodFromFlags(IR::Instr * instrLdFld);
             IR::Instr *     GenerateFastScopedLdFld(IR::Instr * instrLdFld);
             IR::Instr *     GenerateFastScopedStFld(IR::Instr * instrStFld);
             void            GenerateFastAbs(IR::Opnd *dst, IR::Opnd *src, IR::Instr *callInstr, IR::Instr *insertInstr, IR::LabelInstr *labelHelper, IR::LabelInstr *doneLabel);

+ 9 - 1
lib/Backend/TempTracker.cpp

@@ -1076,7 +1076,15 @@ ObjectTemp::IsTempProducing(IR::Instr * instr)
     Js::OpCode opcode = instr->m_opcode;
     if (OpCodeAttr::TempObjectProducing(opcode))
     {
-        return true;
+        if (instr->m_opcode == Js::OpCode::CallDirect)
+        {
+            IR::HelperCallOpnd* helper = instr->GetSrc1()->AsHelperCallOpnd();
+            return HelperMethodAttributes::TempObjectProducing(helper->m_fnHelper);
+        }
+        else
+        {
+            return true;
+        }
     }
 
     // TODO: Process NewScObject and CallI with isCtorCall when the ctor is fixed

+ 1 - 1
lib/Backend/arm64/LowerMD.h

@@ -251,7 +251,7 @@ public:
 
             void                GenerateMemInit(IR::RegOpnd * opnd, int32 offset, size_t value, IR::Instr * insertBeforeInstr, bool isZeroed = false);
 
-            static void            InsertObjectPoison(IR::Opnd* poisonedOpnd, IR::BranchInstr* branchInstr, IR::Instr* insertInstr, bool isForStore);
+            static void         InsertObjectPoison(IR::Opnd* poisonedOpnd, IR::BranchInstr* branchInstr, IR::Instr* insertInstr, bool isForStore);
             IR::BranchInstr*    InsertMissingItemCompareBranch(IR::Opnd* compareSrc, IR::Opnd* missingItemOpnd, Js::OpCode opcode, IR::LabelInstr* target, IR::Instr* insertBeforeInstr);
 
 private:

+ 4 - 1
lib/Common/Codex/Chakra.Common.Codex.vcxproj

@@ -1,10 +1,13 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?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 Project="$(BuildConfigPropsPath)Chakra.Build.ProjectConfiguration.props" />
   <ItemGroup>
     <ClCompile Include="$(MSBuildThisFileDirectory)Utf8Codex.cpp" />
+  </ItemGroup>
+  <ItemGroup>
     <ClInclude Include="Utf8Codex.h" />
+    <ClInclude Include="Utf8Helper.h" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
     <TargetName>Chakra.Common.Codex</TargetName>

+ 65 - 32
lib/Common/Codex/Utf8Codex.cpp

@@ -432,12 +432,17 @@ LSlowPath:
         return true;
     }
 
-    template <bool cesu8Encoding, bool countBytesOnly>
-    __range(0, cchIn * 3)
-    size_t EncodeIntoImpl(_When_(!countBytesOnly, __out_ecount(cchIn * 3)) LPUTF8 buffer, __in_ecount(cchIn) const char16 *source, charcount_t cchIn, const void* bufferEnd)
+    template <Utf8EncodingKind encoding, bool countBytesOnly = false>
+    __range(0, cbDest)
+    size_t EncodeIntoImpl(
+        _When_(!countBytesOnly, _Out_writes_(cbDest)) utf8char_t *destBuffer,
+        __range(0, cchSource * 3) size_t cbDest,
+        _In_reads_(cchSource) const char16 *source,
+        __range(0, INT_MAX) charcount_t cchSource)
     {
-        charcount_t cch = cchIn; // SAL analysis gets confused by EncodeTrueUtf8's dest buffer requirement unless we alias cchIn with a local
-        LPUTF8 dest = buffer;
+        charcount_t cch = cchSource; // SAL analysis gets confused by EncodeTrueUtf8's dest buffer requirement unless we alias cchSource with a local
+        LPUTF8 dest = destBuffer;
+        utf8char_t *bufferEnd = &destBuffer[cbDest];
 
         CodexAssertOrFailFast(dest <= bufferEnd);
 
@@ -459,11 +464,10 @@ LFastPath:
             dest += 4;
             source += 4;
             cch -= 4;
-
         }
 
 LSlowPath:
-        if (cesu8Encoding)
+        if (encoding == Utf8EncodingKind::Cesu8)
         {
             while (cch-- > 0)
             {
@@ -484,42 +488,71 @@ LSlowPath:
             }
         }
 
-        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, false>(buffer, source, cch, &buffer[cch*3]);
-    }
-
-    __range(0, cch * 3)
-    size_t EncodeIntoAndNullTerminate(__out_ecount(cch * 3 + 1) utf8char_t *buffer, __in_ecount(cch) const char16 *source, charcount_t cch)
-    {
-        size_t result = EncodeInto(buffer, source, cch);
-        buffer[result] = 0;
-        return result;
+        return dest - destBuffer;
     }
 
-    __range(0, cch * 3)
-        size_t EncodeTrueUtf8IntoAndNullTerminate(__out_ecount(cch * 3 + 1) utf8char_t *buffer, __in_ecount(cch) const char16 *source, charcount_t cch)
+    template <Utf8EncodingKind encoding>
+    __range(0, cbDest)
+    size_t EncodeInto(
+        _Out_writes_(cbDest) utf8char_t *dest,
+        __range(0, cchSource * 3) size_t cbDest,
+        _In_reads_(cchSource) const char16 *source,
+        __range(0, INT_MAX) charcount_t cchSource)
     {
-        size_t result = EncodeIntoImpl<false, false>(buffer, source, cch, &buffer[3 * cch]);
-        buffer[result] = 0;
-        return result;
+        return EncodeIntoImpl<encoding>(dest, cbDest, source, cchSource);
     }
 
-    __range(0, cch * 3)
-        size_t EncodeTrueUtf8IntoBoundsChecked(__out_ecount(cch * 3 + 1) utf8char_t *buffer, __in_ecount(cch) const char16 *source, charcount_t cch, const void * bufferEnd)
+    template <Utf8EncodingKind encoding>
+    __range(0, cbDest)
+    size_t EncodeIntoAndNullTerminate(
+        _Out_writes_z_(cbDest) utf8char_t *dest,
+        __range(1, cchSource * 3 + 1) size_t cbDest, // must be at least large enough to write null terminator
+        _In_reads_(cchSource) const char16 *source,
+        __range(0, INT_MAX) charcount_t cchSource)
     {
-        size_t result = EncodeIntoImpl<false, false>(buffer, source, cch, bufferEnd);
+        size_t destWriteMaxBytes = cbDest - 1; // leave room for null terminator
+        size_t result = EncodeIntoImpl<encoding>(dest, destWriteMaxBytes, source, cchSource);
+        dest[result] = 0;
         return result;
     }
 
+    template
+    __range(0, cbDest)
+    size_t EncodeInto<Utf8EncodingKind::Cesu8>(
+        _Out_writes_(cbDest) utf8char_t *dest,
+        __range(0, cchSource * 3) size_t cbDest,
+        _In_reads_(cchSource) const char16 *source,
+        __range(0, INT_MAX) charcount_t cchSource);
+
+    template
+    __range(0, cbDest)
+    size_t EncodeInto<Utf8EncodingKind::TrueUtf8>(
+        _Out_writes_(cbDest) utf8char_t *dest,
+        __range(0, cchSource * 3) size_t cbDest,
+        _In_reads_(cchSource) const char16 *source,
+        __range(0, INT_MAX) charcount_t cchSource);
+
+    template
+    __range(0, cbDest)
+    size_t EncodeIntoAndNullTerminate<Utf8EncodingKind::Cesu8>(
+        _Out_writes_z_(cbDest) utf8char_t *dest,
+        __range(1, cchSource * 3 + 1) size_t cbDest,
+        _In_reads_(cchSource) const char16 *source,
+        __range(0, INT_MAX) charcount_t cchSource);
+
+    template
+    __range(0, cbDest)
+    size_t EncodeIntoAndNullTerminate<Utf8EncodingKind::TrueUtf8>(
+        _Out_writes_z_(cbDest) utf8char_t *dest,
+        __range(1, cchSource * 3 + 1) size_t cbDest,
+        _In_reads_(cchSource) const char16 *source,
+        __range(0, INT_MAX) charcount_t cchSource);
+
+    // Since we are not actually encoding, the return value is bounded on cch
     __range(0, cch * 3)
-        size_t CountTrueUtf8(__in_ecount(cch) const char16 *source, charcount_t cch)
+    size_t CountTrueUtf8(__in_ecount(cch) const char16 *source, charcount_t cch)
     {
-        return EncodeIntoImpl<false, true>(nullptr, source, cch, nullptr);
+        return EncodeIntoImpl<Utf8EncodingKind::TrueUtf8, true /*count only*/>(nullptr, 0, source, cch);
     }
 
     // Convert the character index into a byte index.

+ 36 - 23
lib/Common/Codex/Utf8Codex.h

@@ -397,33 +397,46 @@ namespace utf8
 
     // Encode a UTF-8 sequence into a UTF-8 sequence (which is just a memcpy). This is included for convenience in templates
     // when the character encoding is a template parameter.
-    __range(cch, cch)
-    inline size_t EncodeInto(__out_ecount(cch) utf8char_t *buffer, const utf8char_t *source, size_t cch)
+    __range(cbSource, cbDest)
+    __precond(cbDest == cbSource)
+    inline size_t EncodeInto(
+        _Out_writes_(cbDest) utf8char_t *dest,
+        size_t cbDest,
+        _In_reads_(cbSource) const utf8char_t *source,
+        size_t cbSource)
     {
-       memcpy_s(buffer, cch * sizeof(utf8char_t), source, cch * sizeof(utf8char_t));
-       return cch;
+        memcpy_s(dest, cbDest * sizeof(utf8char_t), source, cbSource * sizeof(utf8char_t));
+        return cbDest;
     }
 
-    // Encode a UTF16-LE sequence of cch words into a UTF-8 sequence returning the number of bytes needed.
-    // Since a UTF16 encoding can take up to 3 bytes buffer must refer to a buffer at least 3 times larger
-    // than cch.
-    // Returns the number of bytes copied into the buffer.
-    __range(0, cch * 3)
-    size_t EncodeInto(__out_ecount(cch * 3) LPUTF8 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 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);
-
-    // Like EncodeInto but ensures that we do not write anywhere other than between buffer and bufferEnd.
-    __range(0, cch * 3)
-    size_t EncodeTrueUtf8IntoBoundsChecked(__out_ecount(cch * 3 + 1) utf8char_t *buffer, __in_ecount(cch) const char16 *source, charcount_t cch, const void * bufferEnd);
+    enum class Utf8EncodingKind
+    {
+        Cesu8,
+        TrueUtf8
+    };
 
-    // Determine the number of bytes that a UTF-8 sequence representing a UTF16-LE sequence of cch words
+    // Encode a UTF16-LE sequence of cchSource words (char16) into a UTF-8 sequence returning the number of bytes needed.
+    // Since a UTF16 encoding can encode to at most 3 bytes (utf8char_t) per char16, cbDest (dest buffer size) can be
+    // at most 3 * cchSource.
+    // Returns the number of bytes copied into the dest buffer.
+    template <Utf8EncodingKind encoding>
+    __range(0, cchSource * 3)
+    size_t EncodeInto(
+        _Out_writes_(cbDest) utf8char_t *dest,
+        __range(0, cchSource * 3) size_t cbDest,
+        _In_reads_(cchSource) const char16 *source,
+        __range(0, INT_MAX) charcount_t cchSource);
+
+    // Like EncodeInto but ensures that dest[return value] == 0.
+    template <Utf8EncodingKind encoding>
+    __range(0, cchSource * 3)
+    size_t EncodeIntoAndNullTerminate(
+        _Out_writes_z_(cbDest) utf8char_t *dest,
+        __range(1, cchSource * 3 + 1) size_t cbDest, // must be at least large enough to write null terminator
+        _In_reads_(cchSource) const char16 *source,
+        __range(0, INT_MAX) charcount_t cchSource);
+
+    // Determine the number of UTF-8 bytes needed to represent a UTF16-LE sequence of cch * words (char16)
     __range(0, cch * 3)
     size_t CountTrueUtf8(__in_ecount(cch) const char16 *source, charcount_t cch);
 

+ 27 - 7
lib/Common/Codex/Utf8Helper.h

@@ -17,7 +17,13 @@ namespace utf8
     ///     As long as that function exists, it _must_ be updated alongside any updates here
     ///
     template <typename AllocatorFunction>
-    HRESULT WideStringToNarrow(_In_ AllocatorFunction allocator, _In_ LPCWSTR sourceString, size_t sourceCount, _Out_ LPSTR* destStringPtr, _Out_ size_t* destCount, size_t* allocateCount = nullptr)
+    HRESULT WideStringToNarrow(
+        _In_ AllocatorFunction allocator,
+        _In_ LPCWSTR sourceString,
+        size_t sourceCount,
+        _Out_ LPSTR* destStringPtr,
+        _Out_ size_t* destCount,
+        size_t* allocateCount = nullptr)
     {
         size_t cchSourceString = sourceCount;
 
@@ -26,7 +32,8 @@ namespace utf8
             return E_OUTOFMEMORY;
         }
 
-        size_t cbDestString = (cchSourceString + 1) * 3;
+        // Multiply by 3 for max size of encoded character, plus 1 for the null terminator (don't need 3 bytes for the null terminator)
+        size_t cbDestString = (cchSourceString * 3) + 1;
 
         // Check for overflow- cbDestString should be >= cchSourceString
         if (cbDestString < cchSourceString)
@@ -40,12 +47,16 @@ namespace utf8
             return E_OUTOFMEMORY;
         }
 
-        size_t cbEncoded = utf8::EncodeTrueUtf8IntoAndNullTerminate(destString, sourceString, (charcount_t) cchSourceString);
+        size_t cbEncoded = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>(destString, cbDestString, sourceString, static_cast<charcount_t>(cchSourceString));
         Assert(cbEncoded <= cbDestString);
         static_assert(sizeof(utf8char_t) == sizeof(char), "Needs to be valid for cast");
         *destStringPtr = (char*)destString;
         *destCount = cbEncoded;
-        if (allocateCount != nullptr) *allocateCount = cbEncoded;
+        if (allocateCount != nullptr)
+        {
+            *allocateCount = cbEncoded;
+        }
+
         return S_OK;
     }
 
@@ -54,7 +65,12 @@ namespace utf8
     /// The caller is responsible for providing the buffer
     /// The returned string is null terminated.
     ///
-    inline HRESULT WideStringToNarrowNoAlloc(_In_ LPCWSTR sourceString, size_t sourceCount, __out_ecount(destCount) LPSTR destString, size_t destCount, size_t* writtenCount = nullptr)
+    inline HRESULT WideStringToNarrowNoAlloc(
+        _In_ LPCWSTR sourceString,
+        size_t sourceCount,
+        __out_ecount(destCount) LPSTR destString,
+        size_t destCount,
+        size_t* writtenCount = nullptr)
     {
         size_t cchSourceString = sourceCount;
 
@@ -72,11 +88,15 @@ namespace utf8
         }
         else
         {
-            cbEncoded = utf8::EncodeTrueUtf8IntoBoundsChecked((utf8char_t*)destString, sourceString, (charcount_t)cchSourceString, &destString[destCount]);
+            cbEncoded = utf8::EncodeInto<utf8::Utf8EncodingKind::TrueUtf8>((utf8char_t*)destString, destCount, sourceString, static_cast<charcount_t>(cchSourceString));
             Assert(cbEncoded <= destCount);
         }
 
-        if (writtenCount != nullptr) *writtenCount = cbEncoded;
+        if (writtenCount != nullptr)
+        {
+            *writtenCount = cbEncoded;
+        }
+
         return S_OK;
     }
 

+ 4 - 2
lib/Common/Common.h

@@ -143,9 +143,11 @@ class AutoExpDummyClass
 #pragma warning(push)
 #if defined(PROFILE_RECYCLER_ALLOC) || defined(HEAP_TRACK_ALLOC) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
 #ifdef _MSC_VER
-#include <typeinfo.h>
-#else
+#ifdef _UCRT
 #include <typeinfo>
+#else
+#include <typeinfo.h>
+#endif
 #endif
 #endif
 #pragma warning(pop)

+ 4 - 0
lib/Common/Common/CommonCommonPch.h

@@ -27,8 +27,12 @@
 #ifdef _MSC_VER
 #pragma warning(push)
 #if defined(PROFILE_RECYCLER_ALLOC) || defined(HEAP_TRACK_ALLOC) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
+#ifdef _UCRT
+#include <typeinfo>
+#else
 #include <typeinfo.h>
 #endif
+#endif
 #pragma warning(pop)
 #endif
 

+ 12 - 8
lib/Common/ConfigFlagsList.h

@@ -110,6 +110,7 @@ PHASE(All)
         PHASE(GlobOpt)
             PHASE(PathDepBranchFolding)
             PHASE(OptimizeTryCatch)
+            PHASE_DEFAULT_ON(CaptureByteCodeRegUse)
             PHASE(Backward)
                 PHASE(TrackIntUsage)
                 PHASE(TrackNegativeZero)
@@ -278,6 +279,7 @@ PHASE(All)
             PHASE(FastIndirectEval)
         PHASE(IdleDecommit)
         PHASE(IdleCollect)
+        PHASE(Marshal)
         PHASE(MemoryAllocation)
 #ifdef RECYCLER_PAGE_HEAP
             PHASE(PageHeap)
@@ -533,6 +535,7 @@ PHASE(All)
 #define DEFAULT_CONFIG_PoisonIntArrayStore (true)
 #define DEFAULT_CONFIG_PoisonFloatArrayStore (true)
 #define DEFAULT_CONFIG_PoisonTypedArrayStore (true)
+#define DEFAULT_CONFIG_PoisonStringStore (true)
 #define DEFAULT_CONFIG_PoisonObjectsForStores (true)
 
 #ifdef RECYCLER_PAGE_HEAP
@@ -1121,7 +1124,7 @@ FLAGPR           (Boolean, ES6, ES6IsConcatSpreadable  , "Enable ES6 isConcatSpr
 FLAGPR           (Boolean, ES6, ES6Math                , "Enable ES6 Math extensions"                               , DEFAULT_CONFIG_ES6Math)
 
 #ifndef COMPILE_DISABLE_ESDynamicImport
-    #define COMPILE_DISABLE_ESDynamicImport 0
+#define COMPILE_DISABLE_ESDynamicImport 0
 #endif
 FLAGPR_REGOVR_EXP(Boolean, ES6, ESDynamicImport        , "Enable dynamic import"                                    , DEFAULT_CONFIG_ESDynamicImport)
 
@@ -1316,13 +1319,14 @@ FLAGPR(Boolean, MitigateSpectre, PoisonIntArrayLoad, "Poison loads from Int arra
 FLAGPR(Boolean, MitigateSpectre, PoisonFloatArrayLoad, "Poison loads from Float arrays", DEFAULT_CONFIG_PoisonFloatArrayLoad)
 FLAGPR(Boolean, MitigateSpectre, PoisonTypedArrayLoad, "Poison loads from TypedArrays", DEFAULT_CONFIG_PoisonTypedArrayLoad)
 FLAGPR(Boolean, MitigateSpectre, PoisonStringLoad, "Poison indexed loads from strings", DEFAULT_CONFIG_PoisonStringLoad)
-FLAGPR(Boolean, MitigateSpectre, PoisonObjectsForLoads, "Poison objects after type checks", DEFAULT_CONFIG_PoisonObjectsForLoads)
-
-FLAGPR(Boolean, MitigateSpectre, PoisonVarArrayStore, "Poison stores from Var arrays", DEFAULT_CONFIG_PoisonVarArrayStore)
-FLAGPR(Boolean, MitigateSpectre, PoisonIntArrayStore, "Poison stores from Int arrays", DEFAULT_CONFIG_PoisonIntArrayStore)
-FLAGPR(Boolean, MitigateSpectre, PoisonFloatArrayStore, "Poison stores from Float arrays", DEFAULT_CONFIG_PoisonFloatArrayStore)
-FLAGPR(Boolean, MitigateSpectre, PoisonTypedArrayStore, "Poison stores from TypedArrays", DEFAULT_CONFIG_PoisonTypedArrayStore)
-FLAGPR(Boolean, MitigateSpectre, PoisonObjectsForStores, "Poison objects after type checks", DEFAULT_CONFIG_PoisonObjectsForStores)
+FLAGPR(Boolean, MitigateSpectre, PoisonObjectsForLoads, "Poison objects after type checks for loads", DEFAULT_CONFIG_PoisonObjectsForLoads)
+
+FLAGPR(Boolean, MitigateSpectre, PoisonVarArrayStore, "Poison stores to Var arrays", DEFAULT_CONFIG_PoisonVarArrayStore)
+FLAGPR(Boolean, MitigateSpectre, PoisonIntArrayStore, "Poison stores to Int arrays", DEFAULT_CONFIG_PoisonIntArrayStore)
+FLAGPR(Boolean, MitigateSpectre, PoisonFloatArrayStore, "Poison stores to Float arrays", DEFAULT_CONFIG_PoisonFloatArrayStore)
+FLAGPR(Boolean, MitigateSpectre, PoisonTypedArrayStore, "Poison stores to TypedArrays", DEFAULT_CONFIG_PoisonTypedArrayStore)
+FLAGPR(Boolean, MitigateSpectre, PoisonStringStore, "Poison indexed stores to strings", DEFAULT_CONFIG_PoisonStringStore)
+FLAGPR(Boolean, MitigateSpectre, PoisonObjectsForStores, "Poison objects after type checks for stores", DEFAULT_CONFIG_PoisonObjectsForStores)
 
 FLAGNR(Number,  MinInterpretCount     , "Minimum number of times a function must be interpreted", 0)
 FLAGNR(Number,  MinSimpleJitRunCount  , "Minimum number of times a function must be run in simple jit", 0)

+ 1 - 1
lib/Common/Core/Chakra.Common.Core.vcxproj

@@ -42,7 +42,7 @@
     <ClCompile Include="$(MSBuildThisFileDirectory)GlobalSecurityPolicy.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)Output.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)PerfCounter.cpp" />
-    <ClCompile Include="CRC.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)CRC.cpp" />
     <None Include="PerfCounterImpl.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)PerfCounterSet.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)ProfileInstrument.cpp" />

+ 4 - 0
lib/Common/Core/CommonCorePch.h

@@ -9,7 +9,11 @@
 #ifdef _MSC_VER
 #pragma warning(push)
 #if defined(PROFILE_RECYCLER_ALLOC) || defined(HEAP_TRACK_ALLOC) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
+#ifdef _UCRT
+#include <typeinfo>
+#else
 #include <typeinfo.h>
 #endif
+#endif
 #pragma warning(pop)
 #endif

+ 2 - 1
lib/Common/Core/JitHelperUtils.h

@@ -12,7 +12,8 @@ enum HelperMethodAttribute : BYTE
     AttrNone = 0x00,
     AttrCanThrow = 0x01,     // Can throw non-OOM / non-SO exceptions. Under debugger / Fast F12, these helpers are wrapped with try-catch wrapper.
     AttrInVariant = 0x02,     // The method is "const" method that can be hoisted.
-    AttrCanNotBeReentrant = 0x4
+    AttrCanNotBeReentrant = 0x4,
+    AttrTempObjectProducing = 0x8
 };
 
 #if defined(DBG) && ENABLE_NATIVE_CODEGEN

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

@@ -1161,7 +1161,7 @@ void
 BVSparse<TAllocator>::Dump() const
 {
     bool hasBits = false;
-    Output::Print(_u("[  "));
+    Output::Print(_u("[ "));
     for(BVSparseNode * node = this->head; node != 0 ; node = node->next)
     {
         hasBits = node->data.Dump(node->startIndex, hasBits);

+ 6 - 10
lib/Common/Memory/AllocationPolicyManager.h

@@ -20,9 +20,9 @@ public:
         MemoryFailure = 2,
         MemoryMax = 2,
     };
-typedef bool (__stdcall * PageAllocatorMemoryAllocationCallback)(__in LPVOID context,
-    __in AllocationPolicyManager::MemoryAllocateEvent allocationEvent,
-    __in size_t allocationSize);
+    typedef bool (__stdcall * PageAllocatorMemoryAllocationCallback)(__in LPVOID context,
+        __in AllocationPolicyManager::MemoryAllocateEvent allocationEvent,
+        __in size_t allocationSize);
 
 
 private:
@@ -50,9 +50,7 @@ public:
 
     ~AllocationPolicyManager()
     {
-        // TODO: https://github.com/Microsoft/ChakraCore/issues/5191
-        //       enable the assert when the offending code is fixed.
-        // Assert(currentMemory == 0);
+        Assert(currentMemory == 0);
     }
 
     size_t GetUsage()
@@ -134,7 +132,7 @@ private:
         if (newCurrentMemory < currentMemory ||
             newCurrentMemory > memoryLimit ||
             (memoryAllocationCallback != NULL && !memoryAllocationCallback(context, MemoryAllocateEvent::MemoryAllocate, byteCount)))
-        {            
+        {
             // oopjit number allocator allocated pages, we can't stop it from allocating so just increase the usage number
             if (externalAlloc)
             {
@@ -158,9 +156,7 @@ private:
 
     inline void ReportFreeImpl(MemoryAllocateEvent allocationEvent, size_t byteCount)
     {
-        // TODO: https://github.com/Microsoft/ChakraCore/issues/5191
-        //       enable the assert when the offending code is fixed.
-        // Assert(currentMemory >= byteCount);
+        Assert(currentMemory >= byteCount);
         byteCount = min(byteCount, currentMemory);
 
         currentMemory = currentMemory - byteCount;

+ 5 - 0
lib/Common/Memory/AutoPtr.h

@@ -170,6 +170,11 @@ public:
         return &ptr;
     }
 
+    void SetPtr(T* ptr)
+    {
+        this->ptr = ptr;
+    }
+
 private:
     static IUnknown* ComPtrAssign(IUnknown** pp, IUnknown* lp)
     {

+ 52 - 51
lib/Common/Memory/CollectionFlags.h

@@ -7,84 +7,85 @@
 
 namespace Memory
 {
-
     enum CollectionFlags
     {
-        CollectHeuristic_AllocSize = 0x00000001,
-        CollectHeuristic_Time = 0x00000002,
+        CollectHeuristic_AllocSize          = 0x00000001,
+        CollectHeuristic_Time               = 0x00000002,
         CollectHeuristic_TimeIfScriptActive = 0x00000004,
-        CollectHeuristic_TimeIfInScript = 0x00000008,
-        CollectHeuristic_Never = 0x00000080,
-        CollectHeuristic_Mask = 0x000000FF,
+        CollectHeuristic_TimeIfInScript     = 0x00000008,
+        CollectHeuristic_Never              = 0x00000080,
+        CollectHeuristic_Mask               = 0x000000FF,
 
-        CollectOverride_FinishConcurrent = 0x00001000,
+        CollectOverride_FinishConcurrent    = 0x00001000,
         CollectOverride_ExhaustiveCandidate = 0x00002000,
-        CollectOverride_ForceInThread = 0x00004000,
-        CollectOverride_AllowDispose = 0x00008000,
-        CollectOverride_AllowReentrant = 0x00010000,
-        CollectOverride_ForceFinish = 0x00020000,
-        CollectOverride_Explicit = 0x00040000,
-        CollectOverride_DisableIdleFinish = 0x00080000,
-        CollectOverride_BackgroundFinishMark = 0x00100000,
+        CollectOverride_ForceInThread       = 0x00004000,
+        CollectOverride_AllowDispose        = 0x00008000,
+        CollectOverride_AllowReentrant      = 0x00010000,
+        CollectOverride_ForceFinish         = 0x00020000,
+        CollectOverride_Explicit            = 0x00040000,
+        CollectOverride_DisableIdleFinish   = 0x00080000,
+        CollectOverride_BackgroundFinishMark= 0x00100000,
         CollectOverride_FinishConcurrentTimeout = 0x00200000,
         CollectOverride_NoExhaustiveCollect = 0x00400000,
-        CollectOverride_SkipStack = 0x01000000,
+        CollectOverride_SkipStack           = 0x01000000,
         CollectOverride_CheckScriptContextClose = 0x02000000,
-        CollectMode_Partial = 0x08000000,
-        CollectMode_Concurrent = 0x10000000,
-        CollectMode_Exhaustive = 0x20000000,
-        CollectMode_DecommitNow = 0x40000000,
-        CollectMode_CacheCleanup = 0x80000000,
+        CollectMode_Partial                 = 0x08000000,
+        CollectMode_Concurrent              = 0x10000000,
+        CollectMode_Exhaustive              = 0x20000000,
+        CollectMode_DecommitNow             = 0x40000000,
+        CollectMode_CacheCleanup            = 0x80000000,
 
-        CollectNowForceInThread = CollectOverride_ForceInThread,
+        CollectNowForceInThread         = CollectOverride_ForceInThread,
         CollectNowForceInThreadExternal = CollectOverride_ForceInThread | CollectOverride_AllowDispose,
         CollectNowForceInThreadExternalNoStack = CollectOverride_ForceInThread | CollectOverride_AllowDispose | CollectOverride_SkipStack,
-        CollectNowDefault = CollectOverride_FinishConcurrent,
-        CollectNowDefaultLSCleanup = CollectOverride_FinishConcurrent | CollectOverride_AllowDispose,
-        CollectNowDecommitNowExplicit = CollectNowDefault | CollectMode_DecommitNow | CollectMode_CacheCleanup | CollectOverride_Explicit | CollectOverride_AllowDispose,
-        CollectNowConcurrent = CollectOverride_FinishConcurrent | CollectMode_Concurrent,
-        CollectNowExhaustive = CollectOverride_FinishConcurrent | CollectMode_Exhaustive | CollectOverride_AllowDispose,
-        CollectNowPartial = CollectOverride_FinishConcurrent | CollectMode_Partial,
-        CollectNowConcurrentPartial = CollectMode_Concurrent | CollectNowPartial,
+        CollectNowForceInThreadExternalExhaustive = CollectOverride_ForceInThread | CollectOverride_AllowDispose | CollectMode_Exhaustive,
+        CollectNowForceInThreadExternalExhaustiveNoStack = CollectOverride_ForceInThread | CollectOverride_AllowDispose | CollectMode_Exhaustive | CollectOverride_SkipStack,
+        CollectNowDefault               = CollectOverride_FinishConcurrent,
+        CollectNowDefaultLSCleanup      = CollectOverride_FinishConcurrent | CollectOverride_AllowDispose,
+        CollectNowDecommitNowExplicit   = CollectNowDefault | CollectMode_DecommitNow | CollectMode_CacheCleanup | CollectOverride_Explicit | CollectOverride_AllowDispose,
+        CollectNowConcurrent            = CollectOverride_FinishConcurrent | CollectMode_Concurrent,
+        CollectNowExhaustive            = CollectOverride_FinishConcurrent | CollectMode_Exhaustive | CollectOverride_AllowDispose,
+        CollectNowPartial               = CollectOverride_FinishConcurrent | CollectMode_Partial,
+        CollectNowConcurrentPartial     = CollectMode_Concurrent | CollectNowPartial,
 
-        CollectOnAllocation = CollectHeuristic_AllocSize | CollectHeuristic_Time | CollectMode_Concurrent | CollectMode_Partial | CollectOverride_FinishConcurrent | CollectOverride_AllowReentrant | CollectOverride_FinishConcurrentTimeout,
-        CollectOnTypedArrayAllocation = CollectHeuristic_AllocSize | CollectHeuristic_Time | CollectMode_Concurrent | CollectMode_Partial | CollectOverride_FinishConcurrent | CollectOverride_AllowReentrant | CollectOverride_FinishConcurrentTimeout | CollectOverride_AllowDispose,
-        CollectOnScriptIdle = CollectOverride_CheckScriptContextClose | CollectOverride_FinishConcurrent | CollectMode_Concurrent | CollectMode_CacheCleanup | CollectOverride_SkipStack,
-        CollectOnScriptExit = CollectOverride_CheckScriptContextClose | CollectHeuristic_AllocSize | CollectOverride_FinishConcurrent | CollectMode_Concurrent | CollectMode_CacheCleanup,
-        CollectExhaustiveCandidate = CollectHeuristic_Never | CollectOverride_ExhaustiveCandidate,
-        CollectOnScriptCloseNonPrimary = CollectNowConcurrent | CollectOverride_ExhaustiveCandidate | CollectOverride_AllowDispose,
+        CollectOnAllocation             = CollectHeuristic_AllocSize | CollectHeuristic_Time | CollectMode_Concurrent | CollectMode_Partial | CollectOverride_FinishConcurrent | CollectOverride_AllowReentrant | CollectOverride_FinishConcurrentTimeout,
+        CollectOnTypedArrayAllocation   = CollectHeuristic_AllocSize | CollectHeuristic_Time | CollectMode_Concurrent | CollectMode_Partial | CollectOverride_FinishConcurrent | CollectOverride_AllowReentrant | CollectOverride_FinishConcurrentTimeout | CollectOverride_AllowDispose,
+        CollectOnScriptIdle             = CollectOverride_CheckScriptContextClose | CollectOverride_FinishConcurrent | CollectMode_Concurrent | CollectMode_CacheCleanup | CollectOverride_SkipStack,
+        CollectOnScriptExit             = CollectOverride_CheckScriptContextClose | CollectHeuristic_AllocSize | CollectOverride_FinishConcurrent | CollectMode_Concurrent | CollectMode_CacheCleanup,
+        CollectExhaustiveCandidate      = CollectHeuristic_Never | CollectOverride_ExhaustiveCandidate,
+        CollectOnScriptCloseNonPrimary  = CollectNowConcurrent | CollectOverride_ExhaustiveCandidate | CollectOverride_AllowDispose,
         CollectOnRecoverFromOutOfMemory = CollectOverride_ForceInThread | CollectMode_DecommitNow,
-        CollectOnSuspendCleanup = CollectNowConcurrent | CollectMode_Exhaustive | CollectMode_DecommitNow | CollectOverride_DisableIdleFinish,
+        CollectOnSuspendCleanup         = CollectNowConcurrent | CollectMode_Exhaustive | CollectMode_DecommitNow | CollectOverride_DisableIdleFinish,
 
-        FinishConcurrentOnIdle = CollectMode_Concurrent | CollectOverride_DisableIdleFinish,
-        FinishConcurrentOnIdleAtRoot = CollectMode_Concurrent | CollectOverride_DisableIdleFinish | CollectOverride_SkipStack,
-        FinishConcurrentDefault = CollectMode_Concurrent | CollectOverride_DisableIdleFinish | CollectOverride_BackgroundFinishMark,
-        FinishConcurrentOnExitScript = FinishConcurrentDefault,
-        FinishConcurrentOnEnterScript = FinishConcurrentDefault,
-        FinishConcurrentOnAllocation = FinishConcurrentDefault,
-        FinishDispose = CollectOverride_AllowDispose,
-        FinishDisposeTimed = CollectOverride_AllowDispose | CollectHeuristic_TimeIfScriptActive,
-        ForceFinishCollection = CollectOverride_ForceFinish | CollectOverride_ForceInThread,
+        FinishConcurrentOnIdle          = CollectMode_Concurrent | CollectOverride_DisableIdleFinish,
+        FinishConcurrentOnIdleAtRoot    = CollectMode_Concurrent | CollectOverride_DisableIdleFinish | CollectOverride_SkipStack,
+        FinishConcurrentDefault         = CollectMode_Concurrent | CollectOverride_DisableIdleFinish | CollectOverride_BackgroundFinishMark,
+        FinishConcurrentOnExitScript    = FinishConcurrentDefault,
+        FinishConcurrentOnEnterScript   = FinishConcurrentDefault,
+        FinishConcurrentOnAllocation    = FinishConcurrentDefault,
+        FinishDispose                   = CollectOverride_AllowDispose,
+        FinishDisposeTimed              = CollectOverride_AllowDispose | CollectHeuristic_TimeIfScriptActive,
+        ForceFinishCollection           = CollectOverride_ForceFinish | CollectOverride_ForceInThread,
 
 #ifdef RECYCLER_STRESS
-        CollectStress = CollectNowForceInThread,
+        CollectStress                   = CollectNowForceInThread,
 #if ENABLE_PARTIAL_GC
-        CollectPartialStress = CollectMode_Partial,
+        CollectPartialStress            = CollectMode_Partial,
 #endif
 #if ENABLE_CONCURRENT_GC
-        CollectBackgroundStress = CollectNowDefault,
-        CollectConcurrentStress = CollectNowConcurrent,
+        CollectBackgroundStress         = CollectNowDefault,
+        CollectConcurrentStress         = CollectNowConcurrent,
 #if ENABLE_PARTIAL_GC
-        CollectConcurrentPartialStress = CollectConcurrentStress | CollectPartialStress,
+        CollectConcurrentPartialStress  = CollectConcurrentStress | CollectPartialStress,
 #endif
 #endif
 #endif
 
 #if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
-        CollectNowFinalGC = CollectNowExhaustive | CollectOverride_ForceInThread | CollectOverride_SkipStack | CollectOverride_Explicit | CollectOverride_AllowDispose,
+        CollectNowFinalGC                   = CollectNowExhaustive | CollectOverride_ForceInThread | CollectOverride_SkipStack | CollectOverride_Explicit | CollectOverride_AllowDispose,
 #endif
 #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
-        CollectNowExhaustiveSkipStack = CollectNowExhaustive | CollectOverride_SkipStack, // Used by test
+        CollectNowExhaustiveSkipStack   = CollectNowExhaustive | CollectOverride_SkipStack, // Used by test
 #endif
     };
 

+ 4 - 0
lib/Common/Memory/CommonMemoryPch.h

@@ -38,8 +38,12 @@ typedef _Return_type_success_(return >= 0) LONG NTSTATUS;
 #ifdef _MSC_VER
 #pragma warning(push)
 #if defined(PROFILE_RECYCLER_ALLOC) || defined(HEAP_TRACK_ALLOC) || defined(ENABLE_DEBUG_CONFIG_OPTIONS)
+#ifdef _UCRT
+#include <typeinfo>
+#else
 #include <typeinfo.h>
 #endif
+#endif
 #pragma warning(pop)
 #endif
 

+ 5 - 1
lib/Common/Memory/CustomHeap.h

@@ -434,6 +434,10 @@ public:
     void DumpStats();
 #endif
 
+    bool IsPreReservedSegment(void * segment)
+    {
+        return this->codePageAllocators->IsPreReservedSegment(segment);
+    }
 private:
     /**
      * Inline methods
@@ -579,4 +583,4 @@ BucketId GetBucketForSize(DECLSPEC_GUARD_OVERFLOW size_t bytes);
 void FillDebugBreak(_Out_writes_bytes_all_(byteCount) BYTE* buffer, _In_ size_t byteCount);
 } // namespace CustomHeap
 } // namespace Memory
-#endif
+#endif

+ 0 - 6
lib/Common/Memory/PageAllocator.cpp

@@ -343,8 +343,6 @@ PageSegmentBase<T>::AllocPages(uint pageCount)
     }
     Assert(!IsFull());
 
-#pragma prefast(push)
-#pragma prefast(suppress:__WARNING_LOOP_INDEX_UNDERFLOW, "Prefast about overflow when multiplying index.")
     uint index = this->GetNextBitInFreePagesBitVector(0);
     while (index != -1)
     {
@@ -393,12 +391,9 @@ PageSegmentBase<T>::AllocPages(uint pageCount)
         }
         index = this->GetNextBitInFreePagesBitVector(index + 1);
     }
-#pragma prefast(pop)
     return nullptr;
 }
 
-#pragma prefast(push)
-#pragma prefast(suppress:__WARNING_LOOP_INDEX_UNDERFLOW, "Prefast about overflow when multiplying index.")
 template<typename TVirtualAlloc>
 template<typename T, bool notPageAligned>
 char *
@@ -473,7 +468,6 @@ PageSegmentBase<TVirtualAlloc>::AllocDecommitPages(uint pageCount, T freePages,
 
     return nullptr;
 }
-#pragma prefast(pop)
 
 template<typename T>
 void

+ 10 - 6
lib/Common/Memory/Recycler.cpp

@@ -389,7 +389,7 @@ Recycler::LogMemProtectHeapSize(bool fromGC)
 #ifdef ENABLE_JS_ETW
     if (IS_JS_ETW(EventEnabledMEMPROTECT_GC_HEAP_SIZE()))
     {
-       
+
         size_t usedBytes = autoHeap.GetUsedBytes();
         size_t reservedBytes = autoHeap.GetReservedBytes();
         size_t committedBytes = autoHeap.GetCommittedBytes();
@@ -1076,7 +1076,7 @@ Recycler::LeaveIdleDecommit()
 #ifdef IDLE_DECOMMIT_ENABLED
     bool allowTimer = (this->concurrentIdleDecommitEvent != nullptr);
     IdleDecommitSignal idleDecommitSignal = autoHeap.LeaveIdleDecommit(allowTimer);
-    
+
     if (idleDecommitSignal != IdleDecommitSignal_None)
     {
         Assert(allowTimer);
@@ -1689,6 +1689,8 @@ Recycler::ScanStack()
     }
 #endif
 
+    collectionWrapper->OnScanStackCallback((void**)stackTop, stackScanned, this->savedThreadContext.GetRegisters(), sizeof(void*) * SavedRegisterState::NumRegistersToSave);
+
     bool doSpecialMark = collectionWrapper->DoSpecialMarkOnScanStack();
 
     BEGIN_DUMP_OBJECT(this, _u("Registers"));
@@ -3553,6 +3555,8 @@ template BOOL Recycler::CollectNow<CollectNowConcurrentPartial>();
 template BOOL Recycler::CollectNow<CollectNowForceInThread>();
 template BOOL Recycler::CollectNow<CollectNowForceInThreadExternal>();
 template BOOL Recycler::CollectNow<CollectNowForceInThreadExternalNoStack>();
+template BOOL Recycler::CollectNow<CollectNowForceInThreadExternalExhaustive>();
+template BOOL Recycler::CollectNow<CollectNowForceInThreadExternalExhaustiveNoStack>();
 template BOOL Recycler::CollectNow<CollectOnRecoverFromOutOfMemory>();
 template BOOL Recycler::CollectNow<CollectNowDefault>();
 template BOOL Recycler::CollectNow<CollectOnSuspendCleanup>();
@@ -6138,7 +6142,7 @@ Recycler::DoBackgroundWork(bool forceForeground)
     }
     else if (this->IsConcurrentMarkState())
     {
-        RECYCLER_PROFILE_EXEC_BACKGROUND_BEGIN(this, this->collectionState == CollectionStateConcurrentFinishMark ?
+        RECYCLER_PROFILE_EXEC_BACKGROUND_BEGIN(this, this->collectionState == CollectionStateConcurrentFinishMark?
             Js::BackgroundFinishMarkPhase : Js::ConcurrentMarkPhase);
         GCETW_INTERNAL(GC_START, (this, BackgroundMarkETWEventGCActivationKind(this->collectionState)));
         GCETW_INTERNAL(GC_START2, (this, BackgroundMarkETWEventGCActivationKind(this->collectionState), this->collectionStartReason, this->collectionStartFlags));
@@ -6179,7 +6183,7 @@ Recycler::DoBackgroundWork(bool forceForeground)
         };
         GCETW_INTERNAL(GC_STOP, (this, BackgroundMarkETWEventGCActivationKind(this->collectionState)));
         GCETW_INTERNAL(GC_STOP2, (this, BackgroundMarkETWEventGCActivationKind(this->collectionState), this->collectionStartReason, this->collectionStartFlags));
-        RECYCLER_PROFILE_EXEC_BACKGROUND_END(this, this->collectionState == CollectionStateConcurrentFinishMark ?
+        RECYCLER_PROFILE_EXEC_BACKGROUND_END(this, this->collectionState == CollectionStateConcurrentFinishMark?
             Js::BackgroundFinishMarkPhase : Js::ConcurrentMarkPhase);
 
         this->SetCollectionState(CollectionStateRescanWait);
@@ -6474,7 +6478,7 @@ Recycler::DoTwoPassConcurrentSweepPreCheck()
     if (CONFIG_FLAG_RELEASE(EnableConcurrentSweepAlloc))
     {
         // We will do two pass sweep only when BOTH of the following conditions are met:
-        //      1. GC was triggered while we are in script, as this is the only case when we will make use of the blocks in the 
+        //      1. GC was triggered while we are in script, as this is the only case when we will make use of the blocks in the
         //         SLIST during concurrent sweep.
         //      2. We are not in a Partial GC.
         //      3. At-least one heap bucket exceeds the RecyclerHeuristic::AllocDuringConcurrentSweepHeapBlockThreshold.
@@ -7745,7 +7749,7 @@ void Recycler::VerifyPageHeapFillAfterAlloc(char* memBlock, size_t size, ObjectI
         if (heapBlock->IsLargeHeapBlock())
         {
             LargeHeapBlock* largeHeapBlock = (LargeHeapBlock*)heapBlock;
-            if (largeHeapBlock->InPageHeapMode() 
+            if (largeHeapBlock->InPageHeapMode()
 #ifdef RECYCLER_NO_PAGE_REUSE
                 && !largeHeapBlock->GetPageAllocator(largeHeapBlock->heapInfo)->IsPageReuseDisabled()
 #endif

+ 2 - 0
lib/Common/Memory/Recycler.h

@@ -324,6 +324,7 @@ public:
     virtual BOOL ExecuteRecyclerCollectionFunction(Recycler * recycler, CollectionFunction function, CollectionFlags flags) = 0;
     virtual uint GetRandomNumber() = 0;
     virtual bool DoSpecialMarkOnScanStack() = 0;
+    virtual void OnScanStackCallback(void ** stackTop, size_t byteCount, void ** registers, size_t registersByteCount) = 0;
     virtual void PostSweepRedeferralCallBack() = 0;
 
 #ifdef FAULT_INJECTION
@@ -377,6 +378,7 @@ public:
     virtual BOOL ExecuteRecyclerCollectionFunction(Recycler * recycler, CollectionFunction function, CollectionFlags flags) override;
     virtual uint GetRandomNumber() override { return 0; }
     virtual bool DoSpecialMarkOnScanStack() override { return false; }
+    virtual void OnScanStackCallback(void ** stackTop, size_t byteCount, void ** registers, size_t registersByteCount) override {};
     virtual void PostSweepRedeferralCallBack() override {}
 #ifdef FAULT_INJECTION
     virtual void DisposeScriptContextByFaultInjectionCallBack() override {};

+ 26 - 0
lib/Common/Memory/SectionAllocWrapper.cpp

@@ -625,6 +625,20 @@ FailureCleanup:
     return nullptr;
 }
 
+bool
+SectionAllocWrapper::GetFileInfo(LPVOID address, HANDLE* fileHandle, PVOID* baseAddress)
+{
+    SectionInfo* sectionInfo = sections.GetSection(address);
+    if (!sectionInfo)
+    {
+        return false;
+    }
+
+    *fileHandle = sectionInfo->handle;
+    *baseAddress = sectionInfo->runtimeBaseAddress;
+    return true;
+}
+
 LPVOID
 SectionAllocWrapper::AllocLocal(LPVOID requestAddress, size_t dwSize)
 {
@@ -991,6 +1005,18 @@ PreReservedSectionAllocWrapper::Free(LPVOID lpAddress, size_t dwSize, DWORD dwFr
     return TRUE;
 }
 
+bool
+PreReservedSectionAllocWrapper::GetFileInfo(LPVOID address, HANDLE* fileHandle, PVOID* baseAddress)
+{
+    if (!this->IsPreReservedRegionPresent())
+    {
+        return false;
+    }
+
+    *fileHandle = this->section;
+    *baseAddress = this->preReservedStartAddress;
+    return true;
+}
 
 LPVOID
 PreReservedSectionAllocWrapper::AllocLocal(LPVOID requestAddress, size_t dwSize)

+ 2 - 0
lib/Common/Memory/SectionAllocWrapper.h

@@ -138,6 +138,7 @@ public:
     BOOL    Free(LPVOID lpAddress, size_t dwSize, DWORD dwFreeType);
     LPVOID  AllocLocal(LPVOID lpAddress, DECLSPEC_GUARD_OVERFLOW size_t dwSize);
     BOOL    FreeLocal(LPVOID lpAddress);
+    bool    GetFileInfo(LPVOID address, HANDLE* fileHandle, PVOID* baseAddress);
 
 private:
 
@@ -165,6 +166,7 @@ public:
     BOOL    Free(LPVOID lpAddress, size_t dwSize, DWORD dwFreeType);
     LPVOID  AllocLocal(LPVOID lpAddress, DECLSPEC_GUARD_OVERFLOW size_t dwSize);
     BOOL    FreeLocal(LPVOID lpAddress);
+    bool    GetFileInfo(LPVOID address, HANDLE* fileHandle, PVOID* baseAddress);
 
     bool        IsInRange(void * address);
     static bool IsInRange(void * regionStart, void * address);

+ 3 - 4
lib/Common/Memory/VirtualAllocWrapper.cpp

@@ -17,7 +17,7 @@ LPVOID VirtualAllocWrapper::AllocPages(LPVOID lpAddress, size_t pageCount, DWORD
         return nullptr;
     }
     size_t dwSize = pageCount * AutoSystemInfo::PageSize;
-    
+
     LPVOID address = nullptr;
 
 #if defined(ENABLE_JIT_CLAMP)
@@ -270,7 +270,6 @@ LPVOID PreReservedVirtualAllocWrapper::EnsurePreReservedRegionInternal()
 *   -   Returns an Allocated memory region within this preReserved region with the specified protectFlags.
 *   -   Tracks the committed pages
 */
-
 LPVOID PreReservedVirtualAllocWrapper::AllocPages(LPVOID lpAddress, size_t pageCount,  DWORD allocationType, DWORD protectFlags, bool isCustomHeapAllocation)
 {
     if (pageCount > AutoSystemInfo::MaxPageCount)
@@ -278,7 +277,7 @@ LPVOID PreReservedVirtualAllocWrapper::AllocPages(LPVOID lpAddress, size_t pageC
         return nullptr;
     }
     size_t dwSize = pageCount * AutoSystemInfo::PageSize;
-    
+
     AssertMsg(isCustomHeapAllocation, "PreReservation used for allocations other than CustomHeap?");
 
     Assert(dwSize != 0);
@@ -321,7 +320,7 @@ LPVOID PreReservedVirtualAllocWrapper::AllocPages(LPVOID lpAddress, size_t pageC
             //Check if the region is not already in MEM_COMMIT state.
             MEMORY_BASIC_INFORMATION memBasicInfo;
             size_t bytes = VirtualQuery(addressToReserve, &memBasicInfo, sizeof(memBasicInfo));
-            if (bytes == 0) 
+            if (bytes == 0)
             {
                 MemoryOperationLastError::RecordLastError();
             }

+ 2 - 0
lib/Common/Memory/VirtualAllocWrapper.h

@@ -26,6 +26,7 @@ public:
     BOOL    Free(LPVOID lpAddress, size_t dwSize, DWORD dwFreeType);
     LPVOID  AllocLocal(LPVOID lpAddress, DECLSPEC_GUARD_OVERFLOW size_t dwSize) { return lpAddress; }
     BOOL    FreeLocal(LPVOID lpAddress) { return true; }
+    bool    GetFileInfo(LPVOID address, HANDLE* fileHandle, PVOID* baseAddress) { return true; }
 
     static VirtualAllocWrapper Instance;  // single instance
 private:
@@ -59,6 +60,7 @@ public:
     BOOL        Free(LPVOID lpAddress,  size_t dwSize, DWORD dwFreeType);
     LPVOID  AllocLocal(LPVOID lpAddress, DECLSPEC_GUARD_OVERFLOW size_t dwSize) { return lpAddress; }
     BOOL    FreeLocal(LPVOID lpAddress) { return true; }
+    bool    GetFileInfo(LPVOID address, HANDLE* fileHandle, PVOID* baseAddress) { return true; }
 
     bool        IsInRange(void * address);
     static bool IsInRange(void * regionStart, void * address);

+ 9 - 6
lib/JITClient/JITManager.cpp

@@ -240,18 +240,21 @@ JITManager::ConnectRpcServer(__in HANDLE jitProcessHandle, __in_opt void* server
 
     HRESULT hr = E_FAIL;
 
-
-    hr = CreateBinding(jitProcessHandle, serverSecurityDescriptor, &connectionUuid, &m_rpcBindingHandle);
+    RPC_BINDING_HANDLE bindingHandle;
+    hr = CreateBinding(jitProcessHandle, serverSecurityDescriptor, &connectionUuid, &bindingHandle);
     if (FAILED(hr))
     {
         goto FailureCleanup;
     }
 
-    m_jitConnectionId = connectionUuid;
 
-    hr = ConnectProcess();
+    hr = ConnectProcess(bindingHandle);
     HandleServerCallResult(hr, RemoteCallType::StateUpdate);
 
+    // Only store the binding handle after JIT handshake, so other threads do not prematurely think we are ready to JIT
+    m_rpcBindingHandle = bindingHandle;
+    m_jitConnectionId = connectionUuid;
+
     return hr;
 
 FailureCleanup:
@@ -290,7 +293,7 @@ JITManager::Shutdown()
 }
 
 HRESULT
-JITManager::ConnectProcess()
+JITManager::ConnectProcess(RPC_BINDING_HANDLE rpcBindingHandle)
 {
     Assert(IsOOPJITEnabled());
 
@@ -306,7 +309,7 @@ JITManager::ConnectProcess()
     RpcTryExcept
     {
         hr = ClientConnectProcess(
-            m_rpcBindingHandle,
+            rpcBindingHandle,
 #ifdef USE_RPC_HANDLE_MARSHALLING
             processHandle,
 #endif

+ 1 - 1
lib/JITClient/JITManager.h

@@ -133,7 +133,7 @@ private:
         __in UUID* connectionUuid,
         __out RPC_BINDING_HANDLE* bindingHandle);
 
-    HRESULT ConnectProcess();
+    HRESULT ConnectProcess(RPC_BINDING_HANDLE rpcBindingHandle);
 
     RPC_BINDING_HANDLE m_rpcBindingHandle;
     UUID m_jitConnectionId;

+ 1 - 5
lib/JITServer/JITServer.cpp

@@ -543,11 +543,7 @@ ServerNewInterpreterThunkBlock(
         // Call to set VALID flag for CFG check
         if (CONFIG_FLAG(OOPCFGRegistration))
         {
-            BYTE* callTarget = runtimeAddress;
-#ifdef _M_ARM
-            callTarget = (BYTE*)((uintptr_t)callTarget | 0x1); // Thumb-tag buffer to get actual callable value
-#endif
-            threadContext->SetValidCallTargetForCFG(callTarget);
+            emitBufferManager->SetValidCallTarget(alloc, runtimeAddress, true);
         }
 
         thunkOutput->thunkCount = thunkCount;

+ 1 - 1
lib/Jsrt/Jsrt.cpp

@@ -476,7 +476,7 @@ CHAKRA_API JsPrivateDetachArrayBuffer(_In_ JsValueRef ref, _Out_ void** detached
     return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode
     {
         VALIDATE_JSREF(ref);
-        *detachedState = Js::JavascriptOperators::DetachVarAndGetState(ref);
+        *detachedState = Js::JavascriptOperators::DetachVarAndGetState(ref, false /*queueForDelayFree*/);
         return JsNoError;
     });
 }

+ 1 - 1
lib/Jsrt/JsrtDebugUtils.cpp

@@ -217,7 +217,7 @@ void JsrtDebugUtils::AddPropertyType(Js::DynamicObject * object, Js::IDiagObject
 
         case Js::TypeIds_Enumerator:
         case Js::TypeIds_HostDispatch:
-        case Js::TypeIds_WithScopeObject:
+        case Js::TypeIds_UnscopablesWrapperObject:
         case Js::TypeIds_UndeclBlockVar:
         case Js::TypeIds_EngineInterfaceObject:
         case Js::TypeIds_WinRTDate:

+ 22 - 6
lib/Jsrt/JsrtExternalArrayBuffer.cpp

@@ -12,27 +12,40 @@ namespace Js
     {
     }
 
+    JsrtExternalArrayBuffer::JsrtExternalArrayBuffer(RefCountedBuffer *buffer, uint32 length, JsFinalizeCallback finalizeCallback, void *callbackState, DynamicType *type)
+        : ExternalArrayBuffer(buffer, length, type), finalizeCallback(finalizeCallback), callbackState(callbackState)
+    {
+    }
+
     JsrtExternalArrayBuffer* JsrtExternalArrayBuffer::New(byte *buffer, uint32 length, JsFinalizeCallback finalizeCallback, void *callbackState, DynamicType *type)
     {
         Recycler* recycler = type->GetScriptContext()->GetRecycler();
         return RecyclerNewFinalized(recycler, JsrtExternalArrayBuffer, buffer, length, finalizeCallback, callbackState, type);
     }
 
+    JsrtExternalArrayBuffer* JsrtExternalArrayBuffer::New(RefCountedBuffer *buffer, uint32 length, JsFinalizeCallback finalizeCallback, void *callbackState, DynamicType *type)
+    {
+        Recycler* recycler = type->GetScriptContext()->GetRecycler();
+        return RecyclerNewFinalized(recycler, JsrtExternalArrayBuffer, buffer, length, finalizeCallback, callbackState, type);
+    }
+
     void JsrtExternalArrayBuffer::Finalize(bool isShutdown)
     {
+        ReleaseBufferContent();
+
         if (finalizeCallback != nullptr && !isDetached)
         {
             finalizeCallback(callbackState);
         }
     }
 
-    ArrayBufferDetachedStateBase* JsrtExternalArrayBuffer::CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength)
+    ArrayBufferDetachedStateBase* JsrtExternalArrayBuffer::CreateDetachedState(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength)
     {
         return HeapNew(JsrtExternalArrayBufferDetachedState, buffer, bufferLength, finalizeCallback, callbackState);
     };
 
     JsrtExternalArrayBuffer::JsrtExternalArrayBufferDetachedState::JsrtExternalArrayBufferDetachedState(
-        BYTE* buffer, uint32 bufferLength, JsFinalizeCallback finalizeCallback, void *callbackState)
+        RefCountedBuffer* buffer, uint32 bufferLength, JsFinalizeCallback finalizeCallback, void *callbackState)
         : ExternalArrayBufferDetachedState(buffer, bufferLength), finalizeCallback(finalizeCallback), callbackState(callbackState)
     {}
 
@@ -43,11 +56,14 @@ namespace Js
 
     void JsrtExternalArrayBuffer::JsrtExternalArrayBufferDetachedState::DiscardState()
     {
-        if (finalizeCallback != nullptr)
+        DiscardStateBase([&](byte* data)
         {
-            finalizeCallback(callbackState);
-        }
-        finalizeCallback = nullptr;
+            if (finalizeCallback != nullptr)
+            {
+                finalizeCallback(callbackState);
+            }
+            finalizeCallback = nullptr;
+        });
     }
 
     ArrayBuffer* JsrtExternalArrayBuffer::JsrtExternalArrayBufferDetachedState::Create(JavascriptLibrary* library)

+ 4 - 2
lib/Jsrt/JsrtExternalArrayBuffer.h

@@ -12,10 +12,12 @@ namespace Js {
         DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JsrtExternalArrayBuffer);
 
         JsrtExternalArrayBuffer(byte *buffer, uint32 length, JsFinalizeCallback finalizeCallback, void *callbackState, DynamicType *type);
-        virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
+        JsrtExternalArrayBuffer(RefCountedBuffer *buffer, uint32 length, JsFinalizeCallback finalizeCallback, void *callbackState, DynamicType *type);
+        virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
 
     public:
         static JsrtExternalArrayBuffer* New(byte *buffer, uint32 length, JsFinalizeCallback finalizeCallback, void *callbackState, DynamicType *type);
+        static JsrtExternalArrayBuffer* New(RefCountedBuffer *buffer, uint32 length, JsFinalizeCallback finalizeCallback, void *callbackState, DynamicType *type);
         void Finalize(bool isShutdown) override;
 
     private:
@@ -27,7 +29,7 @@ namespace Js {
             FieldNoBarrier(JsFinalizeCallback) finalizeCallback;
             FieldNoBarrier(void *) callbackState;
         public:
-            JsrtExternalArrayBufferDetachedState(BYTE* buffer, uint32 bufferLength, JsFinalizeCallback finalizeCallback, void *callbackState);
+            JsrtExternalArrayBufferDetachedState(RefCountedBuffer* buffer, uint32 bufferLength, JsFinalizeCallback finalizeCallback, void *callbackState);
             virtual void ClearSelfOnly() override;
             virtual void DiscardState() override;
             virtual ArrayBuffer* Create(JavascriptLibrary* library) override;

+ 8 - 3
lib/Jsrt/JsrtSourceHolder.cpp

@@ -64,7 +64,7 @@ namespace Js
                 *utf8Script = HeapNewArray(utf8char_t, cbUtf8Buffer);
             }
 
-            *utf8Length = utf8::EncodeTrueUtf8IntoAndNullTerminate(*utf8Script, script, static_cast<charcount_t>(length));
+            *utf8Length = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>(*utf8Script, cbUtf8Buffer, script, static_cast<charcount_t>(length));
             *scriptLength = length;
 
             if (utf8AllocLength != nullptr)
@@ -119,6 +119,12 @@ namespace Js
 
     template <typename TLoadCallback, typename TUnloadCallback>
     void JsrtSourceHolder<TLoadCallback, TUnloadCallback>::Finalize(bool isShutdown)
+    {
+        Unload();
+    }
+
+    template <typename TLoadCallback, typename TUnloadCallback>
+    void JsrtSourceHolder<TLoadCallback, TUnloadCallback>::Unload()
     {
         if (scriptUnloadCallback == nullptr)
         {
@@ -202,8 +208,7 @@ namespace Js
                     *utf8Script = HeapNewArray(utf8char_t, cbUtf8Buffer);
                 }
 
-                *utf8Length = utf8::EncodeTrueUtf8IntoAndNullTerminate(*utf8Script,
-                    script, static_cast<charcount_t>(script_length));
+                *utf8Length = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::TrueUtf8>(*utf8Script, cbUtf8Buffer, script, static_cast<charcount_t>(script_length));
                 *scriptLength = script_length;
 
                 if (utf8AllocLength != nullptr)

+ 3 - 1
lib/Jsrt/JsrtSourceHolder.h

@@ -52,7 +52,7 @@ namespace Js
             sourceContext(sourceContext),
 #ifndef NTBUILD
             mappedScriptValue(nullptr),
-            mappedSerializedScriptValue(serializedScriptValue == nullptr ? nullptr : serializedScriptValue->DetachAndGetState()),
+            mappedSerializedScriptValue(serializedScriptValue == nullptr ? nullptr : serializedScriptValue->DetachAndGetState(false /*queueForDelayFree*/)),
 #endif
             mappedSourceByteLength(0),
             mappedSource(nullptr)
@@ -104,6 +104,8 @@ namespace Js
         {
         }
 
+        virtual void Unload() override;
+
         virtual bool Equals(ISourceHolder* other) override
         {
             return this == other ||

+ 11 - 11
lib/Parser/Parse.h

@@ -1130,17 +1130,17 @@ private:
 
     enum FncDeclFlag : ushort
     {
-        fFncNoFlgs                  = 0,
-        fFncDeclaration             = 1 << 0,
-        fFncNoArg                   = 1 << 1,
-        fFncOneArg                  = 1 << 2, //Force exactly one argument.
-        fFncNoName                  = 1 << 3,
-        fFncLambda                  = 1 << 4,
-        fFncMethod                  = 1 << 5,
-        fFncClassMember             = 1 << 6,
-        fFncGenerator               = 1 << 7,
-        fFncAsync                   = 1 << 8,
-        fFncModule                  = 1 << 9,
+        fFncNoFlgs      = 0,
+        fFncDeclaration = 1 << 0,
+        fFncNoArg       = 1 << 1,
+        fFncOneArg      = 1 << 2, //Force exactly one argument.
+        fFncNoName      = 1 << 3,
+        fFncLambda      = 1 << 4,
+        fFncMethod      = 1 << 5,
+        fFncClassMember = 1 << 6,
+        fFncGenerator   = 1 << 7,
+        fFncAsync       = 1 << 8,
+        fFncModule      = 1 << 9,
         fFncClassConstructor        = 1 << 10,
         fFncBaseClassConstructor    = 1 << 11,
     };

+ 34 - 14
lib/Runtime/Base/CrossSite.cpp

@@ -5,7 +5,7 @@
 #include "RuntimeBasePch.h"
 #include "Library/JavascriptProxy.h"
 #include "Library/HostObjectBase.h"
-#include "Types/WithScopeObject.h"
+#include "Types/UnscopablesWrapperObject.h"
 
 #if ENABLE_CROSSSITE_TRACE
 #define TTD_XSITE_LOG(CTX, MSG, VAR) if((CTX)->ShouldPerformRecordOrReplayAction()) \
@@ -33,6 +33,10 @@ namespace Js
         {
             return FALSE;
         }
+        if (PHASE_TRACE1(Js::Phase::MarshalPhase))
+        {
+            Output::Print(_u("NeedMarshalVar: %p (var sc: %p, request sc: %p)\n"), instance, object->GetScriptContext(), requestContext);
+        }
         if (DynamicType::Is(object->GetTypeId()))
         {
             return !UnsafeVarTo<DynamicObject>(object)->IsCrossSiteObject() && !object->IsExternal();
@@ -124,12 +128,12 @@ namespace Js
         for (uint16 i = 0; i < length; i++)
         {
             Var value = display->GetItem(i);
-            if (VarIs<WithScopeObject>(value))
+            if (UnscopablesWrapperObject::Is(value))
             {
                 // Here we are marshalling the wrappedObject and then ReWrapping th object in the new context.
-                RecyclableObject* wrappedObject = VarTo<WithScopeObject>(value)->GetWrappedObject();
+                RecyclableObject* wrappedObject = UnscopablesWrapperObject::FromVar(value)->GetWrappedObject();
                 ScriptContext* wrappedObjectScriptContext = wrappedObject->GetScriptContext();
-                value = JavascriptOperators::ToWithObject(CrossSite::MarshalVar(scriptContext,
+                value = JavascriptOperators::ToUnscopablesWrapperObject(CrossSite::MarshalVar(scriptContext,
                   wrappedObject, wrappedObjectScriptContext), scriptContext);
             }
             else
@@ -165,8 +169,24 @@ namespace Js
             return value;
         }
         Js::RecyclableObject* object =  UnsafeVarTo<RecyclableObject>(value);
+
         if (fRequestWrapper || scriptContext != object->GetScriptContext())
         {
+            if (PHASE_TRACE1(Js::Phase::MarshalPhase))
+            {
+                Output::Print(_u("MarshalVar: %p (var sc: %p, request sc: %p, requestWrapper: %d)\n"), object, object->GetScriptContext(), scriptContext, fRequestWrapper);
+            }
+
+            // Do not allow marshaling if a callable object is being marshalled into a high privileged
+            // script context.
+            if (JavascriptConversion::IsCallable(object))
+            {
+                ScriptContext* objectScriptContext = object->GetScriptContext();
+                if (scriptContext->GetPrivilegeLevel() < objectScriptContext->GetPrivilegeLevel())
+                {
+                    return scriptContext->GetLibrary()->GetUndefined();
+                }
+            }
             return MarshalVarInner(scriptContext, object, fRequestWrapper);
         }
         return value;
@@ -242,17 +262,17 @@ namespace Js
         TypeId typeId = object->GetTypeId();
         AssertMsg(typeId != TypeIds_Enumerator, "enumerator shouldn't be marshalled here");
 
-        // At the moment the mental model for WithScopeObject Marshaling is this:
-        // Are we trying to marshal a WithScopeObject in the Frame Display? - then 1) unwrap in MarshalFrameDisplay,
-        // 2) marshal the wrapped object, 3) Create a new WithScopeObject in the current scriptContext and re-wrap.
-        // We can avoid copying the WithScopeObject because it has no properties and never should.
-        // Thus creating a new WithScopeObject per context in MarshalFrameDisplay should be kosher.
+        // At the moment the mental model for UnscopablesWrapperObject Marshaling is this:
+        // Are we trying to marshal a UnscopablesWrapperObject in the Frame Display? - then 1) unwrap in MarshalFrameDisplay,
+        // 2) marshal the wrapped object, 3) Create a new UnscopablesWrapperObject in the current scriptContext and re-wrap.
+        // We can avoid copying the UnscopablesWrapperObject because it has no properties and never should.
+        // Thus creating a new UnscopablesWrapperObject per context in MarshalFrameDisplay should be kosher.
         // If it is not a FrameDisplay then we should not marshal. We can wrap cross context objects with a
-        // withscopeObject in a different context. When we unwrap for property lookups and the wrapped object
+        // UnscopablesWrapperObject in a different context. When we unwrap for property lookups and the wrapped object
         // is cross context, then we marshal the wrapped object into the current scriptContext, thus avoiding
-        // the need to copy the WithScopeObject itself. Thus We don't have to handle marshaling the WithScopeObject
+        // the need to copy the UnscopablesWrapperObject itself. Thus We don't have to handle marshaling the UnscopablesWrapperObject
         // in non-FrameDisplay cases.
-        AssertMsg(typeId != TypeIds_WithScopeObject, "WithScopeObject shouldn't be marshalled here");
+        AssertMsg(typeId != TypeIds_UnscopablesWrapperObject, "UnscopablesWrapperObject shouldn't be marshalled here");
 
         if (StaticType::Is(typeId))
         {
@@ -452,7 +472,7 @@ namespace Js
         {
             BEGIN_SAFE_REENTRANT_CALL(targetScriptContext->GetThreadContext())
             {
-                 return JavascriptFunction::CallFunction<true>(function, entryPoint, args, true /*useLargeArgCount*/);
+                return JavascriptFunction::CallFunction<true>(function, entryPoint, args, true /*useLargeArgCount*/);
             }
             END_SAFE_REENTRANT_CALL
         }
@@ -533,7 +553,7 @@ namespace Js
 
             BEGIN_SAFE_REENTRANT_CALL(targetScriptContext->GetThreadContext())
             {
-            result = JavascriptFunction::CallFunction<true>(function, entryPoint, args, true /*useLargeArgCount*/);
+                result = JavascriptFunction::CallFunction<true>(function, entryPoint, args, true /*useLargeArgCount*/);
             }
             END_SAFE_REENTRANT_CALL
             ScriptContext* callerScriptContext = callerHostScriptContext->GetScriptContext();

+ 43 - 0
lib/Runtime/Base/DelayLoadLibrary.cpp

@@ -431,6 +431,49 @@ namespace Js
         return FALSE;
 #else
         return SetProcessValidCallTargets(hProcess, VirtualAddress, RegionSize, NumberOfOffsets, OffsetInformation);
+#endif
+    }
+
+    // Note. __declspec(guard(nocf)) causes the CFG check to be removed
+    // inside this function, and is needed only for test binaries (chk and FRETEST)
+#if defined(DELAYLOAD_SET_CFG_TARGET)
+    DECLSPEC_GUARDNOCF
+#endif
+        BOOL DelayLoadWinCoreMemory::SetProcessCallTargetsForMappedView(
+            _In_ HANDLE Process,
+            _In_ PVOID ViewBase,
+            _In_ SIZE_T ViewSize,
+            _In_ ULONG NumberOfOffsets,
+            _Inout_updates_(NumberOfOffsets) PCFG_CALL_TARGET_INFO OffsetInformation,
+            _In_ HANDLE Section,
+            _In_ ULONG64 FileOffset)
+    {
+#if defined(ENABLE_JIT_CLAMP)
+        // Ensure that dynamic code generation is allowed for this thread as
+        // this is required for the call to SetProcessValidCallTargets to
+        // succeed.
+        AutoEnableDynamicCodeGen enableCodeGen;
+#endif
+
+#if defined(DELAYLOAD_SET_CFG_TARGET)
+        if (m_hModule)
+        {
+            if (m_pfnSetProcessValidCallTargetsForMappedView == nullptr)
+            {
+                m_pfnSetProcessValidCallTargetsForMappedView = (PFNCSetProcessValidCallTargetsForMappedView)GetFunction("SetProcessValidCallTargetsForMappedView");
+                if (m_pfnSetProcessValidCallTargetsForMappedView == nullptr)
+                {
+                    return FALSE;
+                }
+            }
+
+            Assert(m_pfnSetProcessValidCallTargetsForMappedView != nullptr);
+            return m_pfnSetProcessValidCallTargetsForMappedView(Process, ViewBase, ViewSize, NumberOfOffsets, OffsetInformation, Section, FileOffset);
+        }
+
+        return FALSE;
+#else
+        return SetProcessValidCallTargetsForMappedView(Process, ViewBase, ViewSize, NumberOfOffsets, OffsetInformation, Section, FileOffset);
 #endif
     }
 #endif

+ 13 - 1
lib/Runtime/Base/DelayLoadLibrary.h

@@ -234,9 +234,11 @@ namespace Js
         typedef FNCSetProcessValidCallTargets* PFNCSetProcessValidCallTargets;
         PFNCSetProcessValidCallTargets m_pfnSetProcessValidCallTargets;
 
+        typedef BOOL(*PFNCSetProcessValidCallTargetsForMappedView)(HANDLE, PVOID, SIZE_T, ULONG, PCFG_CALL_TARGET_INFO, HANDLE, ULONG64);
+        PFNCSetProcessValidCallTargetsForMappedView m_pfnSetProcessValidCallTargetsForMappedView;
     public:
         DelayLoadWinCoreMemory() : DelayLoadLibrary(),
-            m_pfnSetProcessValidCallTargets(nullptr) { }
+            m_pfnSetProcessValidCallTargets(nullptr), m_pfnSetProcessValidCallTargetsForMappedView(nullptr) { }
 
         LPCTSTR GetLibraryName() const { return _u("api-ms-win-core-memory-l1-1-3.dll"); }
 
@@ -247,6 +249,16 @@ namespace Js
             _In_ ULONG NumberOfOffsets,
             _In_reads_(NumberOfOffsets) PCFG_CALL_TARGET_INFO OffsetInformation
             );
+
+        BOOL SetProcessCallTargetsForMappedView(
+            _In_ HANDLE Process,
+            _In_ PVOID ViewBase,
+            _In_ SIZE_T ViewSize,
+            _In_ ULONG NumberOfOffsets,
+            _Inout_updates_(NumberOfOffsets) PCFG_CALL_TARGET_INFO OffsetInformation,
+            _In_ HANDLE Section,
+            _In_ ULONG64 FileOffset
+        );
     };
 #endif
 

+ 6 - 4
lib/Runtime/Base/FunctionBody.cpp

@@ -1639,8 +1639,8 @@ namespace Js
       m_tag21(true),
       m_isMethod(false)
 #if DBG
-        ,m_wasEverAsmjsMode(false)
-        ,scopeObjectSize(0)
+      ,m_wasEverAsmjsMode(false)
+      ,scopeObjectSize(0)
 #endif
     {
         this->functionInfo = RecyclerNew(scriptContext->GetRecycler(), FunctionInfo, entryPoint, attributes, functionId, this);
@@ -3761,10 +3761,12 @@ namespace Js
                 Assert(this->GetOriginalEntryPoint_Unchecked() == (JavascriptMethod)&Js::InterpreterStackFrame::StaticInterpreterAsmThunk);
             }
             else
-#endif
             {
                 Assert(this->GetOriginalEntryPoint_Unchecked() == (JavascriptMethod)&Js::InterpreterStackFrame::StaticInterpreterThunk);
             }
+#else
+            Assert(this->GetOriginalEntryPoint_Unchecked() == (JavascriptMethod)&Js::InterpreterStackFrame::StaticInterpreterThunk);
+#endif
         }
 #endif
     }
@@ -5103,7 +5105,7 @@ namespace Js
                 NEXT_SLISTBASE_ENTRY_EDITING;
             }
 #endif
-            this->dynamicProfileInfo = nullptr;
+        this->dynamicProfileInfo = nullptr;
         }
 #endif
         this->hasExecutionDynamicProfileInfo = false;

+ 16 - 2
lib/Runtime/Base/ScriptContext.cpp

@@ -108,6 +108,7 @@ namespace Js
         isPerformingNonreentrantWork(false),
         isDiagnosticsScriptContext(false),
         m_enumerateNonUserFunctionsOnly(false),
+        scriptContextPrivilegeLevel(ScriptContextPrivilegeLevel::Low),
         recycler(threadContext->EnsureRecycler()),
         CurrentThunk(DefaultEntryThunk),
         CurrentCrossSiteThunk(CrossSite::DefaultThunk),
@@ -1952,17 +1953,18 @@ namespace Js
                 Js::Throw::OutOfMemory();
             }
             Assert(length < MAXLONG);
+            charcount_t ccLength = static_cast<charcount_t>(length);
 
             // Allocate memory for the UTF8 output buffer.
             // We need at most 3 bytes for each Unicode code point.
             // The + 1 is to include the terminating NUL.
             // Nit:  Technically, we know that the NUL only needs 1 byte instead of
             // 3, but that's difficult to express in a SAL annotation for "EncodeInto".
-            size_t cbUtf8Buffer = AllocSizeMath::Mul(AllocSizeMath::Add(length, 1), 3);
+            size_t cbUtf8Buffer = UInt32Math::MulAdd<3, 1>(ccLength);
 
             utf8Script = RecyclerNewArrayLeafTrace(this->GetRecycler(), utf8char_t, cbUtf8Buffer);
 
-            cbNeeded = utf8::EncodeIntoAndNullTerminate(utf8Script, (const char16*)script, static_cast<charcount_t>(length));
+            cbNeeded = utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::Cesu8>(utf8Script, cbUtf8Buffer, (const char16*)script, ccLength);
 
 #if DBG_DUMP && defined(PROFILE_MEM)
             if (Js::Configuration::Global.flags.TraceMemory.IsEnabled(Js::ParsePhase) && Configuration::Global.flags.Verbose)
@@ -6414,6 +6416,18 @@ ScriptContext::GetJitFuncRangeCache()
         return true;
     }
 
+    void ScriptContext::SetIsDiagnosticsScriptContext(bool set)
+    {
+        this->isDiagnosticsScriptContext = set;
+        if (this->isDiagnosticsScriptContext)
+        {
+            this->scriptContextPrivilegeLevel = ScriptContextPrivilegeLevel::Medium;
+        }
+        else
+        {
+            this->scriptContextPrivilegeLevel = ScriptContextPrivilegeLevel::Low;
+        }
+    }
 
     bool ScriptContext::IsScriptContextInDebugMode() const
     {

+ 13 - 1
lib/Runtime/Base/ScriptContext.h

@@ -127,6 +127,13 @@ enum LoadScriptFlag
     LoadScriptFlag_CreateParserState = 0x200            // create the parser state cache while parsing.
 };
 
+enum class ScriptContextPrivilegeLevel
+{
+    Low,
+    Medium,
+    High
+};
+
 #ifdef INLINE_CACHE_STATS
 // Used to store inline cache stats
 
@@ -501,7 +508,9 @@ namespace Js
         static ushort ProcessNameAndGetLength(Js::StringBuilder<ArenaAllocator>* nameBuffer, const WCHAR* name);
 #endif
 
-        void SetIsDiagnosticsScriptContext(bool set) { this->isDiagnosticsScriptContext = set; }
+        void SetPrivilegeLevel(ScriptContextPrivilegeLevel level) { this->scriptContextPrivilegeLevel = level; }
+        ScriptContextPrivilegeLevel GetPrivilegeLevel() { return this->scriptContextPrivilegeLevel; }
+        void SetIsDiagnosticsScriptContext(bool);
         bool IsDiagnosticsScriptContext() const { return this->isDiagnosticsScriptContext; }
         bool IsScriptContextInNonDebugMode() const;
         bool IsScriptContextInDebugMode() const;
@@ -879,6 +888,9 @@ private:
         bool isPerformingNonreentrantWork;
         bool isDiagnosticsScriptContext;   // mentions that current script context belongs to the diagnostics OM.
 
+        // Privilege levels are a way of enforcing a relationship hierarchy between two script contexts
+        // A less privileged script context is not allowed to marshal in objects that live in a more privileged context
+        ScriptContextPrivilegeLevel scriptContextPrivilegeLevel;
         size_t sourceSize;
 
         void CleanSourceListInternal(bool calledDuringMark);

+ 22 - 0
lib/Runtime/Base/SourceHolder.cpp

@@ -8,4 +8,26 @@ namespace Js
 {
     LPCUTF8 const ISourceHolder::emptyString = (LPCUTF8)"\0";
     SimpleSourceHolder const ISourceHolder::emptySourceHolder(NO_WRITE_BARRIER_TAG(emptyString), 0, true);
+
+    void HeapSourceHolder::Dispose(bool fShutdown)
+    {
+        Unload();
+    }
+
+    void HeapSourceHolder::Unload()
+    {
+        if (this->shouldFreeSource)
+        {
+            // REVIEW: Where is the inc for this guy?
+            //PERF_COUNTER_DEC(Basic, ScriptCodeBufferCount);
+            HeapFree(GetProcessHeap(), 0, (void*)this->originalSourceBuffer);
+
+            this->source = nullptr;
+            this->originalSourceBuffer = nullptr;
+            this->isEmpty = true;
+            this->byteLength = 0;
+
+            this->shouldFreeSource = false;
+        }
+    }
 }

+ 22 - 2
lib/Runtime/Base/SourceHolder.h

@@ -24,12 +24,13 @@ namespace Js
         virtual hash_t GetHashCode() = 0;
         virtual bool IsEmpty() = 0;
         virtual bool IsDeferrable() = 0;
+        virtual void Unload() = 0;
     };
 
-    class SimpleSourceHolder sealed : public ISourceHolder
+    class SimpleSourceHolder : public ISourceHolder
     {
         friend class ISourceHolder;
-    private:
+    protected:
         Field(LPCUTF8) source;
         Field(size_t) byteLength;
         Field(bool) isEmpty;
@@ -88,9 +89,28 @@ namespace Js
         {
         }
 
+        virtual void Unload() override { }
+
         virtual bool IsDeferrable() override
         {
             return CONFIG_FLAG(DeferLoadingAvailableSource);
         }
     };
+
+    class HeapSourceHolder : public SimpleSourceHolder
+    {
+    public:
+        HeapSourceHolder(LPCUTF8 source, size_t byteLength, BYTE* originalSourceBuffer):
+            SimpleSourceHolder(source, byteLength),
+            shouldFreeSource(true),
+            originalSourceBuffer(originalSourceBuffer)
+        { }
+
+        virtual void Unload() override;
+        virtual void Dispose(bool isShutdown) override;
+
+    private:
+        bool shouldFreeSource;
+        BYTE* originalSourceBuffer;
+    };
 }

+ 16 - 0
lib/Runtime/Base/ThreadContext.cpp

@@ -1520,6 +1520,8 @@ ThreadContext::EnterScriptEnd(Js::ScriptEntryExitRecord * record, bool doCleanup
             this->hasThrownPendingException = false;
         }
 
+        delayFreeCallback.ClearAll();
+
 #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
         if (Js::Configuration::Global.flags.FreeRejittedCode)
 #endif
@@ -2745,6 +2747,20 @@ ThreadContext::DoTryRedeferral() const
     };
 }
 
+void
+ThreadContext::OnScanStackCallback(void ** stackTop, size_t byteCount, void ** registers, size_t registersByteCount)
+{
+    // Scan the stack to match with current list of delayed free buffer. For those which are not found on the stack
+    // will be released (ref-count decremented)
+
+    if (!this->delayFreeCallback.HasAnyItem())
+    {
+        return;
+    }
+
+    this->delayFreeCallback.ScanStack(stackTop, byteCount, registers, registersByteCount);
+}
+
 bool
 ThreadContext::DoRedeferFunctionBodies() const
 {

+ 16 - 3
lib/Runtime/Base/ThreadContext.h

@@ -14,6 +14,7 @@ namespace Js
     struct ReturnedValue;
     typedef JsUtil::List<ReturnedValue*> ReturnedValueList;
 #endif
+    class DelayedFreeArrayBuffer;
 }
 
 typedef BVSparse<ArenaAllocator> ActiveFunctionSet;
@@ -619,7 +620,7 @@ private:
     bool isThreadBound;
     bool hasThrownPendingException;
     bool * hasBailedOutBitPtr;
-    bool callDispose;
+
 #if ENABLE_JS_REENTRANCY_CHECK
     bool noJsReentrancy;
 #endif
@@ -627,7 +628,7 @@ private:
     bool reentrancySafeOrHandled;
     bool isInReentrancySafeRegion;
 
-    AllocationPolicyManager * allocationPolicyManager; 
+    AllocationPolicyManager * allocationPolicyManager;
 
     JsUtil::ThreadService threadService;
 #if ENABLE_NATIVE_CODEGEN
@@ -643,6 +644,10 @@ private:
     IdleDecommitPageAllocator pageAllocator;
     Recycler* recycler;
 
+    // This instance holds list of delay-free array buffer - this will be used in 
+    // scanning the stack in order to release any delay-free buffer.
+    Js::DelayedFreeArrayBuffer delayFreeCallback;
+
     // Fake RecyclerWeakReference for built-in properties
     class StaticPropertyRecordReference : public RecyclerWeakReference<const Js::PropertyRecord>
     {
@@ -816,7 +821,7 @@ private:
     bool isScriptActive;
 
     // When ETW rundown in background thread which needs to walk scriptContext/functionBody/entryPoint lists,
-    // or when JIT thread is getting auxPtrs from function body, we should not be modifying the list of
+    // or when JIT thread is getting auxPtrs from function body, we should not be modifying the list of 
     // functionBody/entrypoints, or expanding the auxPtrs
     CriticalSection csFunctionBody;
 
@@ -929,6 +934,11 @@ public:
     Js::NoSpecialPropertyThreadRegistry* GetNoSpecialPropertyRegistry() { return &this->noSpecialPropertyRegistry; }
     Js::OnlyWritablePropertyThreadRegistry* GetOnlyWritablePropertyRegistry() { return &this->onlyWritablePropertyRegistry; }
 
+    Js::DelayedFreeArrayBuffer * GetScanStackCallback()
+    {
+        return &this->delayFreeCallback;
+    }
+
 #ifdef ENABLE_DIRECTCALL_TELEMETRY
     DirectCallTelemetry directCallTelemetry;
 #endif
@@ -1690,6 +1700,8 @@ public:
 
     virtual uint GetRandomNumber() override;
     virtual bool DoSpecialMarkOnScanStack() override { return this->DoRedeferFunctionBodies(); }
+    virtual void OnScanStackCallback(void ** stackTop, size_t byteCount, void ** registers, size_t registersByteCount) override;
+
     virtual void PostSweepRedeferralCallBack() override;
 
     // DefaultCollectWrapper
@@ -1713,6 +1725,7 @@ public:
     int numExpirableObjects;
     int expirableCollectModeGcCount;
     bool disableExpiration;
+    bool callDispose;
 
     bool InExpirableCollectMode();
     void TryEnterExpirableCollectMode();

+ 66 - 12
lib/Runtime/Base/ThreadContextInfo.cpp

@@ -15,6 +15,10 @@
 #include "ServerThreadContext.h"
 #endif
 
+#if defined(_UCRT) && _CONTROL_FLOW_GUARD
+#include <cfguard.h>
+#endif
+
 ThreadContextInfo::ThreadContextInfo() :
     m_isAllJITCodeInPreReservedRegion(true),
     m_isClosed(false)
@@ -412,9 +416,16 @@ ThreadContextInfo::IsCFGEnabled()
 #define IS_16BYTE_ALIGNED(address) (((size_t)(address) & 0xF) == 0)
 #define OFFSET_ADDR_WITHIN_PAGE(address) ((size_t)(address) & (AutoSystemInfo::PageSize - 1))
 
+template <bool useFileAPI>
 void
-ThreadContextInfo::SetValidCallTargetForCFG(PVOID callTargetAddress, bool isSetValid)
-{
+ThreadContextInfo::SetValidCallTargetInternal(
+    _In_ PVOID callTargetAddress,
+    _In_opt_ HANDLE fileHandle,
+    _In_opt_ PVOID viewBase,
+    bool isSetValid)
+{
+    AnalysisAssert(!useFileAPI || fileHandle);
+    AnalysisAssert(!useFileAPI || viewBase);
 #ifdef _CONTROL_FLOW_GUARD
     if (IsCFGEnabled())
     {
@@ -434,19 +445,50 @@ ThreadContextInfo::SetValidCallTargetForCFG(PVOID callTargetAddress, bool isSetV
             RaiseFailFastException(nullptr, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS);
         }
 
-        PVOID startAddressOfPage = (PVOID)(PAGE_START_ADDR(callTargetAddress));
-        size_t codeOffset = OFFSET_ADDR_WITHIN_PAGE(callTargetAddress);
-
         CFG_CALL_TARGET_INFO callTargetInfo[1];
+        BOOL isCallTargetRegistrationSucceed;
+        PVOID startAddr = nullptr;
+        if (useFileAPI)
+        {
+            // Fall back to old CFG registration API, since new API seems broken
+            SetValidCallTargetForCFG(callTargetAddress, isSetValid);
+            return;
+#if 0
+            Assert(JITManager::GetJITManager()->IsJITServer());
+            size_t codeOffset = (uintptr_t)callTargetAddress - (uintptr_t)viewBase;
+            size_t regionSize = codeOffset + 1;
+
+            callTargetInfo[0].Offset = codeOffset;
+            callTargetInfo[0].Flags = (isSetValid ? CFG_CALL_TARGET_VALID : 0);
+
+            startAddr = viewBase;
+
+            isCallTargetRegistrationSucceed = GetWinCoreMemoryLibrary()->SetProcessCallTargetsForMappedView(GetProcessHandle(), viewBase, regionSize, 1, callTargetInfo, fileHandle, 0);
+#if ENABLE_DEBUG_CONFIG_OPTIONS
+            if (!isCallTargetRegistrationSucceed)
+            {
+                // Fall back to old CFG registration API for test builds, so that they can run on older OSes
+                SetValidCallTargetForCFG(callTargetAddress, isSetValid);
+                return;
+            }
+#endif
+#endif
+        }
+        else
+        {
+            PVOID startAddressOfPage = (PVOID)(PAGE_START_ADDR(callTargetAddress));
+            size_t codeOffset = OFFSET_ADDR_WITHIN_PAGE(callTargetAddress);
 
-        callTargetInfo[0].Offset = codeOffset;
-        callTargetInfo[0].Flags = (isSetValid ? CFG_CALL_TARGET_VALID : 0);
+            callTargetInfo[0].Offset = codeOffset;
+            callTargetInfo[0].Flags = (isSetValid ? CFG_CALL_TARGET_VALID : 0);
 
-        AssertMsg((size_t)callTargetAddress - (size_t)startAddressOfPage <= AutoSystemInfo::PageSize - 1, "Only last bits corresponding to PageSize should be masked");
-        AssertMsg((size_t)startAddressOfPage + (size_t)codeOffset == (size_t)callTargetAddress, "Wrong masking of address?");
+            startAddr = startAddressOfPage;
 
-        BOOL isCallTargetRegistrationSucceed = GetWinCoreMemoryLibrary()->SetProcessCallTargets(GetProcessHandle(), startAddressOfPage, AutoSystemInfo::PageSize, 1, callTargetInfo);
+            AssertMsg((size_t)callTargetAddress - (size_t)startAddressOfPage <= AutoSystemInfo::PageSize - 1, "Only last bits corresponding to PageSize should be masked");
+            AssertMsg((size_t)startAddressOfPage + (size_t)codeOffset == (size_t)callTargetAddress, "Wrong masking of address?");
 
+            isCallTargetRegistrationSucceed = GetWinCoreMemoryLibrary()->SetProcessCallTargets(GetProcessHandle(), startAddressOfPage, AutoSystemInfo::PageSize, 1, callTargetInfo);
+        }
         if (!isCallTargetRegistrationSucceed)
         {
             DWORD gle = GetLastError();
@@ -469,7 +511,7 @@ ThreadContextInfo::SetValidCallTargetForCFG(PVOID callTargetAddress, bool isSetV
 #if DBG
         if (isSetValid && !JITManager::GetJITManager()->IsOOPJITEnabled())
         {
-            _guard_check_icall((uintptr_t)callTargetAddress);
+            _GUARD_CHECK_ICALL((uintptr_t)callTargetAddress);
         }
 
         if (PHASE_TRACE1(Js::CFGPhase))
@@ -478,7 +520,7 @@ ThreadContextInfo::SetValidCallTargetForCFG(PVOID callTargetAddress, bool isSetV
             {
                 Output::Print(_u("DEREGISTER:"));
             }
-            Output::Print(_u("CFGRegistration: StartAddr: 0x%p , Offset: 0x%x, TargetAddr: 0x%x \n"), (char*)startAddressOfPage, callTargetInfo[0].Offset, ((size_t)startAddressOfPage + (size_t)callTargetInfo[0].Offset));
+            Output::Print(_u("CFGRegistration: StartAddr: 0x%p , Offset: 0x%x, TargetAddr: 0x%x \n"), (char*)startAddr, callTargetInfo[0].Offset, ((size_t)startAddr + (size_t)callTargetInfo[0].Offset));
             Output::Flush();
         }
 #endif
@@ -486,6 +528,18 @@ ThreadContextInfo::SetValidCallTargetForCFG(PVOID callTargetAddress, bool isSetV
 #endif // _CONTROL_FLOW_GUARD
 }
 
+void
+ThreadContextInfo::SetValidCallTargetFile(PVOID callTargetAddress, HANDLE fileHandle, PVOID viewBase, bool isSetValid)
+{
+    ThreadContextInfo::SetValidCallTargetInternal<true>(callTargetAddress, fileHandle, viewBase, isSetValid);
+}
+
+void
+ThreadContextInfo::SetValidCallTargetForCFG(PVOID callTargetAddress, bool isSetValid)
+{
+    ThreadContextInfo::SetValidCallTargetInternal<false>(callTargetAddress, nullptr, nullptr, isSetValid);
+}
+
 bool
 ThreadContextInfo::IsClosed()
 {

+ 8 - 0
lib/Runtime/Base/ThreadContextInfo.h

@@ -85,6 +85,7 @@ public:
 #endif
 
     void SetValidCallTargetForCFG(PVOID callTargetAddress, bool isSetValid = true);
+    void SetValidCallTargetFile(PVOID callTargetAddress, HANDLE fileHandle, PVOID viewBase, bool isSetValid);
     void ResetIsAllJITCodeInPreReservedRegion();
     bool IsAllJITCodeInPreReservedRegion() const;
 
@@ -124,6 +125,13 @@ public:
     Js::DelayLoadWinCoreProcessThreads m_delayLoadWinCoreProcessThreads;
 #endif
 
+    private:
+        template<bool useFileAPI>
+        void SetValidCallTargetInternal(
+            _In_ PVOID callTargetAddress,
+            _In_opt_ HANDLE fileHandle,
+            _In_opt_ PVOID viewBase,
+            bool isSetValid);
 protected:
     class AutoCloseHandle
     {

+ 0 - 16
lib/Runtime/Base/Utf8SourceInfo.cpp

@@ -20,14 +20,12 @@ namespace Js
         ScriptContext* scriptContext, bool isLibraryCode, Js::Var scriptSource):
         sourceHolder(mappableSource),
         m_cchLength(cchLength),
-        m_pHostBuffer(nullptr),
         m_srcInfo(srcInfo),
         m_secondaryHostSourceContext(secondaryHostSourceContext),
 #ifdef ENABLE_SCRIPT_DEBUGGING
         m_debugDocument(nullptr),
 #endif
         m_sourceInfoId(scriptContext->GetThreadContext()->NewSourceInfoNumber()),
-        m_hasHostBuffer(false),
         m_isCesu8(false),
         m_isLibraryCode(isLibraryCode),
         m_isXDomain(false),
@@ -102,22 +100,8 @@ namespace Js
 #ifndef NTBUILD
         this->sourceRef = nullptr;
 #endif
-        if (this->m_hasHostBuffer)
-        {
-            PERF_COUNTER_DEC(Basic, ScriptCodeBufferCount);
-            HeapFree(GetProcessHeap(), 0 , m_pHostBuffer);
-            m_pHostBuffer = nullptr;
-        }
     };
 
-    void
-    Utf8SourceInfo::SetHostBuffer(BYTE * pcszCode)
-    {
-        Assert(!this->m_hasHostBuffer);
-        Assert(this->m_pHostBuffer == nullptr);
-        this->m_hasHostBuffer = true;
-        this->m_pHostBuffer = pcszCode;
-    }
     enum
     {
         fsiHostManaged = 0x01,

+ 0 - 5
lib/Runtime/Base/Utf8SourceInfo.h

@@ -217,8 +217,6 @@ namespace Js
             return matchedFunctionBody;
         }
 
-        void SetHostBuffer(BYTE * pcszCode);
-
 #ifdef ENABLE_SCRIPT_DEBUGGING
         bool HasDebugDocument() const
         {
@@ -376,8 +374,6 @@ namespace Js
         Field(charcount_t) m_cchLength;               // The number of characters encoded in m_utf8Source.
         Field(ISourceHolder*) sourceHolder;
 
-        FieldNoBarrier(BYTE*) m_pHostBuffer;  // Pointer to a host source buffer (null unless this is host code that we need to free)
-
         Field(FunctionBodyDictionary*) functionBodyDictionary;
         Field(DeferredFunctionsDictionary*) m_deferredFunctionsDictionary;
         Field(FunctionInfoList*) topLevelFunctionInfoList;
@@ -404,7 +400,6 @@ namespace Js
 
         Field(bool) m_deferredFunctionsInitialized : 1;
         Field(bool) m_isCesu8 : 1;
-        Field(bool) m_hasHostBuffer : 1;
         Field(bool) m_isLibraryCode : 1;           // true, the current source belongs to the internal library code. Used for debug purpose to not show in debugger
         Field(bool) m_isXDomain : 1;
         // we found that m_isXDomain could cause regression without CORS, so the new flag is just for callee.caller in window.onerror

+ 17 - 13
lib/Runtime/Base/VTuneChakraProfile.cpp

@@ -64,12 +64,13 @@ void VTuneChakraProfile::LogMethodNativeLoadEvent(Js::FunctionBody* body, Js::Fu
 
         size_t methodLength = wcslen(methodNameBuffer);
         Assert(methodLength < _MAX_PATH);
-        size_t length = methodLength * 3 + 1;
-        utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, length);
+        charcount_t ccMethodLength = static_cast<charcount_t>(methodLength);
+        size_t cbUtf8MethodName = UInt32Math::MulAdd<3, 1>(ccMethodLength);
+        utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, cbUtf8MethodName);
         if (utf8MethodName)
         {
             methodInfo.method_id = iJIT_GetNewMethodID();
-            utf8::EncodeIntoAndNullTerminate(utf8MethodName, methodNameBuffer, (charcount_t)methodLength);
+            utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::Cesu8>(utf8MethodName, cbUtf8MethodName, methodNameBuffer, ccMethodLength);
             methodInfo.method_name = (char*)utf8MethodName;
             methodInfo.method_load_address = (void*)entryPoint->GetNativeAddress();
             methodInfo.method_size = (uint)entryPoint->GetCodeSize();        // Size in memory - Must be exact
@@ -107,7 +108,7 @@ void VTuneChakraProfile::LogMethodNativeLoadEvent(Js::FunctionBody* body, Js::Fu
                 HeapDeleteArray(urlLength, utf8Url);
             }
 
-            HeapDeleteArray(length, utf8MethodName);
+            HeapDeleteArray(cbUtf8MethodName, utf8MethodName);
         }
     }
 #endif
@@ -125,14 +126,16 @@ void VTuneChakraProfile::LogLoopBodyLoadEvent(Js::FunctionBody* body, Js::LoopEn
         memset(&methodInfo, 0, sizeof(iJIT_Method_Load));
         const char16* methodName = body->GetExternalDisplayName();
         size_t methodLength = wcslen(methodName);
-        methodLength = min(methodLength, (size_t)UINT_MAX); // Just truncate if it is too big
-        size_t length = methodLength * 3 + /* spaces */ 2 + _countof(LoopStr) + /*size of loop number*/ 10 + /*NULL*/ 1;
-        utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, length);
+        charcount_t ccMethodLength = static_cast<charcount_t>(methodLength);
+        ccMethodLength = min(ccMethodLength, UINT_MAX); // Just truncate if it is too big
+        constexpr size_t sizeToAdd = /* spaces */ 2 + _countof(LoopStr) + /* size of loop number */ 10 + /*NULL*/ 1;
+        size_t cbUtf8MethodName = UInt32Math::MulAdd<3, sizeToAdd>(ccMethodLength);
+        utf8char_t* utf8MethodName = HeapNewNoThrowArray(utf8char_t, cbUtf8MethodName);
         if(utf8MethodName)
         {
             methodInfo.method_id = iJIT_GetNewMethodID();
-            size_t len = utf8::EncodeInto(utf8MethodName, methodName, (charcount_t)methodLength);
-            sprintf_s((char*)(utf8MethodName + len), length - len," %s %d", LoopStr, loopNumber + 1);
+            size_t len = utf8::EncodeInto<utf8::Utf8EncodingKind::Cesu8>(utf8MethodName, cbUtf8MethodName, methodName, ccMethodLength);
+            sprintf_s((char*)(utf8MethodName + len), cbUtf8MethodName - len," %s %d", LoopStr, loopNumber + 1);
             methodInfo.method_name = (char*)utf8MethodName;
             methodInfo.method_load_address = (void*)entryPoint->GetNativeAddress();
             methodInfo.method_size = (uint)entryPoint->GetCodeSize();        // Size in memory - Must be exact
@@ -148,7 +151,7 @@ void VTuneChakraProfile::LogLoopBodyLoadEvent(Js::FunctionBody* body, Js::LoopEn
             {
                 HeapDeleteArray(urlLength, utf8Url);
             }
-            HeapDeleteArray(length, utf8MethodName);
+            HeapDeleteArray(cbUtf8MethodName, utf8MethodName);
         }
     }
 #endif
@@ -166,13 +169,14 @@ utf8char_t* VTuneChakraProfile::GetUrl(Js::FunctionBody* body, size_t* urlBuffer
         if (url)
         {
             size_t urlCharLength = wcslen(url);
-            urlCharLength = min(urlCharLength, (size_t)UINT_MAX);       // Just truncate if it is too big
+            charcount_t ccUrlCharLength = static_cast<charcount_t>(urlCharLength);
+            ccUrlCharLength = min(ccUrlCharLength, UINT_MAX);       // Just truncate if it is too big
 
-            *urlBufferLength = urlCharLength * 3 + 1;
+            *urlBufferLength = UInt32Math::MulAdd<3, 1>(ccUrlCharLength);
             utf8Url = HeapNewNoThrowArray(utf8char_t, *urlBufferLength);
             if (utf8Url)
             {
-                utf8::EncodeIntoAndNullTerminate(utf8Url, url, (charcount_t)urlCharLength);
+                utf8::EncodeIntoAndNullTerminate<utf8::Utf8EncodingKind::Cesu8>(utf8Url, *urlBufferLength, url, ccUrlCharLength);
             }
         }
     }

+ 2 - 2
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -3952,7 +3952,7 @@ void ByteCodeGenerator::EnsureFncScopeSlots(ParseNode *pnode, FuncInfo *funcInfo
         case knopFncDecl:
             if (pnode->AsParseNodeFnc()->IsDeclaration())
             {
-                CheckFncDeclScopeSlot(pnode->AsParseNodeFnc(), funcInfo);
+                this->CheckFncDeclScopeSlot(pnode->AsParseNodeFnc(), funcInfo);
             }
             pnode = pnode->AsParseNodeFnc()->pnodeNext;
             break;
@@ -11437,7 +11437,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
         byteCodeGenerator->Writer()->Reg2(Js::OpCode::Conv_Obj, regVal, pnodeWith->pnodeObj->location);
         if (byteCodeGenerator->GetScriptContext()->GetConfig()->IsES6UnscopablesEnabled())
         {
-            byteCodeGenerator->Writer()->Reg2(Js::OpCode::NewWithObject, pnodeWith->location, regVal);
+            byteCodeGenerator->Writer()->Reg2(Js::OpCode::NewUnscopablesWrapperObject, pnodeWith->location, regVal);
         }
         byteCodeGenerator->EndStatement(pnodeWith);
 

+ 0 - 1
lib/Runtime/ByteCode/ByteCodeGenerator.h

@@ -282,7 +282,6 @@ public:
     void LoadSuperConstructorObject(FuncInfo *funcInfo);
     void EmitSuperCall(FuncInfo* funcInfo, ParseNodeSuperCall * pnodeSuperCall, BOOL fReturnValue);
     void EmitClassConstructorEndCode(FuncInfo *funcInfo);
-    void EmitBaseClassConstructorThisObject(FuncInfo *funcInfo);
 
     // TODO: home the 'this' argument
     void EmitLoadFormalIntoRegister(ParseNode *pnodeFormal, Js::RegSlot pos, FuncInfo *funcInfo);

+ 1 - 1
lib/Runtime/ByteCode/OpCodes.h

@@ -325,7 +325,7 @@ MACRO_EXTEND_WMS(       Conv_Str,           Reg2,           OpOpndHasImplicitCal
 //      OpSideEffect - May throw exception on null/undefined.
 //      Do not call valueOf/toString no implicit call
 MACRO_WMS(              Conv_Obj,           Reg2,           OpSideEffect|OpPostOpDbgBailOut|OpTempObjectTransfer)   // Convert to Object
-MACRO_EXTEND_WMS(       NewWithObject,      Reg2,           OpSideEffect | OpPostOpDbgBailOut)  // Wrap in a with Object
+MACRO_EXTEND_WMS(       NewUnscopablesWrapperObject,      Reg2,           OpSideEffect | OpPostOpDbgBailOut)  // Wrap in a with Object
 MACRO_BACKEND_ONLY(     ToVar,              Reg2,           OpTempNumberProducing|OpTempNumberSources|OpCanCSE)     // Load from int32/float64 to Var(reg)
 // Load from Var(reg) to int32/float64, NOTE: always bail if it is not primitive. so no implicit call, but still mark with CallsValueOf so it won't get automatically dead stored
 // TODO: Consider changing the code so we don't have mark this as CallsValueOf

+ 3 - 3
lib/Runtime/Debug/DebugContext.cpp

@@ -13,8 +13,8 @@ namespace Js
         diagProbesContainer(nullptr),
         isClosed(false),
         debuggerMode(DebuggerMode::NotDebugging),
-        isDebuggerRecording(true),
-        isReparsingSource(false)
+        isReparsingSource(false),
+        isDebuggerRecording(true)
     {
         Assert(scriptContext != nullptr);
     }
@@ -411,4 +411,4 @@ namespace Js
         });
     }
 }
-#endif
+#endif

+ 1 - 1
lib/Runtime/Debug/DebugContext.h

@@ -76,8 +76,8 @@ namespace Js
         ProbeContainer* diagProbesContainer;
         DebuggerMode debuggerMode;
         bool isClosed : 1;
-        bool isDebuggerRecording : 1;
         bool isReparsingSource : 1;
+        bool isDebuggerRecording : 1;
 
         // Private Functions
         void WalkAndAddUtf8SourceInfo(Js::Utf8SourceInfo* sourceInfo, JsUtil::List<Js::Utf8SourceInfo *, Recycler, false, Js::CopyRemovePolicy, RecyclerPointerComparer> *utf8SourceInfoList);

+ 3 - 3
lib/Runtime/Debug/DiagObjectModel.cpp

@@ -2483,7 +2483,7 @@ namespace Js
                     else
                     {
                         RecyclableObject* wrapperObject = nullptr;
-                        if (JavascriptOperators::GetTypeId(object) == TypeIds_WithScopeObject)
+                        if (JavascriptOperators::GetTypeId(object) == TypeIds_UnscopablesWrapperObject)
                         {
                             wrapperObject = object;
                             object = object->GetThisObjectOrUnWrap();
@@ -3427,8 +3427,8 @@ namespace Js
         RecyclableObject *obj               = Js::VarTo<Js::RecyclableObject>(instance);
 
         Assert(obj->GetPrototype() != nullptr);
-        //withscopeObjects prototype is null
-        Assert(obj->GetPrototype()->GetTypeId() != TypeIds_Null || (obj->GetPrototype()->GetTypeId() == TypeIds_Null && obj->GetTypeId() == TypeIds_WithScopeObject));
+        //UnscopablesWrapperObjects prototype is null
+        Assert(obj->GetPrototype()->GetTypeId() != TypeIds_Null || (obj->GetPrototype()->GetTypeId() == TypeIds_Null && obj->GetTypeId() == TypeIds_UnscopablesWrapperObject));
 
         pResolvedObject->obj                = obj->GetPrototype();
         pResolvedObject->originalObj        = (originalInstance != nullptr) ? Js::VarTo<Js::RecyclableObject>(originalInstance) : pResolvedObject->obj;

+ 3 - 2
lib/Runtime/Debug/TTEventLog.cpp

@@ -863,8 +863,9 @@ namespace TTD
     {
         this->RecordGetInitializedEvent_DataOnly<NSLogEvents::ExplicitLogWriteEventLogEntry, NSLogEvents::EventKind::ExplicitLogWriteTag>();
 
-        AutoArrayPtr<char> uri(HeapNewArrayZ(char, uriString->GetLength() * 3), uriString->GetLength() * 3);
-        size_t uriLength = utf8::EncodeInto((LPUTF8)((char*)uri), uriString->GetString(), uriString->GetLength());
+        size_t cbUri = UInt32Math::Mul<3>(uriString->GetLength());
+        AutoArrayPtr<char> uri(HeapNewArrayZ(char, cbUri), cbUri);
+        size_t uriLength = utf8::EncodeInto<utf8::Utf8EncodingKind::Cesu8>((LPUTF8)((char*)uri), cbUri, uriString->GetString(), uriString->GetLength());
 
         this->EmitLog(uri, uriLength);
     }

+ 19 - 19
lib/Runtime/Debug/TTSnapValues.h

@@ -36,7 +36,7 @@ namespace TTD
         //return true if the Var is a tagged number (inline)
         bool IsVarTaggedInline(Js::Var v);
 
-        //return true if the Var is a ptr var value 
+        //return true if the Var is a ptr var value
         bool IsVarPtrValued(Js::Var v);
 
         //return true if the Var is a primitive value (string, number, symbol, etc.)
@@ -50,7 +50,7 @@ namespace TTD
         bool AreInlineVarsEquiv(Js::Var v1, Js::Var v2);
 #endif
 
-        //Ensure a function is fully parsed/deserialized 
+        //Ensure a function is fully parsed/deserialized
         Js::FunctionBody* ForceAndGetFunctionBody(Js::ParseableFunctionInfo* pfi);
 
         void WriteCodeToFile(ThreadContext* threadContext, bool fromEvent, uint32 bodyId, bool isUtf8Source, byte* sourceBuffer, uint32 length);
@@ -77,7 +77,7 @@ namespace TTD
         //de-serialize the TTDVar
         TTDVar ParseTTDVar(bool readSeparator, FileReader* reader);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         bool CheckSnapEquivTTDDouble(double d1, double d2);
         void AssertSnapEquivTTDVar_Helper(const TTDVar v1, const TTDVar v2, TTDCompareMap& compareMap, TTDComparePath::StepKind stepKind, const TTDComparePath::PathEntry& next);
 
@@ -121,7 +121,7 @@ namespace TTD
         void EmitSnapPrimitiveValue(const SnapPrimitiveValue* snapValue, FileWriter* writer, NSTokens::Separator separator);
         void ParseSnapPrimitiveValue(SnapPrimitiveValue* snapValue, bool readSeparator, FileReader* reader, SlabAllocator& alloc, const TTDIdentifierDictionary<TTD_PTR_ID, NSSnapType::SnapType*>& ptrIdToTypeMap);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const SnapPrimitiveValue* v1, const SnapPrimitiveValue* v2, TTDCompareMap& compareMap);
 #endif
 
@@ -130,7 +130,7 @@ namespace TTD
         //If a scope is a slot array this class encodes the slot information and original address of the slot array
         struct SlotArrayInfo
         {
-            //The unique id for the slot array scope entry 
+            //The unique id for the slot array scope entry
             TTD_PTR_ID SlotId;
 
             //The tag of the script context that this slot array is associated with
@@ -158,7 +158,7 @@ namespace TTD
         void EmitSlotArrayInfo(const SlotArrayInfo* slotInfo, FileWriter* writer, NSTokens::Separator separator);
         void ParseSlotArrayInfo(SlotArrayInfo* slotInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const SlotArrayInfo* sai1, const SlotArrayInfo* sai2, TTDCompareMap& compareMap);
 #endif
 
@@ -194,7 +194,7 @@ namespace TTD
         void EmitScriptFunctionScopeInfo(const ScriptFunctionScopeInfo* funcScopeInfo, FileWriter* writer, NSTokens::Separator separator);
         void ParseScriptFunctionScopeInfo(ScriptFunctionScopeInfo* funcScopeInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const ScriptFunctionScopeInfo* funcScopeInfo1, const ScriptFunctionScopeInfo* funcScopeInfo2, TTDCompareMap& compareMap);
 #endif
 
@@ -216,7 +216,7 @@ namespace TTD
         void EmitPromiseCapabilityInfo(const SnapPromiseCapabilityInfo* capabilityInfo, FileWriter* writer, NSTokens::Separator separator);
         void ParsePromiseCapabilityInfo(SnapPromiseCapabilityInfo* capabilityInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const SnapPromiseCapabilityInfo* capabilityInfo1, const SnapPromiseCapabilityInfo* capabilityInfo2, TTDCompareMap& compareMap);
 #endif
 
@@ -235,7 +235,7 @@ namespace TTD
         void EmitPromiseReactionInfo(const SnapPromiseReactionInfo* reactionInfo, FileWriter* writer, NSTokens::Separator separator);
         void ParsePromiseReactionInfo(SnapPromiseReactionInfo* reactionInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const SnapPromiseReactionInfo* reactionInfo1, const SnapPromiseReactionInfo* reactionInfo2, TTDCompareMap& compareMap);
 #endif
 
@@ -256,7 +256,7 @@ namespace TTD
         void EmitSnapFunctionBodyScopeChain(const SnapFunctionBodyScopeChain& scopeChain, FileWriter* writer);
         void ParseSnapFunctionBodyScopeChain(SnapFunctionBodyScopeChain& scopeChain, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const SnapFunctionBodyScopeChain& chain1, const SnapFunctionBodyScopeChain& chain2, TTDCompareMap& compareMap);
 #endif
 
@@ -299,7 +299,7 @@ namespace TTD
         void EmitTopLevelCommonBodyResolveInfo(const TopLevelCommonBodyResolveInfo* fbInfo, bool emitInline, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator);
         void ParseTopLevelCommonBodyResolveInfo(TopLevelCommonBodyResolveInfo* fbInfo, bool readSeparator, bool parseInline, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const TopLevelCommonBodyResolveInfo* fbInfo1, const TopLevelCommonBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap);
 #endif
 
@@ -319,7 +319,7 @@ namespace TTD
         void EmitTopLevelLoadedFunctionBodyInfo(const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator);
         void ParseTopLevelLoadedFunctionBodyInfo(TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo, bool readSeparator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo1, const TopLevelScriptLoadFunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap);
 #endif
 
@@ -336,7 +336,7 @@ namespace TTD
         void EmitTopLevelNewFunctionBodyInfo(const TopLevelNewFunctionBodyResolveInfo* fbInfo, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator);
         void ParseTopLevelNewFunctionBodyInfo(TopLevelNewFunctionBodyResolveInfo* fbInfo, bool readSeparator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const TopLevelNewFunctionBodyResolveInfo* fbInfo1, const TopLevelNewFunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap);
 #endif
 
@@ -349,7 +349,7 @@ namespace TTD
             //Additional data for handling the eval
             uint64 EvalFlags;
             bool RegisterDocument;
-            bool IsIndirect; 
+            bool IsIndirect;
             bool IsStrictMode;
         };
 
@@ -359,7 +359,7 @@ namespace TTD
         void EmitTopLevelEvalFunctionBodyInfo(const TopLevelEvalFunctionBodyResolveInfo* fbInfo, ThreadContext* threadContext, FileWriter* writer, NSTokens::Separator separator);
         void ParseTopLevelEvalFunctionBodyInfo(TopLevelEvalFunctionBodyResolveInfo* fbInfo, bool readSeparator, ThreadContext* threadContext, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const TopLevelEvalFunctionBodyResolveInfo* fbInfo1, const TopLevelEvalFunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap);
 #endif
 
@@ -397,7 +397,7 @@ namespace TTD
         void EmitFunctionBodyInfo(const FunctionBodyResolveInfo* fbInfo, FileWriter* writer, NSTokens::Separator separator);
         void ParseFunctionBodyInfo(FunctionBodyResolveInfo* fbInfo, bool readSeparator, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const FunctionBodyResolveInfo* fbInfo1, const FunctionBodyResolveInfo* fbInfo2, TTDCompareMap& compareMap);
 #endif
 
@@ -405,7 +405,7 @@ namespace TTD
 
         struct SnapRootInfoEntry
         {
-            //The log id value 
+            //The log id value
             TTD_LOG_PTR_ID LogId;
 
             //The object that this log id is mapped to
@@ -417,7 +417,7 @@ namespace TTD
 
         struct SnapPendingAsyncBufferModification
         {
-            //The log id value 
+            //The log id value
             TTD_LOG_PTR_ID LogId;
 
             //The index value associated with this modification
@@ -465,7 +465,7 @@ namespace TTD
         void EmitSnapContext(const SnapContext* snapCtx, FileWriter* writer, NSTokens::Separator separator);
         void ParseSnapContext(SnapContext* intoCtx, bool readSeparator, FileReader* reader, SlabAllocator& alloc);
 
-#if ENABLE_SNAPSHOT_COMPARE 
+#if ENABLE_SNAPSHOT_COMPARE
         void AssertSnapEquiv(const SnapContext* snapCtx1, const SnapContext* snapCtx2, const JsUtil::BaseDictionary<TTD_LOG_PTR_ID, NSSnapValues::SnapRootInfoEntry*, HeapAllocator>& allRootMap1, const JsUtil::BaseDictionary<TTD_LOG_PTR_ID, NSSnapValues::SnapRootInfoEntry*, HeapAllocator>& allRootMap2, TTDCompareMap& compareMap);
 #endif
     }

+ 37 - 2
lib/Runtime/DetachedStateBase.h

@@ -41,6 +41,8 @@ namespace Js
         virtual void ClearSelfOnly() = 0;
         virtual void DiscardState() = 0;
         virtual void Discard() = 0;
+        virtual void AddRefBufferContent() = 0;
+        virtual long ReleaseRefBufferContent() = 0;
     };
 
     typedef enum ArrayBufferAllocationType
@@ -51,19 +53,52 @@ namespace Js
         External = 0x03,
     } ArrayBufferAllocationType;
 
+    class RefCountedBuffer;
+
     class ArrayBufferDetachedStateBase : public DetachedStateBase
     {
     public:
-        BYTE* buffer;
+        RefCountedBuffer* buffer;
         uint32 bufferLength;
         ArrayBufferAllocationType allocationType;
 
-        ArrayBufferDetachedStateBase(TypeId typeId, BYTE* buffer, uint32 bufferLength, ArrayBufferAllocationType allocationType)
+        ArrayBufferDetachedStateBase(TypeId typeId, RefCountedBuffer* buffer, uint32 bufferLength, ArrayBufferAllocationType allocationType)
             : DetachedStateBase(typeId),
             buffer(buffer),
             bufferLength(bufferLength),
             allocationType(allocationType)
         {}
 
+        virtual void AddRefBufferContent() override;
+
+        virtual long ReleaseRefBufferContent() override;
+
+    protected:
+        // Clean up all local state. Different subclasses use different cleanup mechanisms for the buffer allocation.
+        template <typename TFreeFn> void DiscardStateBase(TFreeFn freeFunction)
+        {
+            // this function will be called in the case where transferable object is going away and current arraybuffer is not claimed.
+
+            if (this->buffer != nullptr)
+            {
+                RefCountedBuffer *local = this->buffer;
+                this->buffer = nullptr;
+
+                Assert(local->GetBuffer());
+
+                if (local->GetBuffer() != nullptr)
+                {
+                    long ref = local->Release();
+                    // The ref may not be 0, as we may have put the object in the DelayFree buffer list.
+                    if (ref == 0)
+                    {
+                        freeFunction(local->GetBuffer());
+                        HeapDelete(local);
+                    }
+                }
+            }
+
+            this->bufferLength = 0;
+        }
     };
 }

+ 3 - 2
lib/Runtime/Language/DynamicProfileInfo.cpp

@@ -2451,8 +2451,9 @@ namespace Js
         if (sz)
         {
             charcount_t len = static_cast<charcount_t>(wcslen(sz));
-            utf8char_t * tempBuffer = HeapNewArray(utf8char_t, len * 3);
-            size_t cbNeeded = utf8::EncodeInto(tempBuffer, sz, len);
+            const size_t cbTempBuffer = UInt32Math::Mul<3>(len);
+            utf8char_t * tempBuffer = HeapNewArray(utf8char_t, cbTempBuffer);
+            const size_t cbNeeded = utf8::EncodeInto<utf8::Utf8EncodingKind::Cesu8>(tempBuffer, cbTempBuffer, sz, len);
             fwrite(&cbNeeded, sizeof(cbNeeded), 1, file);
             fwrite(tempBuffer, sizeof(utf8char_t), cbNeeded, file);
             HeapDeleteArray(len * 3, tempBuffer);

+ 4 - 3
lib/Runtime/Language/DynamicProfileStorage.cpp

@@ -161,16 +161,17 @@ bool DynamicProfileStorageReaderWriter::WriteArray(T * t, size_t len)
 bool DynamicProfileStorageReaderWriter::WriteUtf8String(char16 const * str)
 {
     charcount_t len = static_cast<charcount_t>(wcslen(str));
-    utf8char_t * tempBuffer = NoCheckHeapNewArray(utf8char_t, len * 3);
+    const size_t cbTempBuffer = UInt32Math::Mul<3>(len);
+    utf8char_t * tempBuffer = NoCheckHeapNewArray(utf8char_t, cbTempBuffer);
     if (tempBuffer == nullptr)
     {
         Output::Print(_u("ERROR: DynamicProfileStorage: Out of memory writing to file '%s'\n"), filename);
         Output::Flush();
         return false;
     }
-    DWORD cbNeeded = (DWORD)utf8::EncodeInto(tempBuffer, str, len);
+    DWORD cbNeeded = (DWORD)utf8::EncodeInto<utf8::Utf8EncodingKind::Cesu8>(tempBuffer, cbTempBuffer, str, len);
     bool success = Write(cbNeeded) && WriteArray(tempBuffer, cbNeeded);
-    NoCheckHeapDeleteArray(len * 3, tempBuffer);
+    NoCheckHeapDeleteArray(cbTempBuffer, tempBuffer);
     return success;
 }
 

+ 11 - 4
lib/Runtime/Language/InlineCache.h

@@ -458,11 +458,18 @@ namespace Js
                 ScriptContext *const requestContext)
             {
                 *propertyValue = InlineCache::GetPropertyValue<slotType>(cache->GetSourceObject<cacheType>(propertyObject), cache->GetSlotIndex<cacheType>());
-                DebugOnly(Var getPropertyValue = JavascriptOperators::GetProperty(propertyObject, propertyId, requestContext));
-                Assert(*propertyValue == getPropertyValue ||
-                    (VarIs<RootObjectBase>(propertyObject) && *propertyValue == JavascriptOperators::GetRootProperty(propertyObject, propertyId, requestContext))||
+#if DBG
+                Var slowPathValue = JavascriptOperators::GetProperty(propertyObject, propertyId, requestContext);
+                Var rootObjectValue = nullptr;
+                if (VarIs<RootObjectBase>(propertyObject))
+                {
+                    rootObjectValue = JavascriptOperators::GetRootProperty(propertyObject, propertyId, requestContext);
+                }
+                Assert(*propertyValue == slowPathValue ||
+                    (VarIs<RootObjectBase>(propertyObject) && *propertyValue == rootObjectValue) ||
                     // In some cases, such as CustomExternalObject, if implicit calls are disabled GetPropertyQuery may return null. See CustomExternalObject::GetPropertyQuery for an example.
-                    (getPropertyValue == requestContext->GetLibrary()->GetNull() && requestContext->GetThreadContext()->IsDisableImplicitCall() && propertyObject->GetType()->IsExternal()));
+                    (slowPathValue == requestContext->GetLibrary()->GetNull() && requestContext->GetThreadContext()->IsDisableImplicitCall() && propertyObject->GetType()->IsExternal()));
+#endif
             }
         };
     };

+ 1 - 1
lib/Runtime/Language/InterpreterHandler.inl

@@ -95,7 +95,7 @@ EXDEF2    (BRPROP,                  BrOnHasProperty,            OP_BrOnHasProper
   
 EXDEF2_WMS(A1toA1Mem,               Conv_Str,                   JavascriptConversion::ToString)
   DEF2_WMS(A1toA1Mem,               Conv_Obj,                   JavascriptOperators::ToObject)
-EXDEF2_WMS(A1toA1Mem,               NewWithObject,              JavascriptOperators::ToWithObject)
+EXDEF2_WMS(A1toA1Mem,               NewUnscopablesWrapperObject,JavascriptOperators::ToUnscopablesWrapperObject)
   DEF2_WMS(A1toA1Mem,               Conv_Num,                   JavascriptOperators::ToNumber)
   DEF2_WMS(A1toA1Mem,               Incr_A,                     JavascriptMath::Increment)
   DEF2_WMS(A1toA1Mem,               Decr_A,                     JavascriptMath::Decrement)

+ 1 - 1
lib/Runtime/Language/InterpreterHandlerAsmJs.inl

@@ -15,7 +15,7 @@ EXDEF2    (NOPASMJS          , InvalidOpCode, Empty
   DEF2    ( NOPASMJS         , Label        , Empty                                              )
   DEF2    ( BR_ASM           , AsmBr        , OP_Br                                              )
 
-  DEF2_WMS( FALLTHROUGH_ASM  , LdSlotArr    ,  /* Common case with LdSlot */                     )
+  DEF2_WMS( FALLTHROUGH_ASM  , LdSlotArr    , /* Common case with LdSlot */                      )
   DEF3_WMS( CUSTOM_ASMJS     , LdSlot       , OP_LdAsmJsSlot               , ElementSlot         )
 
 // Function Calls

+ 1 - 1
lib/Runtime/Language/InterpreterStackFrame.cpp

@@ -6441,7 +6441,7 @@ skipThunk:
         }
         END_SAFE_REENTRANT_CALL
 
-            PopOut(ArgCount);
+        PopOut(ArgCount);
         JS_ETW(EventWriteJSCRIPT_RECYCLER_ALLOCATE_OBJECT(newVarInstance));
 #if ENABLE_DEBUG_CONFIG_OPTIONS
         if (Js::Configuration::Global.flags.IsEnabled(Js::autoProxyFlag))

+ 27 - 15
lib/Runtime/Language/JavascriptOperators.cpp

@@ -6,7 +6,7 @@
 
 #include "Types/PathTypeHandler.h"
 #include "Types/PropertyIndexRanges.h"
-#include "Types/WithScopeObject.h"
+#include "Types/UnscopablesWrapperObject.h"
 #include "Types/SpreadArgument.h"
 #include "Library/JavascriptPromise.h"
 #include "Library/JavascriptRegularExpression.h"
@@ -801,6 +801,9 @@ using namespace Js;
 
     BOOL JavascriptOperators::StrictEqualString(Var aLeft, JavascriptString* aRight)
     {
+        JIT_HELPER_REENTRANT_HEADER(Op_StrictEqualString);
+        JIT_HELPER_SAME_ATTRIBUTES(Op_StrictEqualString, Op_StrictEqual);
+
         JavascriptString* leftStr = TryFromVar<JavascriptString>(aLeft);
         if (!leftStr)
         {
@@ -1774,7 +1777,7 @@ CommonNumber:
             // Don't cache the information if the value is undecl block var
             // REVIEW: We might want to only check this if we need to (For LdRootFld or ScopedLdFld)
             //         Also we might want to throw here instead of checking it again in the caller
-            if (value && !requestContext->IsUndeclBlockVar(*value) && !VarIs<WithScopeObject>(object))
+            if (value && !requestContext->IsUndeclBlockVar(*value) && !VarIs<UnscopablesWrapperObject>(object))
             {
                 CacheOperators::CachePropertyRead(propertyObject, object, isRoot, propertyId, false, info, requestContext);
             }
@@ -1887,7 +1890,7 @@ CommonNumber:
 
             if (result != PropertyQueryFlags::Property_NotFound)
             {
-                if (!VarIs<WithScopeObject>(object) && info->GetPropertyRecordUsageCache())
+                if (!VarIs<UnscopablesWrapperObject>(object) && info->GetPropertyRecordUsageCache())
                 {
                     PropertyId propertyId = info->GetPropertyRecordUsageCache()->GetPropertyRecord()->GetPropertyId();
                     CacheOperators::CachePropertyRead(instance, object, false, propertyId, false, info, requestContext);
@@ -2077,7 +2080,7 @@ CommonNumber:
 
             if (JavascriptOperators::HasProperty(obj, propertyId))
             {
-                // HasProperty will call WithObjects HasProperty which will do the filtering
+                // HasProperty will call UnscopablesWrapperObject's HasProperty which will do the filtering
                 // All we have to do here is unwrap the object hence the api call
 
                 *thisVar = obj->GetThisObjectOrUnWrap();
@@ -2328,7 +2331,7 @@ CommonNumber:
                     }
                     if (setterValueOrProxy)
                     {
-                        if (!VarIs<WithScopeObject>(receiver) && info->GetPropertyRecordUsageCache() && !JavascriptOperators::IsUndefinedAccessor(setterValueOrProxy, requestContext))
+                        if (!VarIs<UnscopablesWrapperObject>(receiver) && info->GetPropertyRecordUsageCache() && !JavascriptOperators::IsUndefinedAccessor(setterValueOrProxy, requestContext))
                         {
                             CacheOperators::CachePropertyWrite(VarTo<RecyclableObject>(receiver), false, object->GetType(), info->GetPropertyRecordUsageCache()->GetPropertyRecord()->GetPropertyId(), info, requestContext);
                         }
@@ -2546,7 +2549,7 @@ CommonNumber:
                     RecyclableObject* func = VarTo<RecyclableObject>(setterValueOrProxy);
                     Assert(!info || info->GetFlags() == InlineCacheSetterFlag || info->GetPropertyIndex() == Constants::NoSlot);
 
-                    if (VarIs<WithScopeObject>(receiver))
+                    if (VarIs<UnscopablesWrapperObject>(receiver))
                     {
                         receiver = (VarTo<RecyclableObject>(receiver))->GetThisObjectOrUnWrap();
                     }
@@ -5547,12 +5550,12 @@ SetElementIHelper_INDEX_TYPE_IS_NUMBER:
         JIT_HELPER_END(OP_CmGe_A);
     }
 
-    DetachedStateBase* JavascriptOperators::DetachVarAndGetState(Var var)
+    DetachedStateBase* JavascriptOperators::DetachVarAndGetState(Var var, bool queueForDelayFree/* = true*/)
     {
         switch (GetTypeId(var))
         {
         case TypeIds_ArrayBuffer:
-            return Js::VarTo<Js::ArrayBuffer>(var)->DetachAndGetState();
+            return Js::VarTo<Js::ArrayBuffer>(var)->DetachAndGetState(queueForDelayFree);
         default:
             if (!Js::VarTo<Js::RecyclableObject>(var)->IsExternal())
             {
@@ -6711,8 +6714,12 @@ SetElementIHelper_INDEX_TYPE_IS_NUMBER:
 
     void JavascriptOperators::BuildHandlerScope(Var argThis, RecyclableObject * hostObject, FrameDisplay * pDisplay, ScriptContext * scriptContext)
     {
+        // Event handlers need implicit lookups of @@unscopables on parent scopes.
+        // We can intercept the property accesses by wrapping the object with the unscopables handler.
+        // WebIDL: https://heycam.github.io/webidl/#ref-for-Unscopable
+
         Assert(argThis != nullptr);
-        pDisplay->SetItem(0, TaggedNumber::Is(argThis) ? scriptContext->GetLibrary()->CreateNumberObject(argThis) : argThis);
+        pDisplay->SetItem(0, TaggedNumber::Is(argThis) ? scriptContext->GetLibrary()->CreateNumberObject(argThis) : ToUnscopablesWrapperObject(argThis, scriptContext));
         uint16 i = 1;
 
         Var aChild = argThis;
@@ -6736,7 +6743,10 @@ SetElementIHelper_INDEX_TYPE_IS_NUMBER:
                 js_memcpy_s((char*)tmp + tmp->GetOffsetOfScopes(), tmp->GetLength() * sizeof(void *), (char*)pDisplay + pDisplay->GetOffsetOfScopes(), pDisplay->GetLength() * sizeof(void*));
                 pDisplay = tmp;
             }
-            pDisplay->SetItem(i, aParent);
+
+            Var aParentWrapped = ToUnscopablesWrapperObject(aParent, scriptContext);
+            pDisplay->SetItem(i, aParentWrapped);
+
             aChild = aParent;
             i++;
         }
@@ -9725,7 +9735,9 @@ SetElementIHelper_INDEX_TYPE_IS_NUMBER:
     void JavascriptOperators::OP_SetHomeObj(Var method, Var homeObj)
     {
         ScriptFunctionBase *scriptFunction = VarTo<ScriptFunctionBase>(method);
+        JIT_HELPER_NOT_REENTRANT_HEADER(SetHomeObj, reentrancylock, scriptFunction->GetScriptContext()->GetThreadContext());
         scriptFunction->SetHomeObj(homeObj);
+        JIT_HELPER_END(SetHomeObj);
     }
 
     Var JavascriptOperators::OP_LdHomeObj(Var scriptFunction, ScriptContext * scriptContext)
@@ -9804,7 +9816,7 @@ SetElementIHelper_INDEX_TYPE_IS_NUMBER:
 
     Var JavascriptOperators::OP_ImportCall(__in JavascriptFunction *function, __in Var specifier, __in ScriptContext* scriptContext)
     {
-        JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(ImportCall);
+        JIT_HELPER_REENTRANT_HEADER(ImportCall);
         ModuleRecordBase *moduleRecordBase = nullptr;
         SourceTextModuleRecord *moduleRecord = nullptr;
 
@@ -10614,14 +10626,14 @@ SetElementIHelper_INDEX_TYPE_IS_NUMBER:
         JIT_HELPER_END(Op_ConvObject);
     }
 
-    Var JavascriptOperators::ToWithObject(Var aRight, ScriptContext* scriptContext)
+    Var JavascriptOperators::ToUnscopablesWrapperObject(Var aRight, ScriptContext* scriptContext)
     {
-        JIT_HELPER_NOT_REENTRANT_HEADER(Op_NewWithObject, reentrancylock, scriptContext->GetThreadContext());
+        JIT_HELPER_NOT_REENTRANT_HEADER(Op_NewUnscopablesWrapperObject, reentrancylock, scriptContext->GetThreadContext());
         RecyclableObject* object = VarTo<RecyclableObject>(aRight);
 
-        WithScopeObject* withWrapper = RecyclerNew(scriptContext->GetRecycler(), WithScopeObject, object, scriptContext->GetLibrary()->GetWithType());
+        UnscopablesWrapperObject* withWrapper = RecyclerNew(scriptContext->GetRecycler(), UnscopablesWrapperObject, object, scriptContext->GetLibrary()->GetWithType());
         return withWrapper;
-        JIT_HELPER_END(Op_NewWithObject);
+        JIT_HELPER_END(Op_NewUnscopablesWrapperObject);
     }
 
     Var JavascriptOperators::ToNumber(Var aRight, ScriptContext* scriptContext)

+ 3 - 6
lib/Runtime/Language/JavascriptOperators.h

@@ -43,10 +43,7 @@ namespace Js
     if (exceptionObject != nullptr) \
     { \
         Js::Var errorObject = exceptionObject->GetThrownObject(nullptr); \
-        HRESULT hr = (errorObject != nullptr && Js::VarIs<Js::JavascriptError>(errorObject)) \
-                     ? Js::JavascriptError::GetRuntimeError(Js::VarTo<Js::RecyclableObject>(errorObject), nullptr) \
-                     : S_OK; \
-        if (JavascriptError::GetErrorNumberFromResourceID(JSERR_UndefVariable) != (int32)hr) \
+        if (JavascriptError::ShouldTypeofErrorBeReThrown(errorObject)) \
         { \
             if (scriptContext->IsScriptContextInDebugMode()) \
             { \
@@ -125,7 +122,7 @@ namespace Js
         static bool IsConstructorSuperCall(Arguments args);
         static bool GetAndAssertIsConstructorSuperCall(Arguments args);
         static RecyclableObject* ToObject(Var aRight,ScriptContext* scriptContext);
-        static Var ToWithObject(Var aRight, ScriptContext* scriptContext);
+        static Var ToUnscopablesWrapperObject(Var aRight, ScriptContext* scriptContext);
         static Var OP_LdCustomSpreadIteratorList(Var aRight, ScriptContext* scriptContext);
         static Var ToNumber(Var aRight,ScriptContext* scriptContext);
         static Var ToNumberInPlace(Var aRight,ScriptContext* scriptContext, JavascriptNumber* result);
@@ -453,7 +450,7 @@ namespace Js
 
         static FunctionInfo * GetConstructorFunctionInfo(Var instance, ScriptContext * scriptContext);
         // Detach the type array buffer, if possible, and returns the state of the object which can be used to initialize another object
-        static DetachedStateBase* DetachVarAndGetState(Var var);
+        static DetachedStateBase* DetachVarAndGetState(Var var, bool queueForDelayFree = true);
         static bool IsObjectDetached(Var var);
         // This will return a new object from the state returned by the above operation
         static Var NewVarFromDetachedState(DetachedStateBase* state, JavascriptLibrary *library);

+ 0 - 1
lib/Runtime/Language/JavascriptStackWalker.cpp

@@ -1387,7 +1387,6 @@ namespace Js
         if (!boxArgsAndDeepCopy)
         {
             args = &currentFrame->argv[firstArg];
-
         }
         else
         {

+ 252 - 59
lib/Runtime/Library/ArrayBuffer.cpp

@@ -6,6 +6,39 @@
 
 namespace Js
 {
+    long RefCountedBuffer::AddRef()
+    {
+        long ref = InterlockedIncrement(&refCount);
+        AssertOrFailFast(ref > 1);
+        return ref;
+    }
+
+    long RefCountedBuffer::Release()
+    {
+        long ref = InterlockedDecrement(&refCount);
+        AssertOrFailFastMsg(ref >= 0, "Buffer already freed");
+        return ref;
+    }
+
+    void ArrayBufferDetachedStateBase::AddRefBufferContent()
+    {
+        if (buffer != nullptr)
+        {
+            buffer->AddRef();
+        }
+    }
+
+    long ArrayBufferDetachedStateBase::ReleaseRefBufferContent()
+    {
+        if (buffer != nullptr)
+        {
+            long ref = buffer->Release();
+            AssertOrFailFast(ref > 0);
+            return ref;
+        }
+        return 0;
+    }
+
     template <> bool VarIsImpl<ArrayBufferBase>(RecyclableObject* obj)
     {
         return VarIs<ArrayBuffer>(obj) || VarIs<SharedArrayBuffer>(obj);
@@ -35,6 +68,16 @@ namespace Js
         return toReturn;
     }
 
+    uint32 ArrayBuffer::GetByteLength() const
+    {
+        return this->bufferLength;
+    }
+
+    BYTE* ArrayBuffer::GetBuffer() const
+    {
+        return this->bufferContent != nullptr ? this->bufferContent->GetBuffer() : nullptr;
+    }
+
     void ArrayBuffer::DetachBufferFromParent(ArrayBufferParent* parent)
     {
         if (parent == nullptr)
@@ -219,7 +262,7 @@ namespace Js
         // report that we no longer own the memory
         ReportExternalMemoryFree();
 
-        this->buffer = nullptr;
+        this->bufferContent = nullptr;
         this->bufferLength = 0;
         this->isDetached = true;
 
@@ -242,11 +285,19 @@ namespace Js
         }
     }
 
-    ArrayBufferDetachedStateBase* ArrayBuffer::DetachAndGetState()
+    ArrayBufferDetachedStateBase* ArrayBuffer::DetachAndGetState(bool queueForDelayFree/* = true*/)
     {
         // Save the state before detaching
-        AutoPtr<ArrayBufferDetachedStateBase> arrayBufferState(this->CreateDetachedState(this->buffer, this->bufferLength));
+        AutoPtr<ArrayBufferDetachedStateBase> arrayBufferState(this->CreateDetachedState(this->bufferContent, this->bufferLength));
         Detach();
+
+        // Now put this bufferContent to the queue so that we can manage the lifetime of the buffer later.
+        if (queueForDelayFree && arrayBufferState->buffer != nullptr)
+        {
+            DelayedFreeArrayBuffer * local = GetScriptContext()->GetThreadContext()->GetScanStackCallback();
+            local->Push(this->CopyBufferContentForDelayedFree(arrayBufferState->buffer, arrayBufferState->bufferLength));
+        }
+
         return arrayBufferState.Detach();
     }
 
@@ -445,7 +496,8 @@ namespace Js
         }
 
         // Discard the buffer
-        DetachedStateBase* state = arrayBuffer->DetachAndGetState();
+        // We are clearing out the buffer now instead of queueing to flush out JIT bugs.
+        DetachedStateBase* state = arrayBuffer->DetachAndGetState(false /*queueForDelayFree*/);
         state->CleanUp();
 
         return scriptContext->GetLibrary()->GetUndefined();
@@ -565,9 +617,9 @@ namespace Js
         // Don't bother doing memcpy if we aren't copying any elements
         if (byteLength > 0)
         {
-            AssertMsg(arrayBuffer->buffer != nullptr, "buffer must not be null when we copy from it");
+            AssertMsg(arrayBuffer->GetBuffer() != nullptr, "buffer must not be null when we copy from it");
 
-            js_memcpy_s(newBuffer->buffer, byteLength, arrayBuffer->buffer + start, byteLength);
+            js_memcpy_s(newBuffer->GetBuffer(), byteLength, arrayBuffer->GetBuffer() + start, byteLength);
         }
 
         return newBuffer;
@@ -582,18 +634,36 @@ namespace Js
         return args[0];
     }
 
+    ArrayBufferContentForDelayedFreeBase* ArrayBuffer::CopyBufferContentForDelayedFree(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength)
+    {
+        Assert(content != nullptr);
+        FreeFn* freeFn = nullptr;
+#if ENABLE_FAST_ARRAYBUFFER
+        if (IsValidVirtualBufferLength(bufferLength))
+        {
+            freeFn = FreeMemAlloc;
+        }
+        else
+#endif
+        {
+            freeFn = free;
+        }
+
+        // This heap object will be deleted when the Recycler::DelayedFreeArrayBuffer determines to remove this item
+        return HeapNew(ArrayBufferContentForDelayedFree<FreeFn>, content, bufferLength, GetScriptContext()->GetRecycler(), freeFn);
+    }
+
     template <class Allocator>
     ArrayBuffer::ArrayBuffer(uint32 length, DynamicType * type, Allocator allocator) :
-        ArrayBufferBase(type)
+        ArrayBufferBase(type), bufferContent(nullptr), bufferLength(0)
     {
-        buffer = nullptr;
-        bufferLength = 0;
         if (length > MaxArrayBufferLength)
         {
             JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
         }
         else if (length > 0)
         {
+            BYTE * buffer = nullptr;
             Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
             if (recycler->RequestExternalMemoryAllocation(length))
             {
@@ -618,20 +688,45 @@ namespace Js
                 }
             }
 
-            if (buffer != nullptr)
+            if (buffer == nullptr)
             {
-                bufferLength = length;
-                ZeroMemory(buffer, bufferLength);
+                JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
             }
             else
             {
-                JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
+                bufferLength = length;
+                ZeroMemory(buffer, bufferLength);
+                RefCountedBuffer* localContent = HeapNew(RefCountedBuffer, buffer);
+                this->bufferContent = localContent;
             }
         }
     }
 
+    ArrayBuffer::ArrayBuffer(RefCountedBuffer* buffContent, uint32 length, DynamicType * type)
+       : bufferContent(nullptr), bufferLength(length), ArrayBufferBase(type)
+    {
+        if (length > MaxArrayBufferLength)
+        {
+            JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
+        }
+
+        // we take the ownership of the buffer and will have to free it so charge it to our quota.
+        if (!this->GetRecycler()->RequestExternalMemoryAllocation(length))
+        {
+            JavascriptError::ThrowOutOfMemoryError(this->GetScriptContext());
+        }
+
+        this->bufferContent = buffContent;
+
+        // The bufferContent can be null as might have detached an ArrayBuffer which does not have bufferContent.
+        if (this->bufferContent != nullptr)
+        {
+            this->bufferContent->AddRef();
+        }
+    }
+
     ArrayBuffer::ArrayBuffer(byte* buffer, uint32 length, DynamicType * type, bool isExternal) :
-        buffer(buffer), bufferLength(length), ArrayBufferBase(type)
+        bufferContent(nullptr), bufferLength(length), ArrayBufferBase(type)
     {
         if (length > MaxArrayBufferLength)
         {
@@ -646,6 +741,12 @@ namespace Js
                 JavascriptError::ThrowOutOfMemoryError(this->GetScriptContext());
             }
         }
+
+        if (buffer != nullptr)
+        {
+            RefCountedBuffer* localContent = HeapNew(RefCountedBuffer, buffer);
+            this->bufferContent = localContent;
+        }
     }
 
     BOOL ArrayBuffer::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
@@ -660,6 +761,18 @@ namespace Js
         return TRUE;
     }
 
+    void ArrayBuffer::ReleaseBufferContent()
+    {
+        if (this->bufferContent != nullptr && this->bufferContent->GetBuffer() != nullptr)
+        {
+            RefCountedBuffer *content = this->bufferContent;
+            this->bufferContent = nullptr;
+            long refCount = content->Release();
+            AssertOrFailFast(refCount == 0);
+            HeapDelete(content);
+        }
+    }
+
 #if ENABLE_TTD
     void ArrayBufferParent::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
     {
@@ -681,6 +794,12 @@ namespace Js
     {
     }
 
+    JavascriptArrayBuffer::JavascriptArrayBuffer(RefCountedBuffer* buffer, uint32 length, DynamicType * type) :
+        ArrayBuffer(buffer, length, type)
+    {
+    }
+
+
     JavascriptArrayBuffer::JavascriptArrayBuffer(DynamicType * type) : ArrayBuffer(0, type, malloc)
     {
     }
@@ -703,18 +822,16 @@ namespace Js
         return result;
     }
 
-    template <typename FreeFN>
-    void Js::ArrayBuffer::ArrayBufferDetachedState<FreeFN>::DiscardState()
+    JavascriptArrayBuffer* JavascriptArrayBuffer::Create(RefCountedBuffer* content, uint32 length, DynamicType * type)
     {
-        if (this->buffer != nullptr)
-        {
-            freeFunction(this->buffer);
-            this->buffer = nullptr;
-        }
-        this->bufferLength = 0;
+        Recycler* recycler = type->GetScriptContext()->GetRecycler();
+        JavascriptArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptArrayBuffer, content, length, type);
+        Assert(result);
+        recycler->AddExternalMemoryUsage(length);
+        return result;
     }
 
-    ArrayBufferDetachedStateBase* JavascriptArrayBuffer::CreateDetachedState(BYTE* buffer, uint32 bufferLength)
+    ArrayBufferDetachedStateBase* JavascriptArrayBuffer::CreateDetachedState(RefCountedBuffer * content, uint32 bufferLength)
     {
         FreeFn* freeFn = nullptr;
         ArrayBufferAllocationType allocationType;
@@ -730,7 +847,7 @@ namespace Js
             allocationType = ArrayBufferAllocationType::Heap;
             freeFn = free;
         }
-        return HeapNew(ArrayBufferDetachedState<FreeFn>, buffer, bufferLength, freeFn, GetScriptContext()->GetRecycler(), allocationType);
+        return HeapNew(ArrayBufferDetachedState<FreeFn>, content, bufferLength, freeFn, GetScriptContext()->GetRecycler(), allocationType);
     }
 
 
@@ -772,26 +889,41 @@ namespace Js
 
     void JavascriptArrayBuffer::Finalize(bool isShutdown)
     {
-        // Recycler may not be available at Dispose. We need to
-        // free the memory and report that it has been freed at the same
-        // time. Otherwise, AllocationPolicyManager is unable to provide correct feedback
-#if ENABLE_FAST_ARRAYBUFFER
-        //AsmJS Virtual Free
-        if (buffer && IsValidVirtualBufferLength(this->bufferLength))
+        if (this->bufferContent == nullptr)
         {
-            FreeMemAlloc(buffer);
+            return;
         }
-        else
+
+        RefCountedBuffer *content = this->bufferContent;
+        this->bufferContent = nullptr;
+        long refCount = content->Release();
+        if (refCount == 0)
         {
-            free(buffer);
-        }
+            BYTE * buffer = content->GetBuffer();
+            if (buffer)
+            {
+                // Recycler may not be available at Dispose. We need to
+                // free the memory and report that it has been freed at the same
+                // time. Otherwise, AllocationPolicyManager is unable to provide correct feedback
+#if ENABLE_FAST_ARRAYBUFFER
+        //AsmJS Virtual Free
+                if (buffer && IsValidVirtualBufferLength(this->bufferLength))
+                {
+                    FreeMemAlloc(buffer);
+                }
+                else
+                {
+                    free(buffer);
+                }
 #else
-        free(buffer);
+                free(buffer);
 #endif
-        Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
-        recycler->ReportExternalMemoryFree(bufferLength);
+            }
+            Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
+            recycler->ReportExternalMemoryFree(bufferLength);
+            HeapDelete(content);
+        }
 
-        buffer = nullptr;
         bufferLength = 0;
     }
 
@@ -846,12 +978,19 @@ namespace Js
 #endif
         Assert(allocator == WasmVirtualAllocator);
         // Make sure we always have a buffer even if the length is 0
-        if (buffer == nullptr && length == 0)
+        if (bufferContent == nullptr && length == 0)
         {
             // We want to allocate an empty buffer using virtual memory
-            buffer = (BYTE*)allocator(0);
+            BYTE *buffer = (BYTE*)allocator(0);
+            if (buffer == nullptr)
+            {
+                JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
+            }
+
+            RefCountedBuffer* localContent = HeapNew(RefCountedBuffer, buffer);
+            this->bufferContent = localContent;
         }
-        if (buffer == nullptr)
+        if (bufferContent == nullptr)
         {
             JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
         }
@@ -861,6 +1000,15 @@ namespace Js
     WebAssemblyArrayBuffer::WebAssemblyArrayBuffer(uint32 length, DynamicType * type) :
         JavascriptArrayBuffer(length, type, malloc)
     {
+        // Make sure we always have a bufferContent even if the length is 0
+        if (bufferContent == nullptr && length == 0)
+        {
+            bufferContent = HeapNew(RefCountedBuffer, nullptr);
+        }
+        if (bufferContent == nullptr)
+        {
+            JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
+        }
     }
 
     WebAssemblyArrayBuffer::WebAssemblyArrayBuffer(byte* buffer, uint32 length, DynamicType * type):
@@ -910,7 +1058,7 @@ namespace Js
 #endif
     }
 
-    ArrayBufferDetachedStateBase* WebAssemblyArrayBuffer::CreateDetachedState(BYTE* buffer, uint32 bufferLength)
+    ArrayBufferDetachedStateBase* WebAssemblyArrayBuffer::CreateDetachedState(RefCountedBuffer* buffer, uint32 bufferLength)
     {
         JavascriptError::ThrowTypeError(GetScriptContext(), WASMERR_CantDetach);
     }
@@ -927,25 +1075,30 @@ namespace Js
         const auto finalizeGrowMemory = [&](WebAssemblyArrayBuffer* newArrayBuffer)
         {
             AssertOrFailFast(newArrayBuffer && newArrayBuffer->GetByteLength() == newBufferLength);
+            RefCountedBuffer *local = this->GetBufferContent();
             // Detach the buffer from this ArrayBuffer
             this->Detach();
+            if (local != nullptr)
+            {
+                HeapDelete(local);
+            }
             return newArrayBuffer;
         };
 
         // We're not growing the buffer, just create a new WebAssemblyArrayBuffer and detach this
         if (growSize == 0)
         {
-            return finalizeGrowMemory(this->GetLibrary()->CreateWebAssemblyArrayBuffer(this->buffer, this->bufferLength));
+            return finalizeGrowMemory(this->GetLibrary()->CreateWebAssemblyArrayBuffer(this->GetBuffer(), this->bufferLength));
         }
 
 #if ENABLE_FAST_ARRAYBUFFER
         // 8Gb Array case
         if (CONFIG_FLAG(WasmFastArray))
         {
-            AssertOrFailFast(this->buffer);
+            AssertOrFailFast(this->GetBuffer());
             const auto virtualAllocFunc = [&]
             {
-                return !!VirtualAlloc(this->buffer + this->bufferLength, growSize, MEM_COMMIT, PAGE_READWRITE);
+                return !!VirtualAlloc(this->GetBuffer() + this->bufferLength, growSize, MEM_COMMIT, PAGE_READWRITE);
             };
             if (!this->GetRecycler()->DoExternalAllocation(growSize, virtualAllocFunc))
             {
@@ -956,7 +1109,7 @@ namespace Js
             // To avoid double-charge to the allocation quota we will free the "diff" amount here.
             this->GetRecycler()->ReportExternalMemoryFree(growSize);
 
-            return finalizeGrowMemory(this->GetLibrary()->CreateWebAssemblyArrayBuffer(this->buffer, newBufferLength));
+            return finalizeGrowMemory(this->GetLibrary()->CreateWebAssemblyArrayBuffer(this->GetBuffer(), newBufferLength));
         }
 #endif
 
@@ -976,7 +1129,7 @@ namespace Js
             byte* newBuffer = nullptr;
             const auto reallocFunc = [&]
             {
-                newBuffer = ReallocZero(this->buffer, this->bufferLength, newBufferLength);
+                newBuffer = ReallocZero(this->GetBuffer(), this->bufferLength, newBufferLength);
                 if (newBuffer != nullptr)
                 {
                     // Realloc freed this->buffer
@@ -1013,6 +1166,11 @@ namespace Js
     {
     }
 
+    ProjectionArrayBuffer::ProjectionArrayBuffer(RefCountedBuffer* buffer, uint32 length, DynamicType * type) :
+        ArrayBuffer(buffer, length, type)
+    {
+    }
+
     ProjectionArrayBuffer* ProjectionArrayBuffer::Create(uint32 length, DynamicType * type)
     {
         Recycler* recycler = type->GetScriptContext()->GetRecycler();
@@ -1031,17 +1189,37 @@ namespace Js
 
     }
 
+    ProjectionArrayBuffer* ProjectionArrayBuffer::Create(RefCountedBuffer* buffer, uint32 length, DynamicType * type)
+    {
+        Recycler* recycler = type->GetScriptContext()->GetRecycler();
+
+        ProjectionArrayBuffer* result = RecyclerNewFinalized(recycler, ProjectionArrayBuffer, buffer, length, type);
+        // This is user passed [in] buffer, user should AddExternalMemoryUsage before calling jscript, but
+        // I don't see we ask everyone to do this. Let's add the memory pressure here as well.
+        recycler->AddExternalMemoryUsage(length);
+        return result;
+    }
+
     void ProjectionArrayBuffer::Finalize(bool isShutdown)
     {
-        CoTaskMemFree(buffer);
-        // Recycler may not be available at Dispose. We need to
-        // free the memory and report that it has been freed at the same
-        // time. Otherwise, AllocationPolicyManager is unable to provide correct feedback
-        Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
-        recycler->ReportExternalMemoryFree(bufferLength);
+        if (this->bufferContent == nullptr || this->bufferContent->GetBuffer() == nullptr)
+        {
+            return;
+        }
 
-        buffer = nullptr;
-        bufferLength = 0;
+        RefCountedBuffer *content = this->bufferContent;
+        this->bufferContent = nullptr;
+        long refCount = content->Release();
+        if (refCount == 0)
+        {
+            CoTaskMemFree(content->GetBuffer());
+            // Recycler may not be available at Dispose. We need to
+            // free the memory and report that it has been freed at the same
+            // time. Otherwise, AllocationPolicyManager is unable to provide correct feedback
+            Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
+            recycler->ReportExternalMemoryFree(bufferLength);
+            HeapDelete(content);
+        }
     }
 
     void ProjectionArrayBuffer::Dispose(bool isShutdown)
@@ -1049,6 +1227,12 @@ namespace Js
         /* See ProjectionArrayBuffer::Finalize */
     }
 
+    ArrayBufferContentForDelayedFreeBase* ProjectionArrayBuffer::CopyBufferContentForDelayedFree(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength)
+    {
+        // This heap object will be deleted when the Recycler::DelayedFreeArrayBuffer determines to remove this item
+        return HeapNew(ArrayBufferContentForDelayedFree<FreeFn>, content, bufferLength, GetRecycler(), CoTaskMemFree);
+    }
+
     ArrayBuffer* ExternalArrayBufferDetachedState::Create(JavascriptLibrary* library)
     {
         return library->CreateExternalArrayBuffer(buffer, bufferLength);
@@ -1059,13 +1243,18 @@ namespace Js
     {
     }
 
-    ExternalArrayBuffer* ExternalArrayBuffer::Create(byte* buffer, uint32 length, DynamicType * type)
+    ExternalArrayBuffer::ExternalArrayBuffer(RefCountedBuffer *buffer, uint32 length, DynamicType *type)
+        : ArrayBuffer(buffer, length, type)
+    {
+    }
+
+    ExternalArrayBuffer* ExternalArrayBuffer::Create(RefCountedBuffer* buffer, uint32 length, DynamicType * type)
     {
         // This type does not own the external memory, so don't AddExternalMemoryUsage like other ArrayBuffer types do
         return RecyclerNewFinalized(type->GetScriptContext()->GetRecycler(), ExternalArrayBuffer, buffer, length, type);
     }
 
-    ArrayBufferDetachedStateBase* ExternalArrayBuffer::CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength)
+    ArrayBufferDetachedStateBase* ExternalArrayBuffer::CreateDetachedState(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength)
     {
         return HeapNew(ExternalArrayBufferDetachedState, buffer, bufferLength);
     };
@@ -1101,7 +1290,7 @@ namespace Js
     }
 #endif
 
-    ExternalArrayBufferDetachedState::ExternalArrayBufferDetachedState(BYTE* buffer, uint32 bufferLength)
+    ExternalArrayBufferDetachedState::ExternalArrayBufferDetachedState(RefCountedBuffer* buffer, uint32 bufferLength)
         : ArrayBufferDetachedStateBase(TypeIds_ArrayBuffer, buffer, bufferLength, ArrayBufferAllocationType::External)
     {}
 
@@ -1110,9 +1299,13 @@ namespace Js
         HeapDelete(this);
     }
 
+    void NoOpFree(byte* data) { }
+
     void ExternalArrayBufferDetachedState::DiscardState()
     {
-        // Nothing to do as buffer is external
+        // Don't actually free the data as it's externally managed, but do the
+        // appropriate cleanup for our RefCountedBuffer.
+        DiscardStateBase(NoOpFree);
     }
 
     void ExternalArrayBufferDetachedState::Discard()

+ 59 - 18
lib/Runtime/Library/ArrayBuffer.h

@@ -11,6 +11,7 @@ namespace Js
     class ArrayBufferParent;
     class ArrayBuffer;
     class SharedArrayBuffer;
+    class ArrayBufferContentForDelayedFreeBase;
 
     class ArrayBufferBase : public DynamicObject
     {
@@ -101,6 +102,29 @@ namespace Js
 
     template <> bool VarIsImpl<ArrayBufferBase>(RecyclableObject* obj);
 
+    // This encapsulate buffer blob and the refCount.
+    class RefCountedBuffer
+    {
+    private:
+        FieldNoBarrier(BYTE*) buffer; // Points to a heap allocated RGBA buffer, can be null
+
+        // Addref/release counter for current buffer, this is needed hold the current buffer alive
+        Field(long) refCount;
+    public:
+        long AddRef();
+        long Release();
+        BYTE* GetBuffer() { return buffer; };
+        long GetRefCount() { return refCount; }
+
+        static int GetBufferOffset() { return offsetof(RefCountedBuffer, buffer); }
+
+        RefCountedBuffer(BYTE* b)
+            : buffer(b), refCount(1)
+        { }
+    };
+
+
+
     class ArrayBuffer : public ArrayBufferBase
     {
     public:
@@ -116,9 +140,9 @@ namespace Js
         public:
             FreeFN* freeFunction;
             Recycler* recycler;
-            ArrayBufferDetachedState(BYTE* buffer, uint32 bufferLength, FreeFN* freeFunction, Recycler* recycler, ArrayBufferAllocationType allocationType)
+            ArrayBufferDetachedState(RefCountedBuffer* buffer, uint32 bufferLength, FreeFN* freeFunction, Recycler* r, ArrayBufferAllocationType allocationType)
                 : ArrayBufferDetachedStateBase(TypeIds_ArrayBuffer, buffer, bufferLength, allocationType),
-                recycler(recycler),
+                recycler(r),
                 freeFunction(freeFunction)
             {}
 
@@ -127,7 +151,10 @@ namespace Js
                 HeapDelete(this);
             }
 
-            virtual void DiscardState() override;
+            virtual void DiscardState() override
+            {
+                DiscardStateBase(freeFunction);
+            }
 
             virtual void Discard() override
             {
@@ -140,6 +167,8 @@ namespace Js
 
         ArrayBuffer(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type, bool isExternal = false);
 
+        ArrayBuffer(RefCountedBuffer* buffContent, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
+
         class EntryInfo
         {
         public:
@@ -167,12 +196,12 @@ namespace Js
         virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
         virtual BOOL GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
 
-        ArrayBufferDetachedStateBase* DetachAndGetState();
-        virtual uint32 GetByteLength() const override { return bufferLength; }
-        virtual BYTE* GetBuffer() const override { return buffer; }
-
+        ArrayBufferDetachedStateBase* DetachAndGetState(bool queueForDelayFree = true);
+        virtual uint32 GetByteLength() const override;
+        virtual BYTE* GetBuffer() const override;
+        RefCountedBuffer *GetBufferContent() { return bufferContent;  }
+        static int GetBufferContentsOffset() { return offsetof(ArrayBuffer, bufferContent); }
         static int GetByteLengthOffset() { return offsetof(ArrayBuffer, bufferLength); }
-        static int GetBufferOffset() { return offsetof(ArrayBuffer, buffer); }
 
         virtual void AddParent(ArrayBufferParent* parent) override;
 #if defined(TARGET_64)
@@ -189,6 +218,8 @@ namespace Js
         virtual bool IsSharedArrayBuffer() override { return false; }
         virtual ArrayBuffer * GetAsArrayBuffer() override;
 
+        virtual ArrayBufferContentForDelayedFreeBase* CopyBufferContentForDelayedFree(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength);
+
         static uint32 ToIndex(Var value, int32 errorCode, ScriptContext *scriptContext, uint32 MaxAllowedLength, bool checkSameValueZero = true);
 
     protected:
@@ -196,7 +227,10 @@ namespace Js
         void Detach();
 
         typedef void __cdecl FreeFn(void* ptr);
-        virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) = 0;
+        virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) = 0;
+
+        // This function will be called from External buffer and projection buffer as they pass the buffer
+        virtual void ReleaseBufferContent();
 
         //In most cases, the ArrayBuffer will only have one parent
         Field(RecyclerWeakReference<ArrayBufferParent>*) primaryParent;
@@ -211,8 +245,7 @@ namespace Js
         };
 
         Field(OtherParents*) otherParents;
-
-        FieldNoBarrier(BYTE*) buffer;             // Points to a heap allocated RGBA buffer, can be null
+        FieldNoBarrier(RefCountedBuffer *) bufferContent;
         Field(uint32) bufferLength;       // Number of bytes allocated
     };
 
@@ -263,6 +296,7 @@ namespace Js
     public:
         static JavascriptArrayBuffer* Create(DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
         static JavascriptArrayBuffer* Create(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
+        static JavascriptArrayBuffer* Create(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
         virtual void Dispose(bool isShutdown) override;
         virtual void Finalize(bool isShutdown) override;
 
@@ -272,12 +306,13 @@ namespace Js
 
     protected:
         JavascriptArrayBuffer(DynamicType * type);
-        virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
+        virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
 
         template<typename Allocator>
         JavascriptArrayBuffer(uint32 length, DynamicType * type, Allocator allocator): ArrayBuffer(length, type, allocator){}
         JavascriptArrayBuffer(uint32 length, DynamicType * type);
         JavascriptArrayBuffer(byte* buffer, uint32 length, DynamicType * type);
+        JavascriptArrayBuffer(RefCountedBuffer* buffer, uint32 length, DynamicType * type);
 
 #if ENABLE_TTD
     public:
@@ -304,7 +339,7 @@ namespace Js
         virtual bool IsWebAssemblyArrayBuffer() override { return true; }
 
     protected:
-        virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
+        virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
     };
 #endif
 
@@ -316,9 +351,9 @@ namespace Js
         DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(ProjectionArrayBuffer);
 
         typedef void __stdcall FreeFn(LPVOID ptr);
-        virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override
+        virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override
         {
-            return HeapNew(ArrayBufferDetachedState<FreeFn>, buffer, bufferLength, CoTaskMemFree, GetScriptContext()->GetRecycler(), ArrayBufferAllocationType::CoTask);
+            return HeapNew(ArrayBufferDetachedState<FreeFn>, content, bufferLength, CoTaskMemFree, GetScriptContext()->GetRecycler(), ArrayBufferAllocationType::CoTask);
         }
 
     public:
@@ -326,11 +361,16 @@ namespace Js
         static ProjectionArrayBuffer* Create(DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
         // take over ownership. a CoTaskMemAlloc'ed buffer passed in via projection.
         static ProjectionArrayBuffer* Create(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
+        static ProjectionArrayBuffer* Create(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
+
+        virtual ArrayBufferContentForDelayedFreeBase* CopyBufferContentForDelayedFree(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
+
         virtual void Dispose(bool isShutdown) override;
         virtual void Finalize(bool isShutdown) override;
     private:
         ProjectionArrayBuffer(uint32 length, DynamicType * type);
         ProjectionArrayBuffer(byte* buffer, uint32 length, DynamicType * type);
+        ProjectionArrayBuffer(RefCountedBuffer* buffer, uint32 length, DynamicType * type);
     };
 
     // non-owning ArrayBuffer used for wrapping external data
@@ -342,9 +382,10 @@ namespace Js
 
     public:
         ExternalArrayBuffer(byte *buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType *type);
-        static ExternalArrayBuffer* Create(byte* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
+        ExternalArrayBuffer(RefCountedBuffer *buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType *type);
+        static ExternalArrayBuffer* Create(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 length, DynamicType * type);
     protected:
-        virtual ArrayBufferDetachedStateBase* CreateDetachedState(BYTE* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
+        virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer* buffer, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override;
         virtual void ReportExternalMemoryFree() override;
 
 #if ENABLE_TTD
@@ -357,7 +398,7 @@ namespace Js
     class ExternalArrayBufferDetachedState : public ArrayBufferDetachedStateBase
     {
     public:
-        ExternalArrayBufferDetachedState(BYTE* buffer, uint32 bufferLength);
+        ExternalArrayBufferDetachedState(RefCountedBuffer* buffer, uint32 bufferLength);
         virtual void ClearSelfOnly() override;
         virtual void DiscardState() override;
         virtual void Discard() override;

+ 5 - 2
lib/Runtime/Library/BoundFunction.cpp

@@ -134,7 +134,11 @@ namespace Js
                 }
                 else
                 {
-                    args.Values[0] = newVarInstance = JavascriptOperators::CreateFromConstructor(newTarget, scriptContext);
+                    BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
+                    {
+                        args.Values[0] = newVarInstance = JavascriptOperators::CreateFromConstructor(newTarget, scriptContext);
+                    }
+                    END_SAFE_REENTRANT_CALL
                 }
             }
             else if (!VarIs<JavascriptProxy>(targetFunction))
@@ -170,7 +174,6 @@ namespace Js
             }
 
             Field(Var) *newValues = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), newArgCount);
-
             uint index = 0;
 
             //

+ 137 - 136
lib/Runtime/Library/CMakeLists.txt

@@ -1,136 +1,137 @@
-if(BuildJIT)
-    add_definitions(-D_ENABLE_DYNAMIC_THUNKS=1)
-endif()
-
-if(CC_TARGETS_AMD64)
-    set(Wasm_dep
-    WasmLibrary.cpp
-    WebAssembly.cpp
-    WebAssemblyEnvironment.cpp
-    WebAssemblyInstance.cpp
-    WebAssemblyMemory.cpp
-    WebAssemblyModule.cpp
-    WebAssemblyTable.cpp
-    WabtInterface.cpp
-    )
-endif()
-
-if(CAN_BUILD_WABT)
-    include_directories(
-    ../../wabt/chakra
-    )
-endif()
-
-set(CRLIB_SOURCE_CODES
-    ArgumentsObject.cpp
-    ArgumentsObjectEnumerator.cpp
-    ArrayBuffer.cpp
-    AtomicsOperations.cpp
-    AtomicsObject.cpp
-    BoundFunction.cpp
-    BufferStringBuilder.cpp
-    CommonExternalApiImpl.cpp
-    CompoundString.cpp
-    ConcatString.cpp
-    DataView.cpp
-    DateImplementation.cpp
-    ES5Array.cpp
-    ES5ArrayIndexEnumerator.cpp
-    EngineInterfaceObject.cpp
-    ExternalLibraryBase.cpp
-    ForInObjectEnumerator.cpp
-    GlobalObject.cpp
-    IntlEngineInterfaceExtensionObject.cpp
-    JSON.cpp
-    JSONParser.cpp
-    JSONScanner.cpp
-    JSONStack.cpp
-    JSONStringBuilder.cpp
-    JSONStringifier.cpp
-    JavascriptArray.cpp
-    JavascriptArrayIndexEnumerator.cpp
-    JavascriptArrayIndexEnumeratorBase.cpp
-    JavascriptArrayIndexSnapshotEnumerator.cpp
-    JavascriptArrayIterator.cpp
-    JavascriptBoolean.cpp
-    JavascriptBooleanObject.cpp
-    JavascriptBuiltInFunctions.cpp
-    JavascriptDate.cpp
-    JavascriptError.cpp
-    # JavascriptErrorDebug.cpp
-    JavascriptExceptionMetadata.cpp
-    JavascriptExternalFunction.cpp
-    JavascriptFunction.cpp
-    JavascriptGenerator.cpp
-    JavascriptGeneratorFunction.cpp
-    JavascriptIterator.cpp
-    JavascriptLibrary.cpp
-    JavascriptListIterator.cpp
-    JavascriptMap.cpp
-    JavascriptMapIterator.cpp
-    JavascriptNumber.cpp
-    JavascriptNumberObject.cpp
-    JavascriptObject.cpp
-    JavascriptPromise.cpp
-    JavascriptProxy.cpp
-    JavascriptReflect.cpp
-    JavascriptRegExpConstructor.cpp
-    JavascriptRegExpEnumerator.cpp
-    JavascriptRegularExpression.cpp
-    JavascriptRegularExpressionResult.cpp
-    JavascriptSet.cpp
-    JavascriptSetIterator.cpp
-    JavascriptString.cpp
-    JavascriptStringEnumerator.cpp
-    JavascriptStringIterator.cpp
-    JavascriptStringObject.cpp
-    JavascriptBigInt.cpp
-    JavascriptBigIntObject.cpp
-    JavascriptSymbol.cpp
-    JavascriptSymbolObject.cpp
-    JavascriptTypedNumber.cpp
-    JavascriptVariantDate.cpp
-    JavascriptWeakMap.cpp
-    JavascriptWeakSet.cpp
-    JsBuiltInEngineInterfaceExtensionObject.cpp
-    LazyJSONString.cpp
-    LiteralString.cpp
-    MathLibrary.cpp
-    ModuleRoot.cpp
-    ObjectPrototypeObject.cpp
-    ProfileString.cpp
-    PropertyRecordUsageCache.cpp
-    PropertyString.cpp
-    RegexHelper.cpp
-    RootObjectBase.cpp
-    RuntimeFunction.cpp
-    RuntimeLibraryPch.cpp
-    ScriptFunction.cpp
-    SharedArrayBuffer.cpp
-    SingleCharString.cpp
-    SparseArraySegment.cpp
-    StackScriptFunction.cpp
-    StringCopyInfo.cpp
-    SubString.cpp
-    ThrowErrorObject.cpp
-    TypedArray.cpp
-    TypedArrayIndexEnumerator.cpp
-    UriHelper.cpp
-    VerifyMarkFalseReference.cpp
-    ${Wasm_dep}
-    )
-
-if(CC_TARGETS_AMD64)
-    set (CRLIB_SOURCE_CODES ${CRLIB_SOURCE_CODES}
-        amd64/JavascriptFunctionA.S
-    )
-elseif(CC_TARGETS_ARM)
-    set (CRLIB_SOURCE_CODES ${CRLIB_SOURCE_CODES}
-        arm/arm_JavascriptFunctionA.S
-    )
-endif()
-
-add_library (Chakra.Runtime.Library OBJECT ${CRLIB_SOURCE_CODES})
-
-target_include_directories (
-    Chakra.Runtime.Library PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+if(BuildJIT)
+    add_definitions(-D_ENABLE_DYNAMIC_THUNKS=1)
+endif()
+
+if(CC_TARGETS_AMD64)
+    set(Wasm_dep
+    WasmLibrary.cpp
+    WebAssembly.cpp
+    WebAssemblyEnvironment.cpp
+    WebAssemblyInstance.cpp
+    WebAssemblyMemory.cpp
+    WebAssemblyModule.cpp
+    WebAssemblyTable.cpp
+    WabtInterface.cpp
+    )
+endif()
+
+if(CAN_BUILD_WABT)
+    include_directories(
+    ../../wabt/chakra
+    )
+endif()
+
+set(CRLIB_SOURCE_CODES
+    ArgumentsObject.cpp
+    ArgumentsObjectEnumerator.cpp
+    DelayFreeArrayBufferHelper.cpp
+    ArrayBuffer.cpp
+    AtomicsOperations.cpp
+    AtomicsObject.cpp
+    BoundFunction.cpp
+    BufferStringBuilder.cpp
+    CommonExternalApiImpl.cpp
+    CompoundString.cpp
+    ConcatString.cpp
+    DataView.cpp
+    DateImplementation.cpp
+    ES5Array.cpp
+    ES5ArrayIndexEnumerator.cpp
+    EngineInterfaceObject.cpp
+    ExternalLibraryBase.cpp
+    ForInObjectEnumerator.cpp
+    GlobalObject.cpp
+    IntlEngineInterfaceExtensionObject.cpp
+    JSON.cpp
+    JSONParser.cpp
+    JSONScanner.cpp
+    JSONStack.cpp
+    JSONStringBuilder.cpp
+    JSONStringifier.cpp
+    JavascriptArray.cpp
+    JavascriptArrayIndexEnumerator.cpp
+    JavascriptArrayIndexEnumeratorBase.cpp
+    JavascriptArrayIndexSnapshotEnumerator.cpp
+    JavascriptArrayIterator.cpp
+    JavascriptBoolean.cpp
+    JavascriptBooleanObject.cpp
+    JavascriptBuiltInFunctions.cpp
+    JavascriptDate.cpp
+    JavascriptError.cpp
+    # JavascriptErrorDebug.cpp
+    JavascriptExceptionMetadata.cpp
+    JavascriptExternalFunction.cpp
+    JavascriptFunction.cpp
+    JavascriptGenerator.cpp
+    JavascriptGeneratorFunction.cpp
+    JavascriptIterator.cpp
+    JavascriptLibrary.cpp
+    JavascriptListIterator.cpp
+    JavascriptMap.cpp
+    JavascriptMapIterator.cpp
+    JavascriptNumber.cpp
+    JavascriptNumberObject.cpp
+    JavascriptObject.cpp
+    JavascriptPromise.cpp
+    JavascriptProxy.cpp
+    JavascriptReflect.cpp
+    JavascriptRegExpConstructor.cpp
+    JavascriptRegExpEnumerator.cpp
+    JavascriptRegularExpression.cpp
+    JavascriptRegularExpressionResult.cpp
+    JavascriptSet.cpp
+    JavascriptSetIterator.cpp
+    JavascriptString.cpp
+    JavascriptStringEnumerator.cpp
+    JavascriptStringIterator.cpp
+    JavascriptStringObject.cpp
+    JavascriptBigInt.cpp
+    JavascriptBigIntObject.cpp
+    JavascriptSymbol.cpp
+    JavascriptSymbolObject.cpp
+    JavascriptTypedNumber.cpp
+    JavascriptVariantDate.cpp
+    JavascriptWeakMap.cpp
+    JavascriptWeakSet.cpp
+    JsBuiltInEngineInterfaceExtensionObject.cpp
+    LazyJSONString.cpp
+    LiteralString.cpp
+    MathLibrary.cpp
+    ModuleRoot.cpp
+    ObjectPrototypeObject.cpp
+    ProfileString.cpp
+    PropertyRecordUsageCache.cpp
+    PropertyString.cpp
+    RegexHelper.cpp
+    RootObjectBase.cpp
+    RuntimeFunction.cpp
+    RuntimeLibraryPch.cpp
+    ScriptFunction.cpp
+    SharedArrayBuffer.cpp
+    SingleCharString.cpp
+    SparseArraySegment.cpp
+    StackScriptFunction.cpp
+    StringCopyInfo.cpp
+    SubString.cpp
+    ThrowErrorObject.cpp
+    TypedArray.cpp
+    TypedArrayIndexEnumerator.cpp
+    UriHelper.cpp
+    VerifyMarkFalseReference.cpp
+    ${Wasm_dep}
+    )
+
+if(CC_TARGETS_AMD64)
+    set (CRLIB_SOURCE_CODES ${CRLIB_SOURCE_CODES}
+        amd64/JavascriptFunctionA.S
+    )
+elseif(CC_TARGETS_ARM)
+    set (CRLIB_SOURCE_CODES ${CRLIB_SOURCE_CODES}
+        arm/arm_JavascriptFunctionA.S
+    )
+endif()
+
+add_library (Chakra.Runtime.Library OBJECT ${CRLIB_SOURCE_CODES})
+
+target_include_directories (
+    Chakra.Runtime.Library PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

+ 2 - 0
lib/Runtime/Library/Chakra.Runtime.Library.vcxproj

@@ -46,6 +46,7 @@
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="$(MSBuildThisFileDirectory)ArrayBuffer.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)DelayFreeArrayBufferHelper.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)BoundFunction.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)BufferStringBuilder.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)CommonExternalApiImpl.cpp" />
@@ -177,6 +178,7 @@
     <ClInclude Include="JSONStringifier.h" />
     <ClInclude Include="LazyJSONString.h" />
     <ClInclude Include="SharedArrayBuffer.h" />
+    <ClInclude Include="DelayFreeArrayBufferHelper.h" />
     <ClInclude Include="ArrayBuffer.h" />
     <ClInclude Include="BoundFunction.h" />
     <ClInclude Include="BufferStringBuilder.h" />

+ 2 - 0
lib/Runtime/Library/Chakra.Runtime.Library.vcxproj.filters

@@ -2,6 +2,7 @@
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
     <ClCompile Include="$(MsBuildThisFileDirectory)ArrayBuffer.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)DelayFreeArrayBufferHelper.cpp" />
     <ClCompile Include="$(MsBuildThisFileDirectory)BoundFunction.cpp" />
     <ClCompile Include="$(MsBuildThisFileDirectory)BufferStringBuilder.cpp" />
     <ClCompile Include="$(MsBuildThisFileDirectory)CommonExternalApiImpl.cpp" />
@@ -109,6 +110,7 @@
     <ClInclude Include="..\InternalPropertyList.h" />
     <ClInclude Include="..\RuntimeCommon.h" />
     <ClInclude Include="..\SerializableFunctionFields.h" />
+    <ClInclude Include="DelayFreeArrayBufferHelper.h" />
     <ClInclude Include="ArrayBuffer.h" />
     <ClInclude Include="BoundFunction.h" />
     <ClInclude Include="BufferStringBuilder.h" />

+ 4 - 0
lib/Runtime/Library/DataView.cpp

@@ -654,7 +654,11 @@ namespace Js
         AssertMsg(this->GetArrayBuffer()->IsDetached(), "Array buffer should be detached if we're calling this method");
 
         this->length = 0;
+#if INT32VAR
+        this->buffer = (BYTE*)TaggedInt::ToVarUnchecked(0);
+#else
         this->buffer = nullptr;
+#endif
     }
 
 #ifdef _M_ARM

+ 127 - 0
lib/Runtime/Library/DelayFreeArrayBufferHelper.cpp

@@ -0,0 +1,127 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#include "RuntimeLibraryPch.h"
+
+namespace Js
+{
+
+    bool DelayedFreeArrayBuffer::HasAnyItem()
+    {
+        return !this->listOfBuffers.Empty();
+    }
+
+    void DelayedFreeArrayBuffer::Push(ArrayBufferContentForDelayedFreeBase* item)
+    {
+        AssertOrFailFast(item);
+        this->listOfBuffers.Push(item);
+    }
+
+    void Js::DelayedFreeArrayBuffer::ResetToNoMarkObject()
+    {
+        this->listOfBuffers.Map([](Js::ArrayBufferContentForDelayedFreeBase* item) {
+            Assert(item != nullptr);
+            item->SetMarkBit(false);
+        });
+    }
+
+    void DelayedFreeArrayBuffer::ReleaseOnlyNonMarkedObject()
+    {
+        FOREACH_SLIST_ENTRY_EDITING(ArrayBufferContentForDelayedFreeBase*, item, &this->listOfBuffers, iter)
+        {
+            if (!item->IsMarked())
+            {
+                item->Release();
+                item->ClearSelfOnly();
+                iter.RemoveCurrent();
+            }
+            else
+            {
+                // Reset the mark bit
+                item->SetMarkBit(false);
+            }
+        } NEXT_SLIST_ENTRY_EDITING
+    }
+
+    void DelayedFreeArrayBuffer::CheckAndMarkObject(void * candidate)
+    {
+        this->listOfBuffers.Map([&](Js::ArrayBufferContentForDelayedFreeBase* item) {
+            if (!item->IsMarked() && item->IsAddressPartOfBuffer(candidate))
+            {
+                item->SetMarkBit(true);
+            }
+        });
+    }
+
+    void DelayedFreeArrayBuffer::ClearAll()
+    {
+        if (HasAnyItem())
+        {
+            this->listOfBuffers.Map([](Js::ArrayBufferContentForDelayedFreeBase* item) {
+                item->Release();
+                item->ClearSelfOnly();
+            });
+
+            this->listOfBuffers.Clear();
+        }
+    }
+
+    void DelayedFreeArrayBuffer::ScanStack(void ** stackTop, size_t byteCount, void ** registers, size_t registersByteCount)
+    {
+        AssertOrFailFast(HasAnyItem());
+        ResetToNoMarkObject();
+
+        auto BufferFreeFunction = [&](void ** obj, size_t byteCount)
+        {
+            Assert(byteCount != 0);
+            Assert(byteCount % sizeof(void *) == 0);
+
+            void ** objEnd = obj + (byteCount / sizeof(void *));
+
+            do
+            {
+                // We need to ensure that the compiler does not reintroduce reads to the object after inlining.
+                // This could cause the value to change after the marking checks (e.g., the null/low address check).
+                // Intrinsics avoid the expensive memory barrier on ARM (due to /volatile:ms).
+#if defined(_M_ARM64)
+                void * candidate = reinterpret_cast<void *>(__iso_volatile_load64(reinterpret_cast<volatile __int64 *>(obj)));
+#elif defined(_M_ARM)
+                void * candidate = reinterpret_cast<void *>(__iso_volatile_load32(reinterpret_cast<volatile __int32 *>(obj)));
+#else
+                void * candidate = *(static_cast<void * volatile *>(obj));
+#endif
+                CheckAndMarkObject(candidate);
+                obj++;
+            } while (obj != objEnd);
+
+        };
+
+        BufferFreeFunction(registers, registersByteCount);
+        BufferFreeFunction(stackTop, byteCount);
+
+        ReleaseOnlyNonMarkedObject();
+    }
+
+    void ArrayBufferContentForDelayedFreeBase::Release()
+    {
+        // this function will be called when we are releasing instance from the listOfBuffer which we have delayed.
+
+        RefCountedBuffer *content = this->buffer;
+        this->buffer = nullptr;
+        long refCount = content->Release();
+        if (refCount == 0)
+        {
+            FreeTheBuffer(content->GetBuffer());
+            HeapDelete(content);
+        }
+    }
+
+    bool ArrayBufferContentForDelayedFreeBase::IsAddressPartOfBuffer(void *obj)
+    {
+        void *start = this->buffer->GetBuffer();
+        void *end = this->buffer->GetBuffer() + this->bufferLength;
+        return start <= obj && obj < end;
+    }
+
+}

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.