Skip to content

Commit 0f600da

Browse files
committed
[MERGE #5498 @huaiyudavid] Implement Object Rest
Merge pull request #5498 from huaiyudavid:Object_Rest - Currently defaults to off under the ES2018ObjectRestSpread flag. - Parser changes: - Added ParseNodeObjLit as new type of node used to store info for Rest - Bytecode changes: - Added 3 new bytecodes: Restify, NewPropIdArr, and StPropIdArrFromVar - Restify: copies leftover properties from the source object to a new empty object (runtime semantics are equivalent to Object Spread) - NewPropIdArr: creates a new Recycler allocated array to store computed properties' ids (for keeping track of which properties have already been used) - StPropIdArrFromVar: converts a computed property string (stored in a Var) into a property id and stores it in the heap allocated array at the specified index - ByteCodeEmitter only emits the above bytecodes when Rest is seen in the object pattern. This simplifies destructuring if rest is not used (and removes need for Recycler allocated array). The array is also not allocated if no computed properties are seen. - JIT changes: - All new bytecodes are changed to their respective helper calls - Fixed typo in JavascriptString - Added unit tests for Object Rest - test262 results for feature "object-rest": - All tests passed except for async-gen and async related tests
2 parents 2b7ad65 + c2d084d commit 0f600da

28 files changed

+907
-114
lines changed

lib/Backend/IRBuilder.cpp

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,12 +2303,35 @@ void
23032303
IRBuilder::BuildReg4(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSlot, Js::RegSlot src1RegSlot,
23042304
Js::RegSlot src2RegSlot, Js::RegSlot src3RegSlot)
23052305
{
2306-
IR::Instr * instr;
2307-
Assert(newOpcode == Js::OpCode::Concat3);
2306+
IR::Instr * instr = nullptr;
2307+
Assert(newOpcode == Js::OpCode::Concat3 || newOpcode == Js::OpCode::Restify);
23082308

23092309
IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot);
23102310
IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot);
2311-
IR::RegOpnd * src3Opnd = this->BuildSrcOpnd(src3RegSlot);
2311+
IR::RegOpnd * src3Opnd = this->BuildSrcOpnd(src3RegSlot);
2312+
2313+
if (newOpcode == Js::OpCode::Restify)
2314+
{
2315+
IR::RegOpnd * src0Opnd = this->BuildSrcOpnd(dstRegSlot);
2316+
instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src3Opnd, m_func);
2317+
this->AddInstr(instr, offset);
2318+
2319+
instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src2Opnd, instr->GetDst(), m_func);
2320+
this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
2321+
2322+
instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, instr->GetDst(), m_func);
2323+
this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
2324+
2325+
instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src0Opnd, instr->GetDst(), m_func);
2326+
this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
2327+
2328+
IR::Opnd *firstArg = instr->GetDst();
2329+
instr = IR::Instr::New(newOpcode, m_func);
2330+
instr->SetSrc1(firstArg);
2331+
this->AddInstr(instr, Js::Constants::NoByteCodeOffset);
2332+
return;
2333+
}
2334+
23122335
IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot);
23132336

23142337
IR::RegOpnd * str1Opnd = InsertConvPrimStr(src1Opnd, offset, true);
@@ -2997,6 +3020,7 @@ IRBuilder::BuildReg1Unsigned1(Js::OpCode newOpcode, uint offset, Js::RegSlot R0,
29973020
dstOpnd->SetValueTypeFixed();
29983021
}
29993022
}
3023+
30003024
///----------------------------------------------------------------------------
30013025
///
30023026
/// IRBuilder::BuildReg2Int1
@@ -3424,6 +3448,29 @@ IRBuilder::BuildElementSlot(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fie
34243448
}
34253449
break;
34263450

3451+
case Js::OpCode::StPropIdArrFromVar:
3452+
{
3453+
IR::RegOpnd * src0Opnd = this->BuildSrcOpnd(fieldRegSlot);
3454+
IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(regSlot);
3455+
IntConstType value = slotId;
3456+
IR::IntConstOpnd * valOpnd = IR::IntConstOpnd::New(value, TyInt32, m_func);
3457+
3458+
instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src1Opnd, m_func);
3459+
this->AddInstr(instr, offset);
3460+
offset = Js::Constants::NoByteCodeOffset;
3461+
3462+
instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), valOpnd, instr->GetDst(), m_func);
3463+
this->AddInstr(instr, offset);
3464+
3465+
instr = IR::Instr::New(Js::OpCode::ExtendArg_A, IR::RegOpnd::New(TyVar, m_func), src0Opnd, instr->GetDst(), m_func);
3466+
this->AddInstr(instr, offset);
3467+
3468+
IR::Opnd * firstArg = instr->GetDst();
3469+
instr = IR::Instr::New(newOpcode, m_func);
3470+
instr->SetSrc1(firstArg);
3471+
break;
3472+
}
3473+
34273474
default:
34283475
AssertMsg(UNREACHED, "Unknown ElementSlot opcode");
34293476
Fatal();

