Переглянути джерело

[1.7>master] [MERGE #3917 @agarwal-sandeep] 17-10 Security Update that addresses the following issues in ChakraCore

Merge pull request #3917 from agarwal-sandeep:1710Fixes

CVE-2017-11792, CVE-2017-11796, CVE-2017-11797, CVE-2017-11799, CVE-2017-11801, CVE-2017-11802, CVE-2017-11805, CVE-2017-11806, CVE-2017-11807, CVE-2017-11808, CVE-2017-11809, CVE-2017-11811, CVE-2017-11812, CVE-2017-11821
Sandeep Agarwal 8 роки тому
батько
коміт
42bf8be0df
45 змінених файлів з 498 додано та 125 видалено
  1. 1 1
      lib/Backend/GlobOpt.cpp
  2. 7 12
      lib/Backend/IRBuilderAsmJs.cpp
  3. 16 6
      lib/Backend/Lower.cpp
  4. 10 0
      lib/Backend/LowerMDShared.cpp
  5. 1 0
      lib/Backend/LowerMDShared.h
  6. 22 0
      lib/Backend/amd64/LowererMDArch.cpp
  7. 1 0
      lib/Backend/amd64/LowererMDArch.h
  8. 6 0
      lib/Backend/arm/LowerMD.cpp
  9. 1 0
      lib/Backend/arm/LowerMD.h
  10. 1 1
      lib/Common/ChakraCoreVersion.h
  11. 3 0
      lib/Common/ConfigFlagsList.h
  12. 1 1
      lib/JITClient/JITManager.cpp
  13. 9 1
      lib/Parser/Parse.cpp
  14. 32 1
      lib/Parser/Parse.h
  15. 1 0
      lib/Parser/ptree.h
  16. 4 0
      lib/Runtime/Base/CrossSite.cpp
  17. 3 2
      lib/Runtime/Base/FunctionBody.cpp
  18. 2 2
      lib/Runtime/Base/FunctionBody.h
  19. 24 0
      lib/Runtime/Base/FunctionInfo.h
  20. 134 58
      lib/Runtime/ByteCode/ByteCodeEmitter.cpp
  21. 44 5
      lib/Runtime/ByteCode/ByteCodeGenerator.cpp
  22. 5 0
      lib/Runtime/ByteCode/ByteCodeGenerator.h
  23. 12 1
      lib/Runtime/ByteCode/FuncInfo.cpp
  24. 2 0
      lib/Runtime/ByteCode/FuncInfo.h
  25. 1 0
      lib/Runtime/ByteCode/OpCodesAsmJs.h
  26. 1 1
      lib/Runtime/Debug/DiagObjectModel.cpp
  27. 3 3
      lib/Runtime/Debug/TTSnapshotExtractor.cpp
  28. 2 0
      lib/Runtime/Language/AsmJs.cpp
  29. 7 0
      lib/Runtime/Language/AsmJsByteCodeGenerator.cpp
  30. 1 0
      lib/Runtime/Language/AsmJsEncoder.h
  31. 6 0
      lib/Runtime/Language/AsmJsEncoder.inl
  32. 1 0
      lib/Runtime/Language/AsmJsEncoderHandler.inl
  33. 2 6
      lib/Runtime/Language/AsmJsUtils.cpp
  34. 1 0
      lib/Runtime/Language/InterpreterHandlerAsmJs.inl
  35. 8 2
      lib/Runtime/Language/InterpreterStackFrame.cpp
  36. 2 0
      lib/Runtime/Language/InterpreterStackFrame.h
  37. 1 1
      lib/Runtime/Language/JavascriptOperators.cpp
  38. 8 1
      lib/Runtime/Library/JavascriptArray.cpp
  39. 19 2
      lib/Runtime/Library/JavascriptProxy.cpp
  40. 13 2
      lib/Runtime/Library/RegexHelper.cpp
  41. 1 1
      lib/Runtime/Library/ScriptFunction.cpp
  42. 16 15
      lib/Runtime/Library/StackScriptFunction.cpp
  43. 6 0
      lib/WasmReader/WasmByteCodeGenerator.cpp
  44. 46 0
      test/switchStatement/aggressiveintoff.js
  45. 11 0
      test/switchStatement/rlexe.xml

+ 1 - 1
lib/Backend/GlobOpt.cpp

@@ -17140,7 +17140,7 @@ GlobOpt::IsSwitchOptEnabled(Func const * func)
 {
     Assert(func->IsTopFunc());
     return !PHASE_OFF(Js::SwitchOptPhase, func) && !func->IsSwitchOptDisabled() && !IsTypeSpecPhaseOff(func)
-        && func->DoGlobOpt() && !func->HasTry();
+        && DoAggressiveIntTypeSpec(func) && func->DoGlobOpt() && !func->HasTry();
 }
 
 bool

+ 7 - 12
lib/Backend/IRBuilderAsmJs.cpp

@@ -1038,6 +1038,11 @@ IRBuilderAsmJs::BuildEmpty(Js::OpCodeAsmJs newOpcode, uint32 offset)
     Js::RegSlot retSlot;
     switch (newOpcode)
     {
+    case Js::OpCodeAsmJs::CheckHeap:
+        instr = IR::Instr::New(Js::OpCode::ArrayDetachedCheck, m_func);
+        instr->SetSrc1(IR::IndirOpnd::New(BuildSrcOpnd(AsmJsRegSlots::ArrayReg, TyVar), Js::ArrayBuffer::GetIsDetachedOffset(), TyInt8, m_func));
+        AddInstr(instr, offset);
+        break;
     case Js::OpCodeAsmJs::Unreachable_Void:
         instr = IR::Instr::New(Js::OpCode::ThrowRuntimeError, m_func);
         instr->SetSrc1(IR::IntConstOpnd::New(SCODE_CODE(WASMERR_Unreachable), TyInt32, instr->m_func));
@@ -1808,23 +1813,13 @@ IRBuilderAsmJs::BuildAsmCall(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::ArgSl
     {
         m_func->m_argSlotsForFunctionsCalled = argCount;
     }
-    if (m_asmFuncInfo->UsesHeapBuffer())
-    {
-        // heap buffer can change for wasm
 #ifdef ENABLE_WASM
-        if (m_func->GetJITFunctionBody()->IsWasmFunction())
+    // heap buffer can change for wasm
+    if (m_asmFuncInfo->UsesHeapBuffer() && m_func->GetJITFunctionBody()->IsWasmFunction())
         {
             BuildHeapBufferReload(offset);
         }
 #endif
-        // after foreign function call, we need to make sure that the heap hasn't been detached
-        if (newOpcode == Js::OpCodeAsmJs::Call)
-        {
-            IR::Instr * instrArrayDetachedCheck = IR::Instr::New(Js::OpCode::ArrayDetachedCheck, m_func);
-            instrArrayDetachedCheck->SetSrc1(IR::IndirOpnd::New(BuildSrcOpnd(AsmJsRegSlots::ArrayReg, TyVar), Js::ArrayBuffer::GetIsDetachedOffset(), TyInt8, m_func));
-            AddInstr(instrArrayDetachedCheck, offset);
-        }
-    }
 }
 
 void

+ 16 - 6
lib/Backend/Lower.cpp

@@ -13339,6 +13339,14 @@ Lowerer::GenerateBailOut(IR::Instr * instr, IR::BranchInstr * branchInstr, IR::L
         // src1 on BailOnNotStackArgs is helping CSE
         instr->FreeSrc1();
     }
+
+    if (instr->GetSrc2() != nullptr)
+    {
+        // Ideally we should never be in this situation but incase we reached a
+        // condition where we didn't freed src2. Free it here.
+        instr->FreeSrc2();
+    }
+
     // Call the bail out wrapper
     instr->m_opcode = Js::OpCode::Call;
     if(instr->GetDst())
@@ -23339,7 +23347,7 @@ Lowerer::GenerateRecyclerAlloc(IR::JnHelperMethod allocHelper, size_t allocSize,
 }
 
 void
-Lowerer::GenerateMemInit(IR::RegOpnd * opnd, int32 offset, int value, IR::Instr * insertBeforeInstr, bool isZeroed)
+Lowerer::GenerateMemInit(IR::RegOpnd * opnd, int32 offset, int32 value, IR::Instr * insertBeforeInstr, bool isZeroed)
 {
     IRType type = TyInt32;
     if (isZeroed)
@@ -24149,7 +24157,7 @@ Lowerer::LowerNewScopeSlots(IR::Instr * instr, bool doStackSlots)
     IR::RegOpnd * dst = instr->UnlinkDst()->AsRegOpnd();
 
     // dst = RecyclerAlloc(allocSize)
-    // dst[EncodedSlotCountSlotIndex = EncodedSlotCountSlotIOndex];
+    // dst[EncodedSlotCountSlotIndex] = min(actualSlotCount, MaxEncodedSlotCount);
     // dst[ScopeMetadataSlotIndex] = FunctionBody;
     // mov undefinedOpnd, undefined
     // dst[FirstSlotIndex..count] = undefinedOpnd;
@@ -24160,8 +24168,10 @@ Lowerer::LowerNewScopeSlots(IR::Instr * instr, bool doStackSlots)
     {
         GenerateRecyclerAlloc(IR::HelperAllocMemForVarArray, allocSize, dst, instr);
     }
-    GenerateMemInit(dst, Js::ScopeSlots::EncodedSlotCountSlotIndex * sizeof(Js::Var),
-        min<uint>(actualSlotCount, Js::ScopeSlots::MaxEncodedSlotCount), instr, !doStackSlots);
+   
+    m_lowererMD.GenerateMemInit(dst, Js::ScopeSlots::EncodedSlotCountSlotIndex * sizeof(Js::Var),
+            (size_t)min<uint>(actualSlotCount, Js::ScopeSlots::MaxEncodedSlotCount), instr, !doStackSlots);
+
     IR::Opnd * functionInfoOpnd = this->LoadFunctionInfoOpnd(instr);
     GenerateMemInit(dst, Js::ScopeSlots::ScopeMetadataSlotIndex * sizeof(Js::Var),
         functionInfoOpnd, instr, !doStackSlots);
@@ -25101,7 +25111,7 @@ Lowerer::LowerFrameDisplayCheck(IR::Instr * instr)
 
                 indirOpnd = IR::IndirOpnd::New(slotArrayOpnd,
                                                Js::ScopeSlots::EncodedSlotCountSlotIndex * sizeof(Js::Var),
-                                               TyUint32, m_func, true);
+                                               TyVar, m_func, true);
                 IR::IntConstOpnd * slotIdOpnd = IR::IntConstOpnd::New(slotId - Js::ScopeSlots::FirstSlotIndex,
                                                                       TyUint32, m_func);
                 InsertCompareBranch(indirOpnd, slotIdOpnd, Js::OpCode::BrLe_A, true, errorLabel, insertInstr);
@@ -25156,7 +25166,7 @@ Lowerer::LowerSlotArrayCheck(IR::Instr * instr)
 
         IR::IndirOpnd * indirOpnd = IR::IndirOpnd::New(IR::RegOpnd::New(stackSym, TyVar, m_func),
                                                        Js::ScopeSlots::EncodedSlotCountSlotIndex * sizeof(Js::Var),
-                                                       TyUint32, m_func, true);
+                                                       TyVar, m_func, true);
 
         slotIdOpnd->SetValue(slotId - Js::ScopeSlots::FirstSlotIndex);
         InsertCompareBranch(indirOpnd, slotIdOpnd, Js::OpCode::BrGt_A, true, continueLabel, insertInstr);

+ 10 - 0
lib/Backend/LowerMDShared.cpp

@@ -62,6 +62,16 @@ LowererMD::GenerateMemRef(intptr_t addr, IRType type, IR::Instr *instr, bool don
     return IR::MemRefOpnd::New(addr, type, this->m_func);
 }
 
+void
+LowererMD::GenerateMemInit(IR::RegOpnd * opnd, int32 offset, size_t value, IR::Instr * insertBeforeInstr, bool isZeroed)
+{
+#if _M_X64
+    lowererMDArch.GenerateMemInit(opnd, offset, value, insertBeforeInstr, isZeroed);
+#else
+    m_lowerer->GenerateMemInit(opnd, offset, (uint32)value, insertBeforeInstr, isZeroed);
+#endif
+}
+
 ///----------------------------------------------------------------------------
 ///
 /// LowererMD::InvertBranch

+ 1 - 0
lib/Backend/LowerMDShared.h

@@ -72,6 +72,7 @@ public:
 public:
             void            Init(Lowerer *lowerer);
             IR::Opnd *      GenerateMemRef(intptr_t addr, IRType type, IR::Instr *instr, bool dontEncode = false);
+            void            GenerateMemInit(IR::RegOpnd * opnd, int32 offset, size_t value, IR::Instr * insertBeforeInstr, bool isZeroed = false);
             IR::Instr *     ChangeToHelperCall(IR::Instr * instr, IR::JnHelperMethod helperMethod, IR::LabelInstr *labelBailOut = nullptr,
                                                IR::Opnd *opndInstance = nullptr, IR::PropertySymOpnd * propSymOpnd = nullptr, bool isHelperContinuation = false);
             void            FinalLower();

+ 22 - 0
lib/Backend/amd64/LowererMDArch.cpp

@@ -577,6 +577,28 @@ LowererMDArch::SetMaxArgSlots(Js::ArgSlot actualCount /*including this*/)
     return;
 }
 
