Skip to content

Commit 9369845

Browse files
Fix this propagation
No copy of expression result needed anymore
1 parent 6e7d935 commit 9369845

File tree

1 file changed

+46
-27
lines changed

1 file changed

+46
-27
lines changed

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "Language/AsmJs.h"
99
#include "ConfigFlagsList.h"
1010

11+
void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue, bool isConstructorCall = false, bool isTopLevel = false);
1112
void EmitReference(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
1213
void EmitAssignment(ParseNode *asgnNode, ParseNode *lhs, Js::RegSlot rhsLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
1314
void EmitLoad(ParseNode *rhs, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
@@ -26,6 +27,7 @@ void VisitClearTmpRegs(ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator,
2627
///
2728
/// It should be called on every <c>?.</c> location.
2829
/// A call to this function is only valid from a node-emission inside a `knopOptChain` node.
30+
/// See EmitOptionalChain.
2931
/// </summary>
3032
static void EmitNullPropagation(Js::RegSlot targetObjectSlot, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, bool isNullPropagating) {
3133
if (!isNullPropagating)
@@ -41,6 +43,39 @@ static void EmitNullPropagation(Js::RegSlot targetObjectSlot, ByteCodeGenerator
4143
);
4244
}
4345

46+
/// <summary>
47+
/// The whole optional-chain expression will be wrapped in a UniNode with `knopOptChain`.
48+
/// Use this function to emit the whole expression.
49+
/// </summary>
50+
template <class TEmitProc>
51+
static void EmitOptionalChainWrapper(ParseNodeUni *pnodeOptChain, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, TEmitProc emitChainContent) {
52+
Assert(knopOptChain == pnodeOptChain->nop);
53+
54+
Js::ByteCodeLabel previousSkipLabel = funcInfo->currentOptionalChainSkipLabel;
55+
56+
// Create a label that can skip the whole chain and store it in `funcInfo`
57+
Js::ByteCodeLabel skipLabel = byteCodeGenerator->Writer()->DefineLabel();
58+
funcInfo->currentOptionalChainSkipLabel = skipLabel;
59+
60+
// Acquire slot for the result value
61+
// Prefill it with `undefined` (Fallback for short-circuiting)
62+
Js::RegSlot resultSlot = funcInfo->AcquireLoc(pnodeOptChain);
63+
byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, resultSlot);
64+
65+
// Copy values from wrapper to inner expression
66+
ParseNodePtr innerNode = pnodeOptChain->pnode1;
67+
innerNode->isUsed = pnodeOptChain->isUsed;
68+
innerNode->location = pnodeOptChain->location;
69+
70+
// emit chain expression
71+
// Every `?.` node will call `EmitNullPropagation`
72+
// `EmitNullPropagation` short-circuits to `skipLabel` in case of a nullish value
73+
emitChainContent(innerNode);
74+
75+
byteCodeGenerator->Writer()->MarkLabel(skipLabel);
76+
funcInfo->currentOptionalChainSkipLabel = previousSkipLabel;
77+
}
78+
4479
bool CallTargetIsArray(ParseNode *pnode)
4580
{
4681
return pnode->nop == knopName && pnode->AsParseNodeName()->PropertyIdFromNameNode() == Js::PropertyIds::Array;
@@ -272,7 +307,6 @@ bool IsArguments(ParseNode *pnode)
272307
}
273308

274309
bool ApplyEnclosesArgs(ParseNode* fncDecl, ByteCodeGenerator* byteCodeGenerator);
275-
void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, BOOL fReturnValue, bool isConstructorCall = false, bool isTopLevel = false);
276310
void EmitBinaryOpnds(ParseNode* pnode1, ParseNode* pnode2, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Js::RegSlot computedPropertyLocation = Js::Constants::NoRegister, bool isNullPropagating = false);
277311
bool IsExpressionStatement(ParseNode* stmt, const Js::ScriptContext *const scriptContext);
278312
void EmitInvoke(Js::RegSlot location, Js::RegSlot callObjLocation, Js::PropertyId propertyId, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo);
@@ -8078,6 +8112,12 @@ void EmitCallTarget(
80788112

80798113
switch (pnodeTarget->nop)
80808114
{
8115+
case knopOptChain: {
8116+
EmitOptionalChainWrapper(pnodeTarget->AsParseNodeUni(), byteCodeGenerator, funcInfo, [&](ParseNodePtr innerNode) {
8117+
EmitCallTarget(innerNode, fSideEffectArgs, thisLocation, releaseThisLocation, callObjLocation, byteCodeGenerator, funcInfo, callApplyCallSiteId);
8118+
});
8119+
break;
8120+
}
80818121
case knopDot:
80828122
{
80838123
ParseNodeBin * pnodeBinTarget = pnodeTarget->AsParseNodeBin();
@@ -11632,34 +11672,13 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func
1163211672
ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
1163311673
break;
1163411674
}
11635-
// The whole optional-chain expression will be wrapped in a UniNode with `knopOptChain`.
11636-
case knopOptChain: {
11637-
Js::ByteCodeLabel previousSkipLabel = funcInfo->currentOptionalChainSkipLabel;
11638-
11639-
// Create a label that can skip the whole chain and store in `funcInfo`
11640-
Js::ByteCodeLabel skipLabel = byteCodeGenerator->Writer()->DefineLabel();
11641-
funcInfo->currentOptionalChainSkipLabel = skipLabel;
11642-
11643-
// Acquire slot for the result value
11644-
// Prefill it with `undefined` (Fallback for short-circuiting)
11645-
Js::RegSlot resultSlot = funcInfo->AcquireLoc(pnode);
11646-
byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, resultSlot);
1164711675

11648-
// emit chain expression
11649-
// Every `?.` node will call `EmitNullPropagation`
11650-
// `EmitNullPropagation` short-circuits to `skipLabel` in case of a nullish value
11651-
ParseNodePtr innerNode = pnode->AsParseNodeUni()->pnode1;
11652-
Emit(innerNode, byteCodeGenerator, funcInfo, false);
11653-
11654-
// Copy the expression result
11655-
// Only reached if we did not short-circuit
11656-
byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, resultSlot, innerNode->location);
11657-
funcInfo->ReleaseLoc(innerNode);
11658-
11659-
byteCodeGenerator->Writer()->MarkLabel(skipLabel);
11660-
funcInfo->currentOptionalChainSkipLabel = previousSkipLabel;
11676+
case knopOptChain:
11677+
EmitOptionalChainWrapper(pnode->AsParseNodeUni(), byteCodeGenerator, funcInfo, [&](ParseNodePtr innerNode) {
11678+
Emit(innerNode, byteCodeGenerator, funcInfo, false);
11679+
});
1166111680
break;
11662-
}
11681+
1166311682
// this is MemberExpression as rvalue
1166411683
case knopDot:
1166511684
{

0 commit comments

Comments
 (0)