virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { static const FBoolConfigValueHelper ExecutionAfterReturn(TEXT("Kismet"), TEXT("bExecutionAfterReturn"), GEngineIni); if (ExecutionAfterReturn) { // for backward compatibility only FKCHandler_VariableSet::Compile(Context, Node); } else { GenerateAssigments(Context, Node); if (Context.bCreateDebugData && Node) { FBlueprintCompiledStatement& TraceStatement = Context.AppendStatementForNode(Node); TraceStatement.Type = KCST_WireTraceSite; TraceStatement.Comment = Node->NodeComment.IsEmpty() ? Node->GetName() : Node->NodeComment; } // always go to return FBlueprintCompiledStatement& GotoStatement = Context.AppendStatementForNode(Node); GotoStatement.Type = KCST_GotoReturn; } }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { // For imperative nodes, make sure the exec function was actually triggered and not just included due to an output data dependency FEdGraphPinType ExpectedExecPinType; ExpectedExecPinType.PinCategory = UEdGraphSchema_K2::PC_Exec; FEdGraphPinType ExpectedBoolPinType; ExpectedBoolPinType.PinCategory = UEdGraphSchema_K2::PC_Boolean; UEdGraphPin* ExecTriggeringPin = Context.FindRequiredPinByName(Node, UEdGraphSchema_K2::PN_Execute, EGPD_Input); if ((ExecTriggeringPin == NULL) || !Context.ValidatePinType(ExecTriggeringPin, ExpectedExecPinType)) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("NoValidExecutionPinForBranch_Error", "@@ must have a valid execution pin @@").ToString()), Node, ExecTriggeringPin); return; } else if (ExecTriggeringPin->LinkedTo.Num() == 0) { CompilerContext.MessageLog.Warning(*FString::Printf(*LOCTEXT("NodeNeverExecuted_Warning", "@@ will never be executed").ToString()), Node); return; } // Generate the output impulse from this node UEdGraphPin* CondPin = Context.FindRequiredPinByName(Node, CompilerContext.GetSchema()->PN_Condition, EGPD_Input); UEdGraphPin* ThenPin = Context.FindRequiredPinByName(Node, CompilerContext.GetSchema()->PN_Then, EGPD_Output); UEdGraphPin* ElsePin = Context.FindRequiredPinByName(Node, CompilerContext.GetSchema()->PN_Else, EGPD_Output); if (Context.ValidatePinType(ThenPin, ExpectedExecPinType) && Context.ValidatePinType(ElsePin, ExpectedExecPinType) && Context.ValidatePinType(CondPin, ExpectedBoolPinType)) { UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(CondPin); FBPTerminal** CondTerm = Context.NetMap.Find(PinToTry); if (CondTerm != NULL) // { // First skip the if, if the term is false { FBlueprintCompiledStatement& SkipIfGoto = Context.AppendStatementForNode(Node); SkipIfGoto.Type = KCST_GotoIfNot; SkipIfGoto.LHS = *CondTerm; Context.GotoFixupRequestMap.Add(&SkipIfGoto, ElsePin); } // Now go to the If branch { FBlueprintCompiledStatement& GotoThen = Context.AppendStatementForNode(Node); GotoThen.Type = KCST_UnconditionalGoto; GotoThen.LHS = *CondTerm; Context.GotoFixupRequestMap.Add(&GotoThen, ThenPin); } } else { CompilerContext.MessageLog.Error(*LOCTEXT("ResolveTermPassed_Error", "Failed to resolve term passed into @@").ToString(), CondPin); } } }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { UK2Node_FunctionEntry* EntryNode = CastChecked<UK2Node_FunctionEntry>(Node); //check(EntryNode->SignatureName != NAME_None); if (EntryNode->SignatureName == CompilerContext.GetSchema()->FN_ExecuteUbergraphBase) { UEdGraphPin* EntryPointPin = Node->FindPin(CompilerContext.GetSchema()->PN_EntryPoint); FBPTerminal** pTerm = Context.NetMap.Find(EntryPointPin); if ((EntryPointPin != NULL) && (pTerm != NULL)) { FBlueprintCompiledStatement& ComputedGotoStatement = Context.AppendStatementForNode(Node); ComputedGotoStatement.Type = KCST_ComputedGoto; ComputedGotoStatement.LHS = *pTerm; } else { CompilerContext.MessageLog.Error(*LOCTEXT("NoEntryPointPin_Error", "Expected a pin named EntryPoint on @@").ToString(), Node); } } else { // Generate the output impulse from this node GenerateSimpleThenGoto(Context, *Node); } }
void FKCHandler_ClearDelegate::Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) { UK2Node_BaseMCDelegate* DelegateNode = CastChecked<UK2Node_BaseMCDelegate>(Node); UEdGraphPin* SelfPin = CompilerContext.GetSchema()->FindSelfPin(*DelegateNode, EEdGraphPinDirection::EGPD_Input); check(SelfPin); TArray<UEdGraphPin*> Links = SelfPin->LinkedTo; if(!Links.Num()) { Links.Add(SelfPin); } for (auto NetIt = Links.CreateIterator(); NetIt; ++NetIt) { UEdGraphPin* NetPin = *NetIt; check(NetPin); FBlueprintCompiledStatement& AddStatement = Context.AppendStatementForNode(DelegateNode); AddStatement.Type = KCST_ClearMulticastDelegate; FBPTerminal** VarDelegate = InnerTermMap.Find(FDelegateOwnerId(NetPin, DelegateNode)); check(VarDelegate && *VarDelegate); AddStatement.LHS = *VarDelegate; } GenerateSimpleThenGoto(Context, *DelegateNode, DelegateNode->FindPin(CompilerContext.GetSchema()->PN_Then)); FNodeHandlingFunctor::Compile(Context, DelegateNode); }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { UK2Node_MakeArray* ArrayNode = CastChecked<UK2Node_MakeArray>(Node); UEdGraphPin* OutputPin = ArrayNode->GetOutputPin(); FBPTerminal** ArrayTerm = Context.NetMap.Find(OutputPin); check(ArrayTerm); FBlueprintCompiledStatement& CreateArrayStatement = Context.AppendStatementForNode(Node); CreateArrayStatement.Type = KCST_CreateArray; CreateArrayStatement.LHS = *ArrayTerm; for(auto PinIt = Node->Pins.CreateIterator(); PinIt; ++PinIt) { UEdGraphPin* Pin = *PinIt; if(Pin && Pin->Direction == EGPD_Input) { FBPTerminal** InputTerm = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(Pin)); if( InputTerm ) { CreateArrayStatement.RHS.Add(*InputTerm); } } } }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { const UK2Node_DelegateSet* DelegateNode = Cast<UK2Node_DelegateSet>(Node); check(DelegateNode); // Verify that the event has a darget to be bound to UEdGraphPin* DelegateOwnerPin = DelegateNode->GetDelegateOwner(); if (DelegateOwnerPin == nullptr || DelegateOwnerPin->LinkedTo.Num() == 0) { CompilerContext.MessageLog.Error(*FString(*LOCTEXT("FindDynamicallyBoundDelegate_Error", "Couldn't find target for dynamically bound delegate node @@").ToString()), DelegateNode); return; } FBPTerminal** pDelegateOwnerTerm = Context.NetMap.Find(DelegateOwnerPin); // Create a delegate name term FBPTerminal* DelegateNameTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); DelegateNameTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Name; DelegateNameTerm->Name = DelegateNode->GetDelegateTargetEntryPointName().ToString(); DelegateNameTerm->bIsLiteral = true; // Create a local delegate, which we can then add to the multicast delegate FBPTerminal* LocalDelegate = *LocalDelegateMap.Find(Node); FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); Statement.Type = KCST_Assignment; Statement.LHS = LocalDelegate; Statement.RHS.Add(DelegateNameTerm); // Add the local delegate to the MC delegate FBlueprintCompiledStatement& AddStatement = Context.AppendStatementForNode(Node); AddStatement.Type = KCST_AddMulticastDelegate; AddStatement.LHS = *pDelegateOwnerTerm; AddStatement.RHS.Add(LocalDelegate); GenerateSimpleThenGoto(Context, *Node, DelegateNode->FindPin(CompilerContext.GetSchema()->PN_Then)); FNodeHandlingFunctor::Compile(Context, Node); }
void InnerAssignment(FKismetFunctionContext& Context, UEdGraphNode* Node, UEdGraphPin* VariablePin, UEdGraphPin* ValuePin) { FBPTerminal** VariableTerm = Context.NetMap.Find(VariablePin); if (VariableTerm == NULL) { VariableTerm = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(VariablePin)); } FBPTerminal** ValueTerm = Context.LiteralHackMap.Find(ValuePin); if (ValueTerm == NULL) { ValueTerm = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(ValuePin)); } if ((VariableTerm != NULL) && (ValueTerm != NULL)) { FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); Statement.Type = KCST_Assignment; Statement.LHS = *VariableTerm; Statement.RHS.Add(*ValueTerm); if (!(*VariableTerm)->IsTermWritable()) { CompilerContext.MessageLog.Error(*LOCTEXT("WriteConst_Error", "Cannot write to const @@").ToString(), VariablePin); } } else { if (VariablePin != ValuePin) { CompilerContext.MessageLog.Error(*LOCTEXT("ResolveValueIntoVariablePin_Error", "Failed to resolve term @@ passed into @@").ToString(), ValuePin, VariablePin); } else { CompilerContext.MessageLog.Error(*LOCTEXT("ResolveTermPassed_Error", "Failed to resolve term passed into @@").ToString(), VariablePin); } } }
void FKCHandler_CreateDelegate::Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) { UK2Node_CreateDelegate * DelegateNode = CastChecked<UK2Node_CreateDelegate>(Node); check(NULL != DelegateNode); FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); Statement.Type = KCST_BindDelegate; { UEdGraphPin* OutPin = DelegateNode->GetDelegateOutPin(); check(NULL != OutPin); UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(OutPin); check(NULL != Net); FBPTerminal** FoundTerm = Context.NetMap.Find(Net); check(FoundTerm && *FoundTerm); Statement.LHS = *FoundTerm; } { FBPTerminal* DelegateNameTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); DelegateNameTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Name; DelegateNameTerm->Name = DelegateNode->GetFunctionName().ToString(); DelegateNameTerm->bIsLiteral = true; Statement.RHS.Add(DelegateNameTerm); } { UEdGraphPin* InputPin = DelegateNode->GetObjectInPin(); check(NULL != InputPin); UEdGraphPin* Net = FEdGraphUtilities::GetNetFromPin(InputPin); FBPTerminal** FoundTerm = Context.NetMap.Find(Net); check(FoundTerm && *FoundTerm); Statement.RHS.Add(*FoundTerm); } FNodeHandlingFunctor::Compile(Context, Node); }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { // Cast the node and get all the input pins UK2Node_Select* SelectNode = Cast<UK2Node_Select>(Node); TArray<UEdGraphPin*> OptionPins; SelectNode->GetOptionPins(OptionPins); UEdGraphPin* IndexPin = SelectNode->GetIndexPin(); // Get the kismet term for the (Condition or Index) that will determine which option to use UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(IndexPin); FBPTerminal** ConditionTerm = Context.NetMap.Find(PinToTry); // Get the kismet term for the return value UEdGraphPin* ReturnPin = SelectNode->GetReturnValuePin(); FBPTerminal** ReturnTerm = Context.NetMap.Find(ReturnPin); // Don't proceed if there is no return value or there is no selection if (ConditionTerm != NULL && ReturnTerm != NULL) { FName ConditionalFunctionName = ""; UClass* ConditionalFunctionClass = NULL; SelectNode->GetConditionalFunction(ConditionalFunctionName, &ConditionalFunctionClass); UFunction* ConditionFunction = FindField<UFunction>(ConditionalFunctionClass, ConditionalFunctionName); // Find the local boolean for use in the equality call function below (BoolTerm = result of EqualEqual_IntInt or NotEqual_BoolBool) FBPTerminal* BoolTerm = BoolTermMap.FindRef(SelectNode); // We need to keep a pointer to the previous IfNot statement so it can be linked to the next conditional statement FBlueprintCompiledStatement* PrevIfNotStatement = NULL; // Keep an array of all the unconditional goto statements so we can clean up their jumps after the noop statement is created TArray<FBlueprintCompiledStatement*> GotoStatementList; // Loop through all the options for (int32 OptionIdx = 0; OptionIdx < OptionPins.Num(); OptionIdx++) { // Create a CallFunction statement with the condition function from the Select class FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); Statement.Type = KCST_CallFunction; Statement.FunctionToCall = ConditionFunction; Statement.FunctionContext = NULL; Statement.bIsParentContext = false; // BoolTerm will be the return value of the condition statement Statement.LHS = BoolTerm; // The condition passed into the Select node Statement.RHS.Add(*ConditionTerm); // Create a local int for use in the equality call function below (LiteralTerm = the right hand side of the EqualEqual_IntInt or NotEqual_BoolBool statement) FBPTerminal* LiteralTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); LiteralTerm->bIsLiteral = true; LiteralTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Int; LiteralTerm->Name = FString::Printf(TEXT("%d"), OptionIdx); Statement.RHS.Add(LiteralTerm); // If there is a previous IfNot statement, hook this one to that one for jumping if (PrevIfNotStatement) { Statement.bIsJumpTarget = true; PrevIfNotStatement->TargetLabel = &Statement; } // Create a GotoIfNot statement using the BoolTerm from above as the condition FBlueprintCompiledStatement* IfNotStatement = &Context.AppendStatementForNode(Node); IfNotStatement->Type = KCST_GotoIfNot; IfNotStatement->LHS = BoolTerm; // Create an assignment statement FBlueprintCompiledStatement& AssignStatement = Context.AppendStatementForNode(Node); AssignStatement.Type = KCST_Assignment; AssignStatement.LHS = *ReturnTerm; // Get the kismet term from the option pin UEdGraphPin* OptionPinToTry = FEdGraphUtilities::GetNetFromPin(OptionPins[OptionIdx]); FBPTerminal** OptionTerm = Context.NetMap.Find(OptionPinToTry); if (!OptionTerm) { Context.MessageLog.Error(*LOCTEXT("Error_UnregisterOptionPin", "Unregister option pin @@").ToString(), OptionPins[OptionIdx]); return; } AssignStatement.RHS.Add(*OptionTerm); // Create an unconditional goto to exit the node FBlueprintCompiledStatement& GotoStatement = Context.AppendStatementForNode(Node); GotoStatement.Type = KCST_UnconditionalGoto; GotoStatementList.Add(&GotoStatement); // If this is the last IfNot statement, hook the jump to an error message if (OptionIdx == OptionPins.Num() - 1) { // Create a CallFunction statement for doing a print string of our error message FBlueprintCompiledStatement& PrintStatement = Context.AppendStatementForNode(Node); PrintStatement.Type = KCST_CallFunction; PrintStatement.bIsJumpTarget = true; FName PrintStringFunctionName = ""; UClass* PrintStringFunctionClass = NULL; SelectNode->GetPrintStringFunction(PrintStringFunctionName, &PrintStringFunctionClass); UFunction* PrintFunction = FindField<UFunction>(PrintStringFunctionClass, PrintStringFunctionName); PrintStatement.FunctionToCall = PrintFunction; PrintStatement.FunctionContext = NULL; PrintStatement.bIsParentContext = false; // Create a local int for use in the equality call function below (LiteralTerm = the right hand side of the EqualEqual_IntInt or NotEqual_BoolBool statement) FBPTerminal* LiteralStringTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); LiteralStringTerm->bIsLiteral = true; LiteralStringTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String; FString SelectionNodeType(TEXT("NONE")); if (IndexPin) { UEnum* EnumObject = Cast<UEnum>(IndexPin->PinType.PinSubCategoryObject.Get()); if (EnumObject != NULL) { SelectionNodeType = EnumObject->GetName(); } else { // Not an enum, so just use the basic type SelectionNodeType = IndexPin->PinType.PinCategory; } } const UEdGraph* OwningGraph = Context.MessageLog.FindSourceObjectTypeChecked<UEdGraph>( SelectNode->GetGraph() ); LiteralStringTerm->Name = FString::Printf(*LOCTEXT("SelectNodeIndexWarning", "Graph %s: Selection Node of type %s failed! Out of bounds indexing of the options. There are only %d options available.").ToString(), (OwningGraph) ? *OwningGraph->GetFullName() : TEXT("NONE"), *SelectionNodeType, OptionPins.Num()); PrintStatement.RHS.Add(LiteralStringTerm); // Hook the IfNot statement's jump target to this statement IfNotStatement->TargetLabel = &PrintStatement; } PrevIfNotStatement = IfNotStatement; } // Create a noop to jump to so the unconditional goto statements can exit the node after successful assignment FBlueprintCompiledStatement& NopStatement = Context.AppendStatementForNode(Node); NopStatement.Type = KCST_Nop; NopStatement.bIsJumpTarget = true; // Loop through the unconditional goto statements and fix their jump targets for (auto It = GotoStatementList.CreateConstIterator(); It; It++) { (*It)->TargetLabel = &NopStatement; } } }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { UK2Node_Switch* SwitchNode = CastChecked<UK2Node_Switch>(Node); FEdGraphPinType ExpectedExecPinType; ExpectedExecPinType.PinCategory = UEdGraphSchema_K2::PC_Exec; // Make sure that the input pin is connected and valid for this block UEdGraphPin* ExecTriggeringPin = Context.FindRequiredPinByName(SwitchNode, UEdGraphSchema_K2::PN_Execute, EGPD_Input); if ((ExecTriggeringPin == NULL) || !Context.ValidatePinType(ExecTriggeringPin, ExpectedExecPinType)) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("NoValidExecutionPinForSwitch_Error", "@@ must have a valid execution pin @@").ToString()), SwitchNode, ExecTriggeringPin); return; } // Make sure that the selection pin is connected and valid for this block UEdGraphPin* SelectionPin = SwitchNode->GetSelectionPin(); if ((SelectionPin == NULL) || !Context.ValidatePinType(SelectionPin, SwitchNode->GetPinType())) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("NoValidSelectionPinForSwitch_Error", "@@ must have a valid execution pin @@").ToString()), SwitchNode, SelectionPin); return; } // Find the boolean intermediate result term, so we can track whether the compare was successful FBPTerminal* BoolTerm = BoolTermMap.FindRef(SwitchNode); // Generate the output impulse from this node UEdGraphPin* SwitchSelectionNet = FEdGraphUtilities::GetNetFromPin(SelectionPin); FBPTerminal* SwitchSelectionTerm = Context.NetMap.FindRef(SwitchSelectionNet); if ((BoolTerm != NULL) && (SwitchSelectionTerm != NULL)) { UEdGraphNode* TargetNode = NULL; UEdGraphPin* FuncPin = SwitchNode->GetFunctionPin(); FBPTerminal* FuncContext = Context.NetMap.FindRef(FuncPin); UEdGraphPin* DefaultPin = SwitchNode->GetDefaultPin(); // Pull out function to use UClass* FuncClass = Cast<UClass>(FuncPin->PinType.PinSubCategoryObject.Get()); UFunction* FunctionPtr = FindField<UFunction>(FuncClass, *FuncPin->PinName); check(FunctionPtr); // Run thru all the output pins except for the default label for (auto PinIt = SwitchNode->Pins.CreateIterator(); PinIt; ++PinIt) { UEdGraphPin* Pin = *PinIt; if ((Pin->Direction == EGPD_Output) && (Pin != DefaultPin)) { // Create a term for the switch case value FBPTerminal* CaseValueTerm = new (Context.Literals) FBPTerminal(); CaseValueTerm->Name = Pin->PinName; CaseValueTerm->Type = SelectionPin->PinType; CaseValueTerm->SourcePin = Pin; CaseValueTerm->bIsLiteral = true; // Call the comparison function associated with this switch node FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(SwitchNode); Statement.Type = KCST_CallFunction; Statement.FunctionToCall = FunctionPtr; Statement.FunctionContext = FuncContext; Statement.bIsParentContext = false; Statement.LHS = BoolTerm; Statement.RHS.Add(SwitchSelectionTerm); Statement.RHS.Add(CaseValueTerm); // Jump to output if strings are actually equal FBlueprintCompiledStatement& IfFailTest_SucceedAtBeingEqualGoto = Context.AppendStatementForNode(SwitchNode); IfFailTest_SucceedAtBeingEqualGoto.Type = KCST_GotoIfNot; IfFailTest_SucceedAtBeingEqualGoto.LHS = BoolTerm; Context.GotoFixupRequestMap.Add(&IfFailTest_SucceedAtBeingEqualGoto, Pin); } } // Finally output default pin GenerateSimpleThenGoto(Context, *SwitchNode, DefaultPin); } else { CompilerContext.MessageLog.Error(*LOCTEXT("ResolveTermPassed_Error", "Failed to resolve term passed into @@").ToString(), SelectionPin); } }
void FKCHandler_DynamicCast::Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) { const UK2Node_DynamicCast* DynamicCastNode = CastChecked<UK2Node_DynamicCast>(Node); if (DynamicCastNode->TargetType == NULL) { CompilerContext.MessageLog.Error(*LOCTEXT("BadCastNoTargetType_Error", "Node @@ has an invalid target type, please delete and recreate it").ToString(), Node); } // Self Pin UEdGraphPin* SourceObjectPin = DynamicCastNode->GetCastSourcePin(); UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(SourceObjectPin); FBPTerminal** ObjectToCast = Context.NetMap.Find(PinToTry); if (!ObjectToCast) { ObjectToCast = Context.LiteralHackMap.Find(PinToTry); if (!ObjectToCast || !(*ObjectToCast)) { CompilerContext.MessageLog.Error(*LOCTEXT("InvalidConnectionOnNode_Error", "Node @@ has an invalid connection on @@").ToString(), Node, SourceObjectPin); return; } } // Output pin const UEdGraphPin* CastOutputPin = DynamicCastNode->GetCastResultPin(); if( !CastOutputPin ) { CompilerContext.MessageLog.Error(*LOCTEXT("InvalidDynamicCastClass_Error", "Node @@ has an invalid target class").ToString(), Node); return; } FBPTerminal** CastResultTerm = Context.NetMap.Find(CastOutputPin); if (!CastResultTerm || !(*CastResultTerm)) { CompilerContext.MessageLog.Error(*LOCTEXT("InvalidDynamicCastClass_CompilerError", "Node @@ has an invalid target class. (Inner compiler error?)").ToString(), Node); return; } // Create a literal term from the class specified in the node FBPTerminal* ClassTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); ClassTerm->Name = DynamicCastNode->TargetType->GetName(); ClassTerm->bIsLiteral = true; ClassTerm->Source = Node; ClassTerm->ObjectLiteral = DynamicCastNode->TargetType; ClassTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Class; UClass const* const InputObjClass = Cast<UClass>((*ObjectToCast)->Type.PinSubCategoryObject.Get()); UClass const* const OutputObjClass = Cast<UClass>((*CastResultTerm)->Type.PinSubCategoryObject.Get()); const bool bIsOutputInterface = ((OutputObjClass != NULL) && OutputObjClass->HasAnyClassFlags(CLASS_Interface)); const bool bIsInputInterface = ((InputObjClass != NULL) && InputObjClass->HasAnyClassFlags(CLASS_Interface)); EKismetCompiledStatementType CastOpType = KCST_DynamicCast; if (bIsInputInterface) { if (bIsOutputInterface) { CastOpType = KCST_CrossInterfaceCast; } else { CastOpType = KCST_CastInterfaceToObj; } } else if (bIsOutputInterface) { CastOpType = KCST_CastObjToInterface; } if (KCST_MetaCast == CastType) { if (bIsInputInterface || bIsOutputInterface) { CompilerContext.MessageLog.Error(*LOCTEXT("InvalidClassDynamicCastClass_Error", "Node @@ has an invalid target class. Interfaces are not supported.").ToString(), Node); return; } CastOpType = KCST_MetaCast; } // Cast Statement FBlueprintCompiledStatement& CastStatement = Context.AppendStatementForNode(Node); CastStatement.Type = CastOpType; CastStatement.LHS = *CastResultTerm; CastStatement.RHS.Add(ClassTerm); CastStatement.RHS.Add(*ObjectToCast); FBPTerminal** BoolSuccessTerm = nullptr; if (UEdGraphPin* BoolSuccessPin = DynamicCastNode->GetBoolSuccessPin()) { BoolSuccessTerm = Context.NetMap.Find(BoolSuccessPin); } else { BoolSuccessTerm = BoolTermMap.Find(DynamicCastNode); } check(BoolSuccessTerm != nullptr); // Check result of cast statement FBlueprintCompiledStatement& CheckResultStatement = Context.AppendStatementForNode(Node); CheckResultStatement.Type = KCST_ObjectToBool; CheckResultStatement.LHS = *BoolSuccessTerm; CheckResultStatement.RHS.Add(*CastResultTerm); UEdGraphPin* SuccessExecPin = DynamicCastNode->GetValidCastPin(); bool const bIsPureCast = (SuccessExecPin == nullptr); if (!bIsPureCast) { UEdGraphPin* FailurePin = DynamicCastNode->GetInvalidCastPin(); check(FailurePin != nullptr); // Failure condition...skip to the failed output FBlueprintCompiledStatement& FailCastGoto = Context.AppendStatementForNode(Node); FailCastGoto.Type = KCST_GotoIfNot; FailCastGoto.LHS = *BoolSuccessTerm; Context.GotoFixupRequestMap.Add(&FailCastGoto, FailurePin); // Successful cast...hit the success output node FBlueprintCompiledStatement& SuccessCastGoto = Context.AppendStatementForNode(Node); SuccessCastGoto.Type = KCST_UnconditionalGoto; SuccessCastGoto.LHS = *BoolSuccessTerm; Context.GotoFixupRequestMap.Add(&SuccessCastGoto, SuccessExecPin); } }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override { UEdGraphPin* InputPin = NULL; for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) { UEdGraphPin* Pin = Node->Pins[PinIndex]; if (Pin && (EGPD_Input == Pin->Direction)) { InputPin = Pin; break; } } UEdGraphPin *InNet = FEdGraphUtilities::GetNetFromPin(InputPin); UClass *Class = Cast<UClass>(StaticLoadObject(UClass::StaticClass(), NULL, TEXT("class'VaRestPlugin.VaRestJsonObject'"))); FBPTerminal **SourceTerm = Context.NetMap.Find(InNet); if (SourceTerm == nullptr) { return; } for (int32 PinIndex = 0; PinIndex < Node->Pins.Num(); ++PinIndex) { UEdGraphPin* Pin = Node->Pins[PinIndex]; if (Pin && (EGPD_Output == Pin->Direction)) { if (Pin->LinkedTo.Num() < 1) { continue; } FBPTerminal **Target = Context.NetMap.Find(Pin); const FString &FieldName = Pin->PinName; const FString &FieldType = Pin->PinType.PinCategory; FBPTerminal* FieldNameTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); FieldNameTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String; FieldNameTerm->Source = Pin; FieldNameTerm->Name = FieldName; FieldNameTerm->TextLiteral = FText::FromString(FieldName); FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); FName FunctionName; bool bIsArray = Pin->PinType.bIsArray; if (FieldType == CompilerContext.GetSchema()->PC_Boolean) { FunctionName = bIsArray ? TEXT("GetBoolArrayField") : TEXT("GetBoolField"); } else if (FieldType == CompilerContext.GetSchema()->PC_Float) { FunctionName = bIsArray ? TEXT("GetNumberArrayField") : TEXT("GetNumberField"); } else if (FieldType == CompilerContext.GetSchema()->PC_String) { FunctionName = bIsArray ? TEXT("GetStringArrayField") : TEXT("GetStringField"); } else if (FieldType == CompilerContext.GetSchema()->PC_Object) { FunctionName = bIsArray ? TEXT("GetObjectArrayField") : TEXT("GetObjectField"); } else { continue; } UFunction *FunctionPtr = Class->FindFunctionByName(FunctionName); Statement.Type = KCST_CallFunction; Statement.FunctionToCall = FunctionPtr; Statement.FunctionContext = *SourceTerm; Statement.bIsParentContext = false; Statement.LHS = *Target; Statement.RHS.Add(FieldNameTerm); } } }
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) { ///////////////////////////////////////////////////////////////////////////////////// // Get the node, retrieve the helper functions, and create a local "Index" variable ///////////////////////////////////////////////////////////////////////////////////// // Get the multi gate node and the helper functions UK2Node_MultiGate* GateNode = Cast<UK2Node_MultiGate>(Node); // Get function names and class pointers to helper nodes FName MarkBitFunctionName = ""; UClass* MarkBitFunctionClass = NULL; GateNode->GetMarkBitFunction(MarkBitFunctionName, &MarkBitFunctionClass); UFunction* MarkBitFunction = FindField<UFunction>(MarkBitFunctionClass, MarkBitFunctionName); FName HasUnmarkedBitFunctionName = ""; UClass* HasUnmarkedBitFunctionClass = NULL; GateNode->GetHasUnmarkedBitFunction(HasUnmarkedBitFunctionName, &HasUnmarkedBitFunctionClass); UFunction* HasUnmarkedBitFunction = FindField<UFunction>(HasUnmarkedBitFunctionClass, HasUnmarkedBitFunctionName); FName GetUnmarkedBitFunctionName = ""; UClass* GetUnmarkedBitFunctionClass = NULL; GateNode->GetUnmarkedBitFunction(GetUnmarkedBitFunctionName, &GetUnmarkedBitFunctionClass); UFunction* GetUnmarkedBitFunction = FindField<UFunction>(GetUnmarkedBitFunctionClass, GetUnmarkedBitFunctionName); FName ConditionalFunctionName = ""; UClass* ConditionalFunctionClass = NULL; GateNode->GetConditionalFunction(ConditionalFunctionName, &ConditionalFunctionClass); UFunction* ConditionFunction = FindField<UFunction>(ConditionalFunctionClass, ConditionalFunctionName); FName EqualityFunctionName = ""; UClass* EqualityFunctionClass = NULL; GateNode->GetEqualityFunction(EqualityFunctionName, &EqualityFunctionClass); UFunction* EqualityFunction = FindField<UFunction>(EqualityFunctionClass, EqualityFunctionName); FName BoolNotEqualFunctionName = ""; UClass* BoolNotEqualFunctionClass = NULL; GateNode->GetBoolNotEqualFunction(BoolNotEqualFunctionName, &BoolNotEqualFunctionClass); UFunction* BoolNotEqualFunction = FindField<UFunction>(BoolNotEqualFunctionClass, BoolNotEqualFunctionName); FName PrintStringFunctionName = ""; UClass* PrintStringFunctionClass = NULL; GateNode->GetPrintStringFunction(PrintStringFunctionName, &PrintStringFunctionClass); UFunction* PrintFunction = FindField<UFunction>(PrintStringFunctionClass, PrintStringFunctionName); FName ClearBitsFunctionName = ""; UClass* ClearBitsFunctionClass = NULL; GateNode->GetClearAllBitsFunction(ClearBitsFunctionName, &ClearBitsFunctionClass); UFunction* ClearBitsFunction = FindField<UFunction>(ClearBitsFunctionClass, ClearBitsFunctionName); // Find the data terms if there is already a data node from expansion phase FBPTerminal* DataTerm = NULL; if (GateNode->DataNode) { UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->DataNode->GetVariablePin()); FBPTerminal** DataTermPtr = Context.NetMap.Find(PinToTry); DataTerm = (DataTermPtr != NULL) ? *DataTermPtr : NULL; } // Else we built it in the net registration, so find it else { DataTerm = DataTermMap.FindRef(GateNode); } check(DataTerm); // Used for getting all the nets from pins UEdGraphPin* PinToTry = NULL; // The StartIndex passed into the multi gate node PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->GetStartIndexPin()); FBPTerminal** StartIndexPinTerm = Context.NetMap.Find(PinToTry); // Get the bRandom pin as a kismet term from the multi gate node PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->GetIsRandomPin()); FBPTerminal** RandomTerm = Context.NetMap.Find(PinToTry); // Get the Loop pin as a kismet term from the multi gate node PinToTry = FEdGraphUtilities::GetNetFromPin(GateNode->GetLoopPin()); FBPTerminal** LoopTerm = Context.NetMap.Find(PinToTry); // Find the local boolean for use in determining if this is the first run of the node or not FBPTerminal* FirstRunBoolTerm = FirstRunTermMap.FindRef(GateNode); // Create a literal pin that represents a -1 value FBPTerminal* InvalidIndexTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); InvalidIndexTerm->bIsLiteral = true; InvalidIndexTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Int; InvalidIndexTerm->Name = TEXT("-1"); // Create a literal pin that represents a true value FBPTerminal* TrueBoolTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); TrueBoolTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Boolean; TrueBoolTerm->bIsLiteral = true; TrueBoolTerm->Name = TEXT("true"); // Get the out pins and create a literal describing how many logical outs there are TArray<UEdGraphPin*> OutPins; GateNode->GetOutPins(OutPins); FBPTerminal* NumOutsTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); NumOutsTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Int; NumOutsTerm->bIsLiteral = true; NumOutsTerm->Name = FString::Printf(TEXT("%d"), OutPins.Num()); /////////////////////////////////////////////////// // See if this is the first time in /////////////////////////////////////////////////// FFunctionScopedTerms& FuncLocals = FunctionTermMap.FindChecked(Context.Function); check(FuncLocals.GenericBoolTerm != nullptr); // (bIsNotFirstTime != true) FBlueprintCompiledStatement& BoolNotEqualStatement = Context.AppendStatementForNode(Node); BoolNotEqualStatement.Type = KCST_CallFunction; BoolNotEqualStatement.FunctionToCall = BoolNotEqualFunction; BoolNotEqualStatement.FunctionContext = NULL; BoolNotEqualStatement.bIsParentContext = false; // Set the params BoolNotEqualStatement.LHS = FuncLocals.GenericBoolTerm; BoolNotEqualStatement.RHS.Add(FirstRunBoolTerm); BoolNotEqualStatement.RHS.Add(TrueBoolTerm); // if (bIsNotFirstTime == false) // { FBlueprintCompiledStatement& IfFirstTimeStatement = Context.AppendStatementForNode(Node); IfFirstTimeStatement.Type = KCST_GotoIfNot; IfFirstTimeStatement.LHS = FuncLocals.GenericBoolTerm; /////////////////////////////////////////////////////////////////// // This is the first time in... set the bool and the start index /////////////////////////////////////////////////////////////////// // bIsNotFirstTime = true; FBlueprintCompiledStatement& AssignBoolStatement = Context.AppendStatementForNode(Node); AssignBoolStatement.Type = KCST_Assignment; AssignBoolStatement.LHS = FirstRunBoolTerm; AssignBoolStatement.RHS.Add(TrueBoolTerm); ////////////////////////////////////////////////////////////////////// // See if the StartIndex is greater than -1 (they supplied an index) ////////////////////////////////////////////////////////////////////// // (StartIndex > -1) FBlueprintCompiledStatement& Statement = Context.AppendStatementForNode(Node); Statement.Type = KCST_CallFunction; Statement.FunctionToCall = ConditionFunction; Statement.FunctionContext = NULL; Statement.bIsParentContext = false; Statement.LHS = FuncLocals.GenericBoolTerm; Statement.RHS.Add(*StartIndexPinTerm); Statement.RHS.Add(InvalidIndexTerm); // if (StartIndex > -1) // { FBlueprintCompiledStatement& IfHasIndexStatement = Context.AppendStatementForNode(Node); IfHasIndexStatement.Type = KCST_GotoIfNot; IfHasIndexStatement.LHS = FuncLocals.GenericBoolTerm; /////////////////////////////////////////////////////////////////// // They supplied a start index so set the index to it /////////////////////////////////////////////////////////////////// // Index = StartIndex; // (StartIndex is from multi gate pin for it) FBlueprintCompiledStatement& AssignSuppliedIndexStatement = Context.AppendStatementForNode(Node); AssignSuppliedIndexStatement.Type = KCST_Assignment; AssignSuppliedIndexStatement.LHS = FuncLocals.IndexTerm; AssignSuppliedIndexStatement.RHS.Add(*StartIndexPinTerm); // Jump to index usage FBlueprintCompiledStatement& ElseGotoIndexUsageStatement = Context.AppendStatementForNode(Node); ElseGotoIndexUsageStatement.Type = KCST_UnconditionalGoto; // } // else // { /////////////////////////////////////////////////////////////////// // They did NOT supply a start index so figure one out /////////////////////////////////////////////////////////////////// check(FuncLocals.IndexTerm != nullptr); // Index = GetUnmarkedBit(Data, -1, bRandom); FBlueprintCompiledStatement& GetStartIndexStatement = Context.AppendStatementForNode(Node); GetStartIndexStatement.Type = KCST_CallFunction; GetStartIndexStatement.FunctionToCall = GetUnmarkedBitFunction; GetStartIndexStatement.bIsParentContext = false; GetStartIndexStatement.LHS = FuncLocals.IndexTerm; GetStartIndexStatement.RHS.Add(DataTerm); GetStartIndexStatement.RHS.Add(*StartIndexPinTerm); GetStartIndexStatement.RHS.Add(NumOutsTerm); GetStartIndexStatement.RHS.Add(*RandomTerm); // Hook the IfHasIndexStatement jump to this node GetStartIndexStatement.bIsJumpTarget = true; IfHasIndexStatement.TargetLabel = &GetStartIndexStatement; // Jump to index usage FBlueprintCompiledStatement& StartIndexGotoIndexUsageStatement = Context.AppendStatementForNode(Node); StartIndexGotoIndexUsageStatement.Type = KCST_UnconditionalGoto; // } // } // else // { //////////////////////////////////////////////////////////////////////////// // Else this is NOT the first time in, see if there is an available index //////////////////////////////////////////////////////////////////////////// // (HasUnmarkedBit()) FBlueprintCompiledStatement& IsAvailableStatement = Context.AppendStatementForNode(Node); IsAvailableStatement.Type = KCST_CallFunction; IsAvailableStatement.FunctionToCall = HasUnmarkedBitFunction; IsAvailableStatement.FunctionContext = NULL; IsAvailableStatement.bIsParentContext = false; IsAvailableStatement.LHS = FuncLocals.GenericBoolTerm; IsAvailableStatement.RHS.Add(DataTerm); IsAvailableStatement.RHS.Add(NumOutsTerm); // Hook the IfFirstTimeStatement jump to this node IsAvailableStatement.bIsJumpTarget = true; IfFirstTimeStatement.TargetLabel = &IsAvailableStatement; // if (HasUnmarkedBit()) // { FBlueprintCompiledStatement& IfIsAvailableStatement = Context.AppendStatementForNode(Node); IfIsAvailableStatement.Type = KCST_GotoIfNot; IfIsAvailableStatement.LHS = FuncLocals.GenericBoolTerm; //////////////////////////////////////////////////////////////////////////// // Has available index so figure it out and jump to its' usage //////////////////////////////////////////////////////////////////////////// // Index = GetUnmarkedBit(Data, -1, bRandom) FBlueprintCompiledStatement& GetNextIndexStatement = Context.AppendStatementForNode(Node); GetNextIndexStatement.Type = KCST_CallFunction; GetNextIndexStatement.FunctionToCall = GetUnmarkedBitFunction; GetNextIndexStatement.bIsParentContext = false; GetNextIndexStatement.LHS = FuncLocals.IndexTerm; GetNextIndexStatement.RHS.Add(DataTerm); GetNextIndexStatement.RHS.Add(*StartIndexPinTerm); GetNextIndexStatement.RHS.Add(NumOutsTerm); GetNextIndexStatement.RHS.Add(*RandomTerm); // Goto Index usage FBlueprintCompiledStatement& GotoIndexUsageStatement = Context.AppendStatementForNode(Node); GotoIndexUsageStatement.Type = KCST_UnconditionalGoto; // } // else // { //////////////////////////////////////////////////////////////////////////// // No available index, see if we can loop //////////////////////////////////////////////////////////////////////////// // if (bLoop) FBlueprintCompiledStatement& IfLoopingStatement = Context.AppendStatementForNode(Node); IfLoopingStatement.Type = KCST_GotoIfNot; IfLoopingStatement.LHS = *LoopTerm; IfLoopingStatement.bIsJumpTarget = true; IfIsAvailableStatement.TargetLabel = &IfLoopingStatement; // { //////////////////////////////////////////////////////////////////////////// // Reset the data and jump back up to "if (HasUnmarkedBit())" //////////////////////////////////////////////////////////////////////////// // Clear the data // Data = 0; FBlueprintCompiledStatement& ClearDataStatement = Context.AppendStatementForNode(Node); ClearDataStatement.Type = KCST_CallFunction; ClearDataStatement.FunctionToCall = ClearBitsFunction; ClearDataStatement.bIsParentContext = false; ClearDataStatement.RHS.Add(DataTerm); // Goto back up to attempt an index again FBlueprintCompiledStatement& RetryStatement = Context.AppendStatementForNode(Node); RetryStatement.Type = KCST_UnconditionalGoto; IsAvailableStatement.bIsJumpTarget = true; RetryStatement.TargetLabel = &IsAvailableStatement; // } // else // { //////////////////////////////////////////////////////////////////////////// // Dead... Jump to end of thread //////////////////////////////////////////////////////////////////////////// FBlueprintCompiledStatement& NoLoopStatement = Context.AppendStatementForNode(Node); NoLoopStatement.Type = KCST_EndOfThread; NoLoopStatement.bIsJumpTarget = true; IfLoopingStatement.TargetLabel = &NoLoopStatement; // } // } // } ////////////////////////////////////// // We have a valid index so mark it ////////////////////////////////////// // MarkBit(Data, Index); FBlueprintCompiledStatement& MarkIndexStatement = Context.AppendStatementForNode(Node); MarkIndexStatement.Type = KCST_CallFunction; MarkIndexStatement.FunctionToCall = MarkBitFunction; MarkIndexStatement.bIsParentContext = false; MarkIndexStatement.LHS = FuncLocals.IndexTerm; MarkIndexStatement.RHS.Add(DataTerm); MarkIndexStatement.RHS.Add(FuncLocals.IndexTerm); // Setup jump label MarkIndexStatement.bIsJumpTarget = true; GotoIndexUsageStatement.TargetLabel = &MarkIndexStatement; ElseGotoIndexUsageStatement.TargetLabel = &MarkIndexStatement; StartIndexGotoIndexUsageStatement.TargetLabel = &MarkIndexStatement; ///////////////////////////////////////////////////////////////////////// // We have a valid index so mark it, then find the correct exec out pin ///////////////////////////////////////////////////////////////////////// // Call the correct exec pin out of the multi gate node FBlueprintCompiledStatement* PrevIndexEqualityStatement = NULL; FBlueprintCompiledStatement* PrevIfIndexMatchesStatement = NULL; for (int32 OutIdx = 0; OutIdx < OutPins.Num(); OutIdx++) { // (Index == OutIdx) FBlueprintCompiledStatement& IndexEqualityStatement = Context.AppendStatementForNode(Node); IndexEqualityStatement.Type = KCST_CallFunction; IndexEqualityStatement.FunctionToCall = EqualityFunction; IndexEqualityStatement.FunctionContext = NULL; IndexEqualityStatement.bIsParentContext = false; // LiteralIndexTerm will be the right side of the == statemnt FBPTerminal* LiteralIndexTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); LiteralIndexTerm->bIsLiteral = true; LiteralIndexTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_Int; LiteralIndexTerm->Name = FString::Printf(TEXT("%d"), OutIdx); // Set the params IndexEqualityStatement.LHS = FuncLocals.GenericBoolTerm; IndexEqualityStatement.RHS.Add(FuncLocals.IndexTerm); IndexEqualityStatement.RHS.Add(LiteralIndexTerm); // if (Index == OutIdx) FBlueprintCompiledStatement& IfIndexMatchesStatement = Context.AppendStatementForNode(Node); IfIndexMatchesStatement.Type = KCST_GotoIfNot; IfIndexMatchesStatement.LHS = FuncLocals.GenericBoolTerm; // { ////////////////////////////////////// // Found a match - Jump there ////////////////////////////////////// GenerateSimpleThenGoto(Context, *GateNode, OutPins[OutIdx]); // } // else // { //////////////////////////////////////////////////// // Not a match so loop will attempt the next index //////////////////////////////////////////////////// if (PrevIndexEqualityStatement && PrevIfIndexMatchesStatement) { // Attempt next index IndexEqualityStatement.bIsJumpTarget = true; PrevIfIndexMatchesStatement->TargetLabel = &IndexEqualityStatement; } // } PrevIndexEqualityStatement = &IndexEqualityStatement; PrevIfIndexMatchesStatement = &IfIndexMatchesStatement; } check(PrevIfIndexMatchesStatement); // Should have jumped to proper index, print error (should never happen) // Create a CallFunction statement for doing a print string of our error message FBlueprintCompiledStatement& PrintStatement = Context.AppendStatementForNode(Node); PrintStatement.Type = KCST_CallFunction; PrintStatement.bIsJumpTarget = true; PrintStatement.FunctionToCall = PrintFunction; PrintStatement.FunctionContext = NULL; PrintStatement.bIsParentContext = false; // Create a local int for use in the equality call function below (LiteralTerm = the right hand side of the EqualEqual_IntInt or NotEqual_BoolBool statement) FBPTerminal* LiteralStringTerm = Context.CreateLocalTerminal(ETerminalSpecification::TS_Literal); LiteralStringTerm->bIsLiteral = true; LiteralStringTerm->Type.PinCategory = CompilerContext.GetSchema()->PC_String; LiteralStringTerm->Name = FString::Printf(*LOCTEXT("MultiGateNode IndexWarning", "MultiGate Node failed! Out of bounds indexing of the out pins. There are only %d outs available.").ToString(), OutPins.Num()); PrintStatement.RHS.Add(LiteralStringTerm); // Hook the IfNot statement's jump target to this statement PrevIfIndexMatchesStatement->TargetLabel = &PrintStatement; }