+void
+LowererMDArch::GenerateMemInit(IR::RegOpnd * opnd, int32 offset, size_t value, IR::Instr * insertBeforeInstr, bool isZeroed)
+{
+    IRType type = TyVar;
+    if (isZeroed)
+    {
+        if (value == 0)
+        {
+            // Recycler memory are zero initialized
+            return;
+        }
+
+        type = value <= UINT_MAX ?
+            (value <= USHORT_MAX ?
+            (value <= UCHAR_MAX ? TyUint8 : TyUint16) :
+                TyUint32) :
+            type;
+    }
+    Func * func = this->m_func;
+    lowererMD->GetLowerer()->InsertMove(IR::IndirOpnd::New(opnd, offset, type, func), IR::IntConstOpnd::New(value, type, func), insertBeforeInstr);
+}
+
 IR::Instr *
 LowererMDArch::LowerCallIDynamic(IR::Instr *callInstr, IR::Instr*saveThisArgOutInstr, IR::Opnd *argsLength, ushort callFlags, IR::Instr * insertBeforeInstrForCFG)
 {

+ 1 - 0
lib/Backend/amd64/LowererMDArch.h

@@ -155,6 +155,7 @@ private:
     IR::LabelInstr *    GetBailOutStackRestoreLabel(BailOutInfo * bailOutInfo, IR::LabelInstr * exitTargetInstr);
     void                GeneratePreCall(IR::Instr * callInstr, IR::Opnd  *functionObjOpnd, IR::Instr* insertBeforeInstrForCFGCheck = nullptr);
     void                SetMaxArgSlots(Js::ArgSlot actualCount /*including this*/);
+    void                GenerateMemInit(IR::RegOpnd * opnd, int32 offset, size_t value, IR::Instr * insertBeforeInstr, bool isZeroed = false);
 };
 
 #define REG_EH_TARGET      RegArg0

+ 6 - 0
lib/Backend/arm/LowerMD.cpp

@@ -378,6 +378,12 @@ LowererMD::SetMaxArgSlots(Js::ArgSlot actualCount /*including this*/)
     return;
 }
 
