diff --git a/lib/Parser/Parse.cpp b/lib/Parser/Parse.cpp index 7296af7e395..ff18dd356b7 100644 --- a/lib/Parser/Parse.cpp +++ b/lib/Parser/Parse.cpp @@ -90,6 +90,8 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator m_stoppedDeferredParse = FALSE; m_hasParallelJob = false; m_doingFastScan = false; + m_isInParsingArgList = false; + m_hasDestructuringPattern = false; m_scriptContext = scriptContext; m_pCurrentAstSize = nullptr; m_arrayDepth = 0; @@ -1252,7 +1254,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; @@ -3660,6 +3662,7 @@ ParseNodePtr Parser::ParsePostfixOperators( { case tkLParen: { + AutoMarkInParsingArgs autoMarkInParsingArgs(this); if (fInNew) { ParseNodePtr pnodeArgs = ParseArgList(&callOfConstants, &spreadArgCount, &count); @@ -3672,6 +3675,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(); @@ -3743,6 +3748,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(); } @@ -8630,6 +8637,7 @@ ParseNodePtr Parser::ParseExpr(int oplMin, if (buildAST) { + this->SetHasDestructuringPattern(true); pnode = ConvertToPattern(pnode); } diff --git a/lib/Parser/Parse.h b/lib/Parser/Parse.h index 982b527827d..dfe53c276c6 100644 --- a/lib/Parser/Parse.h +++ b/lib/Parser/Parse.h @@ -136,6 +136,12 @@ class Parser bool IsBackgroundParser() const { return m_isInBackground; } bool IsDoingFastScan() const { return m_doingFastScan; } + 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); @@ -395,7 +401,8 @@ class Parser bool m_isInBackground; 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 @@ -1110,6 +1117,30 @@ class Parser } }; + 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); diff --git a/lib/Parser/ptree.h b/lib/Parser/ptree.h index 9dfcde31090..57911d11985 100644 --- a/lib/Parser/ptree.h +++ b/lib/Parser/ptree.h @@ -459,6 +459,7 @@ struct PnCall BYTE isApplyCall : 1; BYTE isEvalCall : 1; BYTE isSuperCall : 1; + BYTE hasDestructuring : 1; }; struct PnStmt diff --git a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp index e5be833e084..b72b931494c 100644 --- a/lib/Runtime/ByteCode/ByteCodeEmitter.cpp +++ b/lib/Runtime/ByteCode/ByteCodeEmitter.cpp @@ -6879,12 +6879,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 *spreadIndices = nullptr +) { - Js::RegSlot regVal = funcInfo->AcquireTmpRegister(); - byteCodeGenerator->Writer()->Reg2(Js::OpCode::LdCustomSpreadIteratorList, regVal, argLoc); - byteCodeGenerator->Writer()->ArgOut(++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(argIndex + 1, regVal, callSiteId); + } + funcInfo->ReleaseTmpRegister(regVal); + } + else + { + if (noArgOuts) + { + byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, argTempLocation, pnode->location); + } + else + { + byteCodeGenerator->Writer()->ArgOut(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 *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(0, thisLocation, callSiteId); + } + + for (Js::ArgSlot index = 0; index < argIndex; index++) + { + byteCodeGenerator->Writer()->ArgOut(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( @@ -6903,57 +7003,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(++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(++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, @@ -7074,13 +7135,18 @@ Js::ArgSlot EmitArgList( ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, Js::ProfileId callSiteId, + Js::ArgSlot argsCountForStartCall, + bool emitArgOutsAtEnd, uint16 spreadArgCount = 0, Js::AuxArray **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; @@ -7100,7 +7166,15 @@ Js::ArgSlot EmitArgList( *spreadIndices = AnewPlus(byteCodeGenerator->GetAllocator(), extraAlloc, Js::AuxArray, 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, rhsLocation, thisLocation, evalLocation, newTargetLocation, byteCodeGenerator, funcInfo, argIndex, callSiteId); @@ -7855,11 +7929,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); } @@ -7880,10 +7955,9 @@ void EmitNew(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* f Js::AuxArray *spreadIndices = nullptr; actualArgCount = EmitArgList(pnode->sxCall.pnodeArgs, Js::Constants::NoRegister, 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); @@ -8025,9 +8099,9 @@ void EmitCall( Js::ProfileId callSiteId = byteCodeGenerator->GetNextCallSiteId(Js::OpCode::CallI); - byteCodeGenerator->Writer()->StartCall(Js::OpCode::StartCall, argSlotCount); Js::AuxArray *spreadIndices; - Js::ArgSlot actualArgCount = EmitArgList(pnodeArgs, rhsLocation, thisLocation, newTargetLocation, fIsEval, fEvaluateComponents, byteCodeGenerator, funcInfo, callSiteId, spreadArgCount, &spreadIndices); + Js::ArgSlot actualArgCount = EmitArgList(pnodeArgs, rhsLocation, thisLocation, newTargetLocation, fIsEval, fEvaluateComponents, byteCodeGenerator, funcInfo, callSiteId, argSlotCount, pnode->sxCall.hasDestructuring, spreadArgCount, &spreadIndices); + Assert(argSlotCount == actualArgCount); if (!fEvaluateComponents)