From df5334f0344939a4977c0be4beb46b354bf8e372 Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Sat, 20 Feb 2021 21:05:59 +0300 Subject: [PATCH 01/11] kill regex props fast paths --- lib/Backend/GlobOptFields.cpp | 23 +- lib/Backend/JnHelperMethodList.h | 23 +- lib/Backend/Lower.cpp | 891 ++++++++++++++-------------- lib/Backend/Lower.h | 6 +- lib/Backend/TempTracker.cpp | 109 ++-- lib/Runtime/Library/RegexHelper.cpp | 179 +++--- lib/Runtime/Library/RegexHelper.h | 12 +- tools/regenByteCode.py | 22 +- 8 files changed, 639 insertions(+), 626 deletions(-) diff --git a/lib/Backend/GlobOptFields.cpp b/lib/Backend/GlobOptFields.cpp index dca85bd2119..216c67439fd 100644 --- a/lib/Backend/GlobOptFields.cpp +++ b/lib/Backend/GlobOptFields.cpp @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "Backend.h" @@ -533,17 +534,17 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse *bv, bo break; case IR::JnHelperMethod::HelperRegExp_Exec: - case IR::JnHelperMethod::HelperRegExp_ExecResultNotUsed: - case IR::JnHelperMethod::HelperRegExp_ExecResultUsed: - case IR::JnHelperMethod::HelperRegExp_ExecResultUsedAndMayBeTemp: - case IR::JnHelperMethod::HelperRegExp_MatchResultNotUsed: - case IR::JnHelperMethod::HelperRegExp_MatchResultUsed: - case IR::JnHelperMethod::HelperRegExp_MatchResultUsedAndMayBeTemp: - case IR::JnHelperMethod::HelperRegExp_ReplaceStringResultUsed: - case IR::JnHelperMethod::HelperRegExp_ReplaceStringResultNotUsed: - case IR::JnHelperMethod::HelperRegExp_SplitResultNotUsed: - case IR::JnHelperMethod::HelperRegExp_SplitResultUsed: - case IR::JnHelperMethod::HelperRegExp_SplitResultUsedAndMayBeTemp: + // case IR::JnHelperMethod::HelperRegExp_ExecResultNotUsed: + // case IR::JnHelperMethod::HelperRegExp_ExecResultUsed: + // case IR::JnHelperMethod::HelperRegExp_ExecResultUsedAndMayBeTemp: + // case IR::JnHelperMethod::HelperRegExp_MatchResultNotUsed: + // case IR::JnHelperMethod::HelperRegExp_MatchResultUsed: + // case IR::JnHelperMethod::HelperRegExp_MatchResultUsedAndMayBeTemp: + // case IR::JnHelperMethod::HelperRegExp_ReplaceStringResultUsed: + // case IR::JnHelperMethod::HelperRegExp_ReplaceStringResultNotUsed: + // case IR::JnHelperMethod::HelperRegExp_SplitResultNotUsed: + // case IR::JnHelperMethod::HelperRegExp_SplitResultUsed: + // case IR::JnHelperMethod::HelperRegExp_SplitResultUsedAndMayBeTemp: case IR::JnHelperMethod::HelperRegExp_SymbolSearch: case IR::JnHelperMethod::HelperString_Match: case IR::JnHelperMethod::HelperString_Search: diff --git a/lib/Backend/JnHelperMethodList.h b/lib/Backend/JnHelperMethodList.h index e8fcbd397d4..c5ea7af4a22 100644 --- a/lib/Backend/JnHelperMethodList.h +++ b/lib/Backend/JnHelperMethodList.h @@ -526,18 +526,19 @@ HELPERCALL(String_PadEnd, Js::JavascriptString::EntryPadEnd, 0) HELPERCALLCHK(GlobalObject_ParseInt, Js::GlobalObject::EntryParseInt, 0) HELPERCALLCHK(Object_HasOwnProperty, Js::JavascriptObject::EntryHasOwnProperty, 0) -HELPERCALL(RegExp_SplitResultUsed, Js::RegexHelper::RegexSplitResultUsed, 0) -HELPERCALL(RegExp_SplitResultUsedAndMayBeTemp, Js::RegexHelper::RegexSplitResultUsedAndMayBeTemp, 0) -HELPERCALL(RegExp_SplitResultNotUsed, Js::RegexHelper::RegexSplitResultNotUsed, 0) -HELPERCALL(RegExp_MatchResultUsed, Js::RegexHelper::RegexMatchResultUsed, 0) -HELPERCALL(RegExp_MatchResultUsedAndMayBeTemp, Js::RegexHelper::RegexMatchResultUsedAndMayBeTemp, 0) -HELPERCALL(RegExp_MatchResultNotUsed, Js::RegexHelper::RegexMatchResultNotUsed, 0) +// TODO: Cleanup +// HELPERCALL(RegExp_SplitResultUsed, Js::RegexHelper::RegexSplitResultUsed, 0) +// HELPERCALL(RegExp_SplitResultUsedAndMayBeTemp, Js::RegexHelper::RegexSplitResultUsedAndMayBeTemp, 0) +// HELPERCALL(RegExp_SplitResultNotUsed, Js::RegexHelper::RegexSplitResultNotUsed, 0) +// HELPERCALL(RegExp_MatchResultUsed, Js::RegexHelper::RegexMatchResultUsed, 0) +// HELPERCALL(RegExp_MatchResultUsedAndMayBeTemp, Js::RegexHelper::RegexMatchResultUsedAndMayBeTemp, 0) +// HELPERCALL(RegExp_MatchResultNotUsed, Js::RegexHelper::RegexMatchResultNotUsed, 0) HELPERCALL(RegExp_Exec, Js::JavascriptRegExp::EntryExec, AttrTempObjectProducing) -HELPERCALL(RegExp_ExecResultUsed, Js::RegexHelper::RegexExecResultUsed, 0) -HELPERCALL(RegExp_ExecResultUsedAndMayBeTemp, Js::RegexHelper::RegexExecResultUsedAndMayBeTemp, 0) -HELPERCALL(RegExp_ExecResultNotUsed, Js::RegexHelper::RegexExecResultNotUsed, 0) -HELPERCALL(RegExp_ReplaceStringResultUsed, Js::RegexHelper::RegexReplaceResultUsed, 0) -HELPERCALL(RegExp_ReplaceStringResultNotUsed, Js::RegexHelper::RegexReplaceResultNotUsed, 0) +// HELPERCALL(RegExp_ExecResultUsed, Js::RegexHelper::RegexExecResultUsed, 0) +// HELPERCALL(RegExp_ExecResultUsedAndMayBeTemp, Js::RegexHelper::RegexExecResultUsedAndMayBeTemp, 0) +// HELPERCALL(RegExp_ExecResultNotUsed, Js::RegexHelper::RegexExecResultNotUsed, 0) +// HELPERCALL(RegExp_ReplaceStringResultUsed, Js::RegexHelper::RegexReplaceResultUsed, 0) +// HELPERCALL(RegExp_ReplaceStringResultNotUsed, Js::RegexHelper::RegexReplaceResultNotUsed, 0) HELPERCALL(RegExp_SymbolSearch, Js::JavascriptRegExp::EntrySymbolSearch, 0) HELPERCALL(Uint8ClampedArraySetItem, (BOOL (*)(Js::Uint8ClampedArray * arr, uint32 index, Js::Var value))&Js::Uint8ClampedArray::DirectSetItem, AttrCanNotBeReentrant) diff --git a/lib/Backend/Lower.cpp b/lib/Backend/Lower.cpp index 7a0b5b00b56..b4dff1adc40 100644 --- a/lib/Backend/Lower.cpp +++ b/lib/Backend/Lower.cpp @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- -// Copyright (C) Microsoft Corporation and contributors. All rights reserved. +// Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "Backend.h" @@ -770,13 +771,14 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa Assert(src1->IsHelperCallOpnd()); switch (src1->AsHelperCallOpnd()->m_fnHelper) { - case IR::JnHelperMethod::HelperString_Split: - case IR::JnHelperMethod::HelperString_Match: - GenerateFastInlineStringSplitMatch(instr); - break; - case IR::JnHelperMethod::HelperRegExp_Exec: - GenerateFastInlineRegExpExec(instr); - break; + // Cause spec deviations + // case IR::JnHelperMethod::HelperString_Split: + // case IR::JnHelperMethod::HelperString_Match: + // GenerateFastInlineStringSplitMatch(instr); + // break; + // case IR::JnHelperMethod::HelperRegExp_Exec: + // GenerateFastInlineRegExpExec(instr); + // break; case IR::JnHelperMethod::HelperGlobalObject_ParseInt: GenerateFastInlineGlobalObjectParseInt(instr); break; @@ -20378,6 +20380,7 @@ Lowerer::GenerateFastInlineHasOwnProperty(IR::Instr * instr) RelocateCallDirectToHelperPath(tmpInstr, labelHelper); } +// TODO: Cleanup bool Lowerer::ShouldGenerateStringReplaceFastPath(IR::Instr * callInstr, IntConstType argCount) { @@ -20390,462 +20393,464 @@ Lowerer::ShouldGenerateStringReplaceFastPath(IR::Instr * callInstr, IntConstType // arg3(s14)<8>.var = ArgOut_A s4.var, arg2(s13)<4>.var #001c <---- c // s0[LikelyString].var = CallI s5[ffunc].var, arg3(s14)<8>.var #0020 - IR::Opnd *linkOpnd = callInstr->GetSrc2(); - Assert(argCount == 2); - - while(linkOpnd->IsSymOpnd()) - { - IR::SymOpnd *src2 = linkOpnd->AsSymOpnd(); - StackSym *sym = src2->m_sym->AsStackSym(); - Assert(sym->m_isSingleDef); - IR::Instr *argInstr = sym->m_instrDef; + return false; + // IR::Opnd *linkOpnd = callInstr->GetSrc2(); + // Assert(argCount == 2); - Assert(argCount >= 0); - // check to see if 'a' and 'c' are likely strings - if((argCount == 2 || argCount == 0) && (!argInstr->GetSrc1()->GetValueType().IsLikelyString())) - { - return false; - } - // we want 'b' to be regex. Don't generate fastpath if it is a tagged int - if((argCount == 1) && (argInstr->GetSrc1()->IsTaggedInt())) - { - return false; - } - argCount--; - linkOpnd = argInstr->GetSrc2(); - } - return true; + // while(linkOpnd->IsSymOpnd()) + // { + // IR::SymOpnd *src2 = linkOpnd->AsSymOpnd(); + // StackSym *sym = src2->m_sym->AsStackSym(); + // Assert(sym->m_isSingleDef); + // IR::Instr *argInstr = sym->m_instrDef; + + // Assert(argCount >= 0); + // // check to see if 'a' and 'c' are likely strings + // if((argCount == 2 || argCount == 0) && (!argInstr->GetSrc1()->GetValueType().IsLikelyString())) + // { + // return false; + // } + // // we want 'b' to be regex. Don't generate fastpath if it is a tagged int + // if((argCount == 1) && (argInstr->GetSrc1()->IsTaggedInt())) + // { + // return false; + // } + // argCount--; + // linkOpnd = argInstr->GetSrc2(); + // } + // return true; } bool Lowerer::GenerateFastReplace(IR::Opnd* strOpnd, IR::Opnd* src1, IR::Opnd* src2, IR::Instr *callInstr, IR::Instr *insertInstr, IR::LabelInstr *labelHelper, IR::LabelInstr *doneLabel) { - // a.replace(b,c) - // We want to emit the fast path if 'a' and 'c' are strings and 'b' is a regex - // - // strOpnd --> a - // src1 --> b - // src2 --> c - - IR::Opnd * callDst = callInstr->GetDst(); - - Assert(strOpnd->GetValueType().IsLikelyString() && src2->GetValueType().IsLikelyString()); - - if(!strOpnd->GetValueType().IsString()) - { - strOpnd = GetRegOpnd(strOpnd, insertInstr, m_func, TyVar); - - this->GenerateStringTest(strOpnd->AsRegOpnd(), insertInstr, labelHelper); - } - - if(!src1->IsNotTaggedValue()) - { - m_lowererMD.GenerateObjectTest(src1, insertInstr, labelHelper); - } - - IR::Opnd * vtableOpnd = LoadVTableValueOpnd(insertInstr, VTableValue::VtableJavascriptRegExp); - - // cmp [regex], vtableAddress - // jne $labelHelper - src1 = GetRegOpnd(src1, insertInstr, m_func, TyVar); - InsertCompareBranch( - IR::IndirOpnd::New(src1->AsRegOpnd(), 0, TyMachPtr, insertInstr->m_func), - vtableOpnd, - Js::OpCode::BrNeq_A, - labelHelper, - insertInstr); - - if(!src2->GetValueType().IsString()) - { - src2 = GetRegOpnd(src2, insertInstr, m_func, TyVar); - this->GenerateStringTest(src2->AsRegOpnd(), insertInstr, labelHelper); - } - - IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, insertInstr->m_func); - if (callDst) - { - helperCallInstr->SetDst(callDst); - } - insertInstr->InsertBefore(helperCallInstr); - - if (insertInstr->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(insertInstr->GetBailOutKind())) - { - helperCallInstr = AddBailoutToHelperCallInstr(helperCallInstr, insertInstr->GetBailOutInfo(), insertInstr->GetBailOutKind(), insertInstr); - } - - //scriptContext, pRegEx, pThis, pReplace (to be pushed in reverse order) - - // pReplace, pThis, pRegEx - this->m_lowererMD.LoadHelperArgument(helperCallInstr, src2); - this->m_lowererMD.LoadHelperArgument(helperCallInstr, strOpnd); - this->m_lowererMD.LoadHelperArgument(helperCallInstr, src1); - - // script context - LoadScriptContext(helperCallInstr); - - if(callDst) - { - m_lowererMD.ChangeToHelperCall(helperCallInstr, IR::JnHelperMethod::HelperRegExp_ReplaceStringResultUsed); - } - else - { - m_lowererMD.ChangeToHelperCall(helperCallInstr, IR::JnHelperMethod::HelperRegExp_ReplaceStringResultNotUsed); - } - - return true; -} - -///---- - -void -Lowerer::GenerateFastInlineStringSplitMatch(IR::Instr * instr) -{ - // a.split(b,c (optional) ) - // We want to emit the fast path when - // 1. c is not present, and - // 2. 'a' is a string and 'b' is a regex. - // - // a.match(b) - // We want to emit the fast path when 'a' is a string and 'b' is a regex. - - Assert(instr->m_opcode == Js::OpCode::CallDirect); - IR::Opnd * callDst = instr->GetDst(); - - //helperCallOpnd - IR::Opnd * src1 = instr->GetSrc1(); - - //ArgOut_A_InlineSpecialized - IR::Instr * tmpInstr = instr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->m_instrDef; - - IR::Opnd * argsOpnd[2]; - if(!instr->FetchOperands(argsOpnd, 2)) - { - return; - } - - if(!argsOpnd[0]->GetValueType().IsLikelyString() || argsOpnd[1]->IsTaggedInt()) - { - return; - } - - IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true); - if(!argsOpnd[0]->GetValueType().IsString()) - { - argsOpnd[0] = GetRegOpnd(argsOpnd[0], instr, m_func, TyVar); - this->GenerateStringTest(argsOpnd[0]->AsRegOpnd(), instr, labelHelper); - } - - if(!argsOpnd[1]->IsNotTaggedValue()) - { - m_lowererMD.GenerateObjectTest(argsOpnd[1], instr, labelHelper); - } - - IR::Opnd * vtableOpnd = LoadVTableValueOpnd(instr, VTableValue::VtableJavascriptRegExp); - - // cmp [regex], vtableAddress - // jne $labelHelper - argsOpnd[1] = GetRegOpnd(argsOpnd[1], instr, m_func, TyVar); - InsertCompareBranch( - IR::IndirOpnd::New(argsOpnd[1]->AsRegOpnd(), 0, TyMachPtr, instr->m_func), - vtableOpnd, - Js::OpCode::BrNeq_A, - labelHelper, - instr); - - IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, instr->m_func); - if (callDst) - { - helperCallInstr->SetDst(callDst); - } - instr->InsertBefore(helperCallInstr); - if (instr->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(instr->GetBailOutKind())) - { - helperCallInstr = AddBailoutToHelperCallInstr(helperCallInstr, instr->GetBailOutInfo(), instr->GetBailOutKind(), instr); - } - - // [stackAllocationPointer, ]scriptcontext, regexp, input[, limit] (to be pushed in reverse order) - - if(src1->AsHelperCallOpnd()->m_fnHelper == IR::JnHelperMethod::HelperString_Split) - { - //limit - //As we are optimizing only for two operands, make limit UINT_MAX - IR::Opnd* limit = IR::IntConstOpnd::New(UINT_MAX, TyUint32, instr->m_func); - this->m_lowererMD.LoadHelperArgument(helperCallInstr, limit); - } - - //input, regexp - this->m_lowererMD.LoadHelperArgument(helperCallInstr, argsOpnd[0]); - this->m_lowererMD.LoadHelperArgument(helperCallInstr, argsOpnd[1]); - - // script context - LoadScriptContext(helperCallInstr); - - IR::JnHelperMethod helperMethod = IR::JnHelperMethod::HelperInvalid; - IR::AutoReuseOpnd autoReuseStackAllocationOpnd; - if(callDst && instr->dstIsTempObject) - { - switch(src1->AsHelperCallOpnd()->m_fnHelper) - { - case IR::JnHelperMethod::HelperString_Split: - helperMethod = IR::JnHelperMethod::HelperRegExp_SplitResultUsedAndMayBeTemp; - break; - - case IR::JnHelperMethod::HelperString_Match: - helperMethod = IR::JnHelperMethod::HelperRegExp_MatchResultUsedAndMayBeTemp; - break; - - default: - Assert(false); - __assume(false); - } - - // Allocate some space on the stack for the result array - IR::RegOpnd *const stackAllocationOpnd = IR::RegOpnd::New(TyVar, m_func); - autoReuseStackAllocationOpnd.Initialize(stackAllocationOpnd, m_func); - stackAllocationOpnd->SetValueType(callDst->GetValueType()); - GenerateMarkTempAlloc(stackAllocationOpnd, Js::JavascriptArray::StackAllocationSize, helperCallInstr); - m_lowererMD.LoadHelperArgument(helperCallInstr, stackAllocationOpnd); - } - else - { - switch(src1->AsHelperCallOpnd()->m_fnHelper) - { - case IR::JnHelperMethod::HelperString_Split: - helperMethod = - callDst - ? IR::JnHelperMethod::HelperRegExp_SplitResultUsed - : IR::JnHelperMethod::HelperRegExp_SplitResultNotUsed; - break; - - case IR::JnHelperMethod::HelperString_Match: - helperMethod = - callDst - ? IR::JnHelperMethod::HelperRegExp_MatchResultUsed - : IR::JnHelperMethod::HelperRegExp_MatchResultNotUsed; - break; - - default: - Assert(false); - __assume(false); - } - } - - m_lowererMD.ChangeToHelperCall(helperCallInstr, helperMethod); - - IR::LabelInstr *doneLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func); - instr->InsertAfter(doneLabel); - instr->InsertBefore(labelHelper); - InsertBranch(Js::OpCode::Br, true, doneLabel, labelHelper); - - RelocateCallDirectToHelperPath(tmpInstr, labelHelper); -} - -void -Lowerer::GenerateFastInlineRegExpExec(IR::Instr * instr) -{ - // a.exec(b) - // We want to emit the fast path when 'a' is a regex and 'b' is a string - - Assert(instr->m_opcode == Js::OpCode::CallDirect); - IR::Opnd * callDst = instr->GetDst(); - - //ArgOut_A_InlineSpecialized - IR::Instr * tmpInstr = instr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->m_instrDef; - - IR::Opnd * argsOpnd[2]; - if (!instr->FetchOperands(argsOpnd, 2)) - { - return; - } - - IR::Opnd *opndString = argsOpnd[1]; - if(!opndString->GetValueType().IsLikelyString() || argsOpnd[0]->IsTaggedInt()) - { - return; - } - - IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true); - if(!opndString->GetValueType().IsString()) - { - opndString = GetRegOpnd(opndString, instr, m_func, TyVar); - this->GenerateStringTest(opndString->AsRegOpnd(), instr, labelHelper); - } - - IR::Opnd *opndRegex = argsOpnd[0]; - if(!opndRegex->IsNotTaggedValue()) - { - m_lowererMD.GenerateObjectTest(opndRegex, instr, labelHelper); - } - - IR::Opnd * vtableOpnd = LoadVTableValueOpnd(instr, VTableValue::VtableJavascriptRegExp); - - // cmp [regex], vtableAddress - // jne $labelHelper - opndRegex = GetRegOpnd(opndRegex, instr, m_func, TyVar); - InsertCompareBranch( - IR::IndirOpnd::New(opndRegex->AsRegOpnd(), 0, TyMachPtr, instr->m_func), - vtableOpnd, - Js::OpCode::BrNeq_A, - labelHelper, - instr); - - IR::LabelInstr *doneLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func); - - if (!PHASE_OFF(Js::ExecBOIFastPathPhase, m_func)) - { - // Load pattern from regex operand - IR::RegOpnd *opndPattern = IR::RegOpnd::New(TyMachPtr, m_func); - Lowerer::InsertMove( - opndPattern, - IR::IndirOpnd::New(opndRegex->AsRegOpnd(), Js::JavascriptRegExp::GetOffsetOfPattern(), TyMachPtr, m_func), - instr); - - // Load program from pattern - IR::RegOpnd *opndProgram = IR::RegOpnd::New(TyMachPtr, m_func); - Lowerer::InsertMove( - opndProgram, - IR::IndirOpnd::New(opndPattern, offsetof(UnifiedRegex::RegexPattern, rep) + offsetof(UnifiedRegex::RegexPattern::UnifiedRep, program), TyMachPtr, m_func), - instr); - - IR::LabelInstr *labelFastHelper = IR::LabelInstr::New(Js::OpCode::Label, m_func); - - // We want the program's tag to be BOILiteral2Tag - InsertCompareBranch( - IR::IndirOpnd::New(opndProgram, (int32)UnifiedRegex::Program::GetOffsetOfTag(), TyUint8, m_func), - IR::IntConstOpnd::New((IntConstType)UnifiedRegex::Program::GetBOILiteral2Tag(), TyUint8, m_func), - Js::OpCode::BrNeq_A, - labelFastHelper, - instr); - - // Test the program's flags for "global" - InsertTestBranch( - IR::IndirOpnd::New(opndProgram, offsetof(UnifiedRegex::Program, flags), TyUint8, m_func), - IR::IntConstOpnd::New(UnifiedRegex::GlobalRegexFlag, TyUint8, m_func), - Js::OpCode::BrNeq_A, - labelFastHelper, - instr); - - IR::LabelInstr *labelNoMatch = IR::LabelInstr::New(Js::OpCode::Label, m_func); - - // If string length < 2... - InsertCompareBranch( - IR::IndirOpnd::New(opndString->AsRegOpnd(), offsetof(Js::JavascriptString, m_charLength), TyUint32, m_func), - IR::IntConstOpnd::New(2, TyUint32, m_func), - Js::OpCode::BrLt_A, - labelNoMatch, - instr); - - // ...or the DWORD doesn't match the pattern... - IR::RegOpnd *opndBuffer = IR::RegOpnd::New(TyMachReg, m_func); - Lowerer::InsertMove( - opndBuffer, - IR::IndirOpnd::New(opndString->AsRegOpnd(), offsetof(Js::JavascriptString, m_pszValue), TyMachPtr, m_func), - instr); - - IR::LabelInstr *labelGotString = IR::LabelInstr::New(Js::OpCode::Label, m_func); - - InsertTestBranch(opndBuffer, opndBuffer, Js::OpCode::BrNeq_A, labelGotString, instr); - - m_lowererMD.LoadHelperArgument(instr, opndString); - IR::Instr *instrCall = IR::Instr::New(Js::OpCode::Call, opndBuffer, IR::HelperCallOpnd::New(IR::HelperString_GetSz, m_func), m_func); - instr->InsertBefore(instrCall); - m_lowererMD.LowerCall(instrCall, 0); - - instr->InsertBefore(labelGotString); - - IR::RegOpnd *opndBufferDWORD = IR::RegOpnd::New(TyUint32, m_func); - Lowerer::InsertMove( - opndBufferDWORD, - IR::IndirOpnd::New(opndBuffer, 0, TyUint32, m_func), - instr); + return false; + // // a.replace(b,c) + // // We want to emit the fast path if 'a' and 'c' are strings and 'b' is a regex + // // + // // strOpnd --> a + // // src1 --> b + // // src2 --> c - InsertCompareBranch( - IR::IndirOpnd::New(opndProgram, (int32)(UnifiedRegex::Program::GetOffsetOfRep() + UnifiedRegex::Program::GetOffsetOfBOILiteral2Literal()), TyUint32, m_func), - opndBufferDWORD, - Js::OpCode::BrEq_A, - labelFastHelper, - instr); + // IR::Opnd * callDst = callInstr->GetDst(); - // ...then set the last index to 0... - instr->InsertBefore(labelNoMatch); + // Assert(strOpnd->GetValueType().IsLikelyString() && src2->GetValueType().IsLikelyString()); - Lowerer::InsertMove( - IR::IndirOpnd::New(opndRegex->AsRegOpnd(), Js::JavascriptRegExp::GetOffsetOfLastIndexVar(), TyVar, m_func), - IR::AddrOpnd::NewNull(m_func), - instr); + // if(!strOpnd->GetValueType().IsString()) + // { + // strOpnd = GetRegOpnd(strOpnd, insertInstr, m_func, TyVar); - Lowerer::InsertMove( - IR::IndirOpnd::New(opndRegex->AsRegOpnd(), Js::JavascriptRegExp::GetOffsetOfLastIndexOrFlag(), TyUint32, m_func), - IR::IntConstOpnd::New(0, TyUint32, m_func), - instr); + // this->GenerateStringTest(strOpnd->AsRegOpnd(), insertInstr, labelHelper); + // } - // ...and set the dst to null... - if (callDst) - { - Lowerer::InsertMove( - callDst, - LoadLibraryValueOpnd(instr, LibraryValue::ValueNull), - instr); - } + // if(!src1->IsNotTaggedValue()) + // { + // m_lowererMD.GenerateObjectTest(src1, insertInstr, labelHelper); + // } - // ...and we're done. - this->InsertBranch(Js::OpCode::Br, doneLabel, instr); + // IR::Opnd * vtableOpnd = LoadVTableValueOpnd(insertInstr, VTableValue::VtableJavascriptRegExp); - instr->InsertBefore(labelFastHelper); - } + // // cmp [regex], vtableAddress + // // jne $labelHelper + // src1 = GetRegOpnd(src1, insertInstr, m_func, TyVar); + // InsertCompareBranch( + // IR::IndirOpnd::New(src1->AsRegOpnd(), 0, TyMachPtr, insertInstr->m_func), + // vtableOpnd, + // Js::OpCode::BrNeq_A, + // labelHelper, + // insertInstr); - IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, instr->m_func); - if (callDst) - { - helperCallInstr->SetDst(callDst); - } - instr->InsertBefore(helperCallInstr); - if (instr->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(instr->GetBailOutKind())) - { - helperCallInstr = AddBailoutToHelperCallInstr(helperCallInstr, instr->GetBailOutInfo(), instr->GetBailOutKind(), instr); - } - // [stackAllocationPointer, ]scriptcontext, regexp, string (to be pushed in reverse order) + // if(!src2->GetValueType().IsString()) + // { + // src2 = GetRegOpnd(src2, insertInstr, m_func, TyVar); + // this->GenerateStringTest(src2->AsRegOpnd(), insertInstr, labelHelper); + // } - //string, regexp - this->m_lowererMD.LoadHelperArgument(helperCallInstr, opndString); - this->m_lowererMD.LoadHelperArgument(helperCallInstr, opndRegex); + // IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, insertInstr->m_func); + // if (callDst) + // { + // helperCallInstr->SetDst(callDst); + // } + // insertInstr->InsertBefore(helperCallInstr); - // script context - LoadScriptContext(helperCallInstr); + // if (insertInstr->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(insertInstr->GetBailOutKind())) + // { + // helperCallInstr = AddBailoutToHelperCallInstr(helperCallInstr, insertInstr->GetBailOutInfo(), insertInstr->GetBailOutKind(), insertInstr); + // } - IR::JnHelperMethod helperMethod; - IR::AutoReuseOpnd autoReuseStackAllocationOpnd; - if (callDst) - { - if (instr->dstIsTempObject) - { - helperMethod = IR::JnHelperMethod::HelperRegExp_ExecResultUsedAndMayBeTemp; + // //scriptContext, pRegEx, pThis, pReplace (to be pushed in reverse order) - // Allocate some space on the stack for the result array - IR::RegOpnd *const stackAllocationOpnd = IR::RegOpnd::New(TyVar, m_func); - autoReuseStackAllocationOpnd.Initialize(stackAllocationOpnd, m_func); - stackAllocationOpnd->SetValueType(callDst->GetValueType()); - GenerateMarkTempAlloc(stackAllocationOpnd, Js::JavascriptArray::StackAllocationSize, helperCallInstr); - m_lowererMD.LoadHelperArgument(helperCallInstr, stackAllocationOpnd); - } - else - { - helperMethod = IR::JnHelperMethod::HelperRegExp_ExecResultUsed; - } - } - else - { - helperMethod = IR::JnHelperMethod::HelperRegExp_ExecResultNotUsed; - } + // // pReplace, pThis, pRegEx + // this->m_lowererMD.LoadHelperArgument(helperCallInstr, src2); + // this->m_lowererMD.LoadHelperArgument(helperCallInstr, strOpnd); + // this->m_lowererMD.LoadHelperArgument(helperCallInstr, src1); - m_lowererMD.ChangeToHelperCall(helperCallInstr, helperMethod); + // // script context + // LoadScriptContext(helperCallInstr); - instr->InsertAfter(doneLabel); - instr->InsertBefore(labelHelper); - InsertBranch(Js::OpCode::Br, true, doneLabel, labelHelper); + // if(callDst) + // { + // m_lowererMD.ChangeToHelperCall(helperCallInstr, IR::JnHelperMethod::HelperRegExp_ReplaceStringResultUsed); + // } + // else + // { + // m_lowererMD.ChangeToHelperCall(helperCallInstr, IR::JnHelperMethod::HelperRegExp_ReplaceStringResultNotUsed); + // } - RelocateCallDirectToHelperPath(tmpInstr, labelHelper); -} + // return true; +} + +// ///---- + +// void +// Lowerer::GenerateFastInlineStringSplitMatch(IR::Instr * instr) +// { +// // a.split(b,c (optional) ) +// // We want to emit the fast path when +// // 1. c is not present, and +// // 2. 'a' is a string and 'b' is a regex. +// // +// // a.match(b) +// // We want to emit the fast path when 'a' is a string and 'b' is a regex. + +// Assert(instr->m_opcode == Js::OpCode::CallDirect); +// IR::Opnd * callDst = instr->GetDst(); + +// //helperCallOpnd +// IR::Opnd * src1 = instr->GetSrc1(); + +// //ArgOut_A_InlineSpecialized +// IR::Instr * tmpInstr = instr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->m_instrDef; + +// IR::Opnd * argsOpnd[2]; +// if(!instr->FetchOperands(argsOpnd, 2)) +// { +// return; +// } + +// if(!argsOpnd[0]->GetValueType().IsLikelyString() || argsOpnd[1]->IsTaggedInt()) +// { +// return; +// } + +// IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true); +// if(!argsOpnd[0]->GetValueType().IsString()) +// { +// argsOpnd[0] = GetRegOpnd(argsOpnd[0], instr, m_func, TyVar); +// this->GenerateStringTest(argsOpnd[0]->AsRegOpnd(), instr, labelHelper); +// } + +// if(!argsOpnd[1]->IsNotTaggedValue()) +// { +// m_lowererMD.GenerateObjectTest(argsOpnd[1], instr, labelHelper); +// } + +// IR::Opnd * vtableOpnd = LoadVTableValueOpnd(instr, VTableValue::VtableJavascriptRegExp); + +// // cmp [regex], vtableAddress +// // jne $labelHelper +// argsOpnd[1] = GetRegOpnd(argsOpnd[1], instr, m_func, TyVar); +// InsertCompareBranch( +// IR::IndirOpnd::New(argsOpnd[1]->AsRegOpnd(), 0, TyMachPtr, instr->m_func), +// vtableOpnd, +// Js::OpCode::BrNeq_A, +// labelHelper, +// instr); + +// IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, instr->m_func); +// if (callDst) +// { +// helperCallInstr->SetDst(callDst); +// } +// instr->InsertBefore(helperCallInstr); +// if (instr->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(instr->GetBailOutKind())) +// { +// helperCallInstr = AddBailoutToHelperCallInstr(helperCallInstr, instr->GetBailOutInfo(), instr->GetBailOutKind(), instr); +// } + +// // [stackAllocationPointer, ]scriptcontext, regexp, input[, limit] (to be pushed in reverse order) + +// if(src1->AsHelperCallOpnd()->m_fnHelper == IR::JnHelperMethod::HelperString_Split) +// { +// //limit +// //As we are optimizing only for two operands, make limit UINT_MAX +// IR::Opnd* limit = IR::IntConstOpnd::New(UINT_MAX, TyUint32, instr->m_func); +// this->m_lowererMD.LoadHelperArgument(helperCallInstr, limit); +// } + +// //input, regexp +// this->m_lowererMD.LoadHelperArgument(helperCallInstr, argsOpnd[0]); +// this->m_lowererMD.LoadHelperArgument(helperCallInstr, argsOpnd[1]); + +// // script context +// LoadScriptContext(helperCallInstr); + +// IR::JnHelperMethod helperMethod = IR::JnHelperMethod::HelperInvalid; +// IR::AutoReuseOpnd autoReuseStackAllocationOpnd; +// if(callDst && instr->dstIsTempObject) +// { +// switch(src1->AsHelperCallOpnd()->m_fnHelper) +// { +// case IR::JnHelperMethod::HelperString_Split: +// helperMethod = IR::JnHelperMethod::HelperRegExp_SplitResultUsedAndMayBeTemp; +// break; + +// case IR::JnHelperMethod::HelperString_Match: +// helperMethod = IR::JnHelperMethod::HelperRegExp_MatchResultUsedAndMayBeTemp; +// break; + +// default: +// Assert(false); +// __assume(false); +// } + +// // Allocate some space on the stack for the result array +// IR::RegOpnd *const stackAllocationOpnd = IR::RegOpnd::New(TyVar, m_func); +// autoReuseStackAllocationOpnd.Initialize(stackAllocationOpnd, m_func); +// stackAllocationOpnd->SetValueType(callDst->GetValueType()); +// GenerateMarkTempAlloc(stackAllocationOpnd, Js::JavascriptArray::StackAllocationSize, helperCallInstr); +// m_lowererMD.LoadHelperArgument(helperCallInstr, stackAllocationOpnd); +// } +// else +// { +// switch(src1->AsHelperCallOpnd()->m_fnHelper) +// { +// case IR::JnHelperMethod::HelperString_Split: +// helperMethod = +// callDst +// ? IR::JnHelperMethod::HelperRegExp_SplitResultUsed +// : IR::JnHelperMethod::HelperRegExp_SplitResultNotUsed; +// break; + +// case IR::JnHelperMethod::HelperString_Match: +// helperMethod = +// callDst +// ? IR::JnHelperMethod::HelperRegExp_MatchResultUsed +// : IR::JnHelperMethod::HelperRegExp_MatchResultNotUsed; +// break; + +// default: +// Assert(false); +// __assume(false); +// } +// } + +// m_lowererMD.ChangeToHelperCall(helperCallInstr, helperMethod); + +// IR::LabelInstr *doneLabel = IR::LabelInstr::New(Js::OpCode::Label, this->m_func); +// instr->InsertAfter(doneLabel); +// instr->InsertBefore(labelHelper); +// InsertBranch(Js::OpCode::Br, true, doneLabel, labelHelper); + +// RelocateCallDirectToHelperPath(tmpInstr, labelHelper); +// } + +// void +// Lowerer::GenerateFastInlineRegExpExec(IR::Instr * instr) +// { +// // a.exec(b) +// // We want to emit the fast path when 'a' is a regex and 'b' is a string + +// Assert(instr->m_opcode == Js::OpCode::CallDirect); +// IR::Opnd * callDst = instr->GetDst(); + +// //ArgOut_A_InlineSpecialized +// IR::Instr * tmpInstr = instr->GetSrc2()->AsSymOpnd()->m_sym->AsStackSym()->m_instrDef; + +// IR::Opnd * argsOpnd[2]; +// if (!instr->FetchOperands(argsOpnd, 2)) +// { +// return; +// } + +// IR::Opnd *opndString = argsOpnd[1]; +// if(!opndString->GetValueType().IsLikelyString() || argsOpnd[0]->IsTaggedInt()) +// { +// return; +// } + +// IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true); +// if(!opndString->GetValueType().IsString()) +// { +// opndString = GetRegOpnd(opndString, instr, m_func, TyVar); +// this->GenerateStringTest(opndString->AsRegOpnd(), instr, labelHelper); +// } + +// IR::Opnd *opndRegex = argsOpnd[0]; +// if(!opndRegex->IsNotTaggedValue()) +// { +// m_lowererMD.GenerateObjectTest(opndRegex, instr, labelHelper); +// } + +// IR::Opnd * vtableOpnd = LoadVTableValueOpnd(instr, VTableValue::VtableJavascriptRegExp); + +// // cmp [regex], vtableAddress +// // jne $labelHelper +// opndRegex = GetRegOpnd(opndRegex, instr, m_func, TyVar); +// InsertCompareBranch( +// IR::IndirOpnd::New(opndRegex->AsRegOpnd(), 0, TyMachPtr, instr->m_func), +// vtableOpnd, +// Js::OpCode::BrNeq_A, +// labelHelper, +// instr); + +// IR::LabelInstr *doneLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func); + +// if (!PHASE_OFF(Js::ExecBOIFastPathPhase, m_func)) +// { +// // Load pattern from regex operand +// IR::RegOpnd *opndPattern = IR::RegOpnd::New(TyMachPtr, m_func); +// Lowerer::InsertMove( +// opndPattern, +// IR::IndirOpnd::New(opndRegex->AsRegOpnd(), Js::JavascriptRegExp::GetOffsetOfPattern(), TyMachPtr, m_func), +// instr); + +// // Load program from pattern +// IR::RegOpnd *opndProgram = IR::RegOpnd::New(TyMachPtr, m_func); +// Lowerer::InsertMove( +// opndProgram, +// IR::IndirOpnd::New(opndPattern, offsetof(UnifiedRegex::RegexPattern, rep) + offsetof(UnifiedRegex::RegexPattern::UnifiedRep, program), TyMachPtr, m_func), +// instr); + +// IR::LabelInstr *labelFastHelper = IR::LabelInstr::New(Js::OpCode::Label, m_func); + +// // We want the program's tag to be BOILiteral2Tag +// InsertCompareBranch( +// IR::IndirOpnd::New(opndProgram, (int32)UnifiedRegex::Program::GetOffsetOfTag(), TyUint8, m_func), +// IR::IntConstOpnd::New((IntConstType)UnifiedRegex::Program::GetBOILiteral2Tag(), TyUint8, m_func), +// Js::OpCode::BrNeq_A, +// labelFastHelper, +// instr); + +// // Test the program's flags for "global" +// InsertTestBranch( +// IR::IndirOpnd::New(opndProgram, offsetof(UnifiedRegex::Program, flags), TyUint8, m_func), +// IR::IntConstOpnd::New(UnifiedRegex::GlobalRegexFlag, TyUint8, m_func), +// Js::OpCode::BrNeq_A, +// labelFastHelper, +// instr); + +// IR::LabelInstr *labelNoMatch = IR::LabelInstr::New(Js::OpCode::Label, m_func); + +// // If string length < 2... +// InsertCompareBranch( +// IR::IndirOpnd::New(opndString->AsRegOpnd(), offsetof(Js::JavascriptString, m_charLength), TyUint32, m_func), +// IR::IntConstOpnd::New(2, TyUint32, m_func), +// Js::OpCode::BrLt_A, +// labelNoMatch, +// instr); + +// // ...or the DWORD doesn't match the pattern... +// IR::RegOpnd *opndBuffer = IR::RegOpnd::New(TyMachReg, m_func); +// Lowerer::InsertMove( +// opndBuffer, +// IR::IndirOpnd::New(opndString->AsRegOpnd(), offsetof(Js::JavascriptString, m_pszValue), TyMachPtr, m_func), +// instr); + +// IR::LabelInstr *labelGotString = IR::LabelInstr::New(Js::OpCode::Label, m_func); + +// InsertTestBranch(opndBuffer, opndBuffer, Js::OpCode::BrNeq_A, labelGotString, instr); + +// m_lowererMD.LoadHelperArgument(instr, opndString); +// IR::Instr *instrCall = IR::Instr::New(Js::OpCode::Call, opndBuffer, IR::HelperCallOpnd::New(IR::HelperString_GetSz, m_func), m_func); +// instr->InsertBefore(instrCall); +// m_lowererMD.LowerCall(instrCall, 0); + +// instr->InsertBefore(labelGotString); + +// IR::RegOpnd *opndBufferDWORD = IR::RegOpnd::New(TyUint32, m_func); +// Lowerer::InsertMove( +// opndBufferDWORD, +// IR::IndirOpnd::New(opndBuffer, 0, TyUint32, m_func), +// instr); + +// InsertCompareBranch( +// IR::IndirOpnd::New(opndProgram, (int32)(UnifiedRegex::Program::GetOffsetOfRep() + UnifiedRegex::Program::GetOffsetOfBOILiteral2Literal()), TyUint32, m_func), +// opndBufferDWORD, +// Js::OpCode::BrEq_A, +// labelFastHelper, +// instr); + +// // ...then set the last index to 0... +// instr->InsertBefore(labelNoMatch); + +// Lowerer::InsertMove( +// IR::IndirOpnd::New(opndRegex->AsRegOpnd(), Js::JavascriptRegExp::GetOffsetOfLastIndexVar(), TyVar, m_func), +// IR::AddrOpnd::NewNull(m_func), +// instr); + +// Lowerer::InsertMove( +// IR::IndirOpnd::New(opndRegex->AsRegOpnd(), Js::JavascriptRegExp::GetOffsetOfLastIndexOrFlag(), TyUint32, m_func), +// IR::IntConstOpnd::New(0, TyUint32, m_func), +// instr); + +// // ...and set the dst to null... +// if (callDst) +// { +// Lowerer::InsertMove( +// callDst, +// LoadLibraryValueOpnd(instr, LibraryValue::ValueNull), +// instr); +// } + +// // ...and we're done. +// this->InsertBranch(Js::OpCode::Br, doneLabel, instr); + +// instr->InsertBefore(labelFastHelper); +// } + +// IR::Instr * helperCallInstr = IR::Instr::New(LowererMD::MDCallOpcode, instr->m_func); +// if (callDst) +// { +// helperCallInstr->SetDst(callDst); +// } +// instr->InsertBefore(helperCallInstr); +// if (instr->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(instr->GetBailOutKind())) +// { +// helperCallInstr = AddBailoutToHelperCallInstr(helperCallInstr, instr->GetBailOutInfo(), instr->GetBailOutKind(), instr); +// } +// // [stackAllocationPointer, ]scriptcontext, regexp, string (to be pushed in reverse order) + +// //string, regexp +// this->m_lowererMD.LoadHelperArgument(helperCallInstr, opndString); +// this->m_lowererMD.LoadHelperArgument(helperCallInstr, opndRegex); + +// // script context +// LoadScriptContext(helperCallInstr); + +// IR::JnHelperMethod helperMethod; +// IR::AutoReuseOpnd autoReuseStackAllocationOpnd; +// if (callDst) +// { +// if (instr->dstIsTempObject) +// { +// helperMethod = IR::JnHelperMethod::HelperRegExp_ExecResultUsedAndMayBeTemp; + +// // Allocate some space on the stack for the result array +// IR::RegOpnd *const stackAllocationOpnd = IR::RegOpnd::New(TyVar, m_func); +// autoReuseStackAllocationOpnd.Initialize(stackAllocationOpnd, m_func); +// stackAllocationOpnd->SetValueType(callDst->GetValueType()); +// GenerateMarkTempAlloc(stackAllocationOpnd, Js::JavascriptArray::StackAllocationSize, helperCallInstr); +// m_lowererMD.LoadHelperArgument(helperCallInstr, stackAllocationOpnd); +// } +// else +// { +// helperMethod = IR::JnHelperMethod::HelperRegExp_ExecResultUsed; +// } +// } +// else +// { +// helperMethod = IR::JnHelperMethod::HelperRegExp_ExecResultNotUsed; +// } + +// m_lowererMD.ChangeToHelperCall(helperCallInstr, helperMethod); + +// instr->InsertAfter(doneLabel); +// instr->InsertBefore(labelHelper); +// InsertBranch(Js::OpCode::Br, true, doneLabel, labelHelper); + +// RelocateCallDirectToHelperPath(tmpInstr, labelHelper); +// } // Generate a fast path for the "in" operator that check quickly if we have an array or not and if the index of the data is contained in the array's length. void Lowerer::GenerateFastArrayIsIn(IR::Instr * instr) diff --git a/lib/Backend/Lower.h b/lib/Backend/Lower.h index ef670d66338..07ed08c6020 100644 --- a/lib/Backend/Lower.h +++ b/lib/Backend/Lower.h @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #pragma once @@ -539,14 +540,15 @@ class Lowerer void GenerateFastInlineHasOwnProperty(IR::Instr * instr); void GenerateFastInlineArrayPush(IR::Instr * instr); void GenerateFastInlineArrayPop(IR::Instr * instr); - void GenerateFastInlineStringSplitMatch(IR::Instr * instr); + // TODO: Cleanup + // void GenerateFastInlineStringSplitMatch(IR::Instr * instr); void GenerateFastInlineMathImul(IR::Instr* instr); void GenerateFastInlineMathClz(IR::Instr* instr); void GenerateCtz(IR::Instr* instr); void GeneratePopCnt(IR::Instr* instr); template void GenerateTruncWithCheck(_In_ IR::Instr* instr); void GenerateFastInlineMathFround(IR::Instr* instr); - void GenerateFastInlineRegExpExec(IR::Instr * instr); + // void GenerateFastInlineRegExpExec(IR::Instr * instr); bool GenerateFastPush(IR::Opnd *baseOpndParam, IR::Opnd *src, IR::Instr *callInstr, IR::Instr *insertInstr, IR::LabelInstr *labelHelper, IR::LabelInstr *doneLabel, IR::LabelInstr * bailOutLabelHelper, bool returnLength = false); bool GenerateFastReplace(IR::Opnd* strOpnd, IR::Opnd* src1, IR::Opnd* src2, IR::Instr *callInstr, IR::Instr *insertInstr, IR::LabelInstr *labelHelper, IR::LabelInstr *doneLabel); bool ShouldGenerateStringReplaceFastPath(IR::Instr * instr, IntConstType argCount); diff --git a/lib/Backend/TempTracker.cpp b/lib/Backend/TempTracker.cpp index 156dc48b07a..9b616d8cb5d 100644 --- a/lib/Backend/TempTracker.cpp +++ b/lib/Backend/TempTracker.cpp @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "Backend.h" @@ -1165,33 +1166,33 @@ ObjectTemp::ProcessBailOnNoProfile(IR::Instr * instr) void ObjectTemp::ProcessInstr(IR::Instr * instr) { - if (instr->m_opcode != Js::OpCode::CallDirect) - { - return; - } - - IR::HelperCallOpnd * helper = instr->GetSrc1()->AsHelperCallOpnd(); - switch (helper->m_fnHelper) - { - case IR::JnHelperMethod::HelperString_Match: - case IR::JnHelperMethod::HelperString_Replace: - { - // First (non-this) parameter is either a regexp or search string. - // It doesn't escape. - IR::Instr * instrArgDef = nullptr; - instr->FindCallArgumentOpnd(2, &instrArgDef); - instrArgDef->dstIsTempObject = true; - break; - } - - case IR::JnHelperMethod::HelperRegExp_Exec: - { - IR::Instr * instrArgDef = nullptr; - instr->FindCallArgumentOpnd(1, &instrArgDef); - instrArgDef->dstIsTempObject = true; - break; - } - }; + // if (instr->m_opcode != Js::OpCode::CallDirect) + // { + // return; + // } + + // IR::HelperCallOpnd * helper = instr->GetSrc1()->AsHelperCallOpnd(); + // switch (helper->m_fnHelper) + // { + // case IR::JnHelperMethod::HelperString_Match: + // case IR::JnHelperMethod::HelperString_Replace: + // { + // // First (non-this) parameter is either a regexp or search string. + // // It doesn't escape. + // IR::Instr * instrArgDef = nullptr; + // instr->FindCallArgumentOpnd(2, &instrArgDef); + // instrArgDef->dstIsTempObject = true; + // break; + // } + + // case IR::JnHelperMethod::HelperRegExp_Exec: + // { + // IR::Instr * instrArgDef = nullptr; + // instr->FindCallArgumentOpnd(1, &instrArgDef); + // instrArgDef->dstIsTempObject = true; + // break; + // } + // }; } @@ -1354,33 +1355,33 @@ ObjectTempVerify::ProcessInstr(IR::Instr * instr, BackwardPass * backwardPass) return; } - if (instr->m_opcode != Js::OpCode::CallDirect) - { - return; - } - - IR::HelperCallOpnd * helper = instr->GetSrc1()->AsHelperCallOpnd(); - switch (helper->m_fnHelper) - { - case IR::JnHelperMethod::HelperString_Match: - case IR::JnHelperMethod::HelperString_Replace: - { - // First (non-this) parameter is either a regexp or search string - // It doesn't escape - IR::Instr * instrArgDef; - instr->FindCallArgumentOpnd(2, &instrArgDef); - Assert(instrArgDef->dstIsTempObject); - break; - } - - case IR::JnHelperMethod::HelperRegExp_Exec: - { - IR::Instr * instrArgDef; - instr->FindCallArgumentOpnd(1, &instrArgDef); - Assert(instrArgDef->dstIsTempObject); - break; - } - }; + // if (instr->m_opcode != Js::OpCode::CallDirect) + // { + // return; + // } + + // IR::HelperCallOpnd * helper = instr->GetSrc1()->AsHelperCallOpnd(); + // switch (helper->m_fnHelper) + // { + // case IR::JnHelperMethod::HelperString_Match: + // case IR::JnHelperMethod::HelperString_Replace: + // { + // // First (non-this) parameter is either a regexp or search string + // // It doesn't escape + // IR::Instr * instrArgDef; + // instr->FindCallArgumentOpnd(2, &instrArgDef); + // Assert(instrArgDef->dstIsTempObject); + // break; + // } + + // case IR::JnHelperMethod::HelperRegExp_Exec: + // { + // IR::Instr * instrArgDef; + // instr->FindCallArgumentOpnd(1, &instrArgDef); + // Assert(instrArgDef->dstIsTempObject); + // break; + // } + // }; } diff --git a/lib/Runtime/Library/RegexHelper.cpp b/lib/Runtime/Library/RegexHelper.cpp index e65eb1bad9d..4954cf4da0f 100644 --- a/lib/Runtime/Library/RegexHelper.cpp +++ b/lib/Runtime/Library/RegexHelper.cpp @@ -2215,27 +2215,28 @@ namespace Js return value; } - Var RegexHelper::RegexMatchResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) - { - return RegexHelper::RegexMatch(scriptContext, regularExpression, input, false); - } - - Var RegexHelper::RegexMatchResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) - { - return RegexHelper::RegexMatch(scriptContext, regularExpression, input, false, stackAllocationPointer); - } - - Var RegexHelper::RegexMatchResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) - { - if (!PHASE_OFF1(Js::RegexResultNotUsedPhase)) - { - return RegexHelper::RegexMatch(scriptContext, regularExpression, input, true); - } - else - { - return RegexHelper::RegexMatch(scriptContext, regularExpression, input, false); - } - } + // TODO: Cleanup + // Var RegexHelper::RegexMatchResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) + // { + // return RegexHelper::RegexMatch(scriptContext, regularExpression, input, false); + // } + + // Var RegexHelper::RegexMatchResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) + // { + // return RegexHelper::RegexMatch(scriptContext, regularExpression, input, false, stackAllocationPointer); + // } + + // Var RegexHelper::RegexMatchResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) + // { + // if (!PHASE_OFF1(Js::RegexResultNotUsedPhase)) + // { + // return RegexHelper::RegexMatch(scriptContext, regularExpression, input, true); + // } + // else + // { + // return RegexHelper::RegexMatch(scriptContext, regularExpression, input, false); + // } + // } Var RegexHelper::RegexMatch(ScriptContext* entryFunctionContext, RecyclableObject *thisObj, JavascriptString *input, bool noResult, void *const stackAllocationPointer) { @@ -2251,27 +2252,28 @@ namespace Js return RegexHelper::CheckCrossContextAndMarshalResult(result, entryFunctionContext); } - Var RegexHelper::RegexExecResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) - { - return RegexHelper::RegexExec(scriptContext, regularExpression, input, false); - } - - Var RegexHelper::RegexExecResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) - { - return RegexHelper::RegexExec(scriptContext, regularExpression, input, false, stackAllocationPointer); - } - - Var RegexHelper::RegexExecResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) - { - if (!PHASE_OFF1(Js::RegexResultNotUsedPhase)) - { - return RegexHelper::RegexExec(scriptContext, regularExpression, input, true); - } - else - { - return RegexHelper::RegexExec(scriptContext, regularExpression, input, false); - } - } + // TODO: Cleanup + // Var RegexHelper::RegexExecResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) + // { + // return RegexHelper::RegexExec(scriptContext, regularExpression, input, false); + // } + + // Var RegexHelper::RegexExecResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) + // { + // return RegexHelper::RegexExec(scriptContext, regularExpression, input, false, stackAllocationPointer); + // } + + // Var RegexHelper::RegexExecResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input) + // { + // if (!PHASE_OFF1(Js::RegexResultNotUsedPhase)) + // { + // return RegexHelper::RegexExec(scriptContext, regularExpression, input, true); + // } + // else + // { + // return RegexHelper::RegexExec(scriptContext, regularExpression, input, false); + // } + // } Var RegexHelper::RegexExec(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, bool noResult, void *const stackAllocationPointer) { @@ -2279,29 +2281,29 @@ namespace Js return RegexHelper::CheckCrossContextAndMarshalResult(result, entryFunctionContext); } - Var RegexHelper::RegexReplaceResultUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace) - { - return entryFunctionContext->GetConfig()->IsES6RegExSymbolsEnabled() - ? RegexHelper::RegexReplace(entryFunctionContext, regularExpression, input, replace, false) - : RegexHelper::RegexEs5Replace(entryFunctionContext, regularExpression, input, replace, false); - } - - Var RegexHelper::RegexReplaceResultNotUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace) - { - if (!PHASE_OFF1(Js::RegexResultNotUsedPhase)) - { - return entryFunctionContext->GetConfig()->IsES6RegExSymbolsEnabled() - ? RegexHelper::RegexReplace(entryFunctionContext, regularExpression, input, replace, true) - : RegexHelper::RegexEs5Replace(entryFunctionContext, regularExpression, input, replace, true); - } - else - { - return entryFunctionContext->GetConfig()->IsES6RegExSymbolsEnabled() - ? RegexHelper::RegexReplace(entryFunctionContext, regularExpression, input, replace, false) - : RegexHelper::RegexEs5Replace(entryFunctionContext, regularExpression, input, replace, false); - } - - } + // Var RegexHelper::RegexReplaceResultUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace) + // { + // return entryFunctionContext->GetConfig()->IsES6RegExSymbolsEnabled() + // ? RegexHelper::RegexReplace(entryFunctionContext, regularExpression, input, replace, false) + // : RegexHelper::RegexEs5Replace(entryFunctionContext, regularExpression, input, replace, false); + // } + + // Var RegexHelper::RegexReplaceResultNotUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace) + // { + // if (!PHASE_OFF1(Js::RegexResultNotUsedPhase)) + // { + // return entryFunctionContext->GetConfig()->IsES6RegExSymbolsEnabled() + // ? RegexHelper::RegexReplace(entryFunctionContext, regularExpression, input, replace, true) + // : RegexHelper::RegexEs5Replace(entryFunctionContext, regularExpression, input, replace, true); + // } + // else + // { + // return entryFunctionContext->GetConfig()->IsES6RegExSymbolsEnabled() + // ? RegexHelper::RegexReplace(entryFunctionContext, regularExpression, input, replace, false) + // : RegexHelper::RegexEs5Replace(entryFunctionContext, regularExpression, input, replace, false); + // } + + // } Var RegexHelper::RegexReplace(ScriptContext* entryFunctionContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptString* replace, bool noResult) { @@ -2332,29 +2334,30 @@ namespace Js return RegexHelper::CheckCrossContextAndMarshalResult(result, entryFunctionContext); } - Var RegexHelper::RegexSplitResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit) - { - return RegexHelper::RegexSplit(scriptContext, regularExpression, input, limit, false); - } - - Var RegexHelper::RegexSplitResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit) - { - Assert(ThreadContext::IsOnStack(stackAllocationPointer)); - - return RegexHelper::RegexSplit(scriptContext, regularExpression, input, limit, false, stackAllocationPointer); - } - - Var RegexHelper::RegexSplitResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit) - { - if (!PHASE_OFF1(Js::RegexResultNotUsedPhase)) - { - return RegexHelper::RegexSplit(scriptContext, regularExpression, input, limit, true); - } - else - { - return RegexHelper::RegexSplit(scriptContext, regularExpression, input, limit, false); - } - } + // TODO: Cleanup + // Var RegexHelper::RegexSplitResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit) + // { + // return RegexHelper::RegexSplit(scriptContext, regularExpression, input, limit, false); + // } + + // Var RegexHelper::RegexSplitResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit) + // { + // Assert(ThreadContext::IsOnStack(stackAllocationPointer)); + + // return RegexHelper::RegexSplit(scriptContext, regularExpression, input, limit, false, stackAllocationPointer); + // } + + // Var RegexHelper::RegexSplitResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit) + // { + // if (!PHASE_OFF1(Js::RegexResultNotUsedPhase)) + // { + // return RegexHelper::RegexSplit(scriptContext, regularExpression, input, limit, true); + // } + // else + // { + // return RegexHelper::RegexSplit(scriptContext, regularExpression, input, limit, false); + // } + // } Var RegexHelper::RegexSplit(ScriptContext* entryFunctionContext, RecyclableObject* thisObj, JavascriptString* input, CharCount limit, bool noResult, void *const stackAllocationPointer) { diff --git a/lib/Runtime/Library/RegexHelper.h b/lib/Runtime/Library/RegexHelper.h index 8ab40bb6f0c..f53f3116d06 100644 --- a/lib/Runtime/Library/RegexHelper.h +++ b/lib/Runtime/Library/RegexHelper.h @@ -102,16 +102,16 @@ namespace Js , CompoundString::Builder<64 * sizeof(void *) / sizeof(char16)>& concatenated ); public: - static Var RegexReplaceResultUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace); - static Var RegexReplaceResultNotUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace); + // TODO: Cleanup + // static Var RegexReplaceResultUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace); + // static Var RegexReplaceResultNotUsed(ScriptContext* entryFunctionContext, JavascriptRegExp* regularExpression, JavascriptString* input, JavascriptString* replace); static Var RegexReplace(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptString* replace, bool noResult); static Var RegexReplaceFunction(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, RecyclableObject* replacefn); static Var StringReplace(JavascriptString* regularExpression, JavascriptString* input, JavascriptString* replace); static Var StringReplace(ScriptContext* scriptContext, JavascriptString* regularExpression, JavascriptString* input, RecyclableObject* replacefn); - static Var RegexSplitResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit); - static Var RegexSplitResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit); - static Var RegexSplitResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit); - static Var RegexSplit(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, CharCount limit, bool noResult, void *const stackAllocationPointer = nullptr); + // static Var RegexSplitResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit); + // static Var RegexSplitResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit); + // static Var RegexSplitResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit); static Var RegexSearch(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); static Var StringSplit(JavascriptString* regularExpression, JavascriptString* input, CharCount limit); static bool IsResultNotUsed(CallFlags flags); diff --git a/tools/regenByteCode.py b/tools/regenByteCode.py index b5042d679f2..90121d24f0c 100755 --- a/tools/regenByteCode.py +++ b/tools/regenByteCode.py @@ -262,17 +262,17 @@ def bytecode_job(out_path, command, in_path, error): guid = str(uuid.uuid4()) output_str = '''//------------------------------------------------------------------------------------------------------- - // Copyright (C) Microsoft. All rights reserved. - // Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. - // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. - //------------------------------------------------------------------------------------------------------- - // NOTE: If there is a merge conflict the correct fix is to make a new GUID. - // This file was generated with tools/regenByteCode.py - - // {%s} - const GUID byteCodeCacheReleaseFileVersion = - { 0x%s, 0x%s, 0x%s, {0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s } }; - ''' % (guid, + // Copyright (C) Microsoft. All rights reserved. + // Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. + // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. + //------------------------------------------------------------------------------------------------------- + // NOTE: If there is a merge conflict the correct fix is to make a new GUID. + // This file was generated with tools/regenByteCode.py + + // {%s} + const GUID byteCodeCacheReleaseFileVersion = + { 0x%s, 0x%s, 0x%s, {0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s, 0x%s } }; + '''.replace('\n ', '\n') % (guid, guid[:8], guid[9:13], guid[14:18], guid[19:21], guid[21:23], guid[24:26], guid[26:28], guid[28:30], guid[30:32], guid[32:34], guid[-2:]) From ec40dc0553c01c5f14febe1fdf8719e896571672 Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Sat, 20 Feb 2021 21:11:26 +0300 Subject: [PATCH 02/11] fix RegExp.prototype[@@search]() --- .../Library/JavascriptRegularExpression.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/Runtime/Library/JavascriptRegularExpression.cpp b/lib/Runtime/Library/JavascriptRegularExpression.cpp index 62804ba4b80..b5b666295e6 100644 --- a/lib/Runtime/Library/JavascriptRegularExpression.cpp +++ b/lib/Runtime/Library/JavascriptRegularExpression.cpp @@ -811,12 +811,25 @@ using namespace Js; JavascriptString* string = GetFirstStringArg(args, scriptContext); + // 4. Let previousLastIndex be ? Get(rx, "lastIndex"). Var previousLastIndex = JavascriptOperators::GetProperty(thisObj, PropertyIds::lastIndex, scriptContext); - SetLastIndexProperty(regEx, TaggedInt::ToVarUnchecked(0), scriptContext); + // 5. If SameValue(previousLastIndex, +0F) is false, then + // a. Perform ? Set(rx, "lastIndex", +0F, true). + if (!JavascriptConversion::SameValue(previousLastIndex, TaggedInt::ToVarUnchecked(0))) + { + SetLastIndexProperty(regEx, TaggedInt::ToVarUnchecked(0), scriptContext); + } Var result = CallExec(thisObj, string, varName, scriptContext); - - SetLastIndexProperty(regEx, previousLastIndex, scriptContext); + + // 7. Let currentLastIndex be ? Get(rx, "lastIndex"). + Var currentLastIndex = JavascriptOperators::GetProperty(thisObj, PropertyIds::lastIndex, scriptContext); + // 8. If SameValue(currentLastIndex, previousLastIndex) is false, then + // a. Perform ? Set(rx, "lastIndex", previousLastIndex, true). + if (!JavascriptConversion::SameValue(currentLastIndex, previousLastIndex)) + { + SetLastIndexProperty(regEx, previousLastIndex, scriptContext); + } return JavascriptOperators::IsNull(result) ? TaggedInt::ToVarUnchecked(-1) From 919c6a342d2ebb191d250d7f667d006cb3370a94 Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Sat, 20 Feb 2021 21:46:26 +0300 Subject: [PATCH 03/11] fix copyrights --- lib/Runtime/Library/JavascriptRegularExpression.cpp | 1 + lib/Runtime/Library/RegexHelper.cpp | 1 + lib/Runtime/Library/RegexHelper.h | 1 + 3 files changed, 3 insertions(+) diff --git a/lib/Runtime/Library/JavascriptRegularExpression.cpp b/lib/Runtime/Library/JavascriptRegularExpression.cpp index b5b666295e6..a693630e662 100644 --- a/lib/Runtime/Library/JavascriptRegularExpression.cpp +++ b/lib/Runtime/Library/JavascriptRegularExpression.cpp @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "RuntimeLibraryPch.h" diff --git a/lib/Runtime/Library/RegexHelper.cpp b/lib/Runtime/Library/RegexHelper.cpp index 4954cf4da0f..21df60027fb 100644 --- a/lib/Runtime/Library/RegexHelper.cpp +++ b/lib/Runtime/Library/RegexHelper.cpp @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "RuntimeLibraryPch.h" diff --git a/lib/Runtime/Library/RegexHelper.h b/lib/Runtime/Library/RegexHelper.h index f53f3116d06..3233ad5f6f7 100644 --- a/lib/Runtime/Library/RegexHelper.h +++ b/lib/Runtime/Library/RegexHelper.h @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #pragma once From ec358a87007431ee58a8dbc8b00522abf87e3a9a Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Mon, 22 Feb 2021 19:59:57 +0300 Subject: [PATCH 04/11] fix RegExp.prototype\[@@split]() --- .../Library/JavascriptRegularExpression.cpp | 19 +-- lib/Runtime/Library/RegexHelper.cpp | 111 +++++++++--------- lib/Runtime/Library/RegexHelper.h | 18 +-- 3 files changed, 71 insertions(+), 77 deletions(-) diff --git a/lib/Runtime/Library/JavascriptRegularExpression.cpp b/lib/Runtime/Library/JavascriptRegularExpression.cpp index a693630e662..087056f70f6 100644 --- a/lib/Runtime/Library/JavascriptRegularExpression.cpp +++ b/lib/Runtime/Library/JavascriptRegularExpression.cpp @@ -847,28 +847,15 @@ using namespace Js; CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(ES6, RegexSymbolSplit, scriptContext); - RecyclableObject *thisObj = GetThisObject(args, _u("RegExp.prototype[Symbol.match]"), scriptContext); + // ! Fun Note: In some time there was RegExp.prototype[Symbol.match] Lol + RecyclableObject *thisObj = GetThisObject(args, _u("RegExp.prototype[Symbol.split]"), scriptContext); JavascriptString* string = GetFirstStringArg(args, scriptContext); - // TODO: SPEC DEVIATION - // - // In RegexHelper::RegexSplit, we check if RegExp properties are overridden in order to determine - // if the algorithm is observable. If it is, we go through the new ES6 algorithm, but otherwise, we - // run the faster ES5 version. - // - // According to the spec, we're supposed to process "limit" after we use some of the RegExp properties. - // However, there doesn't seem to be any reason why "limit" processing can't be pulled above the rest - // in the spec. Therefore, we should see if such a spec update is OK. If not, this would have to be - // moved to its correct place in the code. - uint32 limit = (args.Info.Count < 3 || JavascriptOperators::IsUndefinedObject(args[2])) - ? UINT_MAX - : JavascriptConversion::ToUInt32(args[2], scriptContext); - return RegexHelper::RegexSplit( scriptContext, thisObj, string, - limit, + args, RegexHelper::IsResultNotUsed(callInfo.Flags)); } diff --git a/lib/Runtime/Library/RegexHelper.cpp b/lib/Runtime/Library/RegexHelper.cpp index 21df60027fb..37a2256167f 100644 --- a/lib/Runtime/Library/RegexHelper.cpp +++ b/lib/Runtime/Library/RegexHelper.cpp @@ -365,25 +365,7 @@ namespace Js template Var RegexHelper::RegexMatchImpl(ScriptContext* scriptContext, RecyclableObject *thisObj, JavascriptString *input, bool noResult, void *const stackAllocationPointer) { - ScriptConfiguration const * scriptConfig = scriptContext->GetConfig(); - - // Normally, this check would be done in JavascriptRegExp::EntrySymbolMatch. However, - // since the lowerer inlines String.prototype.match and directly calls the helper, - // the check then would be bypassed. That's the reason we do the check here. - if (scriptConfig->IsES6RegExSymbolsEnabled() - && IsRegexSymbolMatchObservable(thisObj, scriptContext)) - { - // We don't need to pass "updateHistory" here since the call to "exec" will handle it. - return RegexEs6MatchImpl(scriptContext, thisObj, input, noResult, stackAllocationPointer); - } - else - { - PCWSTR varName = scriptConfig->IsES6RegExSymbolsEnabled() - ? _u("RegExp.prototype[Symbol.match]") - : _u("String.prototype.match"); - JavascriptRegExp* regularExpression = JavascriptRegExp::ToRegExp(thisObj, varName, scriptContext); - return RegexEs5MatchImpl(scriptContext, regularExpression, input, noResult, stackAllocationPointer); - } + return RegexEs6MatchImpl(scriptContext, thisObj, input, noResult, stackAllocationPointer); } bool RegexHelper::IsRegexSymbolMatchObservable(RecyclableObject* instance, ScriptContext* scriptContext) @@ -599,6 +581,50 @@ namespace Js return arrayResult; } + // // RegExpBuiltinExec (ES6 22.2.5.2.2) + // Var RegexHelper::RegexExecImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, bool noResult, void *const stackAllocationPointer) + // { + // UnifiedRegex::RegexPattern* pattern = regularExpression->GetPattern(); + + // CharCount inputLength = input->GetLength(); + // /*Var lastIndex = */JavascriptOperators::GetProperty(regularExpression, PropertyIds::lastIndex, scriptContext); + // const bool isGlobal = pattern->IsGlobal(); + // const bool isSticky = pattern->IsSticky(); + // CharCount offset; + // if (!GetInitialOffset(isGlobal, isSticky, regularExpression, inputLength, offset)) + // { + // return scriptContext->GetLibrary()->GetNull(); + // } + + // UnifiedRegex::GroupInfo match; // initially undefined + // const char16* inputStr = input->GetString(); + // if (offset <= inputLength) + // { + // // TODO: Understand what SimpleMatch does + // match = SimpleMatch(scriptContext, pattern, inputStr, inputLength, offset); + // } + + // // else: match remains undefined + // PropagateLastMatch(scriptContext, isGlobal, isSticky, regularExpression, input, match, match, true, true); + + // if (noResult || match.IsUndefined()) + // { + // return scriptContext->GetLibrary()->GetNull(); + // } + + // const int numGroups = pattern->NumGroups(); + // Assert(numGroups >= 0); + // JavascriptArray* result = CreateExecResult(stackAllocationPointer, scriptContext, numGroups, input, match); + // Var nonMatchValue = NonMatchValue(scriptContext, false); + // Field(Var) *elements = ((SparseArraySegment*)result->GetHead())->elements; + // for (uint groupId = 0; groupId < (uint)numGroups; groupId++) + // { + // Assert(groupId < result->GetHead()->left + result->GetHead()->length); + // elements[groupId] = GetGroup(scriptContext, pattern, input, nonMatchValue, groupId); + // } + // return result; + // } + // RegExp.prototype.exec (ES5 15.10.6.2) Var RegexHelper::RegexExecImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, bool noResult, void *const stackAllocationPointer) { @@ -894,20 +920,7 @@ namespace Js Var RegexHelper::RegexReplaceImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, JavascriptString* replace, bool noResult) { - ScriptConfiguration const * scriptConfig = scriptContext->GetConfig(); - - if (scriptConfig->IsES6RegExSymbolsEnabled() && IsRegexSymbolReplaceObservable(thisObj, scriptContext)) - { - return RegexEs6ReplaceImpl(scriptContext, thisObj, input, replace, noResult); - } - else - { - PCWSTR varName = scriptConfig->IsES6RegExSymbolsEnabled() - ? _u("RegExp.prototype[Symbol.replace]") - : _u("String.prototype.replace"); - JavascriptRegExp* regularExpression = JavascriptRegExp::ToRegExp(thisObj, varName, scriptContext); - return RegexEs5ReplaceImpl(scriptContext, regularExpression, input, replace, noResult); - } + return RegexEs6ReplaceImpl(scriptContext, thisObj, input, replace, noResult); } bool RegexHelper::IsRegexSymbolReplaceObservable(RecyclableObject* instance, ScriptContext* scriptContext) @@ -1577,23 +1590,9 @@ namespace Js return splitPattern; } - Var RegexHelper::RegexSplitImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, CharCount limit, bool noResult, void *const stackAllocationPointer) + Var RegexHelper::RegexSplitImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, Arguments& args, bool noResult, void *const stackAllocationPointer) { - ScriptConfiguration const * scriptConfig = scriptContext->GetConfig(); - - if (scriptConfig->IsES6RegExSymbolsEnabled() - && IsRegexSymbolSplitObservable(thisObj, scriptContext)) - { - return RegexEs6SplitImpl(scriptContext, thisObj, input, limit, noResult, stackAllocationPointer); - } - else - { - PCWSTR varName = scriptContext->GetConfig()->IsES6RegExSymbolsEnabled() - ? _u("RegExp.prototype[Symbol.split]") - : _u("String.prototype.split"); - JavascriptRegExp* regularExpression = JavascriptRegExp::ToRegExp(thisObj, varName, scriptContext); - return RegexEs5SplitImpl(scriptContext, regularExpression, input, limit, noResult, stackAllocationPointer); - } + return RegexEs6SplitImpl(scriptContext, thisObj, input, args, noResult, stackAllocationPointer); } bool RegexHelper::IsRegexSymbolSplitObservable(RecyclableObject* instance, ScriptContext* scriptContext) @@ -1605,7 +1604,7 @@ namespace Js || JavascriptRegExp::HasObservableExec(regexPrototype); } - Var RegexHelper::RegexEs6SplitImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, CharCount limit, bool noResult, void *const stackAllocationPointer) + Var RegexHelper::RegexEs6SplitImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, Arguments& args, bool noResult, void *const stackAllocationPointer) { PCWSTR const varName = _u("RegExp.prototype[Symbol.split]"); @@ -1636,12 +1635,17 @@ namespace Js JavascriptArray* arrayResult = scriptContext->GetLibrary()->CreateArray(); + uint32 limit = (args.Info.Count < 3 || JavascriptOperators::IsUndefinedObject(args[2])) + ? UINT_MAX + : JavascriptConversion::ToUInt32(args[2], scriptContext); + + CharCount inputLength = input->GetLength(); // 'size' in spec + if (limit == 0) { return arrayResult; } - CharCount inputLength = input->GetLength(); if (inputLength == 0) { Var result = JavascriptRegExp::CallExec(splitter, input, varName, scriptContext); @@ -1728,6 +1732,7 @@ namespace Js return flags; } + // TODO: Cleanup // String.prototype.split (ES5 15.5.4.14) Var RegexHelper::RegexEs5SplitImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit, bool noResult, void *const stackAllocationPointer) { @@ -2360,9 +2365,9 @@ namespace Js // } // } - Var RegexHelper::RegexSplit(ScriptContext* entryFunctionContext, RecyclableObject* thisObj, JavascriptString* input, CharCount limit, bool noResult, void *const stackAllocationPointer) + Var RegexHelper::RegexSplit(ScriptContext* entryFunctionContext, RecyclableObject* thisObj, JavascriptString* input, Arguments& args, bool noResult, void *const stackAllocationPointer) { - Var result = RegexHelper::RegexSplitImpl(entryFunctionContext, thisObj, input, limit, noResult, stackAllocationPointer); + Var result = RegexHelper::RegexSplitImpl(entryFunctionContext, thisObj, input, args, noResult, stackAllocationPointer); return RegexHelper::CheckCrossContextAndMarshalResult(result, entryFunctionContext); } diff --git a/lib/Runtime/Library/RegexHelper.h b/lib/Runtime/Library/RegexHelper.h index 3233ad5f6f7..0d52ec68dca 100644 --- a/lib/Runtime/Library/RegexHelper.h +++ b/lib/Runtime/Library/RegexHelper.h @@ -72,14 +72,15 @@ namespace Js // public: - static Var RegexMatchResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); - static Var RegexMatchResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); - static Var RegexMatchResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); + // TODO: Cleanup + // static Var RegexMatchResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); + // static Var RegexMatchResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); + // static Var RegexMatchResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); static Var RegexMatch(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, bool noResult, void *const stackAllocationPointer = nullptr); static Var RegexMatchNoHistory(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, bool noResult); - static Var RegexExecResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); - static Var RegexExecResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); - static Var RegexExecResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); + // static Var RegexExecResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); + // static Var RegexExecResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); + // static Var RegexExecResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); static Var RegexExec(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, bool noResult, void *const stackAllocationPointer = nullptr); static Var RegexTest(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input); template static BOOL RegexTest_NonScript(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, const char16 *const input, const CharCount inputLength); @@ -113,6 +114,7 @@ namespace Js // static Var RegexSplitResultUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit); // static Var RegexSplitResultUsedAndMayBeTemp(void *const stackAllocationPointer, ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit); // static Var RegexSplitResultNotUsed(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit); + static Var RegexSplit(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, Arguments& args, bool noResult, void *const stackAllocationPointer = nullptr); static Var RegexSearch(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); static Var StringSplit(JavascriptString* regularExpression, JavascriptString* input, CharCount limit); static bool IsResultNotUsed(CallFlags flags); @@ -139,8 +141,8 @@ namespace Js static Var RegexSearchImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input); inline static UnifiedRegex::RegexPattern *GetSplitPattern(ScriptContext* scriptContext, JavascriptRegExp *regularExpression); static bool IsRegexSymbolSplitObservable(RecyclableObject* instance, ScriptContext* scriptContext); - static Var RegexSplitImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, CharCount limit, bool noResult, void *const stackAllocationPointer = nullptr); - static Var RegexEs6SplitImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, CharCount limit, bool noResult, void *const stackAllocationPointer = nullptr); + static Var RegexSplitImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, Arguments& args, bool noResult, void *const stackAllocationPointer = nullptr); + static Var RegexEs6SplitImpl(ScriptContext* scriptContext, RecyclableObject* thisObj, JavascriptString* input, Arguments& args, bool noResult, void *const stackAllocationPointer = nullptr); static JavascriptString* AppendStickyToFlagsIfNeeded(JavascriptString* flags, ScriptContext* scriptContext); static Var RegexEs5SplitImpl(ScriptContext* scriptContext, JavascriptRegExp* regularExpression, JavascriptString* input, CharCount limit, bool noResult, void *const stackAllocationPointer = nullptr); static bool IsRegexTestObservable(RecyclableObject* instance, ScriptContext* scriptContext); From c8614d55058a566acc09b9a5c88b7a6beb802754 Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Mon, 22 Feb 2021 20:06:28 +0300 Subject: [PATCH 05/11] Fix String.prototype.split() --- lib/Runtime/Library/JavascriptString.cpp | 153 +++++++++++++++++------ lib/Runtime/Library/JavascriptString.h | 3 +- 2 files changed, 117 insertions(+), 39 deletions(-) diff --git a/lib/Runtime/Library/JavascriptString.cpp b/lib/Runtime/Library/JavascriptString.cpp index bcd6b45bb6d..79544775eda 100644 --- a/lib/Runtime/Library/JavascriptString.cpp +++ b/lib/Runtime/Library/JavascriptString.cpp @@ -1915,6 +1915,7 @@ namespace Js return SubstringCore(pThis, idxStart, idxEnd - idxStart, scriptContext); } + // ES6: 22.1.3.21 String.prototype.split ( separator, limit ) Var JavascriptString::EntrySplit(RecyclableObject* function, CallInfo callInfo, ...) { PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault); @@ -1928,65 +1929,141 @@ namespace Js AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, varName); - auto fallback = [&](JavascriptString* stringObj) + if (args.Info.Count < 1 || JavascriptOperators::IsUndefinedOrNull(args[0])) { - return DoStringSplit(args, callInfo, stringObj, scriptContext); - }; - return DelegateToRegExSymbolFunction<2>(args, PropertyIds::_symbolSplit, fallback, varName, scriptContext); + JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NullOrUndefined, varName); + } + + if (args.Info.Count > 1 && !JavascriptOperators::IsUndefinedOrNull(args[1])) + { + Var splitter = GetRegExSymbolFunction(args[1], PropertyIds::_symbolSplit, scriptContext); + if (!JavascriptOperators::IsUndefined(splitter)) + { + Var limit = args.Info.Count > 2 ? args[2] : scriptContext->GetLibrary()->GetUndefined(); + + if (!JavascriptConversion::IsCallable(splitter)) + { + JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_Invalid, varName); + } + + // ! NOTE: Execute with exactly 2 arguments + ThreadContext* threadContext = scriptContext->GetThreadContext(); + return threadContext->ExecuteImplicitCall(UnsafeVarTo(splitter), ImplicitCall_Accessor, [=]()->Js::Var + { + Var result = nullptr; + result = CALL_FUNCTION(threadContext, UnsafeVarTo(splitter), CallInfo(CallFlags_Value, 3), args[1], args[0], limit); + return result; + }); + } + } + + + return DoStringSplit(args, scriptContext); } - Var JavascriptString::DoStringSplit(Arguments& args, CallInfo& callInfo, JavascriptString* input, ScriptContext* scriptContext) + Var JavascriptString::DoStringSplit(Arguments& args, ScriptContext* scriptContext) { - if (args.Info.Count == 1) + JavascriptString* S = JavascriptConversion::ToString(args[0], scriptContext); + JavascriptArray* A = scriptContext->GetLibrary()->CreateArray((uint32_t)0); + uint32_t lengthA = 0; + uint32_t limit = args.Info.Count > 2 && !JavascriptOperators::IsUndefined(args[2]) ? + JavascriptConversion::ToUInt32(args[2], scriptContext) : UINT32_MAX; + + Var separator = args.Info.Count > 1 ? args[1] : scriptContext->GetLibrary()->GetUndefined(); + JavascriptString* R = JavascriptConversion::ToString(separator, scriptContext); + + if (limit == 0) { - JavascriptArray* ary = scriptContext->GetLibrary()->CreateArray(1); - ary->DirectSetItemAt(0, input); - return ary; + return A; } - else + + if (JavascriptOperators::IsUndefined(separator)) { - uint32 limit; - if (args.Info.Count < 3 || JavascriptOperators::IsUndefinedObject(args[2])) - { - limit = UINT_MAX; - } - else + A->DirectAppendItem(S); + return A; + } + + charcount_t s = S->GetLength(); + + if (s == 0) + { + if (R->GetLength() != 0) { - limit = JavascriptConversion::ToUInt32(args[2], scriptContext); + A->DirectAppendItem(S); } + return A; + } + + charcount_t p = 0; + charcount_t q = p; + + do + { + charcount_t e; - // When the config is enabled, the operation is handled by RegExp.prototype[@@split]. - if (!scriptContext->GetConfig()->IsES6RegExSymbolsEnabled() - && VarIs(args[1])) + if (!SplitMatch(S, q, R, &e)) { - return RegexHelper::RegexSplit(scriptContext, UnsafeVarTo(args[1]), input, limit, - RegexHelper::IsResultNotUsed(callInfo.Flags)); + q++; } else { - JavascriptString* separator = JavascriptConversion::ToString(args[1], scriptContext); - - if (callInfo.Flags & CallFlags_NotUsed) + Assert((charcount_t)0 < e && e <= s); + if (e == p) { - return scriptContext->GetLibrary()->GetNull(); + q++; } - - if (!limit) + else { - JavascriptArray* ary = scriptContext->GetLibrary()->CreateArray(0); - return ary; - } +#ifdef ENABLE_SPECTRE_RUNTIME_MITIGATIONS + S = (JavascriptString*)BreakSpeculation(S); +#endif - if (JavascriptOperators::GetTypeId(args[1]) == TypeIds_Undefined) - { - JavascriptArray* ary = scriptContext->GetLibrary()->CreateArray(1); - ary->DirectSetItemAt(0, input); - return ary; - } + Var T = SubstringCore(S, p, q - p, scriptContext); + + A->DirectAppendItem(T); - return RegexHelper::StringSplit(separator, input, limit); + lengthA++; + + if (lengthA == limit) + { + return A; + } + + p = e; + q = p; + } + } + } while (q != s); + +#ifdef ENABLE_SPECTRE_RUNTIME_MITIGATIONS + S = (JavascriptString*)BreakSpeculation(S); +#endif + + Var T = SubstringCore(S, p, s - p, scriptContext); + + A->DirectAppendItem(T); + + return A; + } + + // ES6: 22.1.3.21.1 SplitMatch ( S, q, R ) + bool JavascriptString::SplitMatch(JavascriptString* S, charcount_t q, JavascriptString* R, charcount_t* e) { + charcount_t s = S->GetLength(); + charcount_t r = R->GetLength(); + + if (q + r > s) + { + return false; } + + if (wmemcmp(S->GetString() + q, R->GetString(), r) != 0) + { + return false; + } + + *e = q + r; + return true; } Var JavascriptString::EntrySubstring(RecyclableObject* function, CallInfo callInfo, ...) diff --git a/lib/Runtime/Library/JavascriptString.h b/lib/Runtime/Library/JavascriptString.h index 30cc4b60865..163f8a71cfa 100644 --- a/lib/Runtime/Library/JavascriptString.h +++ b/lib/Runtime/Library/JavascriptString.h @@ -348,7 +348,8 @@ namespace Js static Var TrimLeftRightHelper(JavascriptString* arg, ScriptContext* scriptContext); static Var DoStringReplace(Arguments& args, CallInfo& callInfo, JavascriptString* input, ScriptContext* scriptContext); - static Var DoStringSplit(Arguments& args, CallInfo& callInfo, JavascriptString* input, ScriptContext* scriptContext); + static Var DoStringSplit(Arguments& args, ScriptContext* scriptContext); + static bool SplitMatch(JavascriptString* S, charcount_t q, JavascriptString* R, charcount_t* e); template static Var DelegateToRegExSymbolFunction(ArgumentReader &args, PropertyId symbolPropertyId, FallbackFn fallback, PCWSTR varName, ScriptContext* scriptContext); static Var GetRegExSymbolFunction(Var regExp, PropertyId propertyId, ScriptContext* scriptContext); From 446bd065bbbe9c9168c0e2da7dfb28bbdf1a3cd2 Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Mon, 22 Feb 2021 23:36:55 +0300 Subject: [PATCH 06/11] fix dumb mistake --- lib/Runtime/Library/JavascriptString.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Runtime/Library/JavascriptString.cpp b/lib/Runtime/Library/JavascriptString.cpp index 79544775eda..6357163fa4d 100644 --- a/lib/Runtime/Library/JavascriptString.cpp +++ b/lib/Runtime/Library/JavascriptString.cpp @@ -2007,7 +2007,7 @@ namespace Js } else { - Assert((charcount_t)0 < e && e <= s); + Assert(e <= s); if (e == p) { q++; From 3132cdd113954489690abe00d4bf9a650b005450 Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Tue, 23 Feb 2021 21:40:57 +0300 Subject: [PATCH 07/11] Fix GetRegExSymbolFunction --- lib/Runtime/Library/JavascriptString.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Runtime/Library/JavascriptString.cpp b/lib/Runtime/Library/JavascriptString.cpp index 6357163fa4d..6808c73859b 100644 --- a/lib/Runtime/Library/JavascriptString.cpp +++ b/lib/Runtime/Library/JavascriptString.cpp @@ -1811,10 +1811,11 @@ namespace Js Var JavascriptString::GetRegExSymbolFunction(Var regExp, PropertyId propertyId, ScriptContext* scriptContext) { - return JavascriptOperators::GetPropertyNoCache( + Var func = JavascriptOperators::GetPropertyNoCache( JavascriptOperators::ToObject(regExp, scriptContext), propertyId, scriptContext); + return JavascriptOperators::IsUndefinedOrNull(func) ? scriptContext->GetLibrary()->GetUndefined() : func; } template From fbc00fc291f663e91d57fdc85f2fbe8af8f42403 Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Thu, 25 Feb 2021 22:40:56 +0300 Subject: [PATCH 08/11] Fix AdvanceStringIndex --- lib/Runtime/Library/RegexHelper.cpp | 33 +++++++++++++++++++++++------ lib/Runtime/Library/RegexHelper.h | 1 + 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/Runtime/Library/RegexHelper.cpp b/lib/Runtime/Library/RegexHelper.cpp index 37a2256167f..c4ee42da005 100644 --- a/lib/Runtime/Library/RegexHelper.cpp +++ b/lib/Runtime/Library/RegexHelper.cpp @@ -2396,18 +2396,39 @@ namespace Js { if (matchStr->GetLength() == 0) { - CharCount lastIndex = JavascriptRegExp::GetLastIndexProperty(instance, scriptContext); + + int64 lastIndex = JavascriptConversion::ToLength( + JavascriptOperators::GetProperty(instance, PropertyIds::lastIndex, scriptContext), + scriptContext); lastIndex = AdvanceStringIndex(input, lastIndex, unicode); - JavascriptRegExp::SetLastIndexProperty(instance, lastIndex, scriptContext); + JavascriptRegExp::SetLastIndexProperty( + instance, + JavascriptNumber::ToVar(lastIndex, scriptContext), + scriptContext); } } - CharCount RegexHelper::AdvanceStringIndex(JavascriptString* string, CharCount index, bool isUnicode) + int64_t RegexHelper::AdvanceStringIndex(JavascriptString* string, int64_t index, bool isUnicode) { - // TODO: Change the increment to 2 depending on the "unicode" flag and - // the code point at "index". The increment is currently constant at 1 - // in order to be compatible with the rest of the RegExp code. + if (string->GetLength() > (0 > index || (uint64_t)index + (uint64_t)1 > UINT32_MAX ? UINT32_MAX : (uint32_t)(index + 1)) && + NumberUtilities::IsSurrogateLowerPart(string->GetString()[index]) && + NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1])) + { + return index + 2; + } + + return index + 1; + } + CharCount RegexHelper::AdvanceStringIndex(JavascriptString* string, CharCount index, bool isUnicode) + { + if (string->GetLength() > index + 1 && + NumberUtilities::IsSurrogateLowerPart(string->GetString()[index]) && + NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1])) + { + return JavascriptRegExp::AddIndex(index, 2); + } + return JavascriptRegExp::AddIndex(index, 1); } } diff --git a/lib/Runtime/Library/RegexHelper.h b/lib/Runtime/Library/RegexHelper.h index 0d52ec68dca..7b60e7a8346 100644 --- a/lib/Runtime/Library/RegexHelper.h +++ b/lib/Runtime/Library/RegexHelper.h @@ -152,6 +152,7 @@ namespace Js static RecyclableObject* ExecResultToRecyclableObject(Var result); static JavascriptString* GetMatchStrFromResult(RecyclableObject* result, ScriptContext* scriptContext); static void AdvanceLastIndex(RecyclableObject* instance, JavascriptString* input, JavascriptString* matchStr, bool unicode, ScriptContext* scriptContext); + static int64_t AdvanceStringIndex(JavascriptString* string, int64_t index, bool isUnicode); static charcount_t AdvanceStringIndex(JavascriptString* string, charcount_t index, bool isUnicode); }; } From e4fd4be94ec2e54ab4d666ad871f4282ff95e20b Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Thu, 25 Feb 2021 23:36:28 +0300 Subject: [PATCH 09/11] Fix AdvanceLastIndex again --- lib/Runtime/Library/RegexHelper.cpp | 37 +++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/Runtime/Library/RegexHelper.cpp b/lib/Runtime/Library/RegexHelper.cpp index c4ee42da005..6bf71eb43d7 100644 --- a/lib/Runtime/Library/RegexHelper.cpp +++ b/lib/Runtime/Library/RegexHelper.cpp @@ -1941,16 +1941,33 @@ namespace Js w = scriptContext->GetRegexDebugWriter(); #endif - pattern->rep.unified.matcher->Match - (state.input - , inputLength - , offset - , scriptContext + if (pattern->IsUnicode()) + { + pattern->rep.unified.matcher->Match + (state.input + , inputLength + , offset + , scriptContext #if ENABLE_REGEX_CONFIG_OPTIONS - , stats - , w + , stats + , w #endif - ); + ); + } + else + { + pattern->rep.unified.matcher->Match + (state.input + , inputLength + , offset + , scriptContext +#if ENABLE_REGEX_CONFIG_OPTIONS + , stats + , w +#endif + ); + } + #if ENABLE_REGEX_CONFIG_OPTIONS if (REGEX_CONFIG_FLAG(RegexProfile)) @@ -2412,7 +2429,7 @@ namespace Js { if (string->GetLength() > (0 > index || (uint64_t)index + (uint64_t)1 > UINT32_MAX ? UINT32_MAX : (uint32_t)(index + 1)) && NumberUtilities::IsSurrogateLowerPart(string->GetString()[index]) && - NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1])) + NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1]) && isUnicode) { return index + 2; } @@ -2424,7 +2441,7 @@ namespace Js { if (string->GetLength() > index + 1 && NumberUtilities::IsSurrogateLowerPart(string->GetString()[index]) && - NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1])) + NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1]) && isUnicode) { return JavascriptRegExp::AddIndex(index, 2); } From e5b24b7acf785208401d351136000663262a7ef6 Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Thu, 25 Feb 2021 23:44:31 +0300 Subject: [PATCH 10/11] dont leak --- lib/Runtime/Library/RegexHelper.cpp | 35 +++++++++-------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/lib/Runtime/Library/RegexHelper.cpp b/lib/Runtime/Library/RegexHelper.cpp index 6bf71eb43d7..d20cdd470b2 100644 --- a/lib/Runtime/Library/RegexHelper.cpp +++ b/lib/Runtime/Library/RegexHelper.cpp @@ -1941,33 +1941,18 @@ namespace Js w = scriptContext->GetRegexDebugWriter(); #endif - if (pattern->IsUnicode()) - { - pattern->rep.unified.matcher->Match - (state.input - , inputLength - , offset - , scriptContext -#if ENABLE_REGEX_CONFIG_OPTIONS - , stats - , w -#endif - ); - } - else - { - pattern->rep.unified.matcher->Match - (state.input - , inputLength - , offset - , scriptContext + pattern->rep.unified.matcher->Match + (state.input + , inputLength + , offset + , scriptContext #if ENABLE_REGEX_CONFIG_OPTIONS - , stats - , w + , stats + , w #endif - ); - } - + ); + + #if ENABLE_REGEX_CONFIG_OPTIONS if (REGEX_CONFIG_FLAG(RegexProfile)) From 68e9fa763fe1af74ed35ba289609f318156648cd Mon Sep 17 00:00:00 2001 From: MadProbe <49519179+MadProbe@users.noreply.github.com> Date: Mon, 8 Mar 2021 21:55:42 +0300 Subject: [PATCH 11/11] add JavascriptString::GetCodePoints for later use --- lib/Runtime/Library/JavascriptString.cpp | 54 ++++++++++++++++++++++++ lib/Runtime/Library/JavascriptString.h | 6 ++- lib/Runtime/Library/RegexHelper.cpp | 8 ++-- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/lib/Runtime/Library/JavascriptString.cpp b/lib/Runtime/Library/JavascriptString.cpp index 6808c73859b..c906a20b87e 100644 --- a/lib/Runtime/Library/JavascriptString.cpp +++ b/lib/Runtime/Library/JavascriptString.cpp @@ -2915,6 +2915,60 @@ namespace Js return m_pszValue; } + codepoint_t* JavascriptString::GetCodePoints(ScriptContext* scriptContext) + { + if (m_codePointString != nullptr) + { + return m_codePointString; + } + + const char16* initialString = GetString(); + charcount_t codePointLength = 0; + charcount_t stringLength = GetLength(), i = 0; + while (i < stringLength) + { + if (i + 1 < stringLength && + NumberUtilities::IsSurrogateLowerPart((codepoint_t)initialString[i]) && + NumberUtilities::IsSurrogateUpperPart((codepoint_t)initialString[i + 1])) + { + codePointLength++; + i++; + } + + i++; + } + codepoint_t* codePoints; + charcount_t j = 0; + i = 0; + codePoints = AllocatorNewArrayLeafZ(Recycler, scriptContext->GetRecycler(), codepoint_t, codePointLength); + while (i < stringLength) + { + if (i + 1 < stringLength && + NumberUtilities::IsSurrogateLowerPart((codepoint_t)initialString[i]) && + NumberUtilities::IsSurrogateUpperPart((codepoint_t)initialString[i + 1])) + { + *(codePoints + j++) = NumberUtilities::SurrogatePairAsCodePoint((codepoint_t)initialString[i], (codepoint_t)initialString[i + 1]); + i += 2ui32; + continue; + } + *(codePoints + j++) = (codepoint_t)initialString[i]; + i++; + } + m_codePointString = codePoints; + m_codePointsLength = codePointLength; + return codePoints; + } + + charcount_t JavascriptString::GetCodePointsLength() + { + if (m_codePointsLength == k_InvalidCharCount) + { + GetCodePoints(); // Init the m_codePointsLength and m_codePoints properties + } + + return m_codePointsLength; + } + void const * JavascriptString::GetOriginalStringReference() { // Just return the string buffer diff --git a/lib/Runtime/Library/JavascriptString.h b/lib/Runtime/Library/JavascriptString.h index 163f8a71cfa..3f69f21caf4 100644 --- a/lib/Runtime/Library/JavascriptString.h +++ b/lib/Runtime/Library/JavascriptString.h @@ -40,8 +40,10 @@ namespace Js JavascriptString(JavascriptString&) = delete; private: - Field(const char16*) m_pszValue; // Flattened, '\0' terminated contents + Field(const char16*) m_pszValue; // Flattened, '\0' terminated contents Field(charcount_t) m_charLength; // Length in characters, not including '\0'. + Field(codepoint_t*) m_codePointString; // Code points of the string, may be nullptr if not initialized yet by GetCodePoints call + Field(charcount_t) m_codePointsLength; // Length of the codepoints, may be k_InvalidCharCount if not initialized yet by GetCodePoints call static const charcount_t MaxCharLength = INT_MAX - 1; // Max number of chars not including '\0'. @@ -69,6 +71,8 @@ namespace Js const char16* UnsafeGetBuffer() const; LPCWSTR GetSzCopy(ArenaAllocator* alloc); // Copy to an Arena const char16* GetString(); // Get string, may not be NULL terminated + codepoint_t* GetCodePoints(); + charcount_t GetCodePointsLength(); // NumberUtil::FIntRadStrToDbl and parts of GlobalObject::EntryParseInt were refactored into ToInteger Var ToInteger(int radix = 0); diff --git a/lib/Runtime/Library/RegexHelper.cpp b/lib/Runtime/Library/RegexHelper.cpp index d20cdd470b2..930d45f76a0 100644 --- a/lib/Runtime/Library/RegexHelper.cpp +++ b/lib/Runtime/Library/RegexHelper.cpp @@ -2412,9 +2412,9 @@ namespace Js int64_t RegexHelper::AdvanceStringIndex(JavascriptString* string, int64_t index, bool isUnicode) { - if (string->GetLength() > (0 > index || (uint64_t)index + (uint64_t)1 > UINT32_MAX ? UINT32_MAX : (uint32_t)(index + 1)) && + if (isUnicode && string->GetLength() > (0 > index || (uint64_t)index + 1ui64 > 0xffffffffui64 ? UINT32_MAX : (uint32_t)(index + 1)) && NumberUtilities::IsSurrogateLowerPart(string->GetString()[index]) && - NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1]) && isUnicode) + NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1])) { return index + 2; } @@ -2424,9 +2424,9 @@ namespace Js CharCount RegexHelper::AdvanceStringIndex(JavascriptString* string, CharCount index, bool isUnicode) { - if (string->GetLength() > index + 1 && + if (isUnicode && string->GetLength() > index + 1 && NumberUtilities::IsSurrogateLowerPart(string->GetString()[index]) && - NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1]) && isUnicode) + NumberUtilities::IsSurrogateUpperPart(string->GetString()[index + 1])) { return JavascriptRegExp::AddIndex(index, 2); }