+void
+LowererMD::GenerateMemInit(IR::RegOpnd * opnd, int32 offset, size_t value, IR::Instr * insertBeforeInstr, bool isZeroed)
+{
+    m_lowerer->GenerateMemInit(opnd, offset, (uint32)value, insertBeforeInstr, isZeroed);
+}
+
 IR::Instr *
 LowererMD::LowerCallIDynamic(IR::Instr *callInstr, IR::Instr*saveThisArgOutInstr, IR::Opnd *argsLength, ushort callFlags, IR::Instr * insertBeforeInstrForCFG)
 {

+ 1 - 0
lib/Backend/arm/LowerMD.h

@@ -273,6 +273,7 @@ public:
 
             void                LowerInlineSpreadArgOutLoop(IR::Instr *callInstr, IR::RegOpnd *indexOpnd, IR::RegOpnd *arrayElementsStartOpnd);
             void                LowerTypeof(IR::Instr * typeOfInstr);
+            void                GenerateMemInit(IR::RegOpnd * opnd, int32 offset, size_t value, IR::Instr * insertBeforeInstr, bool isZeroed = false);
 
 public:
     static IR::Instr * InsertCmovCC(const Js::OpCode opCode, IR::Opnd * dst, IR::Opnd* src1, IR::Instr* insertBeforeInstr, bool postRegAlloc);

+ 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 8
-#define CHAKRA_CORE_PATCH_VERSION 0
+#define CHAKRA_CORE_PATCH_VERSION 2
 #define CHAKRA_CORE_VERSION_RELEASE_QFE 0 // Redundant with PATCH_VERSION. Keep this value set to 0.
 
 // -------------

+ 3 - 0
lib/Common/ConfigFlagsList.h

@@ -1056,6 +1056,9 @@ FLAGPR_REGOVR_EXP(Boolean, ES6, ES6RegExPrototypeProperties, "Enable ES6 propert
 #ifndef COMPILE_DISABLE_ES6RegExSymbols
     #define COMPILE_DISABLE_ES6RegExSymbols 0
 #endif
+
+// When we enable ES6RegExSymbols check all String and Regex built-ins which are inlined in JIT and make sure the helper
+// sets implicit call flag before calling into script
 FLAGPR_REGOVR_EXP(Boolean, ES6, ES6RegExSymbols        , "Enable ES6 RegExp symbols"                                , DEFAULT_CONFIG_ES6RegExSymbols)
 
 FLAGPR           (Boolean, ES6, ES6HasInstance         , "Enable ES6 @@hasInstance symbol"                          , DEFAULT_CONFIG_ES6HasInstance)

+ 1 - 1
lib/JITClient/JITManager.cpp

@@ -13,7 +13,7 @@ void * __RPC_USER midl_user_allocate(
 #endif
     size_t size)
 {
-    return (HeapAlloc(GetProcessHeap(), 0, size));
+    return (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size));
 }
 
 void __RPC_USER midl_user_free(_Pre_maybenull_ _Post_invalid_ void * ptr)

+ 9 - 1
lib/Parser/Parse.cpp

@@ -92,6 +92,8 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator
     m_hasParallelJob = false;
     m_doingFastScan = false;
 #endif
+    m_isInParsingArgList = false;
+    m_hasDestructuringPattern = false;
     m_scriptContext = scriptContext;
     m_pCurrentAstSize = nullptr;
     m_arrayDepth = 0;
@@ -1242,7 +1244,7 @@ Parser::CreateCallNode(OpCode nop, ParseNodePtr pnode1, ParseNodePtr pnode2,char
     pnode->sxCall.isApplyCall = false;
     pnode->sxCall.isEvalCall = false;
     pnode->sxCall.isSuperCall = false;
-
+    pnode->sxCall.hasDestructuring = false;
     pnode->ichMin = ichMin;
     pnode->ichLim = ichLim;
 
@@ -3662,6 +3664,7 @@ ParseNodePtr Parser::ParsePostfixOperators(
         {
         case tkLParen:
             {
+                AutoMarkInParsingArgs autoMarkInParsingArgs(this);
                 if (fInNew)
                 {
                     ParseNodePtr pnodeArgs = ParseArgList<buildAST>(&callOfConstants, &spreadArgCount, &count);
@@ -3674,6 +3677,8 @@ ParseNodePtr Parser::ParsePostfixOperators(
                         pnode->sxCall.isApplyCall = false;
                         pnode->sxCall.isEvalCall = false;
                         pnode->sxCall.isSuperCall = false;
+                        pnode->sxCall.hasDestructuring = m_hasDestructuringPattern;
+                        Assert(!m_hasDestructuringPattern || count > 0);
                         pnode->sxCall.argCount = count;
                         pnode->sxCall.spreadArgCount = spreadArgCount;
                         pnode->ichLim = m_pscan->IchLimTok();
@@ -3745,6 +3750,8 @@ ParseNodePtr Parser::ParsePostfixOperators(
                         pnode->sxCall.spreadArgCount = spreadArgCount;
                         pnode->sxCall.isApplyCall = false;
                         pnode->sxCall.isEvalCall = fCallIsEval;
+                        pnode->sxCall.hasDestructuring = m_hasDestructuringPattern;
+                        Assert(!m_hasDestructuringPattern || count > 0);
                         pnode->sxCall.argCount = count;
                         pnode->ichLim = m_pscan->IchLimTok();
                     }
@@ -8672,6 +8679,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
 
             if (buildAST)
             {
+                this->SetHasDestructuringPattern(true);
                 pnode = ConvertToPattern(pnode);
             }
 

+ 32 - 1
lib/Parser/Parse.h

@@ -139,6 +139,12 @@ public:
     bool IsDoingFastScan() const { return false; }
 #endif
 
+    bool GetIsInParsingArgList() const { return m_isInParsingArgList; }
+    void SetIsInParsingArgList(bool set) { m_isInParsingArgList = set; }
+
+    bool GetHasDestructuringPattern() const { return m_hasDestructuringPattern; }
+    void SetHasDestructuringPattern(bool set) { m_hasDestructuringPattern = set; }
+
     static IdentPtr PidFromNode(ParseNodePtr pnode);
 
     ParseNode* CopyPnode(ParseNode* pnode);
@@ -397,7 +403,8 @@ private:
     bool m_inDeferredNestedFunc; // true if parsing a function in deferred mode, nested within the current node  
     bool m_reparsingLambdaParams;
     bool m_disallowImportExportStmt;
-
+    bool m_isInParsingArgList;
+    bool m_hasDestructuringPattern;
     // This bool is used for deferring the shorthand initializer error ( {x = 1}) - as it is allowed in the destructuring grammar.
     bool m_hasDeferredShorthandInitError;
     uint * m_pnestedCount; // count of functions nested at one level below the current node
@@ -1113,6 +1120,30 @@ private:
         }
     };
 
+    class AutoMarkInParsingArgs
+    {
+    public:
+        AutoMarkInParsingArgs(Parser * parser)
+            : m_parser(parser)
+        {
+            m_prevState = m_parser->GetIsInParsingArgList();
+            m_parser->SetHasDestructuringPattern(false);
+            m_parser->SetIsInParsingArgList(true);
+        }
+        ~AutoMarkInParsingArgs()
+        {
+            m_parser->SetIsInParsingArgList(m_prevState);
+            if (!m_prevState)
+            {
+                m_parser->SetHasDestructuringPattern(false);
+            }
+        }
+
+    private:
+        Parser *m_parser;
+        bool m_prevState;
+    };
+
 public:
     charcount_t GetSourceIchLim() { return m_sourceLim; }
     static BOOL NodeEqualsName(ParseNodePtr pnode, LPCOLESTR sz, uint32 cch);

+ 1 - 0
lib/Parser/ptree.h

@@ -459,6 +459,7 @@ struct PnCall
     BYTE isApplyCall : 1;
     BYTE isEvalCall : 1;
     BYTE isSuperCall : 1;
+    BYTE hasDestructuring : 1;
 };
 
 struct PnStmt

+ 4 - 0
lib/Runtime/Base/CrossSite.cpp

@@ -438,6 +438,10 @@ namespace Js
     Var CrossSite::CommonThunk(RecyclableObject* recyclableObject, JavascriptMethod entryPoint, Arguments args)
     {
         DynamicObject* function = DynamicObject::FromVar(recyclableObject);
+
+        FunctionInfo * functionInfo = (JavascriptFunction::Is(function) ? JavascriptFunction::FromVar(function)->GetFunctionInfo() : nullptr);
+        AutoDisableRedeferral autoDisableRedeferral(functionInfo);
+
         ScriptContext* targetScriptContext = function->GetScriptContext();
         Assert(!targetScriptContext->IsClosed());
         Assert(function->IsExternal() || function->IsCrossSiteObject());

+ 3 - 2
lib/Runtime/Base/FunctionBody.cpp

@@ -1890,8 +1890,9 @@ namespace Js
     {
         Assert(pnodeFnc->nop == knopFncDecl);
 
-        Recycler *recycler = GetScriptContext()->GetRecycler();
-        this->SetDeferredStubs(BuildDeferredStubTree(pnodeFnc, recycler));
+        // TODO: Disabling the creation of deferred stubs for now. We need to rethink the design again as the current behavior
+        // is not usable with precise capturing.
+        this->SetDeferredStubs(nullptr);
     }
 
     FunctionInfoArray ParseableFunctionInfo::GetNestedFuncArray()

+ 2 - 2
lib/Runtime/Base/FunctionBody.h

@@ -3586,9 +3586,9 @@ namespace Js
             slotArray[ScopeMetadataSlotIndex] = scopeMetadataObj;
         }
 
-        uint GetCount() const
+        size_t GetCount() const
         {
-            return ::Math::PointerCastToIntegralTruncate<uint>(slotArray[EncodedSlotCountSlotIndex]);
+            return ::Math::PointerCastToIntegralTruncate<size_t>(slotArray[EncodedSlotCountSlotIndex]);
         }
 
         void SetCount(uint count)

+ 24 - 0
lib/Runtime/Base/FunctionInfo.h

@@ -157,4 +157,28 @@ namespace Js
             : FunctionInfo(FORCE_NO_WRITE_BARRIER_TAG(entryPoint), Attributes::DoNotProfile)
         {}
     };
+
+    class AutoDisableRedeferral
+    {
+    public:
+        AutoDisableRedeferral(FunctionInfo * functionInfo) : functionInfo(functionInfo), canDefer(false) 
+        {
+            if (functionInfo)
+            {
+                canDefer = functionInfo->CanBeDeferred();
+                functionInfo->SetAttributes((FunctionInfo::Attributes)(functionInfo->GetAttributes() & ~FunctionInfo::Attributes::CanDefer));
+            }
+        }
+        ~AutoDisableRedeferral() 
+        {
+            if (functionInfo && canDefer)
+            {
+                functionInfo->SetAttributes((FunctionInfo::Attributes(functionInfo->GetAttributes() | FunctionInfo::Attributes::CanDefer)));
+            }
+        }
+    private:
+            FunctionInfo * functionInfo;
+            bool canDefer;
+    };
+
 };

+ 134 - 58
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -2786,7 +2786,9 @@ void ByteCodeGenerator::EmitOneFunction(ParseNode *pnode)
         deferParseFunction->SetReportedInParamsCount(funcInfo->inArgsCount);
     }
 
-    if (deferParseFunction->IsDeferred() || deferParseFunction->CanBeDeferred())
+    // Note: Don't check the actual attributes on the functionInfo here, since CanDefer has been cleared while
+    // we're generating byte code.
+    if (deferParseFunction->IsDeferred() || (funcInfo->originalAttributes & Js::FunctionInfo::Attributes::CanDefer))
     {
         Js::ScopeInfo::SaveEnclosingScopeInfo(this, funcInfo);        
     }
@@ -3483,7 +3485,7 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc)
     Scope * const bodyScope = funcInfo->GetBodyScope();
     Scope * const paramScope = funcInfo->GetParamScope();
 
