Explorar o código

[MERGE #6122 @MikeHolman] May 2019 Security Update

Merge pull request #6122 from pr/MikeHolman/servicing/1905

May 2019 Security Update that addresses the following issues in ChakraCore:

CVE-2019-0911
CVE-2019-0912
CVE-2019-0913
CVE-2019-0914
CVE-2019-0915
CVE-2019-0916
CVE-2019-0917
CVE-2019-0922
CVE-2019-0923
CVE-2019-0924
CVE-2019-0925
CVE-2019-0927
CVE-2019-0933
CVE-2019-0937
Michael Holman %!s(int64=6) %!d(string=hai) anos
pai
achega
d797e3f00e

+ 1 - 1
Build/NuGet/.pack-version

@@ -1 +1 @@
-1.11.8
+1.11.9

+ 1 - 2
lib/Backend/BackwardPass.cpp

@@ -4677,10 +4677,9 @@ BackwardPass::ProcessNewScObject(IR::Instr* instr)
         return;
     }
 
-    if (instr->HasBailOutInfo())
+    if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & ~IR::BailOutKindBits) == IR::BailOutFailedCtorGuardCheck)
     {
         Assert(instr->IsProfiledInstr());
-        Assert(instr->GetBailOutKind() == IR::BailOutFailedCtorGuardCheck);
         Assert(instr->GetDst()->IsRegOpnd());
 
         BasicBlock * block = this->currentBlock;

+ 7 - 0
lib/Backend/GlobOpt.cpp

@@ -13199,6 +13199,7 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
     const bool useValueTypes = !IsLoopPrePass(); // Source value types are not guaranteed to be correct in a loop prepass
     switch(instr->m_opcode)
     {
+        case Js::OpCode::StElemC:
         case Js::OpCode::StElemI_A:
         case Js::OpCode::StElemI_A_Strict:
         {
@@ -13249,8 +13250,13 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
             }
             break;
 
+        case Js::OpCode::ConsoleScopedStFld:
+        case Js::OpCode::ConsoleScopedStFldStrict:
+        case Js::OpCode::ScopedStFld:
+        case Js::OpCode::ScopedStFldStrict:
         case Js::OpCode::StFld:
         case Js::OpCode::StFldStrict:
+        case Js::OpCode::StSuperFld:
         {
             Assert(instr->GetDst());
 
@@ -13462,6 +13468,7 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
             break;
 
         case Js::OpCode::NewScObjectNoCtor:
+        case Js::OpCode::NewScObjectNoCtorFull:
             if(doNativeArrayTypeSpec)
             {
                 // Class/object construction can make something a prototype

+ 8 - 1
lib/Backend/GlobOptArrays.cpp

@@ -1736,7 +1736,14 @@ void GlobOpt::ArraySrcOpt::Optimize()
     {
         if (newBaseValueType != baseValueType)
         {
-            UpdateValue(nullptr, nullptr, nullptr);
+            if (globOpt->IsSafeToTransferInPrePass(baseOpnd, baseValue))
+            {
+                UpdateValue(nullptr, nullptr, nullptr);
+            }
+            else if (isLikelyJsArray && globOpt->IsOperationThatLikelyKillsJsArraysWithNoMissingValues(instr) && baseValueInfo->HasNoMissingValues())
+            {
+                globOpt->ChangeValueType(nullptr, baseValue, baseValueInfo->Type().SetHasNoMissingValues(false), true);
+            }
         }
 
         // For javascript arrays and objects with javascript arrays:

+ 8 - 0
lib/Backend/GlobOptBailOut.cpp

@@ -1337,6 +1337,14 @@ GlobOpt::MayNeedBailOnImplicitCall(IR::Instr const * instr, Value const * src1Va
             );
     }
 
+    case Js::OpCode::NewScObjectNoCtor:
+        if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & ~IR::BailOutKindBits) == IR::BailOutFailedCtorGuardCheck)
+        {
+            // No helper call with this bailout.
+            return false;
+        }
+        break;
+
     default:
         break;
     }

+ 8 - 0
lib/Backend/GlobOptExpr.cpp

@@ -814,20 +814,28 @@ GlobOpt::ProcessArrayValueKills(IR::Instr *instr)
 {
     switch (instr->m_opcode)
     {
+    case Js::OpCode::StElemC:
     case Js::OpCode::StElemI_A:
     case Js::OpCode::StElemI_A_Strict:
     case Js::OpCode::DeleteElemI_A:
     case Js::OpCode::DeleteElemIStrict_A:
+    case Js::OpCode::ConsoleScopedStFld:
+    case Js::OpCode::ConsoleScopedStFldStrict:
+    case Js::OpCode::ScopedStFld:
+    case Js::OpCode::ScopedStFldStrict:
     case Js::OpCode::StFld:
     case Js::OpCode::StRootFld:
     case Js::OpCode::StFldStrict:
     case Js::OpCode::StRootFldStrict:
+    case Js::OpCode::StSuperFld:
     case Js::OpCode::StSlot:
     case Js::OpCode::StSlotChkUndecl:
     case Js::OpCode::DeleteFld:
     case Js::OpCode::DeleteRootFld:
     case Js::OpCode::DeleteFldStrict:
     case Js::OpCode::DeleteRootFldStrict:
+    case Js::OpCode::ScopedDeleteFld:
+    case Js::OpCode::ScopedDeleteFldStrict:
     case Js::OpCode::StArrViewElem:
     // These array helpers may change A.length (and A[i] could be A.length)...
     case Js::OpCode::InlineArrayPush:

+ 51 - 7
lib/Backend/GlobOptFields.cpp

@@ -335,6 +335,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
     IR::JnHelperMethod fnHelper;
     switch(instr->m_opcode)
     {
+    case Js::OpCode::StElemC:
     case Js::OpCode::StElemI_A:
     case Js::OpCode::StElemI_A_Strict:
         Assert(dstOpnd != nullptr);
@@ -366,6 +367,8 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
     case Js::OpCode::DeleteRootFld:
     case Js::OpCode::DeleteFldStrict:
     case Js::OpCode::DeleteRootFldStrict:
+    case Js::OpCode::ScopedDeleteFld:
+    case Js::OpCode::ScopedDeleteFldStrict:
         sym = instr->GetSrc1()->AsSymOpnd()->m_sym;
         KillLiveFields(sym->AsPropertySym(), bv);
         if (inGlobOpt)
@@ -387,13 +390,36 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
             this->KillAllObjectTypes(bv);
         }
         break;
+
+    case Js::OpCode::ConsoleScopedStFld:
+    case Js::OpCode::ConsoleScopedStFldStrict:
+    case Js::OpCode::ScopedStFld:
+    case Js::OpCode::ScopedStFldStrict:
+        // This is already taken care of for FastFld opcodes
+
+        if (inGlobOpt)
+        {
+            KillObjectHeaderInlinedTypeSyms(this->currentBlock, false);
+        }
+
+        // fall through
+
     case Js::OpCode::InitFld:
+    case Js::OpCode::InitConstFld:
+    case Js::OpCode::InitLetFld:
+    case Js::OpCode::InitRootFld:
+    case Js::OpCode::InitRootConstFld:
+    case Js::OpCode::InitRootLetFld:
+#if !FLOATVAR
+    case Js::OpCode::StSlotBoxTemp:
+#endif
     case Js::OpCode::StFld:
     case Js::OpCode::StRootFld:
     case Js::OpCode::StFldStrict:
     case Js::OpCode::StRootFldStrict:
     case Js::OpCode::StSlot:
     case Js::OpCode::StSlotChkUndecl:
+    case Js::OpCode::StSuperFld:
         Assert(dstOpnd != nullptr);
         sym = dstOpnd->AsSymOpnd()->m_sym;
         if (inGlobOpt)
@@ -415,11 +441,19 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
 
     case Js::OpCode::InlineArrayPush:
     case Js::OpCode::InlineArrayPop:
-        KillLiveFields(this->lengthEquivBv, bv);
-        if (inGlobOpt)
+        if(instr->m_func->GetThisOrParentInlinerHasArguments())
         {
-            // Deleting an item, or pushing a property to a non-array, may change object layout
-            KillAllObjectTypes(bv);
+            this->KillAllFields(bv);
+            this->SetAnyPropertyMayBeWrittenTo();
+        }
+        else
+        {
+            KillLiveFields(this->lengthEquivBv, bv);
+            if (inGlobOpt)
+            {
+                // Deleting an item, or pushing a property to a non-array, may change object layout
+                KillAllObjectTypes(bv);
+            }
         }
         break;
 
@@ -444,14 +478,23 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
                 // Kill length field for built-ins that can update it.
                 if (nullptr != this->lengthEquivBv)
                 {
-                    KillLiveFields(this->lengthEquivBv, bv);
+                    // If has arguments, all fields are killed in fall through
+                    if (!instr->m_func->GetThisOrParentInlinerHasArguments())
+                    {
+                        KillLiveFields(this->lengthEquivBv, bv);
+                    }
                 }
                 // fall through
 
             case IR::JnHelperMethod::HelperArray_Reverse:
-                // Deleting an item may change object layout
-                if (inGlobOpt)
+                if (instr->m_func->GetThisOrParentInlinerHasArguments())
+                {
+                    this->KillAllFields(bv);
+                    this->SetAnyPropertyMayBeWrittenTo();
+                }
+                else if (inGlobOpt)
                 {
+                    // Deleting an item may change object layout
                     KillAllObjectTypes(bv);
                 }
                 break;
@@ -492,6 +535,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
     case Js::OpCode::InitClass:
     case Js::OpCode::InitProto:
     case Js::OpCode::NewScObjectNoCtor:
+    case Js::OpCode::NewScObjectNoCtorFull:
         if (inGlobOpt)
         {
             // Opcodes that make an object into a prototype may break object-header-inlining and final type opt.

+ 2 - 2
lib/Backend/IRBuilder.cpp

@@ -1758,7 +1758,7 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
         }
 
     case Js::OpCode::NewScObjectSimple:
-        dstValueType = ValueType::GetObject(ObjectType::Object);
+        dstValueType = ValueType::GetObject(ObjectType::UninitializedObject);
         // fall-through
     case Js::OpCode::LdFuncExpr:
         m_func->DisableCanDoInlineArgOpt();
@@ -5050,7 +5050,7 @@ IRBuilder::BuildAuxiliary(Js::OpCode newOpcode, uint32 offset)
             // lower take it from there...
             srcOpnd = IR::IntConstOpnd::New(auxInsn->Offset, TyUint32, m_func);
             dstOpnd = this->BuildDstOpnd(dstRegSlot);
-            dstOpnd->SetValueType(ValueType::GetObject(ObjectType::Object));
+            dstOpnd->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject));
             instr = IR::Instr::New(newOpcode, dstOpnd, srcOpnd, m_func);
 
             // Because we're going to be making decisions based off the value, we have to defer

+ 2 - 0
lib/Backend/Inline.cpp

@@ -4204,6 +4204,8 @@ Inline::SplitConstructorCallCommon(
     {
         createObjInstr->SetByteCodeOffset(newObjInstr);
         createObjInstr->GetSrc1()->SetIsJITOptimizedReg(true);
+        // We're splitting a single byte code, so the interpreter has to resume from the beginning if we bail out.
+        createObjInstr->forcePreOpBailOutIfNeeded = true;
         newObjInstr->InsertBefore(createObjInstr);
 
         createObjDst->SetValueType(ValueType::GetObject(ObjectType::UninitializedObject));

+ 32 - 14
lib/Backend/Lower.cpp

@@ -4601,18 +4601,40 @@ Lowerer::LowerNewScObject(IR::Instr *newObjInstr, bool callCtor, bool hasArgs, b
         {
             Assert(!newObjDst->CanStoreTemp());
             // createObjDst = NewScObject...(ctorOpnd)
-            newScHelper = !callCtor ?
-                (isBaseClassConstructorNewScObject ?
-                    (hasArgs ? IR::HelperNewScObjectNoCtorFull : IR::HelperNewScObjectNoArgNoCtorFull) :
-                    (hasArgs ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArgNoCtor)) :
-                (hasArgs || usedFixedCtorCache ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArg);
 
             LoadScriptContext(newObjInstr);
-            m_lowererMD.LoadHelperArgument(newObjInstr, newObjInstr->GetSrc1());
 
-            newScObjCall = IR::Instr::New(Js::OpCode::Call, createObjDst, IR::HelperCallOpnd::New(newScHelper, func), func);
-            newObjInstr->InsertBefore(newScObjCall);
-            m_lowererMD.LowerCall(newScObjCall, 0);
+            if (callCtor)
+            {
+                newScHelper = (hasArgs || usedFixedCtorCache ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArg);
+
+                m_lowererMD.LoadHelperArgument(newObjInstr, newObjInstr->GetSrc1());
+
+                newScObjCall = IR::Instr::New(Js::OpCode::Call, createObjDst, IR::HelperCallOpnd::New(newScHelper, func), func);
+                newObjInstr->InsertBefore(newScObjCall);
+                m_lowererMD.LowerCall(newScObjCall, 0);
+            }
+            else
+            {
+                newScHelper = 
+                    (isBaseClassConstructorNewScObject ?
+                        (hasArgs ? IR::HelperNewScObjectNoCtorFull : IR::HelperNewScObjectNoArgNoCtorFull) :
+                        (hasArgs ? IR::HelperNewScObjectNoCtor : IR::HelperNewScObjectNoArgNoCtor));
+
+                // Branch around the helper call to execute the inlined ctor.
+                Assert(callCtorLabel != nullptr);
+                newObjInstr->InsertAfter(callCtorLabel);
+
+                // Change the NewScObject* to a helper call on the spot. This generates implicit call bailout for us if we need one.
+                m_lowererMD.LoadHelperArgument(newObjInstr, newObjInstr->UnlinkSrc1());
+                m_lowererMD.ChangeToHelperCall(newObjInstr, newScHelper);
+
+                // Then we're done.
+                Assert(createObjDst == newObjDst);
+
+                // Return the first instruction above the region we've just lowered.
+                return RemoveLoweredRegionStartMarker(startMarkerInstr);                    
+            }
         }
     }
 
@@ -4857,9 +4879,6 @@ bool Lowerer::TryLowerNewScObjectWithFixedCtorCache(IR::Instr* newObjInstr, IR::
     skipNewScObj = false;
     returnNewScObj = false;
 
-    AssertMsg(!PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->m_func) || !newObjInstr->HasBailOutInfo(),
-        "Why do we have bailout on NewScObject when ObjTypeSpecNewObj is off?");
-
     if (PHASE_OFF(Js::FixedNewObjPhase, newObjInstr->m_func) && PHASE_OFF(Js::ObjTypeSpecNewObjPhase, this->m_func))
     {
         return false;
@@ -4867,11 +4886,10 @@ bool Lowerer::TryLowerNewScObjectWithFixedCtorCache(IR::Instr* newObjInstr, IR::
 
     JITTimeConstructorCache * ctorCache;
 
-    if (newObjInstr->HasBailOutInfo())
+    if (newObjInstr->HasBailOutInfo() && (newObjInstr->GetBailOutKind() & ~IR::BailOutKindBits) == IR::BailOutFailedCtorGuardCheck)
     {
         Assert(newObjInstr->IsNewScObjectInstr());
         Assert(newObjInstr->IsProfiledInstr());
-        Assert(newObjInstr->GetBailOutKind() == IR::BailOutFailedCtorGuardCheck);
 
         emitBailOut = true;
 

+ 1 - 1
lib/Common/ChakraCoreVersion.h

@@ -17,7 +17,7 @@
 // ChakraCore version number definitions (used in ChakraCore binary metadata)
 #define CHAKRA_CORE_MAJOR_VERSION 1
 #define CHAKRA_CORE_MINOR_VERSION 11
-#define CHAKRA_CORE_PATCH_VERSION 8
+#define CHAKRA_CORE_PATCH_VERSION 9
 #define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0.
 
 // -------------

+ 11 - 0
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -3590,6 +3590,7 @@ void ByteCodeGenerator::StartEmitFunction(ParseNodeFnc *pnodeFnc)
 #if ENABLE_TTD
                     && !funcInfo->GetParsedFunctionBody()->GetScriptContext()->GetThreadContext()->IsRuntimeInTTDMode()
 #endif
+                    && !funcInfo->byteCodeFunction->IsCoroutine()
                 );
 
                 if (funcInfo->GetHasCachedScope())
@@ -4005,6 +4006,11 @@ void ByteCodeGenerator::StartEmitCatch(ParseNodeCatch *pnodeCatch)
                 sym->SetIsGlobalCatch(true);
             }
 
+            if (sym->NeedsScopeObject())
+            {
+                scope->SetIsObject();
+            }
+
             Assert(sym->GetScopeSlot() == Js::Constants::NoProperty);
             if (sym->NeedsSlotAlloc(this, funcInfo))
             {
@@ -4028,6 +4034,11 @@ void ByteCodeGenerator::StartEmitCatch(ParseNodeCatch *pnodeCatch)
             sym->SetIsGlobalCatch(true);
         }
 
+        if (sym->NeedsScopeObject())
+        {
+            scope->SetIsObject();
+        }
+
         if (scope->GetMustInstantiate())
         {
             if (sym->IsInSlot(this, funcInfo))

+ 6 - 6
lib/Runtime/ByteCode/ByteCodeGenerator.cpp

@@ -119,10 +119,10 @@ void EndVisitBlock(ParseNodeBlock *pnode, ByteCodeGenerator *byteCodeGenerator)
         Scope *scope = pnode->scope;
         FuncInfo *func = scope->GetFunc();
 
-        if (!byteCodeGenerator->IsInDebugMode() &&
-            scope->HasInnerScopeIndex())
+        if (!(byteCodeGenerator->IsInDebugMode() || func->byteCodeFunction->IsCoroutine())
+            && scope->HasInnerScopeIndex())
         {
-            // In debug mode, don't release the current index, as we're giving each scope a unique index, regardless
+            // In debug mode (or for the generator/async function), don't release the current index, as we're giving each scope a unique index, regardless
             // of nesting.
             Assert(scope->GetInnerScopeIndex() == func->CurrentInnerScopeIndex());
             func->ReleaseInnerScopeIndex();
@@ -155,12 +155,12 @@ void BeginVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
 void EndVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
 {
     Scope *scope = pnode->AsParseNodeCatch()->scope;
+    FuncInfo *func = scope->GetFunc();
 
-    if (scope->HasInnerScopeIndex() && !byteCodeGenerator->IsInDebugMode())
+    if (scope->HasInnerScopeIndex() && !(byteCodeGenerator->IsInDebugMode() || func->byteCodeFunction->IsCoroutine()))
     {
-        // In debug mode, don't release the current index, as we're giving each scope a unique index,
+        // In debug mode (or for the generator/async function), don't release the current index, as we're giving each scope a unique index,
         // regardless of nesting.
-        FuncInfo *func = scope->GetFunc();
 
         Assert(scope->GetInnerScopeIndex() == func->CurrentInnerScopeIndex());
         func->ReleaseInnerScopeIndex();

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

@@ -577,8 +577,8 @@ MACRO_EXTEND_WMS_AND_PROFILED(NewScObjectSpread,   CallIExtended, OpSideEffect|O
 MACRO_WMS_PROFILED2(    NewScObjArray,      CallI,          OpSideEffect|OpUseAllFields|OpCallInstr)        // Create new ScriptObject instance
 MACRO_WMS_PROFILED2(    NewScObjArraySpread, CallIExtended, OpSideEffect|OpUseAllFields|OpCallInstr)        // Create new ScriptObject instance
 MACRO(                  NewScObject_A,      Auxiliary,      OpSideEffect|OpUseAllFields)                    // Create new ScriptObject instance passing only constants
-MACRO_WMS(              NewScObjectNoCtorFull, Reg2,        OpTempObjectCanStoreTemp)                       // Create new object that will be used for the 'this' binding in a base class constructor
-MACRO_BACKEND_ONLY(     NewScObjectNoCtor,  Empty,          OpTempObjectCanStoreTemp)                       // Create new object that will be passed into a constructor
+MACRO_WMS(              NewScObjectNoCtorFull, Reg2,        OpTempObjectCanStoreTemp|OpHasImplicitCall)     // Create new object that will be used for the 'this' binding in a base class constructor
+MACRO_BACKEND_ONLY(     NewScObjectNoCtor,  Empty,          OpTempObjectCanStoreTemp|OpHasImplicitCall)     // Create new object that will be passed into a constructor
 MACRO_BACKEND_ONLY(     GetNewScObject,     Empty,          OpTempObjectTransfer)                           // Determine which object to finally use as the result of NewScObject (object passed into constructor as 'this', or object returned by constructor)
 MACRO_BACKEND_ONLY(     UpdateNewScObjectCache, Empty,      None)                                           // Update the cache used for NewScObject
 MACRO_WMS(              NewScObjectSimple,  Reg1,           OpTempObjectCanStoreTemp)

+ 33 - 1
lib/Runtime/Debug/DebugContext.cpp

@@ -193,6 +193,11 @@ namespace Js
         Js::TempArenaAllocatorObject *tempAllocator = nullptr;
         JsUtil::List<Js::FunctionInfo *, Recycler>* pFunctionsToRegister = nullptr;
         JsUtil::List<Js::Utf8SourceInfo *, Recycler, false, Js::CopyRemovePolicy, RecyclerPointerComparer>* utf8SourceInfoList = nullptr;
+        typedef JsUtil::BaseDictionary<uint32, RegSlot, ArenaAllocator, PowerOf2SizePolicy> FunctionStartToYieldRegister;
+
+        // This container ensures that for Generator/Async functions the yield register is same between non-debug to debug parse.
+        // Each entry represent a function's start position (each function will have unique start position in a file) and that function yield register
+        FunctionStartToYieldRegister *yieldFunctions = nullptr;
 
         HRESULT hr = S_OK;
         ThreadContext* threadContext = this->scriptContext->GetThreadContext();
@@ -201,6 +206,7 @@ namespace Js
         tempAllocator = threadContext->GetTemporaryAllocator(_u("debuggerAlloc"));
 
         utf8SourceInfoList = JsUtil::List<Js::Utf8SourceInfo *, Recycler, false, Js::CopyRemovePolicy, RecyclerPointerComparer>::New(this->scriptContext->GetRecycler());
+        yieldFunctions = Anew(tempAllocator->GetAllocator(), FunctionStartToYieldRegister, tempAllocator->GetAllocator());
 
         this->MapUTF8SourceInfoUntil([&](Js::Utf8SourceInfo * sourceInfo) -> bool
         {
@@ -276,6 +282,23 @@ namespace Js
                 this->hostDebugContext->SetThreadDescription(sourceInfo->GetSourceContextInfo()->url); // the HRESULT is omitted.
             }
 
+            if (shouldReparseFunctions)
+            {
+                yieldFunctions->Clear();
+                BEGIN_TRANSLATE_OOM_TO_HRESULT_NESTED
+                {
+                    sourceInfo->MapFunction([&](Js::FunctionBody *const pFuncBody)
+                    {
+                        if (pFuncBody->IsCoroutine() && pFuncBody->GetYieldRegister() != Js::Constants::NoRegister)
+                        {
+                            yieldFunctions->Add(pFuncBody->StartInDocument(), pFuncBody->GetYieldRegister());
+                        }
+                    });
+                }
+                END_TRANSLATE_OOM_TO_HRESULT(hr);
+                DEBUGGER_ATTACHDETACH_FATAL_ERROR_IF_FAILED(hr);
+            }
+
             bool fHasDoneSourceRundown = false;
             for (int i = 0; i < pFunctionsToRegister->Count(); i++)
             {
@@ -326,8 +349,17 @@ namespace Js
 
             if (shouldReparseFunctions)
             {
-                sourceInfo->MapFunction([](Js::FunctionBody *const pFuncBody)
+                sourceInfo->MapFunction([&](Js::FunctionBody *const pFuncBody)
                 {
+                    if (pFuncBody->IsCoroutine())
+                    {
+                        RegSlot oldYieldRegister = Constants::NoRegister;
+                        if (yieldFunctions->TryGetValue(pFuncBody->StartInDocument(), &oldYieldRegister))
+                        {
+                            AssertOrFailFast(pFuncBody->GetYieldRegister() == oldYieldRegister);
+                        }
+                    }
+
                     if (pFuncBody->IsFunctionParsed())
                     {
                         pFuncBody->ReinitializeExecutionModeAndLimits();

+ 2 - 1
lib/Runtime/Language/ValueType.cpp

@@ -577,7 +577,8 @@ bool ValueType::IsNotArrayOrObjectWithArray() const
 {
     return
         IsNotObject() ||
-        (IsObject() && GetObjectType() != ObjectType::ObjectWithArray && GetObjectType() != ObjectType::Array);
+        (IsObject() && GetObjectType() != ObjectType::ObjectWithArray && GetObjectType() != ObjectType::Array
+         && GetObjectType() != ObjectType::UninitializedObject && GetObjectType() != ObjectType::Object);
 }
 
 bool ValueType::IsNativeArray() const

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

@@ -148,6 +148,10 @@ namespace Js
             {
                 JavascriptError::ThrowRangeError(scriptContext, JSERR_ArrayLengthAssignIncorrect);
             }
+
+            // Conversion can change the type (e.g. from String), invalidating assumptions made by the JIT
+            scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor);
+
             return newLen;
         }
     }

+ 3 - 0
lib/Runtime/Library/JavascriptArray.cpp

@@ -2930,6 +2930,9 @@ using namespace Js;
         double dblValue = JavascriptConversion::ToNumber(newLength, scriptContext);
         if (dblValue == uintValue)
         {
+            // Conversion can change the type (e.g. from String), invalidating assumptions made by the JIT
+            scriptContext->GetThreadContext()->AddImplicitCallFlags(ImplicitCall_Accessor);
+
             this->SetLength(uintValue);
         }
         else