lib/Backend/JnHelperMethodList.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,10 @@ HELPERCALL(SpreadArrayLiteral, Js::JavascriptArray::SpreadArrayArgs, 0)
513513
HELPERCALL(SpreadCall, Js::JavascriptFunction::EntrySpreadCall, 0)
514514

515515
HELPERCALL(SpreadObjectLiteral, Js::JavascriptObject::SpreadObjectLiteral, 0)
516+
HELPERCALL(Restify, Js::JavascriptObject::Restify, 0)
517+
HELPERCALL(NewPropIdArrForCompProps, Js::InterpreterStackFrame::OP_NewPropIdArrForCompProps, AttrCanNotBeReentrant)
518+
HELPERCALL(StPropIdArrFromVar, Js::InterpreterStackFrame::OP_StPropIdArrFromVar, 0)
519+
516520

517521
HELPERCALLCHK(LdHomeObj, Js::JavascriptOperators::OP_LdHomeObj, AttrCanNotBeReentrant)
518522
HELPERCALLCHK(LdFuncObj, Js::JavascriptOperators::OP_LdFuncObj, AttrCanNotBeReentrant)

lib/Backend/Lower.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3094,6 +3094,18 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
30943094
this->LowerBinaryHelperMem(instr, IR::HelperSpreadObjectLiteral);
30953095
break;
30963096

3097+
case Js::OpCode::Restify:
3098+
instrPrev = this->LowerRestify(instr);
3099+
break;
3100+
3101+
case Js::OpCode::NewPropIdArrForCompProps:
3102+
this->LowerUnaryHelperMem(instr, IR::HelperNewPropIdArrForCompProps);
3103+
break;
3104+
3105+
case Js::OpCode::StPropIdArrFromVar:
3106+
instrPrev = this->LowerStPropIdArrFromVar(instr);
3107+
break;
3108+
30973109
default:
30983110
#ifdef ENABLE_WASM_SIMD
30993111
if (IsSimd128Opcode(instr->m_opcode))
@@ -6872,6 +6884,30 @@ Lowerer::LowerNewScGenFuncHomeObj(IR::Instr * newScFuncInstr)
68726884
return newScFuncInstr;
68736885
}
68746886

6887+
IR::Instr *
6888+
Lowerer::LowerStPropIdArrFromVar(IR::Instr * stPropIdInstr)
6889+
{
6890+
IR::HelperCallOpnd *helperOpnd = IR::HelperCallOpnd::New(IR::HelperStPropIdArrFromVar, this->m_func);
6891+
6892+
IR::Opnd * src1 = stPropIdInstr->UnlinkSrc1();
6893+
stPropIdInstr->SetSrc1(helperOpnd);
6894+
stPropIdInstr->SetSrc2(src1);
6895+
6896+
return m_lowererMD.LowerCallHelper(stPropIdInstr);
6897+
}
6898+
6899+
IR::Instr *
6900+
Lowerer::LowerRestify(IR::Instr * newRestInstr)
6901+
{
6902+
IR::HelperCallOpnd *helperOpnd = IR::HelperCallOpnd::New(IR::HelperRestify, this->m_func);
6903+
6904+
IR::Opnd * src1 = newRestInstr->UnlinkSrc1();
6905+
newRestInstr->SetSrc1(helperOpnd);
6906+
newRestInstr->SetSrc2(src1);
6907+
6908+
return m_lowererMD.LowerCallHelper(newRestInstr);
6909+
}
6910+
68756911
///----------------------------------------------------------------------------
68766912
///
68776913
/// Lowerer::LowerScopedLdFld
@@ -8615,7 +8651,7 @@ Lowerer::LowerBinaryHelper(IR::Instr *instr, IR::JnHelperMethod helperMethod)
86158651
// instrPrev.
86168652
IR::Instr *instrPrev = nullptr;
86178653