-    if (funcInfo->byteCodeFunction->IsFunctionParsed())
+    if (funcInfo->byteCodeFunction->IsFunctionParsed() && funcInfo->GetParsedFunctionBody()->GetByteCode() == nullptr)
     {
         if (!(flags & (fscrEval | fscrImplicitThis | fscrImplicitParents)))
         {
@@ -6876,12 +6878,112 @@ void EmitList(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *
     }
 }
 
-void EmitSpreadArgToListBytecodeInstr(ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, Js::RegSlot argLoc, Js::ProfileId callSiteId, Js::ArgSlot &argIndex)
+void EmitOneArg(
+    ParseNode *pnode,
+    BOOL fAssignRegs,
+    ByteCodeGenerator *byteCodeGenerator,
+    FuncInfo *funcInfo,
+    Js::ProfileId callSiteId,
+    Js::ArgSlot &argIndex,
+    Js::ArgSlot &spreadIndex,
+    Js::RegSlot argTempLocation,
+    Js::AuxArray<uint32> *spreadIndices = nullptr
+)
 {
-    Js::RegSlot regVal = funcInfo->AcquireTmpRegister();
-    byteCodeGenerator->Writer()->Reg2(Js::OpCode::LdCustomSpreadIteratorList, regVal, argLoc);
-    byteCodeGenerator->Writer()->ArgOut<true>(++argIndex, regVal, callSiteId);
-    funcInfo->ReleaseTmpRegister(regVal);
+    bool noArgOuts = argTempLocation != Js::Constants::NoRegister;
+
+    // If this is a put, the arguments have already been evaluated (see EmitReference).
+    // We just need to emit the ArgOut instructions.
+    if (fAssignRegs)
+    {
+        Emit(pnode, byteCodeGenerator, funcInfo, false);
+    }
+
+    if (pnode->nop == knopEllipsis)
+    {
+        Assert(spreadIndices != nullptr);
+        spreadIndices->elements[spreadIndex++] = argIndex + 1; // account for 'this'
+        Js::RegSlot regVal = funcInfo->AcquireTmpRegister();
+        byteCodeGenerator->Writer()->Reg2(Js::OpCode::LdCustomSpreadIteratorList, regVal, pnode->location);
+        if (noArgOuts)
+        {
+            byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, argTempLocation, regVal);
+        }
+        else
+        {
+            byteCodeGenerator->Writer()->ArgOut<true>(argIndex + 1, regVal, callSiteId);
+        }
+        funcInfo->ReleaseTmpRegister(regVal);
+    }
+    else
+    {
+        if (noArgOuts)
+        {
+            byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, argTempLocation, pnode->location);
+        }
+        else
+        {
+            byteCodeGenerator->Writer()->ArgOut<true>(argIndex + 1, pnode->location, callSiteId);
+        }
+    }
+    argIndex++;
+
+    if (fAssignRegs)
+    {
+        funcInfo->ReleaseLoc(pnode);
+    }
+}
+
+size_t EmitArgsWithArgOutsAtEnd(
+    ParseNode *pnode,
+    BOOL fAssignRegs,
+    ByteCodeGenerator *byteCodeGenerator,
+    FuncInfo *funcInfo,
+    Js::ProfileId callSiteId,
+    Js::RegSlot thisLocation,
+    Js::ArgSlot argsCountForStartCall,
+    Js::AuxArray<uint32> *spreadIndices = nullptr
+)
+{
+    AssertOrFailFast(pnode != nullptr);
+
+    Js::ArgSlot argIndex = 0;
+    Js::ArgSlot spreadIndex = 0;
+
+    Js::RegSlot argTempLocation = funcInfo->AcquireTmpRegister();
+    Js::RegSlot firstArgTempLocation = argTempLocation;
+
+    while (pnode->nop == knopList)
+    {
+        EmitOneArg(pnode->sxBin.pnode1, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, argIndex, spreadIndex, argTempLocation, spreadIndices);
+        pnode = pnode->sxBin.pnode2;
+        argTempLocation = funcInfo->AcquireTmpRegister();
+    }
+
+    EmitOneArg(pnode, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, argIndex, spreadIndex, argTempLocation, spreadIndices);
+
+    byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argsCountForStartCall);
+
+    // Emit all argOuts now
+
+    if (thisLocation != Js::Constants::NoRegister)
+    {
+        // Emit the "this" object.
+        byteCodeGenerator->Writer()->ArgOut<true>(0, thisLocation, callSiteId);
+    }
+
+    for (Js::ArgSlot index = 0; index < argIndex; index++)
+    {
+        byteCodeGenerator->Writer()->ArgOut<true>(index + 1, firstArgTempLocation + index, callSiteId);
+    }
+
+    // Now release all those temps register
+    for (Js::ArgSlot index = argIndex; index > 0; index--)
+    {
+        funcInfo->ReleaseTmpRegister(argTempLocation--);
+    }
+
+    return argIndex;
 }
 
 size_t EmitArgs(
@@ -6900,57 +7002,18 @@ size_t EmitArgs(
     {
         while (pnode->nop == knopList)
         {
-            // If this is a put, the arguments have already been evaluated (see EmitReference).
-            // We just need to emit the ArgOut instructions.
-            if (fAssignRegs)
-            {
-                Emit(pnode->sxBin.pnode1, byteCodeGenerator, funcInfo, false);
-            }
-
-            if (pnode->sxBin.pnode1->nop == knopEllipsis)
-            {
-                Assert(spreadIndices != nullptr);
-                spreadIndices->elements[spreadIndex++] = argIndex + 1; // account for 'this'
-                EmitSpreadArgToListBytecodeInstr(byteCodeGenerator, funcInfo, pnode->sxBin.pnode1->location, callSiteId, argIndex);
-            }
-            else
-            {
-                byteCodeGenerator->Writer()->ArgOut<true>(++argIndex, pnode->sxBin.pnode1->location, callSiteId);
-            }
-            if (fAssignRegs)
-            {
-                funcInfo->ReleaseLoc(pnode->sxBin.pnode1);
-            }
-
+            EmitOneArg(pnode->sxBin.pnode1, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, argIndex, spreadIndex, Js::Constants::NoRegister, spreadIndices);
             pnode = pnode->sxBin.pnode2;
         }
 
-        // If this is a put, the call target has already been evaluated (see EmitReference).
-        if (fAssignRegs)
-        {
-            Emit(pnode, byteCodeGenerator, funcInfo, false);
-        }
-
-        if (pnode->nop == knopEllipsis)
-        {
-            Assert(spreadIndices != nullptr);
-            spreadIndices->elements[spreadIndex++] = argIndex + 1; // account for 'this'
-            EmitSpreadArgToListBytecodeInstr(byteCodeGenerator, funcInfo, pnode->location, callSiteId, argIndex);
-        }
-        else
-        {
-            byteCodeGenerator->Writer()->ArgOut<true>(++argIndex, pnode->location, callSiteId);
-        }
-
-        if (fAssignRegs)
-        {
-            funcInfo->ReleaseLoc(pnode);
-        }
+        EmitOneArg(pnode, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, argIndex, spreadIndex, Js::Constants::NoRegister, spreadIndices);
     }
 
     return argIndex;
 }
 
+
+
 void EmitArgListStart(
     Js::RegSlot thisLocation,
     ByteCodeGenerator *byteCodeGenerator,
@@ -7060,13 +7123,18 @@ Js::ArgSlot EmitArgList(
     ByteCodeGenerator *byteCodeGenerator,
     FuncInfo *funcInfo,
     Js::ProfileId callSiteId,
+    Js::ArgSlot argsCountForStartCall,
+    bool emitArgOutsAtEnd,
     uint16 spreadArgCount = 0,
     Js::AuxArray<uint32> **spreadIndices = nullptr)
 {
     // This function emits the arguments for a call.
     // ArgOut's with uses immediately following defs.
-
-    EmitArgListStart(thisLocation, byteCodeGenerator, funcInfo, callSiteId);
+    if (!emitArgOutsAtEnd)
+    {
+        byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argsCountForStartCall);
+        EmitArgListStart(thisLocation, byteCodeGenerator, funcInfo, callSiteId);
+    }
 
     Js::RegSlot evalLocation = Js::Constants::NoRegister;
 
@@ -7086,7 +7154,15 @@ Js::ArgSlot EmitArgList(
         *spreadIndices = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::AuxArray<uint32>, spreadArgCount);
     }
 
-    size_t argIndex = EmitArgs(pnode, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, spreadIndices == nullptr ? nullptr : *spreadIndices);
+    size_t argIndex = 0;
+    if (emitArgOutsAtEnd)
+    {
+        argIndex = EmitArgsWithArgOutsAtEnd(pnode, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, thisLocation, argsCountForStartCall, spreadIndices == nullptr ? nullptr : *spreadIndices);
+    }
+    else
+    {
+        argIndex = EmitArgs(pnode, fAssignRegs, byteCodeGenerator, funcInfo, callSiteId, spreadIndices == nullptr ? nullptr : *spreadIndices);
+    }
 
     Js::ArgSlot argumentsCount = EmitArgListEnd(pnode, thisLocation, evalLocation, newTargetLocation, byteCodeGenerator, funcInfo, argIndex, callSiteId);
 
@@ -7822,11 +7898,12 @@ void EmitNew(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* f
     }
     else
     {
-        byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argCount);
+
         uint32 actualArgCount = 0;
 
         if (IsCallOfConstants(pnode))
         {
+            byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argCount);
             funcInfo->ReleaseLoc(pnode->sxCall.pnodeTarget);
             actualArgCount = EmitNewObjectOfConstants(pnode, byteCodeGenerator, funcInfo, argCount);
         }
