@@ -20,6 +20,25 @@ void EmitUseBeforeDeclaration(Symbol *sym, ByteCodeGenerator *byteCodeGenerator,
2020void EmitUseBeforeDeclarationRuntimeError (ByteCodeGenerator *byteCodeGenerator, Js::RegSlot location);
2121void VisitClearTmpRegs (ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator, FuncInfo * funcInfo);
2222
23+ /* *
24+ * This function generates the common code for null-propagation / optional-chaining.
25+ * This works similar to how the c# compiler emits byte-code for optional-chaining.
26+ */
27+ static void EmitNullPropagation (Js::RegSlot targetObjectSlot, ByteCodeGenerator *byteCodeGenerator, FuncInfo* funcInfo, bool isNullPropagating) {
28+ if (!isNullPropagating)
29+ return ;
30+
31+ Assert (funcInfo->currentOptionalChain != 0 );
32+
33+ Js::ByteCodeLabel continueLabel = byteCodeGenerator->Writer ()->DefineLabel ();
34+ byteCodeGenerator->Writer ()->BrReg1 (Js::OpCode::BrTrue_A, continueLabel, targetObjectSlot); // if (targetObject)
35+ {
36+ byteCodeGenerator->Writer ()->Reg1 (Js::OpCode::LdUndef, funcInfo->currentOptionalChain ->resultSlot ); // result = undefined
37+ byteCodeGenerator->Writer ()->Br (funcInfo->currentOptionalChain ->skipLabel );
38+ }
39+ byteCodeGenerator->Writer ()->MarkLabel (continueLabel);
40+ }
41+
2342bool CallTargetIsArray (ParseNode *pnode)
2443{
2544 return pnode->nop == knopName && pnode->AsParseNodeName ()->PropertyIdFromNameNode () == Js::PropertyIds::Array;
@@ -11596,12 +11615,35 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func
1159611615 funcInfo->ReleaseLoc (pnode->AsParseNodeBin ()->pnode1 );
1159711616 funcInfo->AcquireLoc (pnode);
1159811617
11618+ EmitNullPropagation (callObjLocation, byteCodeGenerator, funcInfo, pnode->AsParseNodeBin ()->isNullPropagating );
11619+
1159911620 byteCodeGenerator->Writer ()->Element (
1160011621 Js::OpCode::LdElemI_A, pnode->location , protoLocation, pnode->AsParseNodeBin ()->pnode2 ->location );
1160111622
11623+
1160211624 ENDSTATEMENET_IFTOPLEVEL (isTopLevel, pnode);
1160311625 break ;
1160411626 }
11627+
11628+ case knopOptChain: {
11629+ Js::ByteCodeLabel skipLabel = byteCodeGenerator->Writer ()->DefineLabel ();
11630+ Js::RegSlot targetRegSlot = funcInfo->AcquireLoc (pnode);
11631+
11632+ FuncInfo::OptionalChainInfo* previousChain = funcInfo->currentOptionalChain ;
11633+ FuncInfo::OptionalChainInfo currentOptionalChain = FuncInfo::OptionalChainInfo (skipLabel, targetRegSlot);
11634+ funcInfo->currentOptionalChain = ¤tOptionalChain;
11635+
11636+ ParseNodePtr innerNode = pnode->AsParseNodeUni ()->pnode1 ;
11637+ Emit (innerNode, byteCodeGenerator, funcInfo, false );
11638+
11639+ // Copy result
11640+ byteCodeGenerator->Writer ()->Reg2 (Js::OpCode::Ld_A, targetRegSlot, innerNode->location );
11641+ funcInfo->ReleaseLoc (innerNode);
11642+
11643+ byteCodeGenerator->Writer ()->MarkLabel (skipLabel);
11644+ funcInfo->currentOptionalChain = previousChain;
11645+ break ;
11646+ }
1160511647 // this is MemberExpression as rvalue
1160611648 case knopDot:
1160711649 {
@@ -11622,6 +11664,8 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func
1162211664 Js::PropertyId propertyId = pnode->AsParseNodeBin ()->pnode2 ->AsParseNodeName ()->PropertyIdFromNameNode ();
1162311665 uint cacheId = funcInfo->FindOrAddInlineCacheId (protoLocation, propertyId, false , false );
1162411666
11667+ EmitNullPropagation (callObjLocation, byteCodeGenerator, funcInfo, pnode->AsParseNodeBin ()->isNullPropagating );
11668+
1162511669 if (propertyId == Js::PropertyIds::length)
1162611670 {
1162711671 byteCodeGenerator->Writer ()->PatchableProperty (Js::OpCode::LdLen_A, pnode->location , protoLocation, cacheId);
0 commit comments