8618-
AssertMsg((Js::OpCodeUtil::GetOpCodeLayout(instr->m_opcode) == Js::OpLayoutType::Reg1Unsigned1 && !instr->GetDst()) ||
8654+
AssertMsg((Js::OpCodeUtil::GetOpCodeLayout(instr->m_opcode) == Js::OpLayoutType::Reg1Unsigned1) ||
86198655
Js::OpCodeUtil::GetOpCodeLayout(instr->m_opcode) == Js::OpLayoutType::Reg3 ||
86208656
Js::OpCodeUtil::GetOpCodeLayout(instr->m_opcode) == Js::OpLayoutType::Reg2 ||
86218657
Js::OpCodeUtil::GetOpCodeLayout(instr->m_opcode) == Js::OpLayoutType::Reg2Int1 ||

lib/Backend/Lower.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ class Lowerer
144144
IR::Instr * LowerNewScGenFunc(IR::Instr *instr);
145145
IR::Instr * LowerNewScFuncHomeObj(IR::Instr *instr);
146146
IR::Instr * LowerNewScGenFuncHomeObj(IR::Instr *instr);
147+
IR::Instr * LowerStPropIdArrFromVar(IR::Instr *instr);
148+
IR::Instr * LowerRestify(IR::Instr *instr);
147149
IR::Instr* GenerateCompleteStFld(IR::Instr* instr, bool emitFastPath, IR::JnHelperMethod monoHelperAfterFastPath, IR::JnHelperMethod polyHelperAfterFastPath,
148150
IR::JnHelperMethod monoHelperWithoutFastPath, IR::JnHelperMethod polyHelperWithoutFastPath, bool withPutFlags, Js::PropertyOperationFlags flags);
149151
bool GenerateStFldWithCachedType(IR::Instr * instrStFld, bool* continueAsHelperOut, IR::LabelInstr** labelHelperOut, IR::RegOpnd** typeOpndOut);

lib/Backend/LowerMDShared.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,18 @@ LowererMD::LowerCallHelper(IR::Instr *instrCall)
191191
IR::JnHelperMethod helperMethod = instrCall->GetSrc1()->AsHelperCallOpnd()->m_fnHelper;
192192

193193
instrCall->FreeSrc1();
194-
194+
195195
#ifndef _M_X64
196+
bool callHasDst = instrCall->GetDst() != nullptr;
196197
prevInstr = ChangeToHelperCall(instrCall, helperMethod);
197-
#endif
198-
198+
if (callHasDst)
199+
{
200+
prevInstr = prevInstr->m_prev;
201+
}
202+
Assert(prevInstr->GetSrc1()->IsHelperCallOpnd() && prevInstr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper == helperMethod);
203+
#else
199204
prevInstr = instrCall;
205+
#endif
200206

201207
while (argOpnd)
202208
{
@@ -206,11 +212,14 @@ LowererMD::LowerCallHelper(IR::Instr *instrCall)
206212
Assert(regArg->m_sym->m_isSingleDef);
207213
IR::Instr *instrArg = regArg->m_sym->m_instrDef;
208214

209-
Assert(instrArg->m_opcode == Js::OpCode::ArgOut_A ||
210-
(helperMethod == IR::JnHelperMethod::HelperOP_InitCachedScope && instrArg->m_opcode == Js::OpCode::ExtendArg_A) ||
211-
(helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScFuncHomeObj && instrArg->m_opcode == Js::OpCode::ExtendArg_A) ||
212-
(helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScGenFuncHomeObj && instrArg->m_opcode == Js::OpCode::ExtendArg_A)
213-
);
215+
Assert(instrArg->m_opcode == Js::OpCode::ArgOut_A || instrArg->m_opcode == Js::OpCode::ExtendArg_A &&
216+
(
217+
helperMethod == IR::JnHelperMethod::HelperOP_InitCachedScope ||
218+
helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScFuncHomeObj ||
219+
helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScGenFuncHomeObj ||
220+
helperMethod == IR::JnHelperMethod::HelperRestify ||
221+
helperMethod == IR::JnHelperMethod::HelperStPropIdArrFromVar
222+
));
214223
prevInstr = LoadHelperArgument(prevInstr, instrArg->GetSrc1());
215224

216225
argOpnd = instrArg->GetSrc2();

lib/Backend/arm/LowerMD.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,14 @@ LowererMD::LowerCallHelper(IR::Instr *instrCall)
209209
Assert(regArg->m_sym->m_isSingleDef);
210210
IR::Instr *instrArg = regArg->m_sym->m_instrDef;
211211

212-
Assert(instrArg->m_opcode == Js::OpCode::ArgOut_A ||
213-
(helperMethod == IR::JnHelperMethod::HelperOP_InitCachedScope && instrArg->m_opcode == Js::OpCode::ExtendArg_A) ||
214-
(helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScFuncHomeObj && instrArg->m_opcode == Js::OpCode::ExtendArg_A) ||
215-
(helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScGenFuncHomeObj && instrArg->m_opcode == Js::OpCode::ExtendArg_A));
212+
Assert(instrArg->m_opcode == Js::OpCode::ArgOut_A || instrArg->m_opcode == Js::OpCode::ExtendArg_A &&
213+
(
214+
helperMethod == IR::JnHelperMethod::HelperOP_InitCachedScope ||
215+
helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScFuncHomeObj ||
216+
helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScGenFuncHomeObj ||
217+
helperMethod == IR::JnHelperMethod::HelperRestify ||
218+
helperMethod == IR::JnHelperMethod::HelperStPropIdArrFromVar
219+
));
216220
prevInstr = this->LoadHelperArgument(prevInstr, instrArg->GetSrc1());
217221

218222
argOpnd = instrArg->GetSrc2();

lib/Backend/arm64/LowerMD.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,14 @@ LowererMD::LowerCallHelper(IR::Instr *instrCall)
223223
Assert(regArg->m_sym->m_isSingleDef);
224224
IR::Instr *instrArg = regArg->m_sym->m_instrDef;
225225

226-
Assert(instrArg->m_opcode == Js::OpCode::ArgOut_A ||
227-
(helperMethod == IR::JnHelperMethod::HelperOP_InitCachedScope && instrArg->m_opcode == Js::OpCode::ExtendArg_A) ||
228-
(helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScFuncHomeObj && instrArg->m_opcode == Js::OpCode::ExtendArg_A) ||
229-
(helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScGenFuncHomeObj && instrArg->m_opcode == Js::OpCode::ExtendArg_A));
226+
Assert(instrArg->m_opcode == Js::OpCode::ArgOut_A || instrArg->m_opcode == Js::OpCode::ExtendArg_A &&
227+
(
228+
helperMethod == IR::JnHelperMethod::HelperOP_InitCachedScope ||
229+
helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScFuncHomeObj ||
230+
helperMethod == IR::JnHelperMethod::HelperScrFunc_OP_NewScGenFuncHomeObj ||
231+
helperMethod == IR::JnHelperMethod::HelperRestify ||
232+
helperMethod == IR::JnHelperMethod::HelperStPropIdArrFromVar
233+
));
230234
prevInstr = this->LoadHelperArgument(prevInstr, instrArg->GetSrc1());
231235

232236
argOpnd = instrArg->GetSrc2();

lib/Common/ConfigFlagsList.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ PHASE(All)
626626
#define DEFAULT_CONFIG_ES6Spread (true)
627627
#define DEFAULT_CONFIG_ES6String (true)
628628
#define DEFAULT_CONFIG_ES6StringPrototypeFixes (true)
629-
#define DEFAULT_CONFIG_ES2018ObjectSpread (false)
629+
#define DEFAULT_CONFIG_ES2018ObjectRestSpread (false)
630630
#ifdef COMPILE_DISABLE_ES6PrototypeChain
631631
// If ES6PrototypeChain needs to be disabled by compile flag, DEFAULT_CONFIG_ES6PrototypeChain should be false
632632
#define DEFAULT_CONFIG_ES6PrototypeChain (false)
@@ -1122,7 +1122,7 @@ FLAGPR (Boolean, ES6, ES6Rest , "Enable ES6 Rest parame
11221122
FLAGPR (Boolean, ES6, ES6Spread , "Enable ES6 Spread support" , DEFAULT_CONFIG_ES6Spread)
11231123
FLAGPR (Boolean, ES6, ES6String , "Enable ES6 String extensions" , DEFAULT_CONFIG_ES6String)
11241124
FLAGPR (Boolean, ES6, ES6StringPrototypeFixes, "Enable ES6 String.prototype fixes" , DEFAULT_CONFIG_ES6StringPrototypeFixes)
1125-
FLAGPR (Boolean, ES6, ES2018ObjectSpread , "Enable ES2018 Object Spread" , DEFAULT_CONFIG_ES2018ObjectSpread)
1125+
FLAGPR (Boolean, ES6, ES2018ObjectRestSpread , "Enable ES2018 Object Rest/Spread" , DEFAULT_CONFIG_ES2018ObjectRestSpread)
11261126

11271127
#ifndef COMPILE_DISABLE_ES6PrototypeChain
11281128
#define COMPILE_DISABLE_ES6PrototypeChain 0

0 commit comments

Comments
 (0)