@@ -7847,10 +7924,9 @@ void EmitNew(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* f
 
             Js::AuxArray<uint32> *spreadIndices = nullptr;
             actualArgCount = EmitArgList(pnode->sxCall.pnodeArgs, Js::Constants::NoRegister, Js::Constants::NoRegister,
-                false, true, byteCodeGenerator, funcInfo, callSiteId, pnode->sxCall.spreadArgCount, &spreadIndices);
+                false, true, byteCodeGenerator, funcInfo, callSiteId, argCount, pnode->sxCall.hasDestructuring, pnode->sxCall.spreadArgCount, &spreadIndices);
             funcInfo->ReleaseLoc(pnode->sxCall.pnodeTarget);
 
-
             if (pnode->sxCall.spreadArgCount > 0)
             {
                 Assert(spreadIndices != nullptr);
@@ -7990,9 +8066,9 @@ void EmitCall(
 
     Js::ProfileId callSiteId = byteCodeGenerator->GetNextCallSiteId(Js::OpCode::CallI);
 
-    byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argSlotCount);
     Js::AuxArray<uint32> *spreadIndices;
-    Js::ArgSlot actualArgCount = EmitArgList(pnodeArgs, thisLocation, newTargetLocation, fIsEval, fEvaluateComponents, byteCodeGenerator, funcInfo, callSiteId, spreadArgCount, &spreadIndices);
+    Js::ArgSlot actualArgCount = EmitArgList(pnodeArgs, thisLocation, newTargetLocation, fIsEval, fEvaluateComponents, byteCodeGenerator, funcInfo, callSiteId, argSlotCount, pnode->sxCall.hasDestructuring, spreadArgCount, &spreadIndices);
+
     Assert(argSlotCount == actualArgCount);
 
     if (!fEvaluateComponents)

+ 44 - 5
lib/Runtime/ByteCode/ByteCodeGenerator.cpp

@@ -731,6 +731,32 @@ ByteCodeGenerator::ByteCodeGenerator(Js::ScriptContext* scriptContext, Js::Scope
     m_writer.Create();
 }
 
+void ByteCodeGenerator::FinalizeFuncInfos()
+{
+    if (this->funcInfosToFinalize == nullptr)
+    {
+        return;
+    }
+
+    FOREACH_SLIST_ENTRY(FuncInfo*, funcInfo, this->funcInfosToFinalize)
+    {
+        funcInfo->byteCodeFunction->SetAttributes(funcInfo->originalAttributes);
+    }
+    NEXT_SLIST_ENTRY;
+
+    this->funcInfosToFinalize = nullptr;
+}
+
+void ByteCodeGenerator::AddFuncInfoToFinalizationSet(FuncInfo * funcInfo)
+{
+    if (this->funcInfosToFinalize == nullptr)
+    {
+        this->funcInfosToFinalize = Anew(alloc, SList<FuncInfo*>, alloc);
+    }
+
+    this->funcInfosToFinalize->Prepend(funcInfo);
+}
+
 /* static */
 bool ByteCodeGenerator::IsFalse(ParseNode* node)
 {
@@ -990,7 +1016,7 @@ void ByteCodeGenerator::RestoreScopeInfo(Js::ScopeInfo *scopeInfo, FuncInfo * fu
 
         if (newFunc)
         {
-            func = Anew(alloc, FuncInfo, pfi->GetDisplayName(), alloc, nullptr, nullptr, nullptr, pfi);
+            func = Anew(alloc, FuncInfo, pfi->GetDisplayName(), alloc, this, nullptr, nullptr, nullptr, pfi);
             newFunc = true;
         }
 
@@ -1018,7 +1044,7 @@ void ByteCodeGenerator::RestoreScopeInfo(Js::ScopeInfo *scopeInfo, FuncInfo * fu
         if (func == nullptr || !func->byteCodeFunction->GetIsGlobalFunc())
         {
             func = Anew(alloc, FuncInfo, Js::Constants::GlobalFunction,
-                alloc, nullptr, nullptr/*currentScope*/, nullptr, nullptr/*functionBody*/);
+                alloc, this, nullptr, nullptr/*currentScope*/, nullptr, nullptr/*functionBody*/);
             PushFuncInfo(_u("RestoreScopeInfo"), func);
         }
         func->SetBodyScope(currentScope);
@@ -1121,7 +1147,7 @@ FuncInfo * ByteCodeGenerator::StartBindGlobalStatements(ParseNode *pnode)
     }
 
     FuncInfo *funcInfo = Anew(alloc, FuncInfo, Js::Constants::GlobalFunction,
-        alloc, nullptr, globalScope, pnode, byteCodeFunction);
+        alloc, this, nullptr, globalScope, pnode, byteCodeFunction);
 
     int32 currentAstSize = pnode->sxFnc.astSize;
     if (currentAstSize > this->maxAstSize)
@@ -1416,7 +1442,7 @@ FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLen
         parseableFunctionInfo->SetIsStrictMode();
     }
 
-    FuncInfo *funcInfo = Anew(alloc, FuncInfo, name, alloc, paramScope, bodyScope, pnode, parseableFunctionInfo);
+    FuncInfo *funcInfo = Anew(alloc, FuncInfo, name, alloc, this, paramScope, bodyScope, pnode, parseableFunctionInfo);
 
 #if DBG
     funcInfo->isReused = (reuseNestedFunc != nullptr);
@@ -1916,6 +1942,19 @@ void ByteCodeGenerator::Generate(__in ParseNode *pnode, uint32 grfscr, __in Byte
     sourceContextInfo->EnsureInitialized();
 
     ArenaAllocator localAlloc(_u("ByteCode"), threadContext->GetPageAllocator(), Js::Throw::OutOfMemory);
+
+    // Make sure FuncInfo's get finalized when byte code gen is done.
+    struct AutoFinalizeFuncInfos {
+        AutoFinalizeFuncInfos(ByteCodeGenerator * byteCodeGenerator) : byteCodeGenerator(byteCodeGenerator) {}
+        ~AutoFinalizeFuncInfos() {
+            if (byteCodeGenerator)
+            {
+                byteCodeGenerator->FinalizeFuncInfos();
+            }
+        }
+        ByteCodeGenerator * byteCodeGenerator;
+    } autoFinalizeFuncInfos(byteCodeGenerator);
+
     byteCodeGenerator->parser = parser;
     byteCodeGenerator->SetCurrentSourceIndex(sourceIndex);
     byteCodeGenerator->Begin(&localAlloc, grfscr, *ppRootFunc);
@@ -2082,6 +2121,7 @@ void ByteCodeGenerator::Begin(
     this->loopDepth = 0;
     this->envDepth = 0;
     this->trackEnvDepth = false;
+    this->funcInfosToFinalize = nullptr;
 
     this->funcInfoStack = Anew(alloc, SList<FuncInfo*>, alloc);
 
@@ -3243,7 +3283,6 @@ void VisitNestedScopes(ParseNode* pnodeScopeList, ParseNode* pnodeParent, ByteCo
                         && reuseNestedFunc->IsFunctionBody())
                     {
                         byteCodeGenerator->pCurrentFunction = reuseNestedFunc->GetFunctionBody();
-                        byteCodeGenerator->pCurrentFunction->CleanupToReparse();
                     }
                 }
             }

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

@@ -26,6 +26,8 @@ private:
     // pointer to the root function wrapper that will be invoked by the caller
     Js::ParseableFunctionInfo * pRootFunc;
 
+    SList<FuncInfo*> * funcInfosToFinalize;
+
     int32 maxAstSize;
     uint16 envDepth;
     uint sourceIndex;
@@ -395,6 +397,9 @@ public:
 
     static bool NeedScopeObjectForArguments(FuncInfo *funcInfo, ParseNode *pnodeFnc);
 
+    void AddFuncInfoToFinalizationSet(FuncInfo *funcInfo);
+    void FinalizeFuncInfos();
+
     Js::OpCode GetStSlotOp(Scope *scope, int envIndex, Js::RegSlot scopeLocation, bool chkBlockVar, FuncInfo *funcInfo);
     Js::OpCode GetLdSlotOp(Scope *scope, int envIndex, Js::RegSlot scopeLocation, FuncInfo *funcInfo);
     Js::OpCode GetInitFldOp(Scope *scope, Js::RegSlot scopeLocation, FuncInfo *funcInfo, bool letDecl = false);

+ 12 - 1
lib/Runtime/ByteCode/FuncInfo.cpp

