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 );
1112void EmitReference (ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
1213void EmitAssignment (ParseNode *asgnNode, ParseNode *lhs, Js::RegSlot rhsLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
1314void 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>
3032static 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+
4479bool 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
274309bool ApplyEnclosesArgs (ParseNode* fncDecl, ByteCodeGenerator* byteCodeGenerator);
275- void Emit (ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, BOOL fReturnValue , bool isConstructorCall = false , bool isTopLevel = false );
276310void EmitBinaryOpnds (ParseNode* pnode1, ParseNode* pnode2, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Js::RegSlot computedPropertyLocation = Js::Constants::NoRegister, bool isNullPropagating = false );
277311bool IsExpressionStatement (ParseNode* stmt, const Js::ScriptContext *const scriptContext);
278312void 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