8
8
#include " Language/AsmJs.h"
9
9
#include " ConfigFlagsList.h"
10
10
11
+ void Emit (ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, BOOL fReturnValue , bool isConstructorCall = false , bool isTopLevel = false );
11
12
void EmitReference (ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
12
13
void EmitAssignment (ParseNode *asgnNode, ParseNode *lhs, Js::RegSlot rhsLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
13
14
void EmitLoad (ParseNode *rhs, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
@@ -26,6 +27,7 @@ void VisitClearTmpRegs(ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator,
26
27
// /
27
28
// / It should be called on every <c>?.</c> location.
28
29
// / A call to this function is only valid from a node-emission inside a `knopOptChain` node.
30
+ // / See EmitOptionalChain.
29
31
// / </summary>
30
32
static void EmitNullPropagation (Js::RegSlot targetObjectSlot, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo, bool isNullPropagating) {
31
33
if (!isNullPropagating)
@@ -41,6 +43,39 @@ static void EmitNullPropagation(Js::RegSlot targetObjectSlot, ByteCodeGenerator
41
43
);
42
44
}
43
45
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
+
44
79
bool CallTargetIsArray (ParseNode *pnode)
45
80
{
46
81
return pnode->nop == knopName && pnode->AsParseNodeName ()->PropertyIdFromNameNode () == Js::PropertyIds::Array;
@@ -272,7 +307,6 @@ bool IsArguments(ParseNode *pnode)
272
307
}
273
308
274
309
bool ApplyEnclosesArgs (ParseNode* fncDecl, ByteCodeGenerator* byteCodeGenerator);
275
- void Emit (ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, BOOL fReturnValue , bool isConstructorCall = false , bool isTopLevel = false );
276
310
void EmitBinaryOpnds (ParseNode* pnode1, ParseNode* pnode2, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo, Js::RegSlot computedPropertyLocation = Js::Constants::NoRegister, bool isNullPropagating = false );
277
311
bool IsExpressionStatement (ParseNode* stmt, const Js::ScriptContext *const scriptContext);
278
312
void EmitInvoke (Js::RegSlot location, Js::RegSlot callObjLocation, Js::PropertyId propertyId, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo);
@@ -8078,6 +8112,12 @@ void EmitCallTarget(
8078
8112
8079
8113
switch (pnodeTarget->nop )
8080
8114
{
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
+ }
8081
8121
case knopDot:
8082
8122
{
8083
8123
ParseNodeBin * pnodeBinTarget = pnodeTarget->AsParseNodeBin ();
@@ -11632,34 +11672,13 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func
11632
11672
ENDSTATEMENET_IFTOPLEVEL (isTopLevel, pnode);
11633
11673
break ;
11634
11674
}
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);
11647
11675
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
+ });
11661
11680
break ;
11662
- }
11681
+
11663
11682
// this is MemberExpression as rvalue
11664
11683
case knopDot:
11665
11684
{
0 commit comments