@@ -7,6 +7,7 @@
 FuncInfo::FuncInfo(
     const char16 *name,
     ArenaAllocator *alloc,
+    ByteCodeGenerator *byteCodeGenerator,
     Scope *paramScope,
     Scope *bodyScope,
     ParseNode *pnode,
@@ -85,7 +86,8 @@ FuncInfo::FuncInfo(
     stringTemplateCallsiteRegisterMap(alloc, 17),
     targetStatements(alloc),
     nextForInLoopLevel(0),
-    maxForInLoopLevel(0)
+    maxForInLoopLevel(0),
+    originalAttributes(Js::FunctionInfo::Attributes::None)
 {
     this->byteCodeFunction = byteCodeFunction;
     if (bodyScope != nullptr)
@@ -100,6 +102,15 @@ FuncInfo::FuncInfo(
     {
         this->SetHasMaybeEscapedNestedFunc(DebugOnly(_u("Child")));
     }
+
+    if (byteCodeFunction && !byteCodeFunction->IsDeferred() && byteCodeFunction->CanBeDeferred())
+    {
+        // Disable (re-)deferral of this function temporarily. Add it to the list of FuncInfo's to be processed when 
+        // byte code gen is done.
+        this->originalAttributes = byteCodeFunction->GetAttributes();
+        byteCodeGenerator->AddFuncInfoToFinalizationSet(this);
+        byteCodeFunction->SetAttributes((Js::FunctionInfo::Attributes)(this->originalAttributes & ~Js::FunctionInfo::Attributes::CanDefer));
+    }
 }
 
 bool FuncInfo::IsGlobalFunction() const

+ 2 - 0
lib/Runtime/ByteCode/FuncInfo.h

@@ -119,6 +119,7 @@ public:
     Js::RegSlot firstThunkArgReg;
     short thunkArgCount;
     short staticFuncId;
+    Js::FunctionInfo::Attributes originalAttributes;
 
     uint callsEval : 1;
     uint childCallsEval : 1;
@@ -181,6 +182,7 @@ public:
     FuncInfo(
         const char16 *name,
         ArenaAllocator *alloc,
+        ByteCodeGenerator *byteCodeGenerator,
         Scope *paramScope,
         Scope *bodyScope,
         ParseNode *pnode,

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

@@ -58,6 +58,7 @@ MACRO_WMS       ( Ld_Db                      , Double2         , None
 // External Function calls
 MACRO           ( StartCall                  , StartCall       , None            ) // Initialize memory for a call
 MACRO_WMS       ( Call                       , AsmCall         , None            ) // Execute call and place return value in register
+MACRO           ( CheckHeap                  , Empty           , None            ) // Make sure the heap is not detached
 MACRO_WMS       ( ArgOut_Int                 , Reg1Int1        , None            ) // convert int to var and place it for function call
 MACRO_WMS       ( ArgOut_Long                , Reg1Long1       , None            ) // convert int64 to var and place it for function call
 MACRO_WMS       ( ArgOut_Flt                 , Reg1Float1      , None            ) // convert float to var and place it for function call

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

@@ -641,7 +641,7 @@ namespace Js
                 }
                 else if (pFBody->GetPropertyIdsForScopeSlotArray() != nullptr)
                 {
-                    uint slotArrayCount = slotArray.GetCount();
+                    uint slotArrayCount = static_cast<uint>(slotArray.GetCount());
                     pMembersList = JsUtil::List<DebuggerPropertyDisplayInfo *, ArenaAllocator>::New(arena, slotArrayCount);
 
                     for (uint32 i = 0; i < slotArrayCount; i++)

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

@@ -98,7 +98,8 @@ namespace TTD
             slotInfo->SlotId = TTD_CONVERT_VAR_TO_PTR_ID(scope);
             slotInfo->ScriptContextLogId = ctx->ScriptContextLogTag;
 
-            slotInfo->SlotCount = slots.GetCount();
+            slotInfo->SlotCount = static_cast<uint>(slots.GetCount());
+
             slotInfo->Slots = this->m_pendingSnap->GetSnapshotSlabAllocator().SlabAllocateArray<TTDVar>(slotInfo->SlotCount);
 
             for(uint32 j = 0; j < slotInfo->SlotCount; ++j)
@@ -322,8 +323,7 @@ namespace TTD
                     if(this->m_marks.MarkAndTestAddr<MarkTableTag::SlotArrayTag>(scope))
                     {
                         Js::ScopeSlots slotArray = (Js::Var*)scope;
-                        uint slotArrayCount = slotArray.GetCount();
-
+                        uint slotArrayCount = static_cast<uint>(slotArray.GetCount());
                         if(slotArray.IsFunctionScopeSlotArray())
                         {
                             this->MarkFunctionBody(slotArray.GetFunctionInfo()->GetFunctionBody());

+ 2 - 0
lib/Runtime/Language/AsmJs.cpp

@@ -403,6 +403,8 @@ namespace Js
     bool AsmJSCompiler::CheckNewArrayView( AsmJsModuleCompiler &m, PropertyName varName, ParseNode *newExpr )
     {
         Assert( newExpr->nop == knopNew );
+        m.SetUsesHeapBuffer(true);
+
         ParseNode *ctorExpr = newExpr->sxCall.pnodeTarget;
         ArrayBufferView::ViewType type;
         if( ParserWrapper::IsDotMember(ctorExpr) )

+ 7 - 0
lib/Runtime/Language/AsmJsByteCodeGenerator.cpp

@@ -1285,6 +1285,13 @@ namespace Js
             info.location = mFunction->AcquireTmpRegisterGeneric(expectedType);
             mWriter.AsmCall(OpCodeAsmJs::I_Call, info.location, funcReg, runtimeArg, expectedType, profileId);
         }
+
+        // after foreign function call, we need to make sure that the heap hasn't been detached
+        if (isFFI && mCompiler->UsesHeapBuffer())
+        {
+            mWriter.EmptyAsm(OpCodeAsmJs::CheckHeap);
+            mCompiler->SetUsesHeapBuffer(true);
+        }
         EndStatement(pnode);
 
         return info;

+ 1 - 0
lib/Runtime/Language/AsmJsEncoder.h

@@ -72,6 +72,7 @@ namespace Js
         template <class T> void OP_BrEqConst( const unaligned T* playout );
         template <class T> void OP_BrTrue( const unaligned T* playout );
         template <class T> void OP_Empty( const unaligned T* playout );
+        template <class T> void OP_CheckHeap(const unaligned T* playout);
         template <class T> void Op_LdSlot_Db( const unaligned T* playout );
         template <class T> void Op_LdSlot_Int(const unaligned T* playout);
         template <class T> void Op_LdSlot_Flt(const unaligned T* playout);

+ 6 - 0
lib/Runtime/Language/AsmJsEncoder.inl

@@ -152,6 +152,12 @@ namespace Js
 
     }
 
+    template <class T>
+    void AsmJsEncoder::OP_CheckHeap(const unaligned T* playout)
+    {
+        // Todo
+    }
+
     void AsmJsEncoder::OP_Label( const unaligned OpLayoutEmpty* playout )
     {
         const int labelOffset = mReader.GetCurrentOffset() - 1;

+ 1 - 0
lib/Runtime/Language/AsmJsEncoderHandler.inl

@@ -50,6 +50,7 @@ EXDEF3    ( CUSTOM     , InvalidOpCode     , OP_Empty                , Empty
 // External Calls
   DEF3    ( CUSTOM     , StartCall         , OP_StartCall            , StartCall     )
   DEF3_WMS( CUSTOM     , Call              , OP_Call                 , AsmCall       )
+  DEF3    ( CUSTOM     , CheckHeap         , OP_CheckHeap            , Empty         ) // TODO:: Implement CheckHeap in Templatized jit
   DEF3_WMS( CUSTOM     , ArgOut_Db         , OP_ArgOut_Db            , Reg1Double1   )
   DEF3_WMS( CUSTOM     , ArgOut_Int        , OP_ArgOut_Int           , Reg1Int1      )
   DEF3_WMS( CUSTOM     , Conv_VTD          , OP_Conv_VTD             , Double1Reg1   )

+ 2 - 6
lib/Runtime/Language/AsmJsUtils.cpp

@@ -184,9 +184,6 @@ namespace Js
 #if ENABLE_DEBUG_CONFIG_OPTIONS
         bool allowTestInputs = CONFIG_FLAG(WasmI64);
 #endif
-
-        AsmJsModuleInfo::EnsureHeapAttached(func);
-
         ArgumentReader reader(&callInfo, origArgs);
         uint actualArgCount = reader.Info.Count - 1; // -1 for ScriptFunction
         argDst = argDst + MachPtr; // add one first so as to skip the ScriptFunction argument
@@ -411,6 +408,8 @@ namespace Js
             }
             ++origArgs;
         }
+        AsmJsModuleInfo::EnsureHeapAttached(func);
+
         // for convenience, lets take the opportunity to return the asm.js entrypoint address
         return address;
     }
@@ -574,9 +573,6 @@ namespace Js
         void* dst;
         Var returnValue = 0;
 
-        // TODO (michhol): wasm, heap should not ever be detached
-        AsmJsModuleInfo::EnsureHeapAttached(func);
-
         argSize = ::Math::Align<int32>(argSize, 8);
         // Allocate stack space for args
         PROBE_STACK_CALL(func->GetScriptContext(), func, argSize + Js::Constants::MinStackDefault);

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

@@ -25,6 +25,7 @@ EXDEF2    (NOPASMJS          , InvalidOpCode, Empty
   DEF3_WMS( CUSTOM_ASMJS     , I_Call       , OP_I_AsmCall                 , AsmCall             )
   DEF3_WMS( CUSTOM_ASMJS     , ProfiledI_Call, OP_ProfiledI_AsmCall        , ProfiledAsmCall     )
   DEF3_WMS( CUSTOM_ASMJS     , Call         , OP_AsmCall                   , AsmCall             )
+  DEF3    ( CUSTOM_ASMJS     , CheckHeap    , OP_EnsureHeapAttached        , Empty               )
   DEF2_WMS( D1toR1Out        , I_ArgOut_Db  , OP_I_SetOutAsmDb                                   ) // set double as internal outparam
   DEF2_WMS( D1toR1Out        , ArgOut_Db    , OP_SetOutAsmDb                                     ) // convert double to var and set it as outparam
   DEF2_WMS( I1toR1Out        , I_ArgOut_Int , OP_I_SetOutAsmInt                                  ) // set int as internal outparam

+ 8 - 2
lib/Runtime/Language/InterpreterStackFrame.cpp

@@ -1241,6 +1241,7 @@ namespace Js
                     uint16 envDepth = this->executeFunction->GetEnvDepth();
                     Assert(envDepth != (uint16)-1);
                     newInstance->localFrameDisplay = (FrameDisplay*)nextAllocBytes;
+                    newInstance->localFrameDisplay->SetLength(0); // Start with no scopes. It will get set in NewFrameDisplay
                     nextAllocBytes += sizeof(FrameDisplay) + (envDepth + 1) * sizeof(Var);
                 }
 
@@ -1248,7 +1249,7 @@ namespace Js
                 {
                     uint32 scopeSlots = this->executeFunction->scopeSlotArraySize;
                     Assert(scopeSlots != 0);
-                    ScopeSlots((Var*)nextAllocBytes).SetCount(scopeSlots);
+                    ScopeSlots((Var*)nextAllocBytes).SetCount(0); // Start with count as 0. It will get set in NewScopeSlots
                     newInstance->localClosure = nextAllocBytes;
                     nextAllocBytes += (scopeSlots + ScopeSlots::FirstSlotIndex) * sizeof(Var);
                 }
@@ -1850,7 +1851,7 @@ namespace Js
             // generator object.  The second argument is the ResumeYieldData which is only needed
             // when resuming a generator and so it only used here if a frame already exists on the
             // generator object.
-            AssertMsg(args.Info.Count == 2, "Generator ScriptFunctions should only be invoked by generator APIs with the pair of arguments they pass in -- the generator object and a ResumeYieldData pointer");
+            AssertOrFailFastMsg(args.Info.Count == 2 && ((args.Info.Flags & CallFlags_ExtraArg) == CallFlags_None), "Generator ScriptFunctions should only be invoked by generator APIs with the pair of arguments they pass in -- the generator object and a ResumeYieldData pointer");
             JavascriptGenerator* generator = JavascriptGenerator::FromVar(args[0]);
             newInstance = generator->GetFrame();
 
@@ -3818,7 +3819,11 @@ namespace Js
         }
 
         PopOut(playout->ArgCount);
+    }
 
+    template <class T>
+    void InterpreterStackFrame::OP_EnsureHeapAttached(const unaligned T* playout)
+    {
         AsmJsModuleInfo::EnsureHeapAttached(this->function);
     }
 
@@ -7298,6 +7303,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
 
     FrameDisplay* InterpreterStackFrame::GetLocalFrameDisplay() const
     {
+        Assert(this->localFrameDisplay == nullptr || this->IsClosureInitDone() || this->localFrameDisplay->GetLength() == 0);
         return this->localFrameDisplay;
     }
 

+ 2 - 0
lib/Runtime/Language/InterpreterStackFrame.h

@@ -223,6 +223,7 @@ namespace Js
         FrameDisplay * GetFrameDisplayForNestedFunc() const;
         Var InnerScopeFromRegSlot(RegSlot reg) const;
         void SetClosureInitDone(bool done) { closureInitDone = done; }
+        bool IsClosureInitDone() const { return closureInitDone; }
 
         void ValidateRegValue(Var value, bool allowStackVar = false, bool allowStackVarOnDisabledStackNestedFunc = true) const;
         int OP_GetMemorySize();
@@ -477,6 +478,7 @@ namespace Js
         template <class T> void OP_ProfileReturnTypeCallCommon(const unaligned T *playout, RecyclableObject * aFunc, unsigned flags, ProfileId profileId, const Js::AuxArray<uint32> *spreadIndices = nullptr);
         
         template <class T> void OP_AsmCall(const unaligned T* playout);
+        template <class T> void OP_EnsureHeapAttached(const unaligned T* playout);
 
         template <class T> void OP_CallI(const unaligned T* playout) { OP_CallCommon(playout, OP_CallGetFunc(GetRegAllowStackVar(playout->Function)), Js::CallFlags_None); }
         template <class T> void OP_CallIFlags(const unaligned T* playout) { OP_CallCommon(playout, OP_CallGetFunc(GetRegAllowStackVar(playout->Function)), playout->callFlags); }

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

@@ -6912,7 +6912,7 @@ CommonNumber:
     Field(Var)* JavascriptOperators::OP_CloneScopeSlots(Field(Var) *slotArray, ScriptContext *scriptContext)
     {
         ScopeSlots slots((Js::Var*)slotArray);
-        uint size = ScopeSlots::FirstSlotIndex + slots.GetCount();
+        uint size = ScopeSlots::FirstSlotIndex + static_cast<uint>(slots.GetCount());
 
         Field(Var)* slotArrayClone = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), size);
         CopyArray(slotArrayClone, size, slotArray, size);

+ 8 - 1
lib/Runtime/Library/JavascriptArray.cpp

@@ -11989,7 +11989,14 @@ Case0:
         Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(length, scriptContext) };
         Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
 
-        return RecyclableObject::FromVar(JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext));
+        AssertOrFailFast(Js::RecyclableObject::Is(constructor));
+        ThreadContext* threadContext = scriptContext->GetThreadContext();
+        Var scObject = threadContext->ExecuteImplicitCall((RecyclableObject*)constructor, ImplicitCall_Accessor, [&]()->Js::Var
+        {
+            return JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
+        });
+
+        return RecyclableObject::FromVar(scObject);
     }
     /*static*/
     PropertyId const JavascriptArray::specialPropertyIds[] =

