Skip to content

Commit 2498b2d

Browse files
Comments and review
1 parent 9e11b34 commit 2498b2d

File tree

4 files changed

+30
-26
lines changed

4 files changed

+30
-26
lines changed

lib/Parser/Parse.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4192,19 +4192,20 @@ ParseNodePtr Parser::ParsePostfixOperators(
41924192
ParseNodePtr name = nullptr;
41934193
OpCode opCode = knopDot;
41944194

4195+
// We don't use separate knops for optional-chains
4196+
// Instead mark nodes as null-propagating
41954197
bool isNullPropagating = tkOptChain == m_token.tk;
41964198
if (isNullPropagating)
41974199
{
41984200
isOptionalChain = true;
41994201
}
4200-
// We don't use a custom token but rather tell that knopDot is null-propagating
4201-
// opCode = knopOptChain;
42024202

42034203
this->GetScanner()->Scan();
42044204
if (!m_token.IsIdentifier())
42054205
{
42064206
if (isNullPropagating)
42074207
{
4208+
// We don't need an identifier for an Index `?.[` or Call `?.(`
42084209
switch (m_token.tk)
42094210
{
42104211
case tkLParen:
@@ -4316,7 +4317,7 @@ ParseNodePtr Parser::ParsePostfixOperators(
43164317
default:
43174318
if (buildAST && isOptionalChain)
43184319
{
4319-
// Wrap as optional chain
4320+
// Wrap the whole expression as an optional-chain
43204321
return CreateUniNode(knopOptChain, pnode);
43214322
}
43224323
return pnode;

lib/Runtime/ByteCode/ByteCodeEmitter.cpp

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,23 @@ void EmitUseBeforeDeclaration(Symbol *sym, ByteCodeGenerator *byteCodeGenerator,
2020
void EmitUseBeforeDeclarationRuntimeError(ByteCodeGenerator *byteCodeGenerator, Js::RegSlot location);
2121
void VisitClearTmpRegs(ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator, FuncInfo * funcInfo);
2222

23-
/**
24-
* This function generates the common code for null-propagation / optional-chaining.
25-
*/
23+
/// <summary>
24+
/// This function generates the common code for null-propagation / optional-chaining.
25+
/// If the targetObject is nullish this will short-circuit(skip) to the end of the chain-expression.
26+
///
27+
/// It should be called on every <c>?.</c> location.
28+
/// A call to this function is only valid from a node-emission inside a `knopOptChain` node.
29+
/// </summary>
2630
static void EmitNullPropagation(Js::RegSlot targetObjectSlot, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, bool isNullPropagating) {
2731
if (!isNullPropagating)
2832
return;
2933

30-
Assert(funcInfo->currentOptionalChain != 0);
34+
// Ensure we've setup the skipLabel in the emission of a `knopOptChain`
35+
Assert(funcInfo->currentOptionalChainSkipLabel >= 0);
3136

32-
// if (targetObject == null) goto chainEnd;
37+
// if (targetObject == null) goto skipLabel;
3338
byteCodeGenerator->Writer()->BrReg2(
34-
Js::OpCode::BrEq_A, funcInfo->currentOptionalChain->skipLabel,
39+
Js::OpCode::BrEq_A, funcInfo->currentOptionalChainSkipLabel,
3540
targetObjectSlot, funcInfo->nullConstantRegister
3641
);
3742
}
@@ -11621,31 +11626,35 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func
1162111626
byteCodeGenerator->Writer()->Element(
1162211627
Js::OpCode::LdElemI_A, pnode->location, protoLocation, pnode->AsParseNodeBin()->pnode2->location);
1162311628

11624-
1162511629
ENDSTATEMENET_IFTOPLEVEL(isTopLevel, pnode);
1162611630
break;
1162711631
}
11628-
11632+
// The whole optional-chain expression will be wrapped in a UniNode with `knopOptChain`.
1162911633
case knopOptChain: {
11630-
FuncInfo::OptionalChainInfo *previousChain = funcInfo->currentOptionalChain;
11634+
Js::ByteCodeLabel previousSkipLabel = funcInfo->currentOptionalChainSkipLabel;
1163111635

11636+
// Create a label that can skip the whole chain and store in `funcInfo`
1163211637
Js::ByteCodeLabel skipLabel = byteCodeGenerator->Writer()->DefineLabel();
11633-
FuncInfo::OptionalChainInfo currentOptionalChain = FuncInfo::OptionalChainInfo(skipLabel);
11634-
funcInfo->currentOptionalChain = &currentOptionalChain;
11638+
funcInfo->currentOptionalChainSkipLabel = skipLabel;
1163511639

11640+
// Acquire slot for the result value
11641+
// Prefill it with `undefined` (Fallback for short-circuiting)
1163611642
Js::RegSlot resultSlot = funcInfo->AcquireLoc(pnode);
11637-
byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, resultSlot); // result = undefined
11643+
byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdUndef, resultSlot);
1163811644

11639-
// emit chain
11645+
// emit chain expression
11646+
// Every `?.` node will call `EmitNullPropagation`
11647+
// `EmitNullPropagation` short-circuits to `skipLabel` in case of a nullish value
1164011648
ParseNodePtr innerNode = pnode->AsParseNodeUni()->pnode1;
1164111649
Emit(innerNode, byteCodeGenerator, funcInfo, false);
1164211650

11643-
// Copy result
11651+
// Copy the expression result
11652+
// Only reached if we did not short-circuit
1164411653
byteCodeGenerator->Writer()->Reg2(Js::OpCode::Ld_A, resultSlot, innerNode->location);
1164511654
funcInfo->ReleaseLoc(innerNode);
1164611655

1164711656
byteCodeGenerator->Writer()->MarkLabel(skipLabel);
11648-
funcInfo->currentOptionalChain = previousChain;
11657+
funcInfo->currentOptionalChainSkipLabel = previousSkipLabel;
1164911658
break;
1165011659
}
1165111660
// this is MemberExpression as rvalue

lib/Runtime/ByteCode/FuncInfo.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ FuncInfo::FuncInfo(
3434
outArgsCurrentExpr(0),
3535
innerScopeCount(0),
3636
currentInnerScopeIndex((uint)-1),
37-
currentOptionalChain(nullptr),
37+
currentOptionalChainSkipLabel(-1),
3838
#if DBG
3939
outArgsDepth(0),
4040
#endif

lib/Runtime/ByteCode/FuncInfo.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,7 @@ class FuncInfo
9696
Js::RegSlot outArgsCurrentExpr; // max number of out args accumulated in the current nested expression
9797
uint innerScopeCount;
9898
uint currentInnerScopeIndex;
99-
struct OptionalChainInfo {
100-
Js::ByteCodeLabel skipLabel;
101-
102-
OptionalChainInfo(Js::ByteCodeLabel skipLabel) {
103-
this->skipLabel = skipLabel;
104-
}
105-
} *currentOptionalChain;
99+
Js::ByteCodeLabel currentOptionalChainSkipLabel;
106100
#if DBG
107101
uint32 outArgsDepth; // number of calls nested in an expression
108102
#endif

0 commit comments

Comments
 (0)