+ 19 - 2
lib/Runtime/Library/JavascriptProxy.cpp

@@ -2060,6 +2060,7 @@ namespace Js
 
         BOOL hasOverridingNewTarget = callInfo.Flags & CallFlags_NewTarget;
         bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && args[0] != nullptr && RecyclableObject::Is(args[0]);
+        bool isNewCall = callInfo.Flags & CallFlags_New || callInfo.Flags & CallFlags_NewTarget;
 
         AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
         if (!JavascriptProxy::Is(function))
@@ -2126,7 +2127,16 @@ namespace Js
                 args.Values[0] = newThisObject;
             }
 
-            ushort newCount = (ushort)(args.Info.Count + 1);
+            ushort newCount = (ushort)args.Info.Count;
+            if (isNewCall)
+            {
+                newCount++;
+                if (!newCount)
+                {
+                    ::Math::DefaultOverflowPolicy();
+                }
+            }
+
             Var* newValues;
             const unsigned STACK_ARGS_ALLOCA_THRESHOLD = 8; // Number of stack args we allow before using _alloca
             Var stackArgs[STACK_ARGS_ALLOCA_THRESHOLD];
@@ -2139,14 +2149,21 @@ namespace Js
             {
                 newValues = stackArgs;
             }
-            CallInfo calleeInfo((CallFlags)(args.Info.Flags | CallFlags_ExtraArg | CallFlags_NewTarget), newCount);
+            CallInfo calleeInfo((CallFlags)(args.Info.Flags), newCount);
+            if (isNewCall)
+            {
+                calleeInfo.Flags = (CallFlags)(calleeInfo.Flags | CallFlags_ExtraArg | CallFlags_NewTarget);
+            }
 
             for (uint argCount = 0; argCount < args.Info.Count; argCount++)
             {
                 newValues[argCount] = args.Values[argCount];
             }
 #pragma prefast(suppress:6386)
+            if (isNewCall)
+            {
             newValues[args.Info.Count] = newTarget;
+            }
 
             Js::Arguments arguments(calleeInfo, newValues);
             Var aReturnValue = JavascriptFunction::CallFunction<true>(targetObj, targetObj->GetEntryPoint(), arguments);

+ 13 - 2
lib/Runtime/Library/RegexHelper.cpp

@@ -1304,9 +1304,16 @@ namespace Js
 
             // WARNING: We go off into script land here, which way in turn invoke a regex operation, even on the
             //          same regex.
-            JavascriptString* replace = JavascriptConversion::ToString(replacefn->CallFunction(Arguments(CallInfo((ushort)(numGroups + 3)), replaceArgs)), scriptContext);
+
+            ThreadContext* threadContext = scriptContext->GetThreadContext();
+            Var replaceVar = threadContext->ExecuteImplicitCall(replacefn, ImplicitCall_Accessor, [=]()->Js::Var
+            {
+                return replacefn->CallFunction(Arguments(CallInfo((ushort)(numGroups + 3)), replaceArgs));
+            });
+            JavascriptString* replace = JavascriptConversion::ToString(replaceVar, scriptContext);
             concatenated.Append(input, offset, lastActualMatch.offset - offset);
             concatenated.Append(replace);
+
             if (lastActualMatch.length == 0)
             {
                 if (lastActualMatch.offset < inputLength)
@@ -1442,8 +1449,12 @@ namespace Js
 
         if (indexMatched != CharCountFlag)
         {
+            ThreadContext* threadContext = scriptContext->GetThreadContext();
+            Var replaceVar = threadContext->ExecuteImplicitCall(replacefn, ImplicitCall_Accessor, [=]()->Js::Var
+            {
             Var pThis = scriptContext->GetLibrary()->GetUndefined();
-            Var replaceVar = CALL_FUNCTION(scriptContext->GetThreadContext(), replacefn, CallInfo(4), pThis, match, JavascriptNumber::ToVar((int)indexMatched, scriptContext), input);
+                return CALL_FUNCTION(threadContext, replacefn, CallInfo(4), pThis, match, JavascriptNumber::ToVar((int)indexMatched, scriptContext), input);
+            });
             JavascriptString* replace = JavascriptConversion::ToString(replaceVar, scriptContext);
             const char16* inputStr = input->GetString();
             const char16* prefixStr = inputStr;

+ 1 - 1
lib/Runtime/Library/ScriptFunction.cpp

@@ -560,7 +560,7 @@ namespace Js
             case Js::ScopeType::ScopeType_SlotArray:
             {
                 Js::ScopeSlots slotArray = (Js::Var*)scope;
-                uint slotArrayCount = slotArray.GetCount();
+                uint slotArrayCount = static_cast<uint>(slotArray.GetCount());
 
                 //get the function body associated with the scope
                 if(slotArray.IsFunctionScopeSlotArray())

+ 16 - 15
lib/Runtime/Library/StackScriptFunction.cpp

@@ -209,7 +209,7 @@ namespace Js
 
                     Assert(StackScriptFunction::GetCurrentFunctionObject(interpreterFrame->GetJavascriptFunction()) == caller);
 
-                    if (callerFunctionBody->DoStackFrameDisplay())
+                    if (callerFunctionBody->DoStackFrameDisplay() && interpreterFrame->IsClosureInitDone())
                     {
                         Js::FrameDisplay *stackFrameDisplay = interpreterFrame->GetLocalFrameDisplay();
                         // Local frame display may be null if bailout didn't restore it, which means we don't need it.
@@ -219,13 +219,13 @@ namespace Js
                             interpreterFrame->SetLocalFrameDisplay(boxedFrameDisplay);
                         }
                     }
-                    if (callerFunctionBody->DoStackScopeSlots())
+                    if (callerFunctionBody->DoStackScopeSlots() && interpreterFrame->IsClosureInitDone())
                     {
                         Var* stackScopeSlots = (Var*)interpreterFrame->GetLocalClosure();
                         if (stackScopeSlots)
                         {
                             // Scope slot pointer may be null if bailout didn't restore it, which means we don't need it.
-                            Var* boxedScopeSlots = this->BoxScopeSlots(stackScopeSlots, ScopeSlots(stackScopeSlots).GetCount());
+                            Var* boxedScopeSlots = this->BoxScopeSlots(stackScopeSlots, static_cast<uint>(ScopeSlots(stackScopeSlots).GetCount()));
                             interpreterFrame->SetLocalClosure((Var)boxedScopeSlots);
                         }
                     }
@@ -300,7 +300,7 @@ namespace Js
                             if (stackScopeSlots)
                             {
                                 // Scope slot pointer may be null if bailout didn't restore it, which means we don't need it.
-                                this->BoxScopeSlots(stackScopeSlots, ScopeSlots(stackScopeSlots).GetCount());
+                                this->BoxScopeSlots(stackScopeSlots, static_cast<uint>(ScopeSlots(stackScopeSlots).GetCount()));
                             }
                         }
 
@@ -327,9 +327,12 @@ namespace Js
                 // Everything from that point outward must be boxed.
                 FrameDisplay *frameDisplay;
                 InterpreterStackFrame *interpreterFrame = walker.GetCurrentInterpreterFrame();
+                bool closureInitDone = true; // Set to true as for native frame bailout will always restore the frameDisplay but for interpreter frame if PROBE_STACK fails closureInitDone won't have completed.
+
                 if (interpreterFrame)
                 {
                     frameDisplay = interpreterFrame->GetLocalFrameDisplay();
+                    closureInitDone = interpreterFrame->IsClosureInitDone();
                 }
                 else
                 {
@@ -340,7 +343,7 @@ namespace Js
 #endif
                         JavascriptFunctionArgIndex_StackFrameDisplay];
                 }
-                if (ThreadContext::IsOnStack(frameDisplay))
+                if (ThreadContext::IsOnStack(frameDisplay) && closureInitDone)
                 {
                     int i;
                     for (i = 0; i < frameDisplay->GetLength(); i++)
@@ -358,11 +361,10 @@ namespace Js
                     }
                     for (; i < frameDisplay->GetLength(); i++)
                     {
-                        Var *scopeSlots = (Var*)frameDisplay->GetItem(i);
-                        size_t count = ScopeSlots(scopeSlots).GetCount();
-                        if (count < ScopeSlots::MaxEncodedSlotCount)
+                        Var *pScope = (Var*)frameDisplay->GetItem(i);
+                        if (ScopeSlots::Is(pScope))
                         {
-                            Var *boxedSlots = this->BoxScopeSlots(scopeSlots, static_cast<uint>(count));
+                            Var *boxedSlots = this->BoxScopeSlots(pScope, static_cast<uint>(ScopeSlots(pScope).GetCount()));
                             frameDisplay->SetItem(i, boxedSlots);
                         }
                     }
@@ -638,15 +640,14 @@ namespace Js
         for (uint16 i = 0; i < length; i++)
         {
             // TODO: Once we allocate the slots on the stack, we can only look those slots
-            Var * scopeSlots = (Var *)frameDisplay->GetItem(i);
-            size_t scopeSlotcount = ScopeSlots(scopeSlots).GetCount(); // (size_t)scopeSlots[Js::ScopeSlots::EncodedSlotCountSlotIndex];
+            Var * pScope = (Var *)frameDisplay->GetItem(i);
             // We don't do stack slots if we exceed max encoded slot count
-            if (scopeSlotcount < ScopeSlots::MaxEncodedSlotCount)
+            if (ScopeSlots::Is(pScope))
             {
-                scopeSlots = BoxScopeSlots(scopeSlots, static_cast<uint>(scopeSlotcount));
+                pScope = BoxScopeSlots(pScope, static_cast<uint>(ScopeSlots(pScope).GetCount()));
             }
-            boxedFrameDisplay->SetItem(i, scopeSlots);
-            frameDisplay->SetItem(i, scopeSlots);
+            boxedFrameDisplay->SetItem(i, pScope);
+            frameDisplay->SetItem(i, pScope);
         }
         return boxedFrameDisplay;
     }

+ 6 - 0
lib/WasmReader/WasmByteCodeGenerator.cpp

@@ -1180,6 +1180,12 @@ EmitInfo WasmBytecodeGenerator::EmitCall()
     }
     AdeleteArray(&m_alloc, nArgs, argsList);
 
+    if (isImportCall && (m_module->HasMemory() || m_module->HasMemoryImport()))
+    {
+        m_writer->EmptyAsm(Js::OpCodeAsmJs::CheckHeap);
+        SetUsesMemory(0);
+    }
+
     // track stack requirements for out params
 
     // + 1 for return address

+ 46 - 0
test/switchStatement/aggressiveintoff.js

@@ -0,0 +1,46 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function opt() {
+    for (let i = 0; i < 100; i++) {
+        let j = i - 2;
+        switch (i) {
+            case 2:
+            case 4:
+            case 6:
+            case 8:
+            case 10:
+            case 12:
+            case 14:
+            case 16:
+            case 18:
+            case 20:
+            case 22:
+            case 24:
+            case 26:
+            case 28:
+            case 30:
+            case 32:
+            case 34:
+            case 36:
+            case 38:
+                break;
+        }
+
+        if (i == 90) {
+            i = 'x';
+        }
+    }
+}
+
+function main() {
+    for (let i = 0; i < 100; i++) {
+        opt();
+    }
+}
+
+main();
+
+WScript.Echo('pass');

+ 11 - 0
test/switchStatement/rlexe.xml

@@ -150,4 +150,15 @@
       <files>singleCharStringCase.js</files>
     </default>
   </test>
+  <test>
+    <default>
+      <files>aggressiveintoff.js</files>
+    </default>
+  </test>
+  <test>
+    <default>
+      <files>aggressiveintoff.js</files>
+      <compile-flags>-off:aggressiveinttypespec</compile-flags>
+    </default>
+  </test>
 